jsonb and nested hstore

Started by Andrew Dunstanalmost 12 years ago440 messages
#1Andrew Dunstan
andrew@dunslane.net
2 attachment(s)

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and compressed
them. The jsonb patch should work standalone. The nested hstore patch
depends on it.

All the jsonb functions now use the jsonb API - there is no more turning
jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up some
jsonb docs.

cheers

andrew

Attachments:

jsonb-5.patch.gzapplication/x-gzip; name=jsonb-5.patch.gzDownload
nested-hstore-5.patch.gzapplication/x-gzip; name=nested-hstore-5.patch.gzDownload
����Rnested-hstore-5.patch�\�s7����+P��J20f�>9v��W�8e'U�J�$�)���d��k��C����������_��xph��������3;N�}s��LGc�����N�������@��&��1���*�(UB4���6��f��U�����[i��"6��`�=���O��I<�^^]���o�����;���r����X=�v�����7>�5��Z��"3���{0��,l�}.[�����������A�����?�0�.9'�D�����_�//���hv>��9��'������]^���K�}��m�;�F���E�F��'?����W�^|w�{��G?<�_����;����kVw�b������W���]�����h���mVV��/�!�&�k���4�/�<}���
����2���wd���O�F@z�����'�"L;Sm:6{h��eh-YJI���{?8����\|���W
�����/���gOA�U�7�n�0����������\�����l�s�����\;O��d���l2����t#���#��,i��]=��|� ~��8,e��eW��=���W���GO_��9i��������{d^��K�N�������=@y|I�a��
�������]'ljh8?�1�`\��T�a�R��>��a�,��3���
��,k1�H��h,����6�K6�C63����/^=����#�_>�8�B9�����g�v���,��9�89�4��,��:�*+���]h9�������z�	�
����������L{�<����>��<{F�F����e�������8�h��j�5�BV��������u����Iot=p��xp�\'0�@\�a��
/�}(zPYD�G��������N��q�����}���� JUP!��"yq]���;$�����)N������w����)��Bx}Z�C��V�����s�P���W`�|�����U��&��KwXu�x(�o�%��x��esW'S=���`��
�����T�����~�)�i��s�g�T��{��6��%-%3�UI��Y�J��JQ�(U�e&Q�+QJ�%	�LQd%J�oZ�u����D��+�"I�F	�
Jx�rkJ�����(AM`�d�s&��^�
�4`!3e�D�a�`�'	��9�U�9/<��FJ���
i�$�)[�QB]X�-8�C�k��J���$!(�V >*�JY��MC�T���YIdFyN-�+�*�����
��?�U���'	K3/�+���8u����T�L��F�"���^
���1�t�����R�$�<��^!�X�c��u�:��i�X�dYIp���j�+sJ3��.��@�LpT1��3"�(�Lf��l0��%�f
����$
�MQ">2PYq����o5��"Ir[ >�P��q��p�o�R�e��H�����d�}�����'��93���K�QF2��&x��?_���,�#/hnK���@i(3�s�h�������`�yP�B�P�P`-�8������W*��&~$CP/��*��?Hg�A�`Vr�)��RUJ�z��L��+8-�+d������">������[a�7���k��y���s��Ns'���,��d�U����D{3yj�Ki��MF}���.h)���������+�� B���T	������g�T^�sZQ�a��Tp���YF3Hx���T+�?����M�����e������?�)�,C�lI-���!sU�Q/�*�`�T�	��Y��^��<J���"S��D��������0��4Y����������6��[j��:�#�v���&(`����a�9^&~x���3��t���F|h
-Jd(Y$~�j�aho)��
�3u�)���VZj����6P��PB�q��R�9���8R��RI$���"��JaVz����*��<$~�\q�K�[P%R(Q*��qS^�|�G�N0�#>E�2�F	�t��D���6�#��tP�P�����!"^`�!i�]�G�����2���p������e��c��<��Zy��d�H
#J�gJ$~8�8b�TC��8g�fB�O0* &���������8gL��B	�J���k3Y�z
L���a0y	�k�MB\<��$,�y^�^c�9���Ls��&%!��a���aQ�7n�����Kk2���H�F|�tJ��a�)����q^{���&2h��*�#>�TZg%�G�`�$,�<�h�74&?��s�y��GS�H�������k�q�
��h��&@�w��%��������:��A��q������.����T�������E�?
f<�	J������G�Xar���q���#>!P�r�M)���c�Y,�F&~A�H��� ����fBp�.D�G*h��)���OE�I�/+�ze6�C�\)��+��%JU�\����.�C{��h�������j�B����	����D{�7&�g�nC��A	�!����k������K�0+�:��F;
E����������*���\�r����r�)��q�NAp)��k�)?`�>e���`�5�&5�Y#������PD~�q?.YW)�6.S.&��U��������I�_q�������?ON��zz?n�����t�*^A��;w~���p���;���G�q�%v/�hLNbc���-rm`�}�h���f��4*ZZU����a9z=��=��c�-���-��m[��
��E�~�"�m�����_��0nm%������kZ%�D&����qz����T�?L�xX�N���7)���A�e���	X�r�6��������>��C����_U�a���?\y����z����R�8c�����U��l�y�SS�	]� FVO�4�����8���Sr���������������79��h�� '��G�O��\������Z8�'��hM*A����il.k����B�5b�����������=����R��4�/2�R�1�!�- �G�|��E({L��9���c
�����%U!��S]�DiFPJ�=���k���HR}*�t�h�}��#A�G-rd�Yv��-��`����<�e��Scrd��g�f��&c��L����M��dr�{_���Mv�&�����[�X�A��u�6:�-6[<$�����~~mG�����`Q�@l�����[`6�Z����[|+io��~��K��������b�jq�������?e�����"NC�����:����-�HS�"oF�C�.N�����������/������?~���=}6K>Cu�^��vf������7OO��5���f�������V4�u�S���!����T�D�����_~�o���������g��G�e������e�2�|7�)1U�V	D�9wS���`iy��'�^�J)������2��C���ne�P=T3M��y��j���4tn�RN
Dk���jF��`�( �3I�u(���j�e��wn2lwf��9�a����r)����
c������E&�9P��N��~
��^����k}����m�����lt�����c��5�?��M�����������x�8�!M%��X�)������u�����_S��(��^�R���J�V�AZ�"��,h�&(���1&o�����[l��H��?�+%xc�v�Um�����p����x��4,���6l�h}|zg5}�]�{I��?����o�|����`�������^7��n�M��vPQ�h#*�]lc
�Ow����m�z�jX�-��[X>���.u�n�{K�/W����j�^	��@�~�T�hZc������
����d���d�����b����ep���7�����������Xm��xA��o�v�]�5�w�~�bt�;�F���]Y�k]�e7���.ZU�gM��N�j�T�<n��\.m��(���,�!�w���,�������M�6'=�NI�.��;=��p�a4#Y�����������n��m���U���[V>{M�v���yt��x������u:�����5�/���������)���d
��I|#�w��GS|��A_��5B:+0�����tM�wqQQ�#'z�r���;�&Py��}�"�Taj�`A,������Qx����[�Q��$[�gKZ��h{.�P���xFk�l��;���K�d�+A	��bqkw�4���!��h�k���I��!�����B�]_���_����v	f� /�����C�������f���S����/Af+X���pzrw-��������2^��W��(��?�Dv��8�K_�4_>y���������O����T/������y���GF�E��v����*�MW���B��<�e�a�/�����t��k�?N;o��UcNcU}?zOb(��=���^��Y�����L�C8��2��F��jb{��	,w�5��:��xN��������==��nw�v���C����?<�(^���W�0��J��Y������������Hv�%��(D]����s�?��?�?4��@�,�j8rp���G�
�����Y++��1�}w���&K��f|��u�j����>������D}��	D��)`�]�{�����i�s}_`NQk�1�g5�@���������d�-R�*����K��W��HE��}2����E�}r|�	��#R
0p 8��cq������s,8��"�����S����)�^@��Y6fg��gB{|���`Zr�M�I���J�&�PM����/6F\�U7^�����9��~~���;��fB#��u8RN ���DJ�!���?jk��LO������q�@�����/A�P��X���G����������F�/+�u���?#�@�w�JV.�"��[��<��|\�8�|��89	,m�Nn�MY����-����{��6rda�o�)4��`�6��\	d3	3��L�d������m7����!a�g�"��n�����N���R�T*�J��n69ggoE���h[m�e'�
�7��2h��f�pCM��
j�IG�T���'C�����:`~4��?��Dd�gS������O�8�U�z��>Y��r�q�1���Zo�����p�M�4
M�m��C�em�C�1�;���a^�dR#+���>�'� \�L����n��o�un.�h��:��P���\�O�do���yd���kI�,9��^�Q��(�B		�"q&�K6����B�8jN�����0���B�X_?y�;y�=�PQ���xFh�X���n?@��������GbzJ�5������&v�_~�g8y�#''���k>�i��n������O�,��q"UjVd`	�>}�g�N�}��|���^}A�-�Z`T�n��t��^kg�
���Ob����H!y�O�����|���>���;�_��H� �<�{_���R����h��g|=:a���;jb�1�&�����
�O1[�y�5�

��X+���c3����L/0Fn�e�S�t�'���y�l��n�,s�����%
�'��&V��v��H��F[�vm��XDv�L�i�?�5�.ZmH��u]<�q����7���#w��u����i�k6qs!F�i���a�&�3��v�	_���\�,�WY��8�L^'>�z���F���vbCk��$�u�>q�� �#��� �8�E�r1I��B�PI�6�43���u�jMvJ���el��E$��R�0������	x�J�M&��MMF_������H��@T��	�fi�$3?���?4&�T���I�+0�Sl�O"�,E��`��(:[�9��P+	>]�%��x9���uS����sK9g������b��M���ZL�9)��5����Q����+"gi�"�����8
eh�� )r:9j%gu!<K��b����t��I�����ES�3����j�;���"���=���&7~}�N���xO���!�9���"��7�u��	�_`<[Z[~��w��]���	NN5/�O�4����~]���C�2���x����n���
�?�������m
���So�"3��la�oy���;b����W���l�}r7t�����8�{~�'����j�N�0W������gr*z�.m��]9[5�_����Go���x����!_� ��R����Z�B�{l�;���b�O�c��S��r}�>�����/Q�1�0����W����xT����UkB�Zw�GZ<�9_?�y�o�vg��:s���+�W�W����~��B�r��xGs:;����q;N����h61o� ���)k9�!]�0M��MK���K�[Y��"�n���0��]�v<?�����=q���-�S@-P&3���(�=��xx��� �R��#��l�YL7��{��� !7I��I�K���p�9�����v]��� 6��[�����t9�+�.ih��K���>/����o,�d���("����@6Y��g,7�r���$��=���J�B���Z�PF6n�����k��7�q�VRr�`����?�OM���.��OF@�(�w��1}�fL���7��jK�f(��=����%c�n����Na�8�����D�/L��3I<q����9qo�2����LX]/��������J�E�����c�Q^>"dH�.�(����i�s�B�b�_
��tt����D2�5����{�;�����u����O^f�M�'F�l����[����������gR"F�F�`3���B
d����'h��Fst�nY�����ZTS���,&�����V�������%%l�9������;w4�G��@=�px1}���S|Q�IY}�[��
��[u�[�C��Q��V�T��h���I��:�u�� �p�N;������V�����8���i����7�O���G��h�2(nv,�CzB��XBC����l8�����
t"
o����L+�[Q3*'�f�������$�2��m{�e�m���n*Q
��X�����Y���p.��}��*G���Q�Y���	��t\��KW�|�`�������e�}ne��yxx�O�����wTh*l�_�N3�-��*�I�����7T!�#-Q��`=�zmc������s��}>��T�����5��}�v�����g��Xm5��6�Z�8������|�Q�z��|�Ag�tC�(���������[�u[�����f��W�y��"g6����������94dy��9#�sg�O��`.�L��vf��9�qwX������,���,��_[�5��k~)�\���AsA.Z�5�%����;�L���3s��%�Z��-K�f&'.|��-f2��fq3���&����-���c�y1�����D�d���l3J�lV�Q|53e�<��������s�&��~�&��l�P�Q���
�=`90�1��T�M�}y�=�C���c|sK�|�9����5�`sl�3Bm�aVl�9T����2������~C��^oz[ ������/��Td&o�	>�(�7=��"�
�!	����G�5����-��w?����N1�zg	��N������<	{gI"���d��%	�;���w�%f�,K��Y����|I[dZ�#j�����s���N!i�h�������e[k���s���iq#��^�~f	�enu��^�h	�e����e��fb�<�3pY�)���rv�9����a��%��l��2zx�B0����o[f�fc�Y1���s	�n����y7��gh��X��k��?��n����=��%��,�&�^Yh'�L�����\.��K�Ns��i������L����/��,!�R�p�f��������
�N�]��D�;���Mh�K��?-��X�	�(����hY�K��M��3�4�zn�!�k$L�������X�Dc����ugh������<����Xp��b�*�_�{�
4:��[B��}D-�������@��lP��%���3�`�����t�7��Q���x9�i'��L�e��
�-��F�3��Y�r�h�pI�7�E���a�
oc�
�aWs�l�O\��?����'.����I7�}ot�����
�L�����&S>��"�RU�rt���C�Y)W��f������l�O����^Z���]��E��r�\{�d|���Ec�����o�=
��!
-�\�Xl��~~u�E�m_��2E�$���N�/���:EF�f>���x���������t[4�f�o��6�B�H�-c��'�|-��G���h��pZ�0�4�����f�"~�qn;X�]�ik���"m_M�`���'.�.���x��E�}k�|�~���k�.�(E1���
��i��a��
�>���c��k��{���K!��y�����o���ks���Jkxc&�*�lO6�Wl1�-'����2��-��M%IY���f
A5���]M{^����D��RM�[%*U.K�B4�6-,�}�;v��&'�<���yIJ����xx1aS���c�yI[U=t^\ E����UL��:Ffg,����/�](=�Us��L��,[������-a�:8�� A��Z����U���LY�t��q�3�7}c��yR�'_�?9�		�����A���������lUo��D�6�+�����h��C_,�����6�*�����U���Q[��[���"/�#O��&A��9+I^��B�S[�����O�������W=��_��O?�_�O��t��^Uo�um�{~(��p�;8xs�D�����vw��p�O���J������WP&��g_��h���a�et�AO������'��*VoV�h"V�U�OE0�������9:�{8Z�}�����3r�I�XP��_�4=�@2��@��4���<��Q��O�����rRtz	t��g���s�|��O��|�@�O9a�(8R�M��4{���Q�������u5<�FbDJ�_>���n�8U�9����Qg?q���E��X�T��G;d�}�A��|����6�i�}��k������
��:v�M���Ho�7n�����R��-w����1���S��,�`V-�
ti'���m
������'������L�R���J��R�*n`# w����K�v�	JB��
�)�e�0	�����,{�[���V�B�/�����'�u��2d��Qq�s�4�T��#|/A=��Jj�����k����g�����2���v:'e��1G�9r-N�f3DTZG�H��9;�Y�Zzg:sC��3����]�����Lg����]�����|����`����b`����{����S��;x���ig:���gg��3-�3��R���Kwx3�@��5~;{��p�O:_�]{���{���k!m�� J������;�9������3C��v�����\@�g�}:[]��3Ev%�����v�k�>�E�^�]�A���J�!��j�h������]�F,�$���{:�������-
j���:�[7���h�^�
O��2���am}��������L�QC�����.�������4C�
���E�c��"8�b������E0	e�V�;����=�>�B�?	�D��������o�M��L���5^�c���[����K���3�.�
�'{$�y"����������?Y��Z[��J����/�)�+�U�n#Q��I���]�~�	�����tPZO����x<��bGL'�v!b)I�n{�������z�tg���0�4*@�h0��{����1T�����sr�6Tg������I!@D����u�/n���(v��b�"�i�.�H6�.D3F��-��h:��n�n71��X�9Y,�[����l�K{����*���V��U�C����"~Z�Y��Q��-A������� ����x1{�U��x1�x��z
`�(��%`b(hN��$5`s�"��b��;����{)#����:6%��Nq]����D��f������;����^y������s/V�b�15���*;V�^T��"�!&��y��+�_��[G9OF�0OX��J��B}��<)B�����d4������T<�^QZ�b����^k:�.��P��:�^LW����
�����L��]�NVt�.����t�4��OY���:��i��M�'�+HS��%e�7N�Eh}K����|r�,�	I��E��������7���t�
��(�q}a�/���ZGu��	<;c���u��K�l��)@�LZ�Cf|���M�y�`z������h6O�S�����P�s��uO���7����b*:�eV��gq��|�Dkc����J���
���o�7t�?Z�>�{����W�F���7���6D��=|��[d����FH<�
4��>����;����E��"�L�lAx�� �\ 8��4	��8��n��Q%��I���1�U-�8������W��'�x�B2![�K��@��K���S0��\(L��	�����2]�?�N���$����/���_�8��V[�B3�Uf���I2����(1�m}#^+
`\��@f�����	6%�,��R��!��������sc9`�q��p
�7�������A&���+�du=s��#53��'�gX�1)0��\�����0R6&��m�e��F�pK���&L#O�5�#�K�p������e\#S�%���l9�X��k,R�5�"=��w���	+E��,U��w��)F�1R������9�$i�I��S��=s&5��dq���I�Dm���f�H���l�g�*�l��L�*3M��2+���~3[�����F|�� k�����s�(��.��i�2�^@������)x��]}f���$��E��=��	��'�|p��L���>����N�<�#?���l��j����:����'6;��o9��;M .�O���&��Y^�:2pC����3Y�,�V��2�M\W�~3����� ��0�D�L?5�Y�	5�V6�DOf�+Y�
��+n�3�7��av����d�s%
�s%��s%�D�|}����3g�8e(�x`=:��{|W���jH�w@��fA�`T$(r�@X�%��zR��8�t�����1�*^��"$�bR��"�P�_L0���
�
ixqL�]E_M����9e���h������.�d(��/_�`�v�-�E��f((�v��wR^��T�6�[[��x����|�D����b����G����f��s!�fZ<{U[�M&rK����%BcC��{d��I@�Wd����g��H!������/Q��_��"��/E�����'�����%RD�|�L���)����u0�*T��|��0�K�3%{�MG����u�ky[7�u{����S���p�~�Aw(V)w�E��Z���}�]��'.���h���������`�kT"��6�N�o��Qn�<3pr"�=;	�)�;O���
�C-� $N��������"��1���������h_�K�=�'�8��y�?�����������&>������}x�9�Oz���.�h}�$����VKM��5x���6��}�"�,�����Uo��*~�&���|�q���E�������%�������#�v��w����P�	z�8
)O����"/g�s��2Eh�Slz��E����tHe���s������aR�������G�-	��1�WX4Q\m,�;RB�"G����l������z�i��X�*U��B�qk��(ty���q}as�]_��&��a�24���~(�'"�N�����"]L�AO��1�����1=������{��������>��z��[�=�w0�������wJ6�{������jULG��@�&�V���O�)�&����G�$�v1����N��G��0`���������|���y�������G?�~�����*�1lu�������=�iO���`0=������^�m��y�w������V�W{?=?�I��Nz��`����C��=x�b����=xo|�Q���1*}�?L��|[����=~6�5��=V=����(B.<�3��Y��SZ�r2��v��r��y7���������;\:�f��$��
�l���f����'�����D?K�1vuN���N��<���{I��������}/��j��^��i�~��z|g�HaZ����*������%�f�m���u��z�>_|�3�:��������ho���;����������7L+��������0���a���3Z\����R��u�|�MG���3��b{���&����������������N���5����n��Z���3��\�(��A0
8��\R�4-����h9��s,B����Z��9�7���I0���Z�iyX�{�s���KD�u�`::)���Y'���D��B s������J3+�"���c:�\5��������7��(��R-�7��N`z�w��G`��h���Q�������MDU� �ugn�F� ����dtN;���Q�l��h*��� ��h/���gQ���C�zv��b�����X����	������,�k���1�qxM9�dQ�	�Gx1�&�'�T����'F���C,��q��>6������P��j0�O�oF�#w'��P��h������`r�@���c�oI��!h��0�Xq��r�LW���U����&Gc�����o��Y������(��+��!?�����z�� ?-��3�v��5�)�Sw�W��K���f6�Y��������Z:�p��u2[���^���r&`�����^�������l*�n�
�!�r�p�[r�|���6�"��?��,B�/�~|�����>�t�-�,����Y�]�%� Y����X5I13%��g'�<�����n���(��s�F]&l#��T3f�m�
;E��X5��������.80�.�h��cA�lf���X�l�������&�X��J��zW����E�,����f�wEK9V4��E9V4
���K9����dh����l�k]�Y_i�����b���-����T�<��l{�M�v��)��W{��u���?gK�Y[i*5f��������^���	��r�����#m���5��ywd��!1�������^��h�?�{�2z��6_J]�������J��E��`�G��^�?M��4��	��u&��u�o�@�Q6
B��m������Ln6�g�)��T���r�o�?����V����#��^���hj�&{��jm,�D�"S�~3�?C������k������@��<j��RF^�i�>-u���B�?f����Ec��F���Y�����c0X1�a/8�����B�aB~�����X��D!CXH����<�,������&��Pv!�Pg����f��`�3k�����
O���7�d��@�QH2�{�q���6zaj�
Ltc�m��I�9P(#m4��VH��"1�_�xC-!����o�s���[~A/���vgB���N8�i��A�%Q��B��[-�AO��)������gY������4V�P0u��'��r���P��;,�$�r����|��@++m���f�'7h=�n��B-����&�4>����\���9�8n�!O<���(��8���f6v��e�����^4���%I2��an�s�0�d�|eKh���;������;��g�������M���bh�>Q��8��,�&{�n3t��p��1�����������_t��y�5�!�p�����/�H[~96hn,������R8��l����4��G���]��"�]�Gr^~��
����1<
��3������J���}^w��O������bs�uO����?-��N4>o�O���`�����`����9���^�� ������c38��|�^[{�����������F;��1��{F�F��Or���e����������;d��� �.��� ��f����8�;t�w�;lH��Y@�	���:�q0�X
��@�\?^������=����b�u/Pz�b��D�����|<�}e�vG�4@Y?$P����^��q%.����>_����1�7d�GR��,����k�y��c0M��w�w����;�;���S���w��$�����q��������2�w�:�O��{�9���;���R�4u����u0�E�u���<s��������TW#���J���3�+4�g����V��l=�_A��Gj���������W����k,��a�e2��eq*S���wR�b�J������ ��)�u�$�����4�,S^�Qm��ibT�Hfv���UD
�����B�%{����M_���i!��j=}o��q@��%����@������������G���t�_����!����o����!���0.��<������1��&}t��Z������3�_�Y��!��s�p��7���u�V���x�G��������}��ek��<�gK���d����@F0/��Bo���s����c'��V�v$^����/e*ur>�TB�,]��)�V�N��]��)�2#q���f�&�,�&��_�
�L&�*f����0W����Dl)	k*������b!��D^����6S�h�+�3��L��y[s����J\* ��^ �
�Z<>k��f��p�\|�Dh�G��rC��BT������_����������U��������=�b��]Zn���C�%u�<4_K��!����5�&.�w��C�m��!��uy(z��<�OH�w^j�-4�A!��C��}yh���K�]�q����]�+�rl\����W����.�M���.�w�����]�����q���?��o��C_�����y=���
k�z����z�@��Q��v�E�v������+0�_����s��a����<'�1'd��#�abf�Q�9�1,f~:�!����Pzj~���qf��>w��?>'
�L�T�r�@����j�7���p��B����$�<�ii�=,x�� ���	�q<�!P6B��c����x����JF�U���E��dT�P(�v�QE�p���*2�NcG�?��G����G��*�$����!��-�����Ud���N��'����!��8���#|�l��Jg2;~�X:���;%�1��@��t���n���CO�J����H��
d������
4OL�T��������M�����X��Y8Jh�1���4��T�J���d��L:����F�qI����K����4e;F&4���
�T�w���;�)�{v��r}��Jo��M%��1Uc��g��3�5t��\f��2s���W������.�fL���5k"��f��E61��y;�����r0����T[��\F�����,V�L,�o��+��KDgK��dt�����m�i���V���s>��d����%p�y{~:G����J6>�����le�����l%�%Qt������{;��3i��M��GgS���l��Dt6�&�M�Q���o���v�$fw	�)�5{fEgstw����-:��;:[c!��%�������/a0���� e����f�����F%���k2:��;��mvOb������e��4u����4sO3�w�����i�>M���`��)��E���	�ut��#:[#������HFgk���b������Z��J�����"����Ce��� �N
Y��lL+:[������ �+:�&���������l����V�N��-y������Q���f�tYP���lKo�Ht��#��m���Gg[z���-����
���%':[����l��U�����|��-Y4+:��tFt6-�r��E�+#:[03:�.���l���DgKR(:[j����R���-`��l��f����"l(Z���=�K��`���
��<!��pmK�o1���*w����0W���dn)�kj�������,_6\[����%[XB��FJ�6����\��^91����-+h�����)�Ad$�1��H���C���S���tMe��svV�)=qgr�����:��t|�eu�>^�����tz�����Z'N�AHg��$6v/&���P/��OF�)S���?a�Y��U3K�{��H�8B�A8v�������\����H����Y���������nw�����h��+sZ�
h��`<��w�Y��h��{5i)DF�[��<��[=�<�'����x��P�N���]1�b��'��M*�*Bi�������+��b�W��\_������'������Q����tr!�Y*A�$����G����u�������"?�$�y�{,[�C&r�;��?�,�;�����u�������awp�D���t�~V5]L���M6z����#�R|'V���!���{���<������A���u�<��Cn6���w,�k�J�s�{����y6�V�u
�w�@3����a�c)<b�q�]�"���+�l�����C���T����4���@FB�x�q X��J9	#t�+���f�f�f�R���+��m
Y�h������_�_�(��"�@/��VZ��oy~�w(��#��{k���a����?�/��G"���4~��������a�q��z��<�SX���������:uW����.���x}�RNu�6�����%tX�vU����{����R����xb��������z����)p*����
��F��$�53��������4;�7;U*�m�v���t�� ?p�|���=Xj��������6�t|��,?�~x��,�?]�}��p��.�v�;:xg��H�{kQ�������*�\\?z�����Zt����1��=�FK��-#X��7?���U�=� �v�#$�r���
J&���w����B�S�-�����`?�fv�Y!���%K�����
{qT��$l%j"��I�(2�]T?q=F6�����Sg��d��q����`��'vQ-�*\P��Ki�%K��r�n���@�?������z��E+u�������V�������#��I�:�u�^p���a7��O@f�,��'�`�K���vF�3j�?���A�E�>������X;���s2>3�rI�HE��/�����{�"����Q��_���ld��T~N��.�����H��rp�=��T@�I2�a&�����tt���A��:@+0H4��PO
i(����O��E����2�e�
�Bz���{P���W�jtQI=����t5$,���i05}p�����O������!�b�9�=&�Q���8	>#O�R�d�8����8�i��S��.AB�u�I��������#`:��S��dJ�N.��I
-j��	�:������_���{8�P��*���r��m�N��H�J,G��ZftA���#/��{
B7�&����5��m|��s�������z^7YuG���X���?��i��$�R��{��=����YO��o�Y��������D�xx����G���h<w�����z�s{�~>xy����UjH��I�'���B�/�=�b�/���
���}� �8�de��u������Q'���?�8�)�rKo��������n�G�^~��S%Nh�GR��6��P�����O' �j��M��uz�������b�G ����`���~�Q����:v�!Z����2cT���(�a]$��p��D'F�Z�.�����>��2�1���@{0�^�&~e�X3��������rp�)��(!�Z���.�����J���fgW��������=t�;��/>&�{��~�q�1!�D�2�6c�r�<}�w'#^p�9�}���xI!.`S�&]NeD�����wDk����&W�X�`���j
@�	����`#~gk���t�1r��P5�uq��-��r����0?�A��u~(-aK��-�o��DNO�_��Af~O��M��W��~����Mx�������E8�n���z�&�p�O�H	J�1q�R.���4��MZ���93h���1��CI�5�5���	�@2���4�V�5�5B��`��6�J��'����1 j���X�8��'�.=%uJ�h�h�;4���������R��x��G(yD�#*}�%��w�p�#��|�������yw|U���k��B��������J��h���m��3��F���v��j7A�.�7�J���-��$!%��+�0S��^Q�I����2�=�K,j^�6x5�y���0��92���?/_�J��}��#��C��hU�@\Q��7^�01��(�x��m|���o�~��3��3�����1'T��{?����Uk���t�8�C����#9��j���]�\���1�J$^ )y>
qS��t�/�	����)��%��X����:�&?�/%W'X�*4�;�]V��@q�@?�@%�.�3+�I���6��S���DM��%�?4UUGT�B�1�3S������S]�\g�9��?���/����U��A��������������r&1&����<�x�yq}�>�Q������D��R�]����>��N�N����6�H{%�
��s����ky�m�$0�9()5�CsY�MO��o�!����|�WQ �
�j\C�o�
6�Z.���%�`z][BA/����h��P���0�������~@�_9��5����BxD���\"��3�$6I���������nz��'��A�����v	2��%��]f���/���v��~���mc<�{�R��
�.�di��#�qk��EU_\Y����*j�����wC�u��R����w�ON�7��H���c���RQ4_)"k2��|�j�`�Y���j>�u�'.�6@"�N�2�5����	+HQ��Ji�b`,8q���%G�*� [V�2�z��������P����Q��U0$D������^�Lt���9h����|7������}5r�zU�}�����Y ��[O�w�#���o��=�����ASE����
�8�6l.�<��P�A[��oh��sZ�HU�x"� x��]���0/=$BB������;���/:��Gr�����U�f��S5U���G��"9c�����FR��� ��hx|��-�������\�=qY�`A�}*�����\|���q?|Md�k���1h���(n:�_��`�`D(v�2���+��������)_��+��i�W�W���T�����|�3@�
��;���O$P���5�:]�/kJ�JY����a�j�I�P�4�hm�Dp���N����u�Ms�GX
�0�9"*h����\_����^��|aw'YF,Q��~yk���ht�b�����Q8�"�1c�E����� �Q�K
LR�����h���hy��'��4��5f��H�I(���^�fy��-��bs"^��Cc�]���=�/d`gX�aB�^�<�<����| �R�����x�������B�.1uD%)����E���>:�Z���?��4T��6`��G�{tA���P�U���aq?#WO������1F��X^'���`=�� ue�U���,��������:�x�Ngc�W�-�UD���Km�)1��#U��@5.D�9Y��D*���gdO��)�����FE�����OS��*��cB���+y.���������N��^�c�I�:��7�������������X���T���?�h���^�J7X9��V����w��F�4���J���`�����o�G�7�8mF 2|7�B46��'��j�l����Q��'�-`����AO�����u��5�L�~�u��G�BCX�r����j�0E�r��
��J�!Im�\�fm���$�m�1�X�D=�#�'=�![s�����}=N&�y�y4����Arnx
���;�+�p��������v�V�i�����kPLyA��r�P��r.
�R�u��BonEq��F�wECyW��M������p��|�4^H��?{�^�	G�$�nw'�ra2�����x{���:��V�s��M�}R�^�������<��G�>�����uT�}vm8=�0�#�$��Sis4��3�y�r�K��.'�~p��V��2�,���(m�sbb[����wX��d0-�d���{��(�xaQ�zDwD�����)'��e/@(��<��f������k+�VH��-t��W�����:��z0���v������"7C/���z�"�]�<���.*���c-z�#����}�������~��U�m\�'
K/���{\���"k��x�r�����#i�S���s��!�����1pt����
'�'}��C������>���>��0��dG����n��Pu��B�)�����=����%�T>~���x�j���/�?.T)�����,��P�g�������Ow$w���^G��Je��>C�P�������������F�x��!
W���j��;
6����.�������X���`�U��U���I����������
.!���;sC��A����e)4���"��������L	��������J�2��V/g�:S���o�e���k�y��I8?���3I.�9���YB��7��o%�����k��FQ��8���6u��V��K ���M��Njq�Z����&�K-UT�-) �Ar������z����Cl��+q�S����tl�g�N0	��3�b7s>�"����0c�No�������6Z��~������s��m:�@x��[���k��EKu�o����x2�#������������Bs������Et�a(�WF���Oz��C�&�y�Y�E[����Y�\cVs1�����e;�I�U���f�`O�a<U��
���+�����|s��V-y�_�J��%J��o��D1���Q��4&�\�_z���R�aa)��I�����4�'�s6;O? YQ�f���0|�UY��}d@���%���esW�Ua�������	-i����i��
OW��I��6a���6+c6�N�*�{�����'�|��
Uz�1`�V��/������@?JO����TR.����#���*T�QI�����K�����z�����OB���~s8����/9U�;8xs���?'�h7��Y������P�7l��K_�v�|�1�0��',14�9���n�x��;�ZT�'��D��w���,���V�8�h�&�P��S��Gk2����^#��)��F�A��bC�Q��`���h�������P��$B[��T7����%�B�Mh�-7L����a�+V�����=V�6��5�.��!�-����D�������!]+G��#�������=�r�MUv����frj�[�0����{�L����S.)��
�����1Y��%�E���K��Y7'4F4�$�tu���H��<�����A4ff����+qrC���-�"���M����.O����C����/vw$%�-.��06L������"_z"m|��\'FS$����aM�~���Qr��*v�"���Q�S7O#j@��s�y�P���F���xq#�z
k�����.���^D���c��>����:5��c-c�*7��.��:x��?BetP�e�Q7m�+���)�^���A���o�����X����d<����\4�U%��V�X�D*��0�w7#�Y�6
6��@����C�����dL:�hX#NW�'j�a�)���YHCiO	��P���J�L=V��n�+X[P��[�S`5���@u�~Z:�;��z���=��v\Ei����'o�i�h����fR,k^8{�)�tA�q�bK����v~��S���;���"��1�)D4��tm�k��W* ��i���pu�Q
�C�&�����|V�GP���V��%���[���'��^�y�9�D�<�CO1�	�n�tu���?�%�?�WA��\����=��%|m4���,�T�$>�����&���\��?������j�a}�E����O��^��O%/9gp#s;�����+��{����?=�[W�S���'����;�"��R��P�>V\�p�Ba/o����;F�;>�{������mT�������D�]:���&C�#�J����7��=~q�&6�o�F���Dm��>::(3P�?@k�o��v��md��gh���g6-8@r�cc��V�������gw>k_b�~H�Q`MaQ���x���G5�F�,c�*4U����7� ��9U�c�x**<�<�6=o[����h-2w�r���6f�(6Jm����(�����?��K�#�����i��+�DZ�8��]��H����'���q�\����:�Gz��r�����W�v��������
m�U��'2���j%�����#`	�G����X�+�T�m�)F���2
��*��������E�y��h{j\���/�uL:$�1|���[��6_Zo�6O
C�pr"E�#N���Y2[����'ZY������,*�*�w�6M��R�t�M\(G��{<���G��'����t�{,��3;�:��������^�����G��a�LYXD�8�L�?��3�Qq f��d{�9D]+��,�
�5�F�`)���
���YT��d�t��ZQ��F����:��|�<���V��j8�=M(��n���Z����/�^���[�Q��\X�?���)�d���1�?�f.\����
��:�[z��X;�"}�~�1C�6u�}D|
��Ax�]Iq}�?]�q���7�<
q���D���������W����/~>���KP�Y2v����zM����R����������+�l�-�vf]���*���F��hc}}�~g�d�/�x�]��E���z^<Js������`E�,�x�$�c�,_�{	��y�N�\u��_�x���J&(K��
s:�)���������n����y�a	LX�&]dO:��;b���UM�������GFfKI������o�����<�H����:O��<�{a���W�W��#,����SRv����>i9�}��HV����X=Y�v�ib_�BB�d
I�*�>���d�\��M�FE�hP"�Lr�rC`��/�(n�+��/�������|�1f�gR9����v���D{Sw�(�����}�7�6�`x�)������S��u����a�O��G���L��
�G���Q��
?���6+�o�%�f�1d��WHu���]*q;f��)�����av���������C[�����!~�����X��:{��� u��g�4yM�W���m"�fU��Z��<�������n<$��
:���O�����3�Ju������bY��H&Vz
�9H4��@z,���b�������0,���V���{Wj�����e����S)����+;�]F,���y#}��������u��<��_�:���������9��������<�VXh�JI��mF�3lQ�T����W��h�l�@L�A�;n���X����?�^0��6A����%
��N���Ga������;�n��=l�0h��m�7h�R�6)3hm)��Gm2�����"���e{����$��)�Q'�M��7l=����{P|����K���t�:o��p��Gm���}���������l��R���A/��!5�<'�VB{�D��28�����Hw0��h�dU��X��T>�����c�F}��]�
N��t�����+m%�j��R�[�A�g���O|������o��R}I�)/��/��hr��X>'����y���=q
�L ,6\3G/��<����!�.��"�?����X�zRk������V�d�Vm�������.d�1$6�R/�c�0�Y��>�I�������h}E*,f��T�Y�cj�)+ZgiV��*�J�[����<�z����~=�[i��/D����.'���v�7*s�������H�	��t��I�1'g�����}�D�S.��vfZ�~�dg��L��Q����xFF���D��#�� C��nr���?���1
j'�����<�E���L{U��g�]=���s^�s28�13��S�H�7�/=�y(�E����Q�n���?�}���e����lDFKo5��Kv.H[>�I�W��b0�����H��n�D3�������\�}dK$��)u�[|�����Z&�$�����g�q�99e;�y(
���U��������+�D;�[���N�S���'(�������(2������d�����t%����T0(k,�$2zTb"���"S�T�3TKS�~�-c�G�Q���H��f3����6�W��}����D$Pq���������ZF
-�HO���p����K�
i����i����j])y�o���P�
m���#'h���>E��gn�j��.��;��Q��v�EX���B��2]�H{���_�7��qp�a����_�d�9�L�����x�h����{�������d�e3p4�lx�{���a���>��H���'��	~���1
�g��U��&���h|5����n]�?~�lo�6�>s8=����J�4u@�y\�����4]�5���P��
�w�:r��������|���/�L_����%c`�z�C�-��h�����(�tt�?f:Px��������r!h�p�?�1�r\%�+z��C���Z����	�0T�!a�g���9RwC?���|[eJ�l���4�{�������������DK��i���!j�6�L0���T�.(���'�B���qD`J��9���PwwD{}
G�]��Uk����2�*��	R�����:*&G&��-�J���h���q<A�s$������d�'!�=!8W���I p�(���8�v�c!N�����^�y!ig���`�	O�{N�|�OkuLv��'�k4�a"�afTJ�zq
��L	�����S�1���v?��'F�v~PC���9�9����P/��#`R��~k��e*�����o^�Z����u�@��������A������V��	��V]��[S7mdz.��b��`�9����N�(R��ev�ky�/*�]�W(��th!�gl[V�6��SSr��J�Nuf������N��X���H��,@��Y	��
��~���OV`���1:��eg�(�j)�X�a����X�9����n��?��}OdL+�4���B�!K������X^k���-�fiZ��'��y���S
��c�q>t��a�C�-����X�_��]�?�H����jAt�t������9�[sW�Q�r��W��R�"Q��X�����J�hI�Z|x�sP���%
�.�F�X����t��P���Y/��[|{Yy_����k���d������C��x0��*�w��1��x����"���n:�b��C*�J�"�N��w#����

gE����f-��	�E;����i��l�5@c/����hn'�K��J��&��JCn��t��k|�{H�bG��]�z2,P�t;������Z���K��;E����8��:�f��-n�v-Q57����"��~>x����B�����j��T���
ir$!0h$�9��.>��k��gc����k�I���1B��l����{|�Y�V'��2C�Zd@@��P:��s)�2D��h���
��bm�ZW�X�G��I<���������j�j�w�p[��b����Y�Pp���U������k�P���p��'��[��dW�RM����dnp��E�Go����Tz��n �6ia�w�$�2�=�ra�fV�9e+�"��g5l�r\�����H#\��'���j���f�3��-�W����Z��J�Pu��\���|
/�,���_;i�$�����J,�����dF�����)�A�5���c�����a���_��J�+��aV����RE��Wb���X��Q���|?/w�	-����3Q�|,t��������+5K��j
G�9A45��[S�$�g��$C��Q���%��%
g-�s�=�wd+�������$|	{+������_L�&�?�W�e�a�7���?�tw���KU,�:P�2�tF�+p�.WP/%)*������(E��<�#��-�r25eo0�w�(L��,LI���k+�g�4&����t�1V���xz��j�������������`��O�M����d���H��w�����
d[�)��KvY4���<b�d��J����a|
8�(>���c�>'�H
�Np�PU����-�L�q��d,������s�BuSY��|��o\q�����x	�xg�f-�ku�MO�����W��3�I\�o%������w�6�Hh�>�&��E*�W+��9������ck��A���V�&o-
\=�=�'��4��"���F���(�Q�;*���B��X�YeP��_���������"br��a5�.S�UWV��h��]��\�(��.}^�t/1NEE������Nl��|@��T��9�Q%6���ez%��k�H!	��}����GVU���L�u�R�[�= U-�"e�S&�E��&�CR�j�%w����E�J�$��������Y���&�%��Zf�m�xc7���gC�����gl����Dy|uw��G�pm��5��[�X��#~�����`E�s���NLc�Q���jLz�k���(�����������c<�X�V��K�G����f�p�o���ovN:��E�YS���i*����V[4b2�d��L}6�����Tm������'��?��S���0e�F�Xx9�to�����Z��=�s��n��y�/�w�����/qUq�:��:+�+��������2Q��t_<���m��f.z�m���o���^�������t;32�n(�S��D��9��������W��0gq��dt~�_OL9��������PY6Y7N+��N�W�th��8
��NV�wt��`��v� ���P�K�Z�6:4�f��������<P�-�Z��[$�3��c��EH���}�1���r�}�.F$8��M5�QH�o�PCD�N��O���_z!��2$P��`z6����
�B9����\LA��;��b^V~�6�/-D�ft0���)�/.&��&~�C�}���W���U��q�z�������(�`��y�H��d���
�o������dh8����{$�}>�?'����29����S�sPy}���@��s�~w/���G�^�q�nxD������X�����-	'������F�=8������MJD��
�MY���.M�����;�#$f��J���+'S`m��1`K��-s�SP�c����Q�K������:���r7���b�4���?���|���_�
�)���V����|�N������9����S>o�B�����w^0�!�Blf��<����9�{�����O?������J�]#j��Z�
g-T��2�����i���%�J�4@�9�)����]�]>�7�$����g��$Rj�cVQjD#��F:��u�G�d:��s`G` �f:�F�O�"�O*������[��IvM������2�g2�q���G��� 9�62����J���cD.����sM�c��K��ju��<�E4�Olj�3�^M4~]�n��m���x��C@�����w��-pv������"=���H��;�Ut�Bv��#V���U��Hz�r������7���l!Y���]?����jCt���������A�E��-��v\7�Q���F%�Q�\�����:*�ln[��hn5�(�3�Zy�����fMS
���������<S�m�N	3�-��n�M0����gy��&d��[��|j���[�:��CR�dw���'c9���<a��q8�<�e�!kU�Vz������;�*�	����+h�������c6o�z T��m
��b����(��:?mC��H������/��JD3$����C%7�c[���4������t�	�#U�/�����w�S!F3
�����a(ud��=����`�#]F��HC� jg�O�qT���?��*�F��{�cnp����������W���Kwd
a?�R���w-�T�3u������K]��M��F�
'i�T����������a��2�������ax����2]���Y^<��@�fX���@5"��A���>�BX��=-4��	��3	b;
�6�f7�G�D��a-O�F+�u��/�8�&�������L"�l�z�3�IY�l�R#�a��p,�}��v���E�,s���c�Y�&�d��~5�P���Z�:��������=@2e���\M1�D�I%��m�M�%Pz�:���;s��>��O�y�[-aL���*���j2}W<�=�S�Z
�n�g����hj���6<(�-r���S�Zvi�A�D��a�q%�F@-���dk����&v3!m2���:�C���S�,i��w��*������tu�$pd-%���M��i?N��{�%�U���V���{�����/m�WH��i��zL.*5j��k>�i��?�J;��U�w;=�\nws6���+X�������X8�Kr�'9�2��1K�F%�KXd���kg�.`����L��e�V%%Y)���*$�2��*P�!ej�1
����^3���,��0O�����8�`T����������^��` ��v	�)OT����~��9$:��l ���c<�����u`VE2Uu�5��[9���&�����:���V$�������4%�	���������X��h5$�%}�����7Ju�q�w�K�������Q�P`��K_�xYV!��+��~�"E�@�z�������T��B_�r\��X/���\8q���T
c���`���h-f��-G�y9+g�\���5�|�5����v�d����b1QfI���[?�'~��
�/'��o�]�`MdQ�:[�(�tYA���L��h7�?�3h��l"����?�	�U,���gB��IS(>-`u��7���8�)��v�qw�Ftp��!RC��;�����z������*����EC!v�l	h{F9=IO�l^�Qa�E��N�"�������a��
O��(A�����?��� �����xX�a����t-H6�]������ma����I-����_>m�c���90����`o�To�5�L�����}om���08]d��aL��nH.���=��h�������i0&��`��"���@�K�U'�8�����q�C�DQt8�G�pn%�	�>���ul5d��1��N�!�9��gC�IR�����@�b����X��)�1���G��	a�M��i����r��d~G����&�\2'�v�L�v�����(������N8����<��V���c�Y���7iEz��
�a�7x��X�����m)��<�j���,���en5�������i��dT��4�F����c�<�c�9OE���U�nY����b1��&xC�j��w8�7`��tWS����-��6�����m���'��B��!���1��'�U�Pas��g,�$�B��H�'�>Pg����@�	c�������D#�����.������jm���-x����Lu�o<�yjb��5v
k>�m =2�I��'2+e��$'��������4�r������Nv1�:L�I���*Um�p�
���JG�M���c2Vn�	9�`D�y�6������I���{�y�%O"�-�l[�C��.�]�7�
���h��V"(�Q����$~/��I�J9�6-O�&	G�����2|Xo�_������J��X��H`��D�5�1��U�
�x���f���j��,���U��6��� /���B'{�2s�_��,}0��59�(j����L�&]�����' �c3f<�32Q�Z�,W���@����)��C���u�R����u+�y,C2�����K���|���KM�>��-���XwE���r��QI����1�F� �(����+�����`^�q���W��
�'�p�������G	���Q�,�Q�'>
��o��*����V������+CD�[I9<���&��6h�a�#W^�Y'w���?Cb15�{I�� _K�]�w
��o@X�>cF��w,C�{VTd%�f��\�V
i����D������~Iy�V��HBiRn4)�����,Vgd`����H{�e�����R����:��l����R�����4�k���R�����%M�����$}�-�nf���;���3������a�QI�4����y;L�������y�YZ_Ap�j�-5��t_C�>%���yJ�b5��'����&�k�v>�b������B�4QP�6m�~st������R'"���p �:����^�-���}[1=2���/�Q�
���&��nF�K �9���6�q�/Q��_%����:�,��I�a����b���/�3�E�I�m���/4����F<Gi��M���.2.��2�]���5��Z�T�������^��5ik���yg }]e�u�X�EUV��4��YJSu�X�����7%?U�^j�����Z�S�����4HpM�/�)���Y�L��r�a�%
h����0X��u�I�d����a��2v\�N�'t�yL<�,w��u�.��i��HV���ZY�6���IZ��Ak��0�Q`���5�5���(Tr�N�������r�1���c�Sp��KX'�YG�
�Vo#X���H�^�2�t�i��3���6���4���:uw��EN=�@a_���_���b�5a�X��e�r"�����bg��5!%��p�1�,�b�����C������60�^�NT*��0�m��6�������Q
��<s��v^��m��Y3K���zj�x�	KQ�?�6[@��mos�KmV���U�#��CR�v\#�0c�6���Z�
���"H�����EI�+L��4c�����2z������X�(��,����qo�r7�N����5N�Ts^J���}������k��
��uA]�B��>~�2�t�!������z.�����]�>Y!/3�\��E��i$^���x%4�t;�.d�J����^���T����[3���e�����V��-�!\��s�33���\P�%m��G����'e��~��KM�6�����U�JL���<�)X����l=�����v���N����f;��J���C��L��L�8SL�:k@�h�	��V��?���~�C]�A�l����Q>�L�(]]�jf(�{x��� g�����'G.M����J*r�F�����}Rl���Y�)�
tE=�����j��b��h�j�s���\PMtPb	���(���b1�f��Q�������)�)C�@
2����F6��������V?s��gS����{k�|�	���o���{2!�y0�pp%@��VXBp�c���=N����k����#�E������8Q�9��G�A�3����*����~�bz1F��O�G��Z{�]|���^�A9���
��o�����q�e:�%dT����������^
���H%��bZy������v��>���z�P�3��0����_Ev�awP�]�)��>�G}��;gu��;hv2�M�e�6������CoM��JE!��Ux�,�PN1���K�cM��U�d�Im���w
��Z����%H�5���pT������qg���P�c���=T��c�<^��H�I��YZ��D�L����S���
^�{��{Bg����c�Q�!��n���7�)�� ����P�~���w�����s
(��cz��Om�j@f���m
uc�
��U6
K�#mU���TK��i
��i*��*��.�_7��������S����D������9�'��zJp1���j���L�O��a�8&����#'���4W�EEw�%EV�G
�T�Rt~x�����\3���T�S���,TYJ$�E����9]u��g�J��������1��}������~zs����V�R*��sA���!������$z��P�3CI�V9
��>�CBq���j�]�P�0��U�m�=��gT���eZK�>��"�B�t���@��w��)Z8�j1ht���dSn\>��������P]�2oU�*����fr���������\�~�;������_??�U�%������$���vTrF��%�c0���T�8�R�L���J���
��Q����U��-��X�-ENODmG����\TlO��s���eEgD%����r�j�0f�V�Pl����gl#�>g�P�Y+�TH�!J���7y���F$����m
�z��fTR�V��TC��([� -��,3-HE,B�S������
i[w��;z.����I���~���P5c�~���t���A*I���R��5m�o�i�#�>0�E����}�bX��'���0�,L���xr,������]�4����p	}6|�R,?"n�v���v���V��� ��Y(�8HA?�'�����$��3�*�@��>��XO���C���WF��6IJ���1���dC=x�9��W���e��� ����!ot���=����l"�:>;����!�����p�V���dA �`4�x1>��>#��59����W��kml<�o�|i�~~�dw�\�}���	C:\�`�h�`��w����v�7�$����}�&�EB(a#�7��
��g4��������k�Eo�P����`����%,!,���'�a��P%�	z��T	z�0�zx�&����9�<�S��gZb��B�w�kf������\��|r~"�����.�=X��]�2|��HH��N�?�X!?��g�b_OX1���o86i%g��0����?��c1""��~8�P<�jP@���W��	��>�������.&�\�u\<L1bcy
�@�W0l>���gX�7Bz�I'#B�; �.0:��#F`��S�����9+��0s7��{�RH�bDk@�bZ�	�b�iF:� ��R5��[����L-;_�v��n��R-�[��S�K(������/U$gm�2�r�]�T���C	�9�&�`@0�G<
�E�.*�<�����q�qQ�Id���)+�*�����%Go�,&'C[��0�a�0�tD0)�����<��6G��
I6L4V� ���L/�:��'�zR�D+���0�V]%�NfVj��J���3~�����F����]l�f�&���o^���\�x��������z�����������g�y#��"��pI����1�Z��@<�{!��������6�Nko������?'����(@y������APz��D�t4+(>�����1=Q:�2Nx,Ne3���:R�A�z�KMo���S
G��(�w�%�~�LjR��CN�G�F����bu�"0��L_*�����?8<�a����o������������_�z�z��h���9���C�����ZWA�}%�Q���b�|�X�{~*��B�0�������21�����FW�}�[��]
TO�>�H��Y�8�)8���:�	�Ide\�����b���d�V���j���d}5�
E�l/���)T��T�[�e&�T�3	���v?%��n��Dc�5d�/`��{i����sP���N���	Kb�41i��|�Q�{�U�
dJv���J�;���jvu�)\�����fVt����V��1��r����LQ�iiQ��C�E����^�6��#����e0�7�;|#��6
�����:X��i�Q(PT������Pk,$�D [k�BF�����E{���;q(�U��.�����A�,N*�v=����u�q"(�/���4�,�C�������(�x��S��c�w�$4�>�)�	�~R�"����jM��X�G�u�1��=��%U�B.u�<��j4
%�n���B�7��i
�$��������
��X��bJia���������[:Gk�l���t�b��!�u�}V�#,�}�N��$b����A%E�nU[RT��$����x����B+�#�;��c�h%�J�Pn�4���'
3O�F,����I�����;����g.��I��&&-���S�����M��F�O&��3v���pyRG��T��Z<9=q?����b�{����d�Z�X*���VI���.��M#��h�1��5��h=�,�s@:�@P<��y��\BAyf�U���ng�?���(���*���8R��1g8rh�x�j�0�=M�,
F�GR�%�1��$rd��
������[c��K�7�xM��A8���S����	Ba�1g?��IB���.�����������Q�������b`����Z�68�����F�������13�C
��2R������h<r,��	z_�_�c���L�P�������6��*AX�S�f�S�`s�4HHa�<$���'�����T�	[�a�F��#�IMR�.	��v�?�|h4�0S���S�C�$�����vRM��m�\�3H���?�����k�ml/%�K��yte.���v�R��~������9{_����P���U�,�k��3��	mT��3:���R�s�a�X���1�_����e�����jo/TM[���V����^�&6�(��IKC�
x@��0�v�^�xsG����7r�\�xs�#p��'�S�'y+��PZ�+�k��������f�k��|�����o��<M���W�K.KKx�=��\q����?����������R7�ykG�L����|��8��yZg��9�K�����/����k���Q��+�-tF5���Ly����AEp(���ZT�=?����B+����=f�Qg*-��S���l\lU�eg��Q9�T�v"M4����a�D-W�-d����<d��U����*����������n/�6j�M"�w��c�M��l9��P�%��������2�5�H������_���I�E�5�e9���8�����32���Ip��ml>���f5E�eJ*��b�)�G,�&��Q���!��C�d��������=��K��4�{�(�a���H�|b�����a��������������Mk0��}k������@��p�{��N�����W8bv�>�t���B��F���D\ �g���G�f�'kQ�^6�I�G}T���\ ��z]>+8��(:�1�=��~Q��BFK�1�H;y�����-��f|�-s��� 5U�,�a�&��LQ�T:J�7��4u
Q)���6������~~yp�����u��g��������F��bNke�L�:��kXSY����M��u��Jk����������:���U
���v�J���%+��P�g[0_��m?z����>�<2�!�'6YCL���5K�r�U�����Q\D�1����$�J�8�'��j]V����o�l�����<��`"� ���a�?G�Q��Jc�,��� k��-�2,�������V��PZ�@/����
wIc��'�/A��K��^���,������P����wWd;#{K������"XGq�~����@uv�h���I�&�	�;�t7����u�,������g\I�J8��������U�No�QS�"����@�?�.z��V����]������p��=z���F���y3io{y+e���o�(����qx��b���]p�3��~<Q$6�|���`����S������(���U���4\�io�D�����Xxn�0}@�;���:�R��MT�5�3��K<y���a�H����e��(�2L���Jq�����iH�2t 6pQ���d<Q'M�DE�_�O�Z��m�b;�?n����I�H�z"�;mjo.T�~���������H��Y�V�� �@��+��X�%���4��c(2tJK��f��J
��X�
UE��?S�`��D��-��
a����GL���* F�l����w�u���
,�@aCy��36���������
,��4z���>���$S��	c�������r�t�U�e\��fK7��Z�K��Z���8�f�fx;����P����������&z������"f����]����Fd~�8'�A�<f��������_a
�x����H��hF�v�?���`Y���3Z`3t�>|�8������l��0�7A'����gC���0K�gd�UX*_�u#��6(9\
�=�]��/�����_pNZ�H�4�"gC'^5�o�b���w{&������^BX~�r�fd���L>D�PdX���t��a�t�$��d��6��P�\��C7�T�6���,�\�D����0���7�l�>�Y^��1|�������L�d
h���V#X���k?J���������|R��)w���b�N0�u�
�;Q2>�8	\-�W���z���`S�Au�\a~M�a�,�.������Pv�8����~���if]�3�;�����v��2����)���<�RH(��Ca�?G3�e�@����h�=��au�E��[Q,f��tr��q��+rJ!�@w4
�E��-�E-�E[���2�6���o�6������-
� ���u�eZ�_@��"�<��3�%��t�����������@�}(���W��#5a����=�I��!�������y�R�~�f�S�q!���)&��tU������w�����������2O-M3���D���9�i��H�|I!j��?)I/��������%-DK9���W���^�$���:=C�D� ��z��_�d$NF2�K�
d �5�l*�P�lF�d_��?&0�.�/Z1���B)����K��jz
���]���B���]���-��3���1����	��&����h�7��?x���^�^%�Q�0�0+(��A�|F�k���0�3&�<cGI���h��s�=����u�;�� �'h���8�(�]`�Y`W�~�2�U`u:��*K�
��������]�����T��
����+rLt[l�k���&�6L'�@N�$�*�C�s�`��|rE����Q]r�?ew������#��&6�ehH�M�sE���AA����:�vN�W$� �]s�((������!����c��`���E��1\9���3P�S���2z�da����-�7s���������
m0�z7T�2���ne�,�$�%
�w��[n��i���
�!m�?�677R�g�f;��m�k���m�-nZ������?��-���IT%��"�}��FQ���.]��0�w<BXT���/\c\r��W��>��Z�����~C� 3W�><|�q�6+�TE$3mq)y�_E������F${�LO
9~��)�1`M�����x�"�p@��B�pb��c8�pH

�M�*���lJ�E��-=w�D��s���wx���gA��������w/h(��"
���t��=mE��K�k�V`��F�)�a�������!�����1����g���.G�0�8����-�Rs�)�S���h����\���jB�n���z�+\M����?�c&��F����=�W#E'Y�)U��������H��a����1�	,W/Y��C���)A���)Bbo�dHnQ�f:E/�K~~^Y^����Z�e�.u/D�\�Ky�@_��,3Q��t2��r�Y5A��"l0���x��U/
����I�r��4�����T���20��!�������$/���$D�}�zG�0�?��NH� �
���b��v">���8kqb�;iD�(����v	�p�4�?��n�m�C���mb%�����R�8�E9_�Uq�N�1Nj��D��I���C����fE_6pf�A�\4MV�b5xL��m,M�3s�s:w�����#�-��tJ�leB]��:�hX��.�uH���R��U�N�_��t4���y����&8V�y8<}��>iT��KH�F/L7���rN/3���X��/�G@�������z %/�3+->^���e(�	����t�a�09��"�����I����z�`�(�s��e*��1^��c�E���ww�Fq-��"�-�B����������;d������Hi�b4K�r���5�*9�My�GdQt���.��
q�5#���:'P"m�m�U5�,�����S-�
e����3�L��0�B���V�D�z��w�+M=M:�\�2�4�D��C�>E	���5��vT������VO8��nk�N��Q�M�I�O�^s�G*-7!b.kK�Dz����Z��#V
?G��|0�����Ga���A�'�N��E�k��5
����t�F�&3�2��cjd���0�p��f�q��#�<T��kx��n��Z<Q���k#qP���0�:�Z|E�!��c��v�h�j����T��UKX�S�m��E&��
�c�Ui=H=��R��t��E��/96�rg�������m\�;	������V���T���T�~�D�9S����
$������xsBq�	t���Jy��;"����v�i����d�n�>t�N�
�����?p��w����t��P�Y;�|��v����yg3��xL:G����HX#�
��=88�B����zyCQ���0>���i�����=h��k�hDk�t��P���S��b���4��L���I��j������a����U��1�U��U��V��XJ����4�E]�5K��P�/�	=u>|���c���Cy�������c�������Et�(�����;�O��O� ��7��x��l4�����c�����P�����AE��\��*��t�Ueb��%%��G
�ny�a(�xf���f@
�_�r"�M����q�&��=&�=u�>^��N�����|[��^gck}}�?i�����[[+�f3�J��	}H�����-����SB������<z������k�1��pg�-\)�@��R�'�T�w�<����L���&O��a�h
)&5}v$�|5aI�����>�������$���X+�c�53*o_���n�&�����s�F��t�k���*����C����S��0�t`J��HE
�v~?������, ���scWVE�
@[�l�|�~$�x<)�As"�9���8	D'�~�H��pS��!{�E��[��j���4sBF��9>��CB�Cd�X��J�����211U.�YS�Q��]�y)����_t�2��/"7^��a�V��Q����{�C����G��0��-��p���2Z!���%�v���6wSLJ�Ke�Cn�F)��D1�ud��82�F,lY�)'�&�m��Q���t��r��|-�{��<�����UM��3��e��9,��-j�G#�+Z��O!��\���h,f
OcaT'��.�����%���:������d������2�L���Q�o���9,�]@.V�������Al���pC8E�-�����(�&�?�Q�n(�5�A�<#��`PGs��c�A��^R�l�U�B�{�,��~�m���
��ar���_�a�d���N�Q��cd�9�|���h�}�����]�n��v����g�Gf�/�^od8b��[#4�8W�j
�z��#�@-��MF����H�V�<
�e�^}�q��c�����4 �k��`��aS=��{�m��'I0�i5�M�N�Lq�R@dh-@�s6a�(kM("i��-GI��v����0������^0�My�!�p5�7>��4�M�����'d�R\i�c/���L:�9��+FAq�'�\GT�f`��~A��~*������y4�HF�:Vy9uL4���HR#Nh�����=�b���$�����
�)���=�Tu�
r�4�?8	��3`��-V�)`�M/��W�������z,�Jz[�bGtI���/
�(F:��n�a�i4�h���+2}�����k��#�;Qi���e����
?f{���h�y�,�HTmz0�}�e���E��f*2�J^���N��������U�E�s/���R�"�@��H�?6Nv��@�(1��X���2��;^�sDvhL�[�
p�]������6U�e�d �d�gm`��@�s���S=Q���43��!�*K����*q�b8T��5��N��p����������a.F�����������6��\BO�i�����\����"�LK�"�9F�V�0kE��T��A3n},�b4�6(G\��&��o��J���]H�M��4����dT��f��+���?�e�x^���n���8N��e`�`���c<-�V�����cjH7=jkc� ����S7�����vw�;u_�N���QBw+L|��n ��>���+b^cU���xV�<�tW��E~��},
���1��vD��q�gc��kbx���5��������e�H7�JG� ��SL�aN�80��g��FG{9N� *xt�&x�1zb��Q��*���6�s<�8~4X������������������b���Q��$�{��W&��_�y��3�����/��Sa}����Z�Bc��$x������N������Z7u@�o�Ew�3N���_��W�."�'�����B	F�8wb��m�B�>�s����������!��D������ukrB"����>�c�K��S���T������"lG~�J�~gtSPD��~����h��5��`�0R>L��`d�K;���.)�T�U��a^�2� r�\/�h����E�d)������:B����S5����'�f��,�H�D�/��c���9���i%:�b��39�<�����%�c~����2�L���K����iC�:���M|�����u�K$��y~�w��'�	���*��l@�!)RI�Y�p���'u�P��8I�a���=�FxN)�%^2d���/UO>���
E
^q�`�"`#��"�w���e��[���EToZ���n|]F�v�/�kG�i%i��[�U���/	`��s��q}=\�"�o��4��������"����f�Q���;cj�8cZ��w�;�EXw����f������973����������BeY��d�����^J��x���%��+���b���MA�c�������61�mn�8+�W��nIh�uxJ�z���	s���m�O�$����rK�����X�}��Q[8�e����S\�"�L�r/�����V��XB
�M���S��&�O��I/:1|�%+N�PG������`"����7�~�f�d>�5���`��a���1&-6�,VV;..�1�9�����L\W|�J��q(�e���}i�?��C�"��KU�����G]��3��y�������9QD�����'�L�)1�SJx� /���y)G)�e�������;�\�2J�����^�h�qQ�A�����;;Ln��NC���������\���+��=�?�E&�;Q����:VGk��lg/2�D�(B zW`�g.��(��r*�����P��n����8�o�*��}��4�����v�<��Wi�������ebt�Kb1�$���C,��n����t�U��MUG�N��}#J���K���X�
�T�����i*d89�MW���**~��+��F?8O��1}�m��86&3�0b�3m�#�'�*YP��y�����&�w�w<�����5��|�R�)��>��SN]���� ������]��H�n%#�4���Vd�h��{���<�L���V^h���������!e,r�p���'S�`+P���������?�E�����{��Jb�~����#ng�^�s������oc��d9�jU��r7��),":Zy��Sc/�$�?�������pUz�'���=M��e?���#�Jl��2�ecx��k?��-o��N�����B��u������o&me�
[��*'���6R�|B<S��m}Gy�������6����:�.��	��p3M/h�`8��F��8{���z<�}���	���X��������`"���noz�-X[>�Z��kK�"I{�Z�+��.��a�0Z��3��2C��b�`�eO����]��k��%N�|�m=�V�����b�A0u�L��.�hV�a������B����
���Gd,<3z��I%��_=+�V/>��A���%�zY3�R6x��N-\�aXT@�����|<�����X/1fZ%%-�q��7���`���Q���'��s�03���OG��l:���#GJ�$��^r6��:�������wY[����8&T��,�d��!f��f�!����E���E��1Nt�������W�zfb�F����M�~
����)���hf�h��g#��0'q0�t!z�A�B]�J�t�7�q��F}��&��%�QRP'$e��d��Q�:|����G��~��U����z�cZ�"�\�'c�(D
���"&B�����]��c�� ���)/- �V0�`�����]�8J\&c(y�#�k��z�����/��G#��D�/ Cw;Q/�a�8o4��c�1�^x�b����.�9�=�)�E���*���S3R5a���4	z�~����P���x����P�!��O��x��r��'��R���I��oL�n�y$�����p�09u	y���F�V��^Qt���t����JW
/�Q����>nP:�N�5�U�X�T�LK3��~l��
�8.��Z���	k�&*i�)���.��pk~�m+m���JH��i) 0�
le%��FRi��	�TRP��l��V�(}�t-�A6������w{�bSixJ�����m���L�R�#.��L�M�����7^��GVd�^r
Q�[)�g�B�''Aw
�;&����	"s��@:#��
*R�XFen��o�+t�#��x���C�RfX�i�VTB�t7�"������73�V�~GS��D��&�S�Q��i����xB�kp��\� i��q�N�0�
����4��Ae�s%��c�)w�(�"�)����>�;������?5����04�}��*tEN�x��]��4���P�c��4�P��	�hq�Z��j�#�sL<���F���x'��,k��E�Y�{�������)����3tzX��_o$�70-Tj
�s".d�6���*�	h����������Dz�c���2\����������*�aZ�P_�1�nX���WjE=��nC�"4�������-V�=V�MV�����N�;���i��)�HG�2=V%wgf����\��
�su�GV�%Y ����3l���v�Lk�[3U��*�N�����l[9�^�}�W�U�it��,�6�g���k�7���{ga���0�y�����M_vk�i���jM���<�l�m�XS�"����'����K�##�=(-sTZ��E����:I�����mJ�|���]i>�-�G��ZU�@�G/BB�{�V�fL3�$YP��-\U�6H�����*��&�<L��dA�I1i'1i&��������{�H�ma����
3~��\NP����B����ep"�#�V���}��N�5a���
31��e���O��
��O��"����S�td
����2����.U���x4R��6��)���Xx������L�=�'�vj��Ss�bfr�1	9iJ���d�h�,w-�`������u����lf��w�P�]_]��|��w{�8��G1�[����#�a���{�\����C�Z�QC������Ce��1��U����F�Q��>�&!����Rmg1����^�\���j#�/���Z2_��l1i��?��~:.�,iWt���%�>SHR�0#!
�������i%�u�J[J�],M%��P>[ng��y����.��y��g(H�����g/G5{,��Z��H`jdU���
#P#G�!�Pl��Ne���28bK&��X:�P}��e��z��6#�E��a���p<Ie��PCNpY^s�j���=A�O�)���������q�(�Uv$8UZ��x�"�����l��So�&4��z�+d��9��KD�����,)m(��������8���d��!�tPS�n�	C�>Q�bq�n��Zp�h+(0u+r�+�X`�B���'��1���
`�H���$0�10nZ�ro�]��\(8�*����@��S6�P�o�{h�eiq5en���d���hY�FL�� a
!���UY�h{9�Q�I+jTl}�@���P����Y��C������0��"��?#�N(��x1�,M�K�0��)��t�Gj���~\��Y{���1�rH,���V*~�f�f��Ft>0�g�
RZ�Q���e������8��c�=W^�#�
�H�����UN��9�x�EfFs��bB/|�q���5���cj�:�3��t�[(�Ma��=3Sm,i��mf]-�JI4B���R���[&T�F�>�2�mPi���N���b����y���M���:���	���u�~�	s;�������]�*��R����[�o|�h�j�����t��o������?������V�����>�wGO1^?�/=%�6��5�^:�i%��Wi"�Y"�I"t����.��Ut��8��k��k##��d��HC�J������F����r��>�.
�
�e�!�Y������_������|I����+.����JS:@����T�VnJ?|O2)�%��������q��ahu[R����OK�F��k��7��)�o�4�����>[o,��b�8b�^\)�3;�}��w�60��F_�D45�4��\�R��RmO���:���
��+
 �%((���h���x���>������u������<!��
��������P���'2��p4�D^>
UUn"Oc�3XS r��V/��,2���q��A/����*���I����K+g��J /�l�]�� �3f]F��5�����K�M���rV���et�?^���h���VMf�8F.���v+E��Z�$R�
?0:��P}1���<���'��`j�����B���K!�
O��'��S(��b4��S��c���my�(�Fx'���1�H��#~�X�5rE:�A���o������
.��[�*J�APv}l'_h�U�U-CL�N�W��l{�������J�)O���������+���zr|�n	C���Y>d	�&����:��!�t�����b%V��l��.?2�[/ap[��^:a�����u����j}S���`�a��;��b[j9�4YtA�u�(8�FB�m�����p��T����,�`�L�lp�?��Lu�����kPl
g�����m�J��H��-�A�����Wj���k�]5��\�5�*�V/�vv�;�dhK}g�}5�V�s������.As���(��p{��3kq7��5F)������Q�]NC��5%B��Hd���q6�&r�(6nL�[E�� Z��B��;UT���G�"�w6���P1r���#�ug~d8�
	RJ8���eecd����F�����B�$y)�w�{��+�����1��'xl�qo�M)����{T���O�z�sZ�����p�R�������,0������������WL�F��f�'��d�Ih��
��eG��]��<���`Fs<������E�
�Vv7c�01�O��R��c�K��Z*��o4.B���RJfE��q]����dG��f��?�h����0��8����%O^�y�S�����[��R��]�&��1�.���bk�-���D)�t ����ka��e�2�^��c�9y��g�2�m����l�b��FQ��9�x��sZ��[>+����
0��
����#�B\��g(����s4�1�����W�2�k���4Tw���1����Kx�������'!���&�[6f�~����#y��L���|<�������m��*�����Zk���������N�_I����B�R�b��k�[�����<[�E4}����4��X�oMYv+�G��<�g��]<�w2��
�����~���D����z"��M�G�#  fe�V5#�Z��d+,�����q<x��A�hV��
�\�Y�)U������_������)Rf�$a��(!r����
����Mf�]s`���U��63�Jt�-���T�U.'�6��W	/�f���Dfnp�)�r�������H���(���]���PB��g�)5��[�#.�ct��tB�������OJ�$C���%�2f�����,����H^UimQ���@w�S�k?���K��<����?����In��E�ZSX��~�������I0���[�"y���������]��l?�a�
�����Vo{�w`����o��w?��w?�vw�}���������+�y�6����d�cN��"���1�"�TS=q���kJ��Q��g�m���"�=�F�0��Z��R����n��r�q� D��������Tw}���7��G�U��v	�v%��\��,J���e�X�,Y�e���{��xBn���oZ��(��%+��>�j>���&�9i2�/2�4s��@%�����"3/�O��(�
C��5q�@ ��E
�����q�C3�L�N�o�K�4N�$���?���ug�c�]CH<�Q�a���B���j�!�
4������t�V�d��6f\}3FB�	�a�rg��������;��u��@�e�msv|�{��g2�����5+[�������;��B�B���
�^ p:����%+��
���^<|64���=���e���M��������O�GO���am�������Qw�
)�lnr�t&y�
��/�w�\6�8��Cre@�����?��e�3�i�\ZV�d�b�v���v�e2��R�y2,u��c�U�v�@���o�G��������%��J��J��J�w��-�^�Xf��e��P��bi�n���M�a�vs�b��������,������=fNW?�R�g������V@�+M��)��GmV,�����y��'���'�&����lA!DVP�RY�<�@"��=c�'�Y�')��(�'�yf�[�Y�L��M=0�����]�J��16~���H�bfG�40�dM�`5S\��N�f�V����L�`5M�m��%�-��ZM������@A_��YJ5�}����>��*OA��f�w��5W���-��,���Q��(��k=�$'m�:�iUZ��~����k��j.S�S��?����W5t�!�7������9�����+I|��c,�,'=��RO9E�!b�p|��^�v��eb��j���I��%C+�z�D�����*�z���R���Faj��9����5[��N�J-D���PeC�t������j����S��z1[��5�����k\M��G�	i�op\��%�f?�l�u��_tS���m�N�E��~�Q�����_����!�k%OU�.�i[�����8�����yk��"p��=�}�mA)y=�|�K���J�R��r�#*bl9�]�]�����z�j�sq���7&�Pus3l���_)	��T����(r����iX����)���G�9�Y��s2 �����H��A�DS�g����8����|F�)�c��]���/F������M�3r��t ���.	9�yJ�����D�0�'��m��3�Ct��<�����@
��`J�&�et��	^��{��K����2]`X�����p;�+��������	�2x�����b�4/�����@��4�����x��:CS�2-�]�b���r��'<7n�������^/�h9�=�$����(�.�����������+��+8����@�O�[D<^�Y=��0z'���k���zy�:�H����T\�|1��`+�`�����(�qH��%lRL���[
z�^�}x)�c+��9�p�#+`��v`%2�e_./�V��q��,��Vy��^���J����q���/��g��G��}R��V%u�L��-��o(e2r�D��no0�������uN�S���%"s�Y�����(N=�7������=�\$:�(T������kQ�����ODwc�*�(j��������a���w%�����c�kQ-��5�l������5"Fu}�� `&v��N{����|�Q������6S1;�<*�3S��=���tC�[�!3��o�����������}okC���������r��Ul��]}��u������
�|i�U7����	�fE���"^C�
/@Z6�����Z9�.]�n�z��H|�:�t�P���b�&���49=G�����4K�J#iR�a��������5:��k��'�
�= �&�v|w�I�)U�����#3�%7��-\K=��o�Z��8HO�H��E9h���@��������Zanc1��J��m�k��s����j��Y}���P6����Q���!�m���3=�[Z#��\Q�!E��1��8Xxy�2�4������i/�8���2B����%L��h!�5�����(�'���6���VH�����e������l%1T=����;BX���=����XV��5��p4�F���S��]!���#;��t�A�2^���?��--<i�]�e=�<�v����R�5Q�TA����ff�2���>-��QJ���!"Z��/0(�R��S\�,��8�u�b��gbN���w�L�U
�iZs��3���3��Jv�^��B��ox�64�n=�����L�s����P��,��~�C1�1�?D�q�6N\���nG��i�P����h6��D���0s�5CX���'�I8���a�)L;�b4dg�x��~Qn��c��4�T�OF/����I��A��(�%8�����������z���/��0������K��t��N��v+�>/��2���@������K�H'��D���'����a��'�2�{�6C�&���������+�~#$���e87�?�I���4��Im���x��f~S)�sPf�l~��������J��;P��ea[�i	:�$���s���c���9�<��R9
�	������](�KA&��4�t��wCPy~�5�1���SI�!V�.��^dh3������h��'�a�h�(�#>z�X9���A�������P��&~���z��)��@�C�ot1���$g���N�)��_�k��r��+�"e�"P4�����o����L�6OMQ&�?H���H���cl��� C���j\���Q��r��N���Qg��jrg�)�[��O���SH%�'��R�+d��Y'�='L���h
�������&������.����U4�&nTG���9�����%��M��0��a��~)�Q-n*���nmB�G{���xl����1<��v�\_r���S6|����;�2	����VK^���n�������V��1`������l��8��Z��H��5*Z�$cHYEB�����+��F���K-�T
�hO��������eMd��S�9������V.���������	{K�h�"�^����v~&�P����#�����"�����QE�Dn�����1�-��tF�F������^���E����1
�������B8�c
���bgLj)����y�w@���{��P#.n���aQH�3G9��8,��Z���.�Db+nop�
M��"_�`����5,N�dE�]���@��Au����F��o^����r:����v��k�_���pz���?�����~V��S����^7LD.n�}*~w��-�k�B��O�[�s�����$�V�/�������������]k;Q��J��N�G��e������*���Y��Wa!��Y�(0��^���������.�
6��i+�0����1�4#�OJ�&�Z�1I��n|�M�<L���p�ewu��X�Z�g�jw��i�MN�3��}M.sO���<���^�@�V���}3/�N)�*;W���v	s��BC���P�
)u�.�q!%����u���Pt0EA��9�[t���>NR*�>^�H�|X�2hV�Rr�#o�����jJ*�@�#��/����Q���\��M
�f���}�e7��>�4���Y�����"#��`!n.�B����Y��2���R,IX��� �&{���j$J[�9���T�WJ[@Yz��������wn9����qDe�G'�����>��:CvVE��2�������E,���V���Y���g�8&K���A���O3��N�i���������l�F{����\�\{E������H���T��f�k�]�� ��"���lV�G��Mg�<�Z�C6��������L�M �&���	�#a��,���p&�-4Dgz�n��N�����&������_j�-�{k�����[�F7�o���/iD���T#R�A*E~���_i������^�����YA�(j~����A��B��GH�M���������*�g]|>�L����NF�K��������E�AUT���E��Z������{	���7w��K�T��e<���:|@%�Tt��zX��g��"����KY/��e���G+f���H��SP�����Y�����")��i��/)�j)������_�H�2='
q� v���������VN[b_g��f�"���i����F�K��z���4qF��/����BT(rQ(��j�R��:b�,���1%
������"��0 nbE�J�:�M�������]���w�����79��������b�#�H]���4#Ik\6��
E)�0���Az&.�nfm�,9��w��I�8�sm&�����?&�M�:��.��7>i�76����+r�����r�TQ��������H�cA5�]������0�1i�w���:��l�;�]�"&F�pQL�>�:��	*n�^����5�\f��WRO�����Q�A�e��Y-\�� �������d�i�P��Z~M��kQ�hkTe�r&��|/R�~x����	@u�z
���1���cz�NFfUOHL��Z�4#�g�F���6�(��!jR�"y��U��W IBFY�������w��^�'Awj�h��>KG�����g�Vy=����K�zc������F�7�hF���>��l6��&�<u����eN���h%X��T��=U�:Q3���C��$
hI��%��ckg�H�*N%��c�]2�\����^���U�����zQ��l�K�Y1]F��0g�������mi@��e�I.�N7#Wj
���i;�n��k[zD��p���zd�I3����i��J�O�P�!�O(�S+��������J�P�@�I;�:^��Kz,�\���������1b�e�L<0IFH��W��m���
b~��&��F�����Q��9}f3�:�6;�&�f�v��v]^�-���?X!�g�:���j�9�e���W��Q���7w;�Y.���hK����A���Vm�
�Y �5���(q�G.��j'�U����	�p�&��R(U�~i�=$y�;t�9�E��K@&��x�Q�<�����8v�$0�u.��Q����)��/�y����[>G��-�%U��t�.bwY������P��:R@�����X��t}VX��.w��X�*����I��x���b?�-_^-9e�X��V,��,���IYyT�	�n(�G���o=��?�*��c������T��N��]�qb�k��G�����imE~�e�J�8(v����H��2��1;���FK����_3�K�(�"B��|�;��l)�7�����c��k(zJ�
O��'BE�y3���t
�""3C����K�)+�*y�.N#:wa	�R&��y�S����2NK%NK�RYg��+���)���Ri�nu63Y��+�%�x{G'�R��C�Q�������c��k��y�tF��l�i�n�c��
;���E��/Y�0���i�%����k�$.o������������5����b�}�I/�l�����4��7��^����N���O.����`p%F����?>z�=��L�oy�G���x����Y�����p���lc=�#��yP�v��Q�9r#1�S�K�r��M���J�$5�2��M&�����/�/9Ac^���l#�!_a�(�����}��5�����%!�W����`�.�O`3?=#�u��������
����"�$�2���T;^��b�<�.$����2KN��Y���q����0�		g�Y�����S�h�k+f���m�=3E���2M�-W��YB�@m�x�1����{�P�}s����6��"j���+���F�����������W�&�������7lk�Wc9�N�cb#9g�G���g��P���r�x���d�2�F�%�%��mG��-n�,m��(w/�V!�&���{�������G#~�S|+��-���xmU�S%}�'u��jWc$K\�������n��1".P�@�!��\�N&��S]����m�2�!�A!&���x��Kc�#����Mdax3���(�j�e`!.U��e��P��~H9���~1�Vt��:��.��]f�:	>�����Qd����z�������c�A�0c�\�AX-���b�cxU�]��)�EE(��bu�l}���YY�M75�?-b�}0�*�#��b���?���BG�/A�������E�
����v�O�g�����
�H���/:���4�s��X�Xc�E�0����s�# ;%W�~� �U���������UU�����d����^<��IU�B����a�y#�PZ��	�������S9�����������{��r\�=c�G������$�u��*wZA�N,-�Dr3�<]�'������:4\���%������9��]V@X��0>�k�f�(�����Y�����E����(�o%����\�:����Dm]"zb������	\���XU�VCAC`	(��v@���R�P
,�f���
Hv��������"�2Z_��2�#@Q�.�����N>4\0����L��9�����5|�����f������~����.u���@t2_�a�<�@��"���x�����������u�����C��Mq�\�C�\i4y�q���6D��m�p�]i�[k.���k����'�y|�e
��Jpq�����{�x1_M��gSQ��E�����Fk����$8��W����*�K�q;S���&�����I
Yh����r�Z�I3k
l�����-���6B �t$���.�s��Aab�`p1�Z�p���D�@��+p����?���N�z����v��D�����#��P!J8����#�th��&��{��G�������n�b�h���c�����Y��(~����l=���BpU�W��
a�j���������R��:%������������&����8�Q�?�~
��&M"(t���x5��|��/��WW�'�8z����_�	����q�����������I�w�_���y��h�%������-����������>�v���o��v~��M�6�����~{����w���O�oo�����P�C�(��4��]]Q|h��tq�JK����v����>|���Z�7�%��M�
#wO����W���w��6��PxR)��������������xU|O�>Y��R�����&�N��C��VX�l������������G,����WZf��vv����`y���DK�����o�o4P��yq��o��F�)����/���y�V9�Z�m�969��mG��&w�>�{����(�n'��=��]�`f-^������������	���H���o@3�O0L���FI:���)!"��$�����#e[]	7��=-t�P�R��*�e��E9��[���a�S1�MF����]@vC�_�HH��h|e�`��-�H�!�}Q��������3����yN  \]�������31CPO�xe��~���;�S�5riP@-�t1�_j��[��Z��y$N�jf�?P��d�bR��D{������?
j�$�������C��������
aBAJO���n�����6/~~~�
J�������>z��v��������TRt��m���q�R.�g��;(�U�D%n�j( 6a�5hpz����{5�wiWd�.-X�w/���A�
U`c%Y���E��<�x�mu�����u�H
`����o�����""�aVa��HH�(>�X���b�"g���)�E-�R�t#��
�n$�|
�����������.��|�#��}��O�{B�h�]#S!��jU��~������<c-�K;6��w���`���*/����������U�����
��e�3>=��C4��.�A-";�^7|�A^��#����#z�t�~f�h$gh�&�d�h����#'SI�������=6i����'���X�������`p��=�m�0�9,�
��0�����|�&^��$9�OU�C�VO�p.J�A<����v42�L�4L.�J����
�k��w{����@�\]��A0
��t�(k�g+�MY���������7A�O�N��u����V�C�oL>4|U�}C����!�����cu���r�W�U�;��hTf���#E��I�F�DM���g��A<&1�!k� ^�e	��pZy�36�1+%����k|2	�\�n5S�x(��4�O���1���)�o�M�J\t�a����K3�l�3��aH�}N�JCp�1�C�	��@vmG0�IlaV%��.�6k�mT�_:���n7v��\P���%��>0jIH��{�[�q��S��7Z�DE���A�b[3�w;�_8��������8����[�������Cc�� ]��[�i;T�������")e�&�5�k��F#�- ]X��:x��u����6��i� ��.�4�_��k� �~/O��o=:��O�N6�����l���>��������xc�}��1!�PPGW}g���T������z�O�p����ki�^_�J}W��U�m#5��K{�0M�B7����������P�Z����
���:������>�?}d����8���a����E�u�RH�'O�2�m�]�eM�;\�)�wM7]_i���;�R���LA���hz'�b����o=��	���W�R��$P ��E��h|��v>�I���N�vWa����u��M��<4����7��#c\�y�����.e����/:�~w5�%����X����j��P�V?��s7�J�`�p�HC|v�P�����h,X���{'��M���������N�C���������	Ss��A�l��������|Z���g��:����^~(>�>�Q(����p����D��!
�,/�e���&
���%�����?�����M����r�F�j|a_��bu��WW�^mg�e����3�ra����h��G	���
I����>��VD��n���$��'Da�~�{��e����(��}�t��$��C���������w+�<]%��B2$?��F��2b�����1{H�P�����n�mW�~��SX���z��=K�/f��	L�F��O��
������b���Z�q�U/��G�����O+�h�7���2�O7��|��=q
_���4hl��A��5��o	���������{�k���bh�?zp��Z�����h�<�V�{��^�U���u�6)3�7�2�]��>1�u|��x������� Hm����m���\�Z�_��^��F��7oD
fUUU�J��'�Y����w�}}�_��Z���=*�k�{����{�z�������
�����u3c�j�i�H������5��q�q���o6Q^���btA�{�\���Jl�S��kn�o�N�������5>��9�&�fN����#Y�S��n�������9��VsI%.liL������d�@jg���*"�,
S?�����Ec��f�r�-��he����5��H.��)���W��$���[��_������.V_��s�����OTm^X�����!�����g,�/��NQ���
���.~�����1�_�����^���G5j}4Ez��q-O����O����������#����MT�#���,Q�/b�m�����������"����mG�!~<x�K���vn]�����#���|p��p�rAmq��(�����+�P������\c�MA+�'��R���_y~~1�+\�H��"E���>*�-����3s� iR��z�4�X�����$����6P�"���2����Y���'�>P�P�TL�u~���^�������'���>F���F�x�(�,�A�4Y������r
��S�,���1�'��JA�����'-���;���
t-�uL7��!����6��6a��������_�i1N �/sN�#����D���m��.H6,�X?�Qc>Zk>��S��l�xi��}�H�h�w��Q�i6`x�7���Y���e{�`��`�X���I�K�@3������.� d-a�h����:��R��&J��II�,��+��Z������5��!-��rM�k��*����;���i����d�xa��h��Q���&��Q�A�����������/r_��J�n��������������l�;���YB����C�R�r��^���M����/Vg�����[(,���g�{��y4�?�Zf��A��.�(D�/��[���0H��Aj-������x�R(��V���e
��n�)��d_sw!$n.���c������0�n.������Y���e��Y���e��Y����������fFsqS�����\����n%�������@��h���R�@R�����=DG��-k������+�Y��2���
c~�r�������ON���!�|�,B��5K���P��@s�h{�z��
�����{��LU����m�Xok�z��h���������)��wJq�����,��);���wJO�;��������|���"%*�2��:�$f��i9���?[���lQ
��E�����?+��?+��?+��?+��?+��?+���)1Q��?[���laJ��������?+��?+��?+��?+��?+��?+��'���f=�5���(��=�5�l��,�:3�D
�L�Z3�k�Xos�z�Ym��isfT�f�x��XVOI�����}.^�����8
�M���������oB�/q�5^ ����lG7��D^����g��)��%(�%6	���5+Y*�>���p��Q��]}�--be�S�}+��F��f�g�-�x���Y&�W�8:�2�>��$��,T�f��%w�n���X�94/�����Ph��B�.Zo�����t0����,�	#��;���7�t�=g���'
���H�HV+V�j}��%9�I��:�9�_'����������a�}��i-�2�d�*�.X�U��F�r���E1l���b�9	(7�AR��6{�-���y��p]����x�M���Kn�*'��;��h��������?=��uj@G�c�lktp��o���h�Z�r��f���;�%����,���'')�������e5A
�����v�y]��en���\� �P�����u��]}�nV��n�����_}��1�zP}"3;]�X
�����ot{xtC��7�(q��l��������?
W��!��������1e��M~�e]N�>�z����1�5���M��A���p���xOHyf�<���%0�����wc9`�����K�Ir
l����^i������.�)��?	��d��`s���p�%��,	nwIp���YI�-���xK��R���@���mF��������
0C��d��=3��!�d��3+����xBfN��y({����7G�����vI�.��E4�p9��|`s���5����k>��M_���[D�,A������|�yI�^������Y��MR������M�\�� e]�(*��,���0p?)��Fd������Jw�lq������hB	v��{3��������?G�����BAXA��P�A��/I����'�tzu<�`R��H��=��x[(��)���=�����#����P�Z�9�q��a���,���9
��������aH�<�
�����;�l��h{	@=1��@<ar�%52���ZrV�[v����{1\s0�e��6t����rK�g7���F gd�?K�

b|
"d��k������@�u����[��E[�lm�NZ��F[����[��l]-��&����1VNx���35����Y���f���8����Yu���"���J}��N����C�f��.������wF]���U����(�����g���z�s��>��6�r#�}3��z��t���pZ��h=�������-72F��5|���kd`#gyC���F� 6�F��9���ql��D����hf�D3s$��#���f�H4�F��1���hf�D3{$�y#�
���g�X�����������x{���Y���K�.�BaP�TaO|���sn��y�7J�m%i}�+4[F�a�=�qM�l��"5��XsJ�Y�p�u���hb ��\�8u���gva���N^J����Zw6�F-��s�N��XJQ����&F�]�5b'eF,��s�R
'G,�`�������j��{B���N����c��%���F�WG9��� �
�K2�Be�\��HI.SE�x5��p���L/�+:�^��~7����aZ��2'V�X����/_��
�X$���%N�)e6�[[����'�G��`�U�\�$��Q_�N��}#��V8�mzb����u�3E8F�����4�>V�����/(_"�b�g[�t�O��[^o�_�7�M�>�����{3"z�j[�U{4C������=�X���x�M���F�-��w�j���w���7�X�=�0������������O$�������R�����F,�����V{ssw(�S��wq��o����;�o%���b��t��G��d���e)�����	�wz�������JO:���[����V�9���L����H��
����E���zH��xz�����]��-�����������!Z*/��cZ��`8
��,Oq��e�O����?�%:M>�<�a�G'�cpu�R�������sz�
_`BS��t<����������Ip�?�6U[�|�s#�O:�����WOf����{<fASH4��Pt0akpr1�ut���k����{�i='����8��W�����b�H����*��D��?���'�c�y�����^L�+��u!�\q7d�_���B$��@�$�Z]gh�[�*�'>��a�D���=,l�`_P��0$�O}`=@q�����{��
�.�"U��L�����DLG�]�(�,Nl������aL���dl1k�����
���3�s�L�$�"������7��S~��
���P7z!��t5'>p�d=�!�~�q��>���]�a�%��C�g�3|���#`��"�:�����@���������� �����=���{F�r:�C0�$�,����x4�&{������?����?��N����Qu������W���j"*����5w���f���FN�L����g`p\q���:D���r�
s��4�&���>L������^��D;��(��	L��>��C��1��0��Q�b�u�+a ��<���'0�@�|M��	��"��d�8f�M��;��m���,�v�(���b	_��+�8����W�����8���x��[C=������Aw��]!>�O�n�=\�0-��5I����E�i��	�@{Z!}�c�=�N�G82 �G�1
����+���k�I������zX1��n���t�P}�$���B�7.Q�A�������+JhW$��W8��sX;x��]%�A�7���~B+\���Nr]��a�-K&d^x]1E=�a�f�L�/���)��U`A�%p�!q-	�t���N��`x Iottm~�aJjq4"Hr�@��{A��\��1`I����z"���3���'h�����;�h#�E����w��b���rF=�]%�2;zBD{2������#	�N�00��C.�������'H�	�f�����*�xiP�5�8]H;T�Y;���q��C!k��$s�4P�:���$k�� Kw���d���V����jg���Z5����X�c��2����B~�����t���������A36����-78���%��KP�����s�^A��q'�v�Vb�C��$��c������.`b7�1�c��i�5#��x8�Q�x���]~0��=t�#LQ��-�;������
4�}�����s\�����_V?T62�e�����,i������Qlq`!t���J�9?|��.2o�T����2A/����O���pC�r���!iQ�'E��f��|^
G�����Q��J���AV���/��3�S���4���_�O��
n�`�7E��
��E���S7��X�3���i��������a ��<1��*����g�@�e��������^q��7gB��p�����/)s�!�����oM&|$��@�*��E�H����D#~2u�=�59XZ`��
���v!����a��KXk��\�K$n�af�����NW_��j�~@�e��LF�Z��d���m�
O��	[�����m9����0�5�YA�WwP��RjN)��Pjh+��[?7�Eb���X��P�qT.�}�g�����^����&x�I��7L<nC�a8�>�]j��2�@CX�����6�	��>�K@x@\�����U���t��N��w�*�3����=�QKHA�iy����!��TY��o���s�UEI �V�c����s�CQ��&NQ���2�����������K^F����a�S�Td���1���zj�dc���.����v@?�W����{��Y������_�U�����t:~r��i0���hr��7�q�]?����?��>|�Z�������{�����d 5p�K!�����-��k�N
_���k;�B��&_#!���z���@��^u:�/���f���'���G���4��<�N�T�k���
�
�NGCn^vG�1������NDm����m�	
�D�>�lnF�}P >�o7+�������d��8�(;���u��N����Gu���q��~����a5W{������n4��;=�v��uN�4\=��;s�%h��E��1F�������;�(����$���L��m�-������S�������'g����`�����Q�����> �[��(�Pn�{ �����d;VTvP�&��6�7h������hx�?���y�-4s%���"���Av<�Q�4 �+�t	a4`�Oz��<���8��t&��x,��[i3�����:���I�;�8����z)j��4<>���u	����q*[X����f7��F)}���7;;�OO���
��b�_���n*G6��H���� \Ua��h�	�I���i@��I���Z�A��&T������c`{��wW����D��_�w�o��b�ESSM ��{��f������.R?t0�d~�'s{��=t�ej�,�{��C<��>���)c�}z����Ak"� ���_�nn��1���4���=B`<�;��x��7�[��������w���8u5�4���J �������2vK��<,#���%���	�6����@�����:������������\:����O�1�,:�(�OG<R<E�T���R���'i#���f������qz�$J RKz�u�=�_y���_�������*�J��9v��l	��L�!������.�����8������8��%H��B?+��Z���r�<��ghU�4C�EG��aG7���;%������� _�H�B|=+����\�����b!J�i/���'����E.~�h+_t�t
���z���y1�y�Z.�7%x�a-���e1��s,��4�UT;���c������x9�wH�|��!��i?�8q���u+hv�/M��)��j(��]�u�2;��a�/Mq�V��,�����n�&��732�"��9���������C��r�����������E���*��w�U�2[[����C��uc�'?B��E1�;,�t��H�]��$��%�;�~�W|��������W���&]dk�[�����U�To������/���1�	��?�Q����RvS�$��8�4�]��E�^D�?��]54=��I�-n��g�t���_�GO����=dl?��Ec������tg�gsoL��z#��kl�F��#3���I
�O���Y�u4W[�$���>�_3�9���Y(�F�������L��f(����Ai���\��<�L}v�?/b�Rb^��y��O�|�/,<��N��Ip����)���?�0�2��W�!sq����A��6�u�b�E�Q'S1���aO��z6-J����ZJ��x����T���(w0����)�w��QK?
-U�=(��2�=v~�7�����R��ZI����-��mEW+�*���P�����Fo���S!����KQj�����"GD�-H�T]Fa���x��GvZ���v�6�x3I#����Y5��88�mJ����%��}��,/x,K��3x]*k����1�/`�8e��"�3�����/B[
1��M"�;�J1�sy_W;����{la��1�0�=��;e���������}�����e�9w��1
C�7�� ������;��6��-t��z���Z�E?}�p�,��,�*.B��H�A���`�A
�A6v	,O�����c�1��������N���z������4��i<���7w��M#I�{~����	V��_'�e��B�P��]��[!f�g�@��o�y�{��4�%���E���zzzzz��	�'�k�� ��H�C��H��G^�����)\F���������#����R>�1������������iHo�4D����JZ� �
Ir`-,G� �bq��	N�c6��t'��"c�YX�#�hv�3��KX�	B������a�R7&pjSL9
F���+�����y~D���0��vD'����e	����BQ�=s����z�0���������8M�����>�p���n6&4Y�gh�WW�A���P!X����\K���\� |m�u>��q;��Ea�.��wnG����<g������"u�uK�0��3t�
CRI[�H*g�,I�m�'��;2�ct��pX������z�rtS�r�[�+����+�RL{�K��MS���P%/�+�en�����h�����6�2O��c�=r��'������k��j��]+��,)��t.��m��W"%��6�]���������{�~i�V���0Mh��'�p��Jz;��<y����t�(TQ'�q���+�O��8ij�����*2Q��'�RM]/��]�*��v�+���v��������J8q��q�Q�����[g�*���|�e�����i�l�����[j�Q}j������(�I�t�z3�e�} Y�J��nL�W��>�n
���7��P���SF{�6�m�� N�^N-���;����U*e�I����*7*TU�����|e[��P�����G�����R:���k���(�$���M���H����6��S
���};TT�(�CR���7��p�H' ��j�g7���Q�ex���!���#Q�%�:�8�5&�^���q!����F��-���g��I�Q��"��V��E\P���o���j��p��������s4��*���N`[�1t��S(��4}U��i6���[�uh���r%G����g��C9��[�p���=Pw�N�������S���.p������hM�6���g���%�D;p�g�!�||�n����\������W>�j�e$������}!��
�N����f �F�J�_�0�Mx�����V�`LQ�!�t6��7d{Z�9��N���R�XS�"|o��UY$��.�&|w��@���J�$�n���WL��&_��Yt�EV|��B�c��,�j-�d�WW��n^�F#���y$m�Xuk�������)�s|x�nf�b|R\����;��B��5������`7����})�������c�Mz����BJ�����D�#��>��R�����+���T7�����.��l�bC�g�k�6]j��.��Y����!M"|��)�d����=�N�-�;���Tq����[V�����O�`��u*^h��g�b��IU��!�>>v=O1�`�������]�U	������sS".��]�i�
)\��L�)?�z���/+'��!���hy�"��a
\e�S�P���Ll��m�*�bd����[����{�V���lc�� ��m�����<J��YK~�w����"Hur�}.���a�����z���-5�#�����x���pM��]�����=w��~T�8ru�F[��d��:���`���R��'\rs����^��o)����*�HkU����6��53 M��R�O������"�6��P.Q���?yb�A������h�'�p�P�<��-�3�d|F���D�
�Q�~��������������E�X���}�N�������{��X�/����pk���Z��N��������a�Kw����Z�C���Wy7p��,)���e�Q����f2���Z-�"���F+����R����w����������������5u�f
[����?�������$��?�#�����[�I��&���
;`Z���%7��l��,��vd>�`��b�K�������������Hf3g|����y����*Y����a,����>�,�F�v�����h�ZH��
�.�<$�bDW�
��3��� ��DZ�X������5��[��*sVx�	��u������5������z���zxX3�S����5<:t=J������s���_���\M'������bqS������9�={������i����yJPGw2� �p1��Q�������a��.\D����8-���-Z������S����b(/L�YmgO��&�<"T%g��,�Q�[��pDm� x��,�9���}��w����O�7����~]6�����gd'��"�9�qT��Q��LI��:QS�A!E;�`�J�)r�c��Jlb���<���H#�:G��t����w����R���L� �.h#��0Kip8*)����c��>e�@�����^�M�T��r
�d��4�$/�)���]����}��}����uz���.�:P��_��86z���� i����iB�b���}"�i={7�i=����n�n2'X_��i
��
J�p6Q���4���?�>�������S�����
��a��Q-j�8j���z��QEgT�I�l@<g'���p��gZi���j%_L����xn���;W����p%��YW9�y|�8��������4H��&:�:W]�O2Z`�jv��C�������Uw�WN��
��i3���a���J&�6Q���z
c�	/�������j���'��	iiiJ~MI�6!e]�����r�	zE
@���|&!w�Z���N� [�9������+0���Y�j�����^�e�����{,$��D�\2U�	���x�����&z��������2� ��dzy,U�N��� 60IE�y�.���@�1s_9��J&�����BA<��D��x���i
�L�73]���������E��������'v:{)�u��hb�%�X:�K_s���� ���sL����x�v�������W�j��&��N��h(���-�f�=��F,�������8���A�}�P 6�J]��o�U��D�inqOs�)g������Q@m��:n������5�5Ri9��s�K�����
��Qp��B*��:�Z�u��wf��>Rd�������i�����<Gw�'u�c����2�8{���N���f�� �S��h�P�h{��9�*���/_Zm(��o��o\�.�?(Y�R�3�{uv6�����>��~@�CX
��f6�`���t������y;��8�C���?�JT��(nD'�b���k@�����}��Hnk��
�u�./�������:W�v�
QZ�u��J��2�`W��^H8���/�C�D�~�S_+�]�w{�9iJ��W(� �����|$�J$r*�-��Tp��<��ci��'Qkz���R���w:Jq��������w8�������_�Q���������8W�!����$��%(3_��{�O������$����Y��3q�+�ml����\@\_��,��������
�7%�~.����9yd0��9$�m��SU]����T�m��0E�������pE�V����N�1y�j��R�~rt?�G�#����C�k������[)M���a�L��%���q1U���/Y(�9�Q�VY-S=���A�s�d"��?�|$����X�UW�$���"�������3t!]|�I,�/`;Z-&��m|��H;�fs&T������k:o�3��!��%�y��'e��Oi)he����hu�z�c0����iY����d56��=�u������ !T�N����z���ORg�Pl��H�uA)n�.�}���9���o5�:'l�MO
���F�bt�E��x�
��>SN�� ��2�����"�I�<�;�*9^nP&�s��3��c�g��
#2Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#1)
1 attachment(s)
Re: jsonb and nested hstore

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and compressed
them. The jsonb patch should work standalone. The nested hstore patch
depends on it.

All the jsonb functions now use the jsonb API - there is no more
turning jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up
some jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

cheers

andrew

Attachments:

jsonb-6.patchtext/x-patch; name=jsonb-6.patchDownload
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 6bf4cf6..12832cb 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -143,6 +143,12 @@
       </row>
 
       <row>
+       <entry><type>jsonb</type></entry>
+       <entry></entry>
+       <entry>JSON data, decomposed</entry>
+      </row>
+
+      <row>
        <entry><type>line</type></entry>
        <entry></entry>
        <entry>infinite line on a plane</entry>
@@ -4225,27 +4231,58 @@ SET xmloption TO { DOCUMENT | CONTENT };
   </sect1>
 
   <sect1 id="datatype-json">
-   <title><acronym>JSON</> Type</title>
+   <title><acronym>JSON</> Types</title>
 
    <indexterm zone="datatype-json">
     <primary>JSON</primary>
    </indexterm>
 
+   <indexterm zone="datatype-json">
+    <primary>JSONB</primary>
+   </indexterm>
+
    <para>
-    The <type>json</type> data type can be used to store JSON (JavaScript
-    Object Notation) data, as specified in <ulink
-    url="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</ulink>.  Such
-    data can also be stored as <type>text</type>, but the
-    <type>json</type> data type has the advantage of checking that each
-    stored value is a valid JSON value.  There are also related support
+    JSON data types are for storing JSON (JavaScript Object Notation)
+    data, as specified in <ulink url="http://www.ietf.org/rfc/rfc4627.txt"
+    >RFC 4627</ulink>. Such data can also be stored as <type>text</type>,
+    but the JSON data types have the advantage of checking that each
+    stored value is a valid JSON value. There are also related support
     functions available; see <xref linkend="functions-json">.
    </para>
 
    <para>
+    There are two JSON data types: <type>json</type> and <type>jsonb</type>.
+    Both accept identical sets of values as input. The difference is primarily
+    a matter of efficiency. The <type>json</type> data type stores an exact
+    copy of the the input text, and the processing functions have to reparse
+    it to precess it, while the <type>jsonb</type> is stored in a decomposed
+    form that makes it slightly less efficient to input but very much faster
+    to process, since it never needs reparsing.
+   </para>
+
+   <para>
+    The other difference between the types is that the <type>json</type> type
+    is guaranteed to contain an exact copy of the input, including
+    preservation of semantically insignificant white space, and the order of
+    keys within JSON objects. Also, because the exact text is kept, if a JSON
+    object within the value contains the same key more than once, all the
+    key/value pairs are kept. In that case, the processing functions consider
+    the last value as the operative one. By contrast, <type>jsonb</type>
+    does not preserve white space, does not preserve the order of object keys,
+    and does not keep duplicate object keys. Only the last value for a key
+    specified in the input is kept.
+   </para>
+
+   <para>
+    In general, most applications will find it advantageous to store JSON data
+    as <type>jsonb</type>, unless they have quite specialised needs.
+   </para>
+
+   <para>
     <productname>PostgreSQL</productname> allows only one server encoding
-    per database.  It is therefore not possible for JSON to conform rigidly
-    to the specification unless the server encoding is UTF-8.  Attempts to
-    directly include characters which cannot be represented in the server
+    per database.  It is therefore not possible for the JSON types to conform
+    rigidly to the specification unless the server encoding is UTF-8. Attempts
+    to directly include characters which cannot be represented in the server
     encoding will fail; conversely, characters which can be represented in
     the server encoding but not in UTF-8 will be allowed.
     <literal>\uXXXX</literal> escapes are allowed regardless of the server
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c0a75de..1cc0ad1 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10040,13 +10040,27 @@ table2-mapping
      </tgroup>
    </table>
 
-  <para>
-   <xref linkend="functions-json-table"> shows the functions that are available
-   for creating and manipulating JSON (see <xref linkend="datatype-json">) data.
+  <note>
+   <para>
+    The operators above can take either <type>json</type> or <type>jsonb</type>
+    values as their left hand operands. In general they work much faster with
+    <type>jsonb</type>.
+   </para>
+  </note>
+
+  <!-- 
+     The release notes contain a reference to "functions-json-table". Since
+     that table is now split in two, the id has been parked here so we don't
+     have to change the release notes.
+  -->
+  <para id="functions-json-table">
+   <xref linkend="functions-json-creation-table"> shows the functions that are
+   available for creating <type>json</type> values.
+   (see <xref linkend="datatype-json">)
   </para>
 
-  <table id="functions-json-table">
-    <title>JSON Support Functions</title>
+  <table id="functions-json-creation-table">
+    <title>JSON Creation Functions</title>
     <tgroup cols="5">
      <thead>
       <row>
@@ -10107,13 +10121,38 @@ table2-mapping
        <entry><literal>to_json('Fred said "Hi."'::text)</literal></entry>
        <entry><literal>"Fred said \"Hi.\""</literal></entry>
       </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+
+  <para>
+   <xref linkend="functions-json-processing-table"> shows the functions that
+   are available for processing <type>json</type> and <type>jsonb</type> values.
+   (see <xref linkend="datatype-json">)
+  </para>
+
+  <table id="functions-json-processing-table">
+    <title>JSON Processing Functions</title>
+    <tgroup cols="5">
+     <thead>
       <row>
-       <entry>
-         <indexterm>
+       <entry>Function</entry>
+       <entry>Return Type</entry>
+       <entry>Description</entry>
+       <entry>Example</entry>
+       <entry>Example Result</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry><indexterm>
           <primary>json_array_length</primary>
-         </indexterm>
-         <literal>json_array_length(json)</literal>
-       </entry>
+         </indexterm><indexterm>
+          <primary>jsonb_array_length</primary>
+         </indexterm><para><literal>json_array_length(json)</literal>
+         </para><para><literal>jsonb_array_length(jsonb)</literal>
+       </para></entry>
        <entry><type>int</type></entry>
        <entry>
          Returns the number of elements in the outermost JSON array.
@@ -10122,13 +10161,16 @@ table2-mapping
        <entry><literal>5</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_each</primary>
-         </indexterm>
-         <literal>json_each(json)</literal>
-       </entry>
-       <entry><type>SETOF key text, value json</type></entry>
+         </indexterm><indexterm>
+          <primary>jsonb_each</primary>
+         </indexterm><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>
+       <entry><para><literal>SETOF key text, value json</literal>
+         </para><para><literal>SETOF key text, value jsonb</literal>
+       </para></entry>
        <entry>
          Expands the outermost JSON object into a set of key/value pairs.
        </entry>
@@ -10143,12 +10185,13 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_each_text</primary>
-         </indexterm>
-         <literal>json_each_text(from_json json)</literal>
-       </entry>
+         </indexterm><indexterm>
+          <primary>jsonb_each_text</primary>
+         </indexterm><para><literal>json_each_text(from_json json)</literal>
+         </para><para><literal>jsonb_each_text(from_json jsonb)</literal>
+       </para></entry>
        <entry><type>SETOF key text, value text</type></entry>
        <entry>
          Expands the outermost JSON object into a set of key/value pairs. The
@@ -10165,13 +10208,14 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_extract_path</primary>
-         </indexterm>
-         <literal>json_extract_path(from_json json, VARIADIC path_elems text[])</literal>
-       </entry>
-       <entry><type>json</type></entry>
+         </indexterm><indexterm>
+          <primary>jsonb_extract_path</primary>
+         </indexterm><para><literal>json_extract_path(from_json json, VARIADIC path_elems text[])</literal></para><para><literal>jsonb_extract_path(from_jsonb jsonb, VARIADIC path_elems text[])</literal>
+       </para></entry>
+       <entry><para><type>json</type></para><para><type>jsonb</type>
+       </para></entry>
        <entry>
          Returns JSON value pointed to by <parameter>path_elems</parameter>.
        </entry>
@@ -10179,12 +10223,12 @@ table2-mapping
        <entry><literal>{"f5":99,"f6":"foo"}</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_extract_path_text</primary>
-         </indexterm>
-         <literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
-       </entry>
+         </indexterm><indexterm>
+          <primary>jsonb_extract_path_text</primary>
+         </indexterm><para><literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal></para><para><literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
+       </para></entry>
        <entry><type>text</type></entry>
        <entry>
          Returns JSON value pointed to by <parameter>path_elems</parameter>.
@@ -10193,12 +10237,13 @@ table2-mapping
        <entry><literal>foo</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_object_keys</primary>
-         </indexterm>
-         <literal>json_object_keys(json)</literal>
-       </entry>
+         </indexterm><indexterm>
+          <primary>jsonb_object_keys</primary>
+         </indexterm><para><literal>json_object_keys(json)</literal>
+         </para><para><literal>jsonb_object_keys(jsonb)</literal>
+       </para></entry>
        <entry><type>SETOF text</type></entry>
        <entry>
           Returns set of keys in the JSON object.  Only the <quote>outer</quote> object will be displayed.
@@ -10214,18 +10259,20 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_populate_record</primary>
-         </indexterm>
-         <literal>json_populate_record(base anyelement, from_json json, [, use_json_as_text bool=false]</literal>
-       </entry>
+         </indexterm><indexterm>
+          <primary>jsonb_populate_record</primary>
+         </indexterm><para><literal>json_populate_record(base anyelement, from_json json, [, use_json_as_text bool=false])</literal>
+         </para><para><literal>jsonb_populate_record(base anyelement, from_json jsonb, [, use_json_as_text bool=false])</literal>
+       </para></entry>
        <entry><type>anyelement</type></entry>
        <entry>
          Expands the object in <replaceable>from_json</replaceable> to a row whose columns match
          the record type defined by base. Conversion will be best
          effort; columns in base with no corresponding key in <replaceable>from_json</replaceable>
-         will be left null. If a column is specified more than once, the last value is used.
+         will be left null. When processing <type>json</type>, if a column is 
+         specified more than once, the last value is used.
        </entry>
        <entry><literal>select * from json_populate_record(null::x, '{"a":1,"b":2}')</literal></entry>
        <entry>
@@ -10237,19 +10284,21 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_populate_recordset</primary>
-         </indexterm>
-         <literal>json_populate_recordset(base anyelement, from_json json, [, use_json_as_text bool=false]</literal>
-       </entry>
+         </indexterm><indexterm>
+          <primary>jsonb_populate_recordset</primary>
+         </indexterm><para><literal>json_populate_recordset(base anyelement, from_json json, [, use_json_as_text bool=false])</literal>
+         </para><para><literal>jsonb_populate_recordset(base anyelement, from_json jsonb, [, use_json_as_text bool=false])</literal>
+       </para></entry>
        <entry><type>SETOF anyelement</type></entry>
        <entry>
          Expands the outermost set of objects in <replaceable>from_json</replaceable> to a set
          whose columns match the record type defined by base.
          Conversion will be best effort; columns in base with no
          corresponding key in <replaceable>from_json</replaceable> will be left null.
-         If a column is specified more than once, the last value is used.
+         When processing <type>json</type>, if a column is specified more 
+         than once, the last value is used.
        </entry>
        <entry><literal>select * from json_populate_recordset(null::x, '[{"a":1,"b":2},{"a":3,"b":4}]')</literal></entry>
        <entry>
@@ -10262,13 +10311,16 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_array_elements</primary>
-         </indexterm>
-         <literal>json_array_elements(json)</literal>
-       </entry>
-       <entry><type>SETOF json</type></entry>
+         </indexterm><indexterm>
+          <primary>jsonb_array_elements</primary>
+         </indexterm><para><literal>json_array_elements(json)</literal>
+         </para><para><literal>jsonb_array_elements(jsonb)</literal>
+       </para></entry>
+       <entry><para><type>SETOF json</type>
+         </para><para><type>SETOF jsonb</type>
+       </para></entry>
        <entry>
          Expands a JSON array to a set of JSON values.
        </entry>
@@ -10284,12 +10336,13 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
+       <entry><indexterm>
           <primary>json_typeof</primary>
-         </indexterm>
-         <literal>json_typeof(json)</literal>
-       </entry>
+         </indexterm><indexterm>
+          <primary>jsonb_typeof</primary>
+         </indexterm><para><literal>json_typeof(json)</literal>
+         </para><para><literal>jsonb_typeof(jsonb)</literal>
+       </para></entry>
        <entry><type>text</type></entry>
        <entry>
          Returns the type of the outermost JSON value as a text string.  The types are
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 043d118..ca6d14c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -796,3 +796,11 @@ CREATE OR REPLACE FUNCTION
 CREATE OR REPLACE FUNCTION
   json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
   RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'jsonb_populate_recordset';
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 481db16..ab51a5e 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1262,7 +1262,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 			pfree(outputstr);
 			break;
 		case TYPCATEGORY_JSON:
-			/* JSON will already be escaped */
+			/* JSON and JSONB will already be escaped */
 			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			appendStringInfoString(result, outputstr);
 			pfree(outputstr);
@@ -1390,7 +1390,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 		tcategory = TYPCATEGORY_JSON_CAST;
 	else if (element_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (element_type == JSONOID)
+	else if (element_type == JSONOID || element_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(element_type);
@@ -1485,7 +1485,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
 			tcategory = TYPCATEGORY_ARRAY;
 		else if (tupdesc->attrs[i]->atttypid == RECORDOID)
 			tcategory = TYPCATEGORY_COMPOSITE;
-		else if (tupdesc->attrs[i]->atttypid == JSONOID)
+		else if (tupdesc->attrs[i]->atttypid == JSONOID ||
+				 tupdesc->attrs[i]->atttypid == JSONBOID)
 			tcategory = TYPCATEGORY_JSON;
 		else
 			tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
@@ -1611,7 +1612,7 @@ to_json(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1705,7 +1706,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1807,12 +1808,15 @@ escape_json(StringInfo buf, const char *str)
 Datum
 json_typeof(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonTokenType tok;
 	char	   *type;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
+
 	/* Lex exactly one token from the input and check its type. */
 	json_lex(lex);
 	tok = lex_peek(lex);
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..107ebf0
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,544 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+
+static size_t
+checkStringLen(size_t len)
+{
+	if (len > JSONB_MAX_STRING_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for jsonb string")));
+	return len;
+}
+
+typedef struct JsonbInState
+{
+	ToJsonbState *state;
+	JsonbValue *res;
+}	JsonbInState;
+
+
+/*
+ * for jsonb we always want the de-escaped value - that's what's in token
+ */
+
+static void
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+
+		case JSON_TOKEN_STRING:
+			v.type = jbvString;
+			v.string.len = token ? checkStringLen(strlen(token)) : 0;
+			v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+			v.size += v.string.len;
+			break;
+		case JSON_TOKEN_NUMBER:
+			v.type = jbvNumeric;
+			v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+
+			v.size += VARSIZE_ANY(v.numeric) +sizeof(JEntry) /* alignment */ ;
+			break;
+		case JSON_TOKEN_TRUE:
+			v.type = jbvBool;
+			v.boolean = true;
+			break;
+		case JSON_TOKEN_FALSE:
+			v.type = jbvBool;
+			v.boolean = false;
+			break;
+		case JSON_TOKEN_NULL:
+			v.type = jbvNull;
+			break;
+		default:				/* nothing else should be here in fact */
+			break;
+	}
+
+	if (_state->state == NULL)
+	{
+		/* single scalar */
+		JsonbValue	va;
+
+		va.type = jbvArray;
+		va.array.scalar = true;
+		va.array.nelems = 1;
+
+		_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, &va);
+		_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+		_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+	}
+	else
+	{
+		JsonbValue *o = &_state->state->v;
+
+		switch (o->type)
+		{
+			case jbvArray:
+				_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+				break;
+			case jbvHash:
+				_state->res = pushJsonbValue(&_state->state, WJB_VALUE, &v);
+				break;
+			default:
+				elog(ERROR, "Wrong state");
+		}
+	}
+}
+
+static void
+jsonb_in_object_start(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_OBJECT, NULL);
+}
+
+static void
+jsonb_in_object_end(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_OBJECT, NULL);
+}
+
+static void
+jsonb_in_array_start(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, NULL);
+}
+
+static void
+jsonb_in_array_end(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+}
+
+static void
+jsonb_in_object_field_start(void *state, char *fname, bool isnull)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.type = jbvString;
+	v.string.len = fname ? checkStringLen(strlen(fname)) : 0;
+	v.string.val = fname ? pnstrdup(fname, v.string.len) : NULL;
+	v.size = sizeof(JEntry) + v.string.len;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_KEY, &v);
+}
+
+Datum
+jsonb_in(PG_FUNCTION_ARGS)
+{
+	char	   *json = PG_GETARG_CSTRING(0);
+	text	   *result = cstring_to_text(json);
+	JsonLexContext *lex;
+	JsonbInState state;
+	JsonSemAction sem;
+
+	memset(&state, 0, sizeof(state));
+	memset(&sem, 0, sizeof(sem));
+	lex = makeJsonLexContext(result, true);
+
+	sem.semstate = (void *) &state;
+
+	sem.object_start = jsonb_in_object_start;
+	sem.array_start = jsonb_in_array_start;
+	sem.object_end = jsonb_in_object_end;
+	sem.array_end = jsonb_in_array_end;
+	sem.scalar = jsonb_in_scalar;
+	sem.object_field_start = jsonb_in_object_field_start;
+
+	pg_parse_json(lex, &sem);
+
+	/* after parsing, the item membar has the composed jsonn structure */
+	PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
+}
+
+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);
+
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	uint32		hentry = c & JENTRY_TYPEMASK;
+
+	if (hentry == JENTRY_ISNULL)
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+	}
+	else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	{
+		recvJsonb(buf, v, level + 1, (uint32) c);
+	}
+	else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	{
+		v->type = jbvBool;
+		v->size = sizeof(JEntry);
+		v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	}
+	else if (hentry == JENTRY_ISNUMERIC)
+	{
+		v->type = jbvNumeric;
+		v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+									   Int32GetDatum(0), Int32GetDatum(-1)));
+
+		v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);
+	}
+	else if (hentry == JENTRY_ISSTRING)
+	{
+		v->type = jbvString;
+		v->string.val = pq_getmsgtext(buf, c, &c);
+		v->string.len = checkStringLen(c);
+		v->size = sizeof(JEntry) + v->string.len;
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
+
+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	uint32		hentry;
+	uint32		i;
+
+	hentry = header & JENTRY_TYPEMASK;
+
+	v->size = 3 * sizeof(JEntry);
+	if (hentry == JENTRY_ISOBJECT)
+	{
+		v->type = jbvHash;
+		v->hash.npairs = header & JB_COUNT_MASK;
+		if (v->hash.npairs > 0)
+		{
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+			for (i = 0; i < v->hash.npairs; i++)
+			{
+				recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != jbvString)
+					elog(ERROR, "jsonb's key could be only a string");
+
+				recvJsonbValue(buf, &v->hash.pairs[i].value, level, pq_getmsgint(buf, 4));
+
+				v->size += v->hash.pairs[i].key.size + v->hash.pairs[i].value.size;
+			}
+
+			uniqueJsonbValue(v);
+		}
+	}
+	else if (hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	{
+		v->type = jbvArray;
+		v->array.nelems = header & JB_COUNT_MASK;
+		v->array.scalar = (hentry == JENTRY_ISCALAR) ? true : false;
+
+		if (v->array.scalar && v->array.nelems != 1)
+			elog(ERROR, "bogus input");
+
+		if (v->array.nelems > 0)
+		{
+			v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+			for (i = 0; i < v->array.nelems; i++)
+			{
+				recvJsonbValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4));
+				v->size += v->array.elems[i].size;
+			}
+		}
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
+
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	JsonbValue	v;
+
+	recvJsonb(buf, &v, 0, pq_getmsgint(buf, 4));
+
+	PG_RETURN_POINTER(JsonbValueToJsonb(&v));
+}
+
+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	switch (v->type)
+	{
+		case jbvNull:
+			appendBinaryStringInfo(out, "null", 4);
+			break;
+		case jbvString:
+			escape_json(out, pnstrdup(v->string.val, v->string.len));
+			break;
+		case jbvBool:
+			if (v->boolean)
+				appendBinaryStringInfo(out, "true", 4);
+			else
+				appendBinaryStringInfo(out, "false", 5);
+			break;
+		case jbvNumeric:
+			appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+			break;
+		default:
+			elog(PANIC, "Unknown type");
+	}
+}
+
+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	bool		first = true;
+	JsonbIterator *it;
+	int			type;
+	JsonbValue	v;
+	int			level = 0;
+
+	if (out == NULL)
+		out = makeStringInfo();
+
+	if (in == NULL)
+	{
+		appendStringInfoString(out, "");
+		return out->data;
+	}
+
+	enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
+
+	it = JsonbIteratorInit(in);
+
+	while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	{
+reout:
+		switch (type)
+		{
+			case WJB_BEGIN_ARRAY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				if (v.array.scalar == false)
+					appendStringInfoChar(out, '[');
+				level++;
+				break;
+			case WJB_BEGIN_OBJECT:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+				appendStringInfoCharMacro(out, '{');
+
+				level++;
+				break;
+			case WJB_KEY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				putEscapedValue(out, &v);
+				appendBinaryStringInfo(out, ": ", 2);
+
+				type = JsonbIteratorGet(&it, &v, false);
+				if (type == WJB_VALUE)
+				{
+					first = false;
+					putEscapedValue(out, &v);
+				}
+				else
+				{
+					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+					goto reout;
+				}
+				break;
+			case WJB_ELEM:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				else
+					first = false;
+
+				putEscapedValue(out, &v);
+				break;
+			case WJB_END_ARRAY:
+				level--;
+				if (v.array.scalar == false)
+					appendStringInfoChar(out, ']');
+				first = false;
+				break;
+			case WJB_END_OBJECT:
+				level--;
+				appendStringInfoCharMacro(out, '}');
+				first = false;
+				break;
+			default:
+				elog(PANIC, "Wrong flags");
+		}
+	}
+
+	Assert(level == 0);
+
+	return out->data;
+}
+
+Datum
+jsonb_out(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	PG_RETURN_CSTRING(out);
+}
+
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *in = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+
+	pq_begintypsend(&buf);
+
+	if (JB_ISEMPTY(in))
+	{
+		pq_sendint(&buf, 0, 4);
+	}
+	else
+	{
+		JsonbIterator *it;
+		int			type;
+		JsonbValue	v;
+		uint32		flag;
+		bytea	   *nbuf;
+
+		enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */ );
+
+		it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+		{
+			switch (type)
+			{
+				case WJB_BEGIN_ARRAY:
+					flag = (v.array.scalar) ? JENTRY_ISCALAR : JENTRY_ISARRAY;
+					pq_sendint(&buf, v.array.nelems | flag, 4);
+					break;
+				case WJB_BEGIN_OBJECT:
+					pq_sendint(&buf, v.hash.npairs | JENTRY_ISOBJECT, 4);
+					break;
+				case WJB_KEY:
+					pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+					pq_sendtext(&buf, v.string.val, v.string.len);
+					break;
+				case WJB_ELEM:
+				case WJB_VALUE:
+					switch (v.type)
+					{
+						case jbvNull:
+							pq_sendint(&buf, JENTRY_ISNULL, 4);
+							break;
+						case jbvString:
+							pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+							pq_sendtext(&buf, v.string.val, v.string.len);
+							break;
+						case jbvBool:
+							pq_sendint(&buf, (v.boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE, 4);
+							break;
+						case jbvNumeric:
+							nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
+							pq_sendint(&buf, VARSIZE_ANY(nbuf) | JENTRY_ISNUMERIC, 4);
+							pq_sendbytes(&buf, (char *) nbuf, VARSIZE_ANY(nbuf));
+							break;
+						default:
+							elog(PANIC, "Wrong type: %u", v.type);
+					}
+					break;
+				case WJB_END_ARRAY:
+				case WJB_END_OBJECT:
+					break;
+				default:
+					elog(PANIC, "Wrong flags");
+			}
+		}
+	}
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *in = PG_GETARG_JSONB(0);
+	JsonbIterator *it;
+	JsonbValue	v;
+	char	   *result;
+
+	if (JB_ROOT_IS_OBJECT(in))
+		result = "object";
+	else if (JB_ROOT_IS_ARRAY(in) && !JB_ROOT_IS_SCALAR(in))
+		result = "array";
+	else
+	{
+		Assert(JB_ROOT_IS_SCALAR(in));
+
+		it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		/*
+		 * a root scalar is stored as an array of one element, so we get the
+		 * array and then its first (and only) member.
+		 */
+		(void) JsonbIteratorGet(&it, &v, true);
+		(void) JsonbIteratorGet(&it, &v, true);
+		switch (v.type)
+		{
+			case jbvNull:
+				result = "null";
+				break;
+			case jbvString:
+				result = "string";
+				break;
+			case jbvBool:
+				result = "boolean";
+				break;
+			case jbvNumeric:
+				result = "number";
+				break;
+			default:
+				elog(ERROR, "Wrong jsonb scalar type: %u", v.type);
+		}
+	}
+
+	PG_RETURN_TEXT_P(cstring_to_text(result));
+}
diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c
new file mode 100644
index 0000000..79da6eb
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_support.c
@@ -0,0 +1,1261 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_support.c
+ *	  Support functions for jsonb
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+/*
+ * turn a JsonbValue into a Jsonb
+ */
+Jsonb *
+JsonbValueToJsonb(JsonbValue *v)
+{
+	Jsonb	   *out;
+
+	if (v == NULL)
+	{
+		out = NULL;
+	}
+	else if (v->type == jbvString || v->type == jbvBool ||
+			 v->type == jbvNumeric || v->type == jbvNull)
+	{
+		/* scalar value */
+
+		ToJsonbState *state = NULL;
+		JsonbValue *res;
+		uint32		sz;
+		JsonbValue	scalarArray;
+
+		scalarArray.type = jbvArray;
+		scalarArray.array.scalar = true;
+		scalarArray.array.nelems = 1;
+
+		pushJsonbValue(&state, WJB_BEGIN_ARRAY, &scalarArray);
+		pushJsonbValue(&state, WJB_ELEM, v);
+		res = pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+		out = palloc(VARHDRSZ + res->size);
+		sz = compressJsonb(res, VARDATA(out));
+		Assert(sz <= res->size);
+		SET_VARSIZE(out, sz + VARHDRSZ);
+	}
+	else if (v->type == jbvHash || v->type == jbvArray)
+	{
+		uint32		sz;
+
+		out = palloc(VARHDRSZ + v->size);
+		sz = compressJsonb(v, VARDATA(out));
+		Assert(sz <= v->size);
+		SET_VARSIZE(out, VARHDRSZ + sz);
+	}
+	else
+	{
+		out = palloc(VARHDRSZ + v->binary.len);
+
+		Assert(v->type == jbvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+/*
+ * Sort and unique pairs in hash-like JsonbValue
+ */
+void
+uniqueJsonbValue(JsonbValue *v)
+{
+	bool		hasNonUniq = false;
+
+	Assert(v->type == jbvHash);
+
+	if (v->hash.npairs > 1)
+		qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs),
+				  compareJsonbPair, &hasNonUniq);
+
+	if (hasNonUniq)
+	{
+		JsonbPair  *ptr = v->hash.pairs + 1,
+				   *res = v->hash.pairs;
+
+		while (ptr - v->hash.pairs < v->hash.npairs)
+		{
+			if (ptr->key.string.len == res->key.string.len &&
+				memcmp(ptr->key.string.val, res->key.string.val,
+					   ptr->key.string.len) == 0)
+			{
+				v->size -= ptr->key.size + ptr->value.size;
+			}
+			else
+			{
+				res++;
+				if (ptr != res)
+					memcpy(res, ptr, sizeof(*res));
+			}
+			ptr++;
+		}
+
+		v->hash.npairs = res + 1 - v->hash.pairs;
+	}
+}
+
+/****************************************************************************
+ *						   Compare Functions								*
+ ****************************************************************************/
+
+/*
+ * Compare two jbvString JsonbValue values, third argument
+ * 'arg', if it's not null, should be a pointer to bool
+ * value which will be set to true if strings are equal and
+ * untouched otherwise.
+ */
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue *va = a;
+	const JsonbValue *vb = b;
+	int			res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool *) arg = true;
+	}
+	else
+	{
+		res = (va->string.len > vb->string.len) ? 1 : -1;
+	}
+
+	return res;
+}
+
+/*
+ * qsort helper to compare JsonbPair values, third argument
+ * arg will be trasferred as is to subsequent
+ * compareJsonbStringValue() call. Pairs with equals keys are
+ * ordered with respect of order field.
+ */
+int
+compareJsonbPair(const void *a, const void *b, void *arg)
+{
+	const JsonbPair *pa = a;
+	const JsonbPair *pb = b;
+	int			res;
+
+	res = compareJsonbStringValue(&pa->key, &pb->key, arg);
+
+	/*
+	 * guarantee keeping order of equal pair. Unique algorithm will prefer
+	 * first element as value
+	 */
+
+	if (res == 0)
+		res = (pa->order > pb->order) ? -1 : 1;
+
+	return res;
+}
+
+/*
+ * some constant order of JsonbValue
+ */
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{
+	if (a->type == b->type)
+	{
+		switch (a->type)
+		{
+			case jbvNull:
+				return 0;
+			case jbvString:
+				return compareJsonbStringValue(a, b, NULL);
+			case jbvBool:
+				if (a->boolean == b->boolean)
+					return 0;
+				return (a->boolean > b->boolean) ? 1 : -1;
+			case jbvNumeric:
+				return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(a->numeric),
+											   PointerGetDatum(b->numeric)));
+			case jbvArray:
+				if (a->array.nelems == b->array.nelems)
+				{
+					int			i,
+								r;
+
+					for (i = 0; i < a->array.nelems; i++)
+						if ((r = compareJsonbValue(a->array.elems + i,
+												   b->array.elems + i)) != 0)
+							return r;
+
+					return 0;
+				}
+
+				return (a->array.nelems > b->array.nelems) ? 1 : -1;
+			case jbvHash:
+				if (a->hash.npairs == b->hash.npairs)
+				{
+					int			i,
+								r;
+
+					for (i = 0; i < a->hash.npairs; i++)
+					{
+						if ((r = compareJsonbStringValue(&a->hash.pairs[i].key,
+													   &b->hash.pairs[i].key,
+														 NULL)) != 0)
+							return r;
+						if ((r = compareJsonbValue(&a->hash.pairs[i].value,
+											  &b->hash.pairs[i].value)) != 0)
+							return r;
+					}
+
+					return 0;
+				}
+
+				return (a->hash.npairs > b->hash.npairs) ? 1 : -1;
+			case jbvBinary:
+				return compareJsonbBinaryValue(a->binary.data, b->binary.data);
+			default:
+				elog(PANIC, "unknown JsonbValue->type: %d", a->type);
+		}
+	}
+
+	return (a->type > b->type) ? 1 : -1;
+}
+
+/*
+ * Some order for Jsonb values
+ */
+int
+compareJsonbBinaryValue(char *a, char *b)
+{
+	JsonbIterator *it1,
+			   *it2;
+	int			res = 0;
+
+	it1 = JsonbIteratorInit(a);
+	it2 = JsonbIteratorInit(b);
+
+	while (res == 0)
+	{
+		JsonbValue	v1,
+					v2;
+		int			r1,
+					r2;
+
+		r1 = JsonbIteratorGet(&it1, &v1, false);
+		r2 = JsonbIteratorGet(&it2, &v2, false);
+
+		if (r1 == r2)
+		{
+			if (r1 == 0)
+				break;			/* equal */
+
+			if (v1.type == v2.type)
+			{
+				switch (v1.type)
+				{
+					case jbvString:
+						res = compareJsonbStringValue(&v1, &v2, NULL);
+						break;
+					case jbvBool:
+						if (v1.boolean == v2.boolean)
+							res = 0;
+						else
+							res = (v1.boolean > v2.boolean) ? 1 : -1;
+						break;
+					case jbvNumeric:
+						res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(v1.numeric),
+											   PointerGetDatum(v2.numeric)));
+						break;
+					case jbvArray:
+						if (v1.array.nelems != v2.array.nelems)
+							res = (v1.array.nelems > v2.array.nelems) ? 1 : -1;
+						break;
+					case jbvHash:
+						if (v1.hash.npairs != v2.hash.npairs)
+							res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1;
+						break;
+					default:
+						break;
+				}
+			}
+			else
+			{
+				res = (v1.type > v2.type) ? 1 : -1;		/* dummy order */
+			}
+		}
+		else
+		{
+			res = (r1 > r2) ? 1 : -1;	/* dummy order */
+		}
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *			find string key in hash or element by value in array			*
+ ****************************************************************************/
+JsonbValue *
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK));
+		int			i;
+
+		for (i = (lowbound) ? *lowbound : 0; i < (header & JB_COUNT_MASK); i++)
+		{
+			JEntry	   *e = array + i;
+
+			if (JBE_ISNULL(*e) && key->type == jbvNull)
+			{
+				r.type = jbvNull;
+				if (lowbound)
+					*lowbound = i;
+				r.size = sizeof(JEntry);
+
+				return &r;
+			}
+			else if (JBE_ISSTRING(*e) && key->type == jbvString)
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e),
+						   key->string.len) == 0)
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*e);
+					r.string.len = key->string.len;
+					r.size = sizeof(JEntry) + r.string.len;
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+			{
+				if ((JBE_ISBOOL_TRUE(*e) && key->boolean == true) ||
+					(JBE_ISBOOL_FALSE(*e) && key->boolean == false))
+				{
+					r = *key;
+					r.size = sizeof(JEntry);
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
+			{
+				if (DatumGetBool(DirectFunctionCall2(numeric_eq,
+							   PointerGetDatum(data + INTALIGN(JBE_OFF(*e))),
+									 PointerGetDatum(key->numeric))) == true)
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+		}
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+		uint32		stopLow = lowbound ? *lowbound : 0,
+					stopHigh = (header & JB_COUNT_MASK),
+					stopMiddle;
+
+		if (key->type != jbvString)
+			return NULL;
+
+		while (stopLow < stopHigh)
+		{
+			int			difference;
+			JEntry	   *e;
+
+			stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+			e = array + stopMiddle * 2;
+
+			if (key->string.len == JBE_LEN(*e))
+				difference = memcmp(data + JBE_OFF(*e), key->string.val,
+									key->string.len);
+			else
+				difference = (JBE_LEN(*e) > key->string.len) ? 1 : -1;
+
+			if (difference == 0)
+			{
+				JEntry	   *v = e + 1;
+
+				if (lowbound)
+					*lowbound = stopMiddle + 1;
+
+				if (JBE_ISSTRING(*v))
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*v);
+					r.string.len = JBE_LEN(*v);
+					r.size = sizeof(JEntry) + r.string.len;
+				}
+				else if (JBE_ISBOOL(*v))
+				{
+					r.type = jbvBool;
+					r.boolean = (JBE_ISBOOL_TRUE(*v)) ? true : false;
+					r.size = sizeof(JEntry);
+				}
+				else if (JBE_ISNUMERIC(*v))
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*v)));
+
+					r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+				}
+				else if (JBE_ISNULL(*v))
+				{
+					r.type = jbvNull;
+					r.size = sizeof(JEntry);
+				}
+				else
+				{
+					r.type = jbvBinary;
+					r.binary.data = data + INTALIGN(JBE_OFF(*v));
+					r.binary.len = JBE_LEN(*v) -
+						(INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
+					r.size = 2 * sizeof(JEntry) + r.binary.len;
+				}
+
+				return &r;
+			}
+			else if (difference < 0)
+			{
+				stopLow = stopMiddle + 1;
+			}
+			else
+			{
+				stopHigh = stopMiddle;
+			}
+		}
+
+		if (lowbound)
+			*lowbound = stopLow;
+	}
+
+	return NULL;
+}
+
+/*
+ * Just wrapped for findUncompressedJsonbValueByValue()
+ * with simple string key representation
+ */
+JsonbValue *
+findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound,
+						   char *key, uint32 keylen)
+{
+	JsonbValue	v;
+
+	if (key == NULL)
+	{
+		v.type = jbvNull;
+	}
+	else
+	{
+		v.type = jbvString;
+		v.string.val = key;
+		v.string.len = keylen;
+	}
+
+	return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v);
+}
+
+/*
+ * Get i-th value of array or hash. if i < 0 then it counts from
+ * the end of array/hash. Note: returns pointer to statically
+ * allocated JsonbValue.
+ */
+JsonbValue *
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;
+	JEntry	   *array,
+			   *e;
+	char	   *data;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (i >= 0)
+	{
+		if (i >= (header & JB_COUNT_MASK))
+			return NULL;
+	}
+	else
+	{
+		if (-i > (header & JB_COUNT_MASK))
+			return NULL;
+
+		i = (header & JB_COUNT_MASK) + i;
+	}
+
+	array = (JEntry *) (buffer + sizeof(header));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		e = array + i;
+		data = (char *) (array + (header & JB_COUNT_MASK));
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		e = array + i * 2 + 1;
+		data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+	}
+	else
+	{
+		return NULL;
+	}
+
+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+		r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2 * sizeof(JEntry);
+	}
+
+	return &r;
+}
+
+/****************************************************************************
+ *					  Walk on tree representation of jsonb					*
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level)
+{
+	int			i;
+
+	switch (v->type)
+	{
+		case jbvArray:
+			cb(cb_arg, v, WJB_BEGIN_ARRAY, level);
+			for (i = 0; i < v->array.nelems; i++)
+			{
+				if (v->array.elems[i].type == jbvNull ||
+					v->array.elems[i].type == jbvString ||
+					v->array.elems[i].type == jbvBool ||
+					v->array.elems[i].type == jbvNumeric ||
+					v->array.elems[i].type == jbvBinary)
+					cb(cb_arg, v->array.elems + i, WJB_ELEM, level);
+				else
+					walkUncompressedJsonbDo(v->array.elems + i, cb, cb_arg,
+											level + 1);
+			}
+			cb(cb_arg, v, WJB_END_ARRAY, level);
+			break;
+		case jbvHash:
+			cb(cb_arg, v, WJB_BEGIN_OBJECT, level);
+
+			for (i = 0; i < v->hash.npairs; i++)
+			{
+				cb(cb_arg, &v->hash.pairs[i].key, WJB_KEY, level);
+
+				if (v->hash.pairs[i].value.type == jbvNull ||
+					v->hash.pairs[i].value.type == jbvString ||
+					v->hash.pairs[i].value.type == jbvBool ||
+					v->hash.pairs[i].value.type == jbvNumeric ||
+					v->hash.pairs[i].value.type == jbvBinary)
+					cb(cb_arg, &v->hash.pairs[i].value, WJB_VALUE, level);
+				else
+					walkUncompressedJsonbDo(&v->hash.pairs[i].value, cb, cb_arg,
+											level + 1);
+			}
+
+			cb(cb_arg, v, WJB_END_OBJECT, level);
+			break;
+		default:
+			elog(PANIC, "impossible JsonbValue->type: %d", v->type);
+	}
+}
+
+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *						   Iteration over binary jsonb						*
+ ****************************************************************************/
+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{
+	uint32		header = *(uint32 *) buffer;
+
+	it->type = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+	it->nelems = header & JB_COUNT_MASK;
+	it->buffer = buffer;
+
+
+	buffer += sizeof(uint32);
+	it->array = (JEntry *) buffer;
+
+	it->state = jbi_start;
+
+	switch (it->type)
+	{
+		case JB_FLAG_ARRAY:
+			it->data = buffer + it->nelems * sizeof(JEntry);
+			it->isScalar = (header & JB_FLAG_SCALAR) ? true : false;
+			Assert(it->isScalar == false || it->nelems == 1);
+			break;
+		case JB_FLAG_OBJECT:
+			it->data = buffer + it->nelems * sizeof(JEntry) * 2;
+			break;
+		default:
+			elog(PANIC, "impossible type: %08x", it->type);
+	}
+}
+
+JsonbIterator *
+JsonbIteratorInit(char *buffer)
+{
+	JsonbIterator *it = palloc(sizeof(*it));
+
+	parseBuffer(it, buffer);
+	it->next = NULL;
+
+	return it;
+}
+
+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry * e, bool skipNested)
+{
+	if (JBE_ISSTRING(*e))
+	{
+		v->type = jbvString;
+		v->string.val = (*it)->data + JBE_OFF(*e);
+		v->string.len = JBE_LEN(*e);
+		v->size = sizeof(JEntry) + v->string.len;
+
+		return false;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		v->type = jbvBool;
+		v->boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		v->type = jbvNumeric;
+		v->numeric = (Numeric) ((*it)->data + INTALIGN(JBE_OFF(*e)));
+
+		v->size = 2 * sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+		return false;
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (skipNested)
+	{
+		v->type = jbvBinary;
+		v->binary.data = (*it)->data + INTALIGN(JBE_OFF(*e));
+		v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		v->size = v->binary.len + 2 * sizeof(JEntry);
+
+		return false;
+	}
+	else
+	{
+		JsonbIterator *nit = palloc(sizeof(*nit));
+
+		parseBuffer(nit, (*it)->data + INTALIGN(JBE_OFF(*e)));
+		nit->next = *it;
+		*it = nit;
+
+		return true;
+	}
+}
+
+static JsonbIterator *
+up(JsonbIterator *it)
+{
+	JsonbIterator *v = it->next;
+
+	pfree(it);
+
+	return v;
+}
+
+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int			res;
+
+	if (*it == NULL)
+		return 0;
+
+	/*
+	 * Encode all possible states by one integer. That's possible because enum
+	 * members of JsonbIterator->state uses different bits than
+	 * JB_FLAG_ARRAY/JB_FLAG_OBJECT. See definition of JsonbIterator
+	 */
+
+	switch ((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:
+			(*it)->state = jbi_elem;
+			(*it)->i = 0;
+			v->type = jbvArray;
+			v->array.nelems = (*it)->nelems;
+			res = WJB_BEGIN_ARRAY;
+			v->array.scalar = (*it)->isScalar;
+			break;
+		case JB_FLAG_ARRAY | jbi_elem:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_ARRAY;
+			}
+			else if (formAnswer(it, v, &(*it)->array[(*it)->i++], skipNested))
+			{
+				res = JsonbIteratorGet(it, v, skipNested);
+			}
+			else
+			{
+				res = WJB_ELEM;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_start:
+			(*it)->state = jbi_key;
+			(*it)->i = 0;
+			v->type = jbvHash;
+			v->hash.npairs = (*it)->nelems;
+			res = WJB_BEGIN_OBJECT;
+			break;
+		case JB_FLAG_OBJECT | jbi_key:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_OBJECT;
+			}
+			else
+			{
+				formAnswer(it, v, &(*it)->array[(*it)->i * 2], false);
+				(*it)->state = jbi_value;
+				res = WJB_KEY;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_value:
+			(*it)->state = jbi_key;
+			if (formAnswer(it, v, &(*it)->array[((*it)->i++) * 2 + 1], skipNested))
+				res = JsonbIteratorGet(it, v, skipNested);
+			else
+				res = WJB_VALUE;
+			break;
+		default:
+			elog(PANIC, "unknown state %08x", (*it)->type & (*it)->state);
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *		  Transformation from tree to binary representation of jsonb		*
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	   *begin;
+	char	   *ptr;
+
+	struct
+	{
+		uint32		i;
+		uint32	   *header;
+		JEntry	   *array;
+		char	   *begin;
+	}		   *levelstate, *lptr, *pptr;
+
+	uint32		maxlevel;
+
+}	CompressState;
+
+#define curLevelState	state->lptr
+#define prevLevelState	state->pptr
+
+static void
+putJEntryString(CompressState * state, JsonbValue *value, uint32 level, uint32 i)
+{
+	curLevelState = state->levelstate + level;
+
+	if (i == 0)
+		curLevelState->array[0].entry = JENTRY_ISFIRST;
+	else
+		curLevelState->array[i].entry = 0;
+
+	switch (value->type)
+	{
+		case jbvNull:
+			curLevelState->array[i].entry |= JENTRY_ISNULL;
+
+			if (i > 0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvString:
+			memcpy(state->ptr, value->string.val, value->string.len);
+			state->ptr += value->string.len;
+
+			if (i == 0)
+				curLevelState->array[i].entry |= value->string.len;
+			else
+				curLevelState->array[i].entry |=
+					(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+					value->string.len;
+			break;
+		case jbvBool:
+			curLevelState->array[i].entry |= (value->boolean) ?
+				JENTRY_ISTRUE : JENTRY_ISFALSE;
+
+			if (i > 0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvNumeric:
+			{
+				int			addlen = INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin);
+				int			numlen = VARSIZE_ANY(value->numeric);
+
+				switch (addlen)
+				{
+					case 3:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 2:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 1:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->numeric, numlen);
+				state->ptr += numlen;
+
+				curLevelState->array[i].entry |= JENTRY_ISNUMERIC;
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + numlen;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + numlen;
+				break;
+			}
+		case jbvBinary:
+			{
+				int			addlen = INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin);
+
+				switch (addlen)
+				{
+					case 3:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 2:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 1:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->binary.data, value->binary.len);
+				state->ptr += value->binary.len;
+
+				curLevelState->array[i].entry |= JENTRY_ISNEST;
+
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + value->binary.len;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + value->binary.len;
+			}
+			break;
+		default:
+			elog(PANIC, "Unsupported JsonbValue type: %d", value->type);
+	}
+}
+
+static void
+compressCallback(void *arg, JsonbValue *value, uint32 flags, uint32 level)
+{
+	CompressState *state = arg;
+
+	if (level == state->maxlevel)
+	{
+		state->maxlevel *= 2;
+		state->levelstate = repalloc(state->levelstate,
+							   sizeof(*state->levelstate) * state->maxlevel);
+	}
+
+	curLevelState = state->levelstate + level;
+
+	if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
+	{
+		Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_BEGIN_OBJECT) && value->type == jbvHash));
+
+		curLevelState->begin = state->ptr;
+
+		switch (INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin))
+		{
+			case 3:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 2:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 1:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 0:
+			default:
+				break;
+		}
+
+		curLevelState->header = (uint32 *) state->ptr;
+		state->ptr += sizeof(*curLevelState->header);
+
+		curLevelState->array = (JEntry *) state->ptr;
+		curLevelState->i = 0;
+
+		if (value->type == jbvArray)
+		{
+			*curLevelState->header = value->array.nelems | JB_FLAG_ARRAY;
+			state->ptr += sizeof(JEntry) * value->array.nelems;
+
+			if (value->array.scalar)
+			{
+				Assert(value->array.nelems == 1);
+				Assert(level == 0);
+				*curLevelState->header |= JB_FLAG_SCALAR;
+			}
+		}
+		else
+		{
+			*curLevelState->header = value->hash.npairs | JB_FLAG_OBJECT;
+			state->ptr += sizeof(JEntry) * value->hash.npairs * 2;
+		}
+	}
+	else if (flags & WJB_ELEM)
+	{
+		putJEntryString(state, value, level, curLevelState->i);
+		curLevelState->i++;
+	}
+	else if (flags & WJB_KEY)
+	{
+		Assert(value->type == jbvString);
+
+		putJEntryString(state, value, level, curLevelState->i * 2);
+	}
+	else if (flags & WJB_VALUE)
+	{
+		putJEntryString(state, value, level, curLevelState->i * 2 + 1);
+		curLevelState->i++;
+	}
+	else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
+	{
+		uint32		len,
+					i;
+
+		Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_END_OBJECT) && value->type == jbvHash));
+		if (level == 0)
+			return;
+
+		len = state->ptr - (char *) curLevelState->begin;
+
+		prevLevelState = curLevelState - 1;
+
+		if (*prevLevelState->header & JB_FLAG_ARRAY)
+		{
+			i = prevLevelState->i;
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			if (i == 0)
+				prevLevelState->array[0].entry |= JENTRY_ISFIRST | len;
+			else
+				prevLevelState->array[i].entry |=
+					(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else if (*prevLevelState->header & JB_FLAG_OBJECT)
+		{
+			i = 2 * prevLevelState->i + 1;		/* VALUE, not a KEY */
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			prevLevelState->array[i].entry |=
+				(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else
+		{
+			elog(PANIC, "Wrong parent");
+		}
+
+		Assert(state->ptr - curLevelState->begin <= value->size);
+		prevLevelState->i++;
+	}
+	else
+	{
+		elog(PANIC, "Wrong flags");
+	}
+}
+
+/*
+ * puts JsonbValue tree into preallocated buffer
+ */
+uint32
+compressJsonb(JsonbValue *v, char *buffer)
+{
+	uint32		l = 0;
+	CompressState state;
+
+	state.begin = state.ptr = buffer;
+	state.maxlevel = 8;
+	state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+
+	walkUncompressedJsonb(v, compressCallback, &state);
+
+	l = state.ptr - buffer;
+	Assert(l <= v->size);
+
+	return l;
+}
+
+/****************************************************************************
+ *					Iteration-like forming jsonb							*
+ ****************************************************************************/
+static ToJsonbState *
+pushState(ToJsonbState ** state)
+{
+	ToJsonbState *ns = palloc(sizeof(*ns));
+
+	ns->next = *state;
+	return ns;
+}
+
+static void
+appendArray(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *a = &state->v;
+
+	Assert(a->type == jbvArray);
+
+	if (a->array.nelems >= state->size)
+	{
+		state->size *= 2;
+		a->array.elems = repalloc(a->array.elems,
+								  sizeof(*a->array.elems) * state->size);
+	}
+
+	a->array.elems[a->array.nelems++] = *v;
+
+	a->size += v->size;
+}
+
+static void
+appendKey(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	if (h->hash.npairs >= state->size)
+	{
+		state->size *= 2;
+		h->hash.pairs = repalloc(h->hash.pairs,
+								 sizeof(*h->hash.pairs) * state->size);
+	}
+
+	h->hash.pairs[h->hash.npairs].key = *v;
+	h->hash.pairs[h->hash.npairs].order = h->hash.npairs;
+
+	h->size += v->size;
+}
+
+static void
+appendValue(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	h->hash.pairs[h->hash.npairs++].value = *v;
+
+	h->size += v->size;
+}
+
+/*
+ * Pushes the value into state. With r = WJB_END_OBJECT and v = NULL
+ * it will order and unique hash's keys otherwise we believe that
+ * pushed keys was ordered and unique.
+ * Initial state of ToJsonbState is NULL.
+ */
+JsonbValue *
+pushJsonbValue(ToJsonbState ** state, int r /* WJB_* */ , JsonbValue *v)
+{
+	JsonbValue *h = NULL;
+
+	switch (r)
+	{
+		case WJB_BEGIN_ARRAY:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvArray;
+			(*state)->v.size = 3 * sizeof(JEntry);
+			(*state)->v.array.nelems = 0;
+			(*state)->v.array.scalar = (v && v->array.scalar) ? true : false;
+			(*state)->size = (v && v->type == jbvArray && v->array.nelems > 0)
+				? v->array.nelems : 4;
+			(*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) *
+											 (*state)->size);
+			break;
+		case WJB_BEGIN_OBJECT:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvHash;
+			(*state)->v.size = 3 * sizeof(JEntry);
+			(*state)->v.hash.npairs = 0;
+			(*state)->size = (v && v->type == jbvHash && v->hash.npairs > 0) ?
+				v->hash.npairs : 4;
+			(*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) *
+											(*state)->size);
+			break;
+		case WJB_ELEM:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric ||
+				   v->type == jbvBinary);
+			appendArray(*state, v);
+			break;
+		case WJB_KEY:
+			Assert(v->type == jbvString);
+			appendKey(*state, v);
+			break;
+		case WJB_VALUE:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric ||
+				   v->type == jbvBinary);
+			appendValue(*state, v);
+			break;
+		case WJB_END_OBJECT:
+			h = &(*state)->v;
+			/* v != NULL => we believe that keys was already sorted */
+			if (v == NULL)
+				uniqueJsonbValue(h);
+
+			/*
+			 * no break here - end of hash requres some extra work but rest is
+			 * the same as for array
+			 */
+		case WJB_END_ARRAY:
+			h = &(*state)->v;
+
+			/*
+			 * pop stack and push current array/hash as value in parent
+			 * array/hash
+			 */
+			*state = (*state)->next;
+			if (*state)
+			{
+				switch ((*state)->v.type)
+				{
+					case jbvArray:
+						appendArray(*state, h);
+						break;
+					case jbvHash:
+						appendValue(*state, h);
+						break;
+					default:
+						elog(PANIC, "wrong parent type: %d", (*state)->v.type);
+				}
+			}
+			break;
+		default:
+			elog(PANIC, "wrong type: %08x", r);
+	}
+
+	return h;
+}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index e5b093e..76da613 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -51,6 +52,7 @@ static inline Datum get_path_all(PG_FUNCTION_ARGS, bool as_text);
 static inline text *get_worker(text *json, char *field, int elem_index,
 		   char **tpath, int *ipath, int npath,
 		   bool normalize_results);
+static inline Datum get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text);
 
 /* semantic action functions for json_array_length */
 static void alen_object_start(void *state);
@@ -59,6 +61,7 @@ static void alen_array_element_start(void *state, bool isnull);
 
 /* common worker for json_each* functions */
 static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text);
+static inline Datum each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text);
 
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
@@ -211,6 +214,9 @@ typedef struct PopulateRecordsetState
 	MemoryContext fn_mcxt;		/* used to stash IO funcs */
 } PopulateRecordsetState;
 
+/* turn a jsonb object into a record */
+static inline void make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state);
+
 /*
  * SQL function json_object-keys
  *
@@ -218,12 +224,89 @@ typedef struct PopulateRecordsetState
  *
  * This SRF operates in value-per-call mode. It processes the
  * object during the first call, and the keys are simply stashed
- * in an array, whise size is expanded as necessary. This is probably
+ * in an array, whose size is expanded as necessary. This is probably
  * safe enough for a list of keys of a single object, since they are
  * limited in size to NAMEDATALEN and the number of keys is unlikely to
  * be so huge that it has major memory implications.
  */
 
+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext oldcontext;
+		Jsonb	   *jb = PG_GETARG_JSONB(0);
+		bool		skipNested = false;
+		JsonbIterator *it;
+		JsonbValue	v;
+		int			r = 0;
+
+		if (JB_ROOT_IS_SCALAR(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on a scalar")));
+		else if (JB_ROOT_IS_ARRAY(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on an array")));
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc(sizeof(OkeysState));
+
+		state->result_size = JB_ROOT_COUNT(jb);
+		state->result_count = 0;
+		state->sent_count = 0;
+		state->result = palloc(state->result_size * sizeof(char *));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+
+			if (r == WJB_KEY)
+			{
+				char	   *cstr;
+
+				cstr = palloc(v.string.len + 1 * sizeof(char));
+				memcpy(cstr, v.string.val, v.string.len);
+				cstr[v.string.len] = '\0';
+				state->result[state->result_count++] = cstr;
+			}
+		}
+
+
+		MemoryContextSwitchTo(oldcontext);
+		funcctx->user_fctx = (void *) state;
+
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	state = (OkeysState *) funcctx->user_fctx;
+
+	if (state->sent_count < state->result_count)
+	{
+		char	   *nxt = state->result[state->sent_count++];
+
+		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+	}
+
+	/* cleanup to reduce or eliminate memory leaks */
+	for (i = 0; i < state->result_count; i++)
+		pfree(state->result[i]);
+	pfree(state->result);
+	pfree(state);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
@@ -336,9 +419,9 @@ okeys_scalar(void *state, char *token, JsonTokenType tokentype)
 }
 
 /*
- * json getter functions
+ * json and jsonb getter functions
  * these implement the -> ->> #> and #>> operators
- * and the json_extract_path*(json, text, ...) functions
+ * and the json{b?}_extract_path*(json, text, ...) functions
  */
 
 
@@ -359,6 +442,51 @@ json_object_field(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *key = text_to_cstring(PG_GETARG_TEXT_P(1));
+	int			klen = strlen(key);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field on a scalar")));
+	else if (JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field on an array")));
+
+	Assert(JB_ROOT_IS_OBJECT(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				/*
+				 * The next thing the iterator fetches should be the value, no
+				 * matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_object_field_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -375,6 +503,74 @@ json_object_field_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field_text(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *key = text_to_cstring(PG_GETARG_TEXT_P(1));
+	int			klen = strlen(key);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field_text on a scalar")));
+	else if (JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field_text on an array")));
+
+	Assert(JB_ROOT_IS_OBJECT(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				text	   *result;
+
+				/*
+				 * The next thing the iterator fetches should be the value, no
+				 * matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+
+				/*
+				 * if it's a scalar string it needs to be de-escaped,
+				 * otherwise just return the text
+				 */
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL();
+				}
+				else
+				{
+					StringInfo	jtext = makeStringInfo();
+					Jsonb	   *tjb = JsonbValueToJsonb(&v);
+
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_array_element(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -390,6 +586,44 @@ json_array_element(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+	int			element_number = 0;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element on a scalar")));
+	else if (JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element on an object")));
+
+	Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			if (element_number++ == element)
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_array_element_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -405,6 +639,69 @@ json_array_element_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element_text(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+	int			element_number = 0;
+
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element_text on a scalar")));
+	else if (JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("cannot call jsonb_array_element_text on an object")));
+
+	Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			if (element_number++ == element)
+			{
+				/*
+				 * if it's a scalar string it needs to be de-escaped,
+				 * otherwise just return the text
+				 */
+				text	   *result;
+
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL();
+				}
+				else
+				{
+					StringInfo	jtext = makeStringInfo();
+					Jsonb	   *tjb = JsonbValueToJsonb(&v);
+
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_extract_path(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, false);
@@ -422,7 +719,8 @@ json_extract_path_text(PG_FUNCTION_ARGS)
 static inline Datum
 get_path_all(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
@@ -434,6 +732,19 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	long		ind;
 	char	   *endptr;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb	   *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -472,9 +783,17 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
+	{
+		if (val_type == JSONOID || as_text)
+			PG_RETURN_TEXT_P(result);
+		else
+			PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	}
 	else
+	{
+		/* null is null regardless */
 		PG_RETURN_NULL();
+	}
 }
 
 /*
@@ -654,7 +973,7 @@ get_object_field_end(void *state, char *fname, bool isnull)
 		/*
 		 * make a text object from the string from the prevously noted json
 		 * start up to the end of the previous token (the lexer is by now
-		 * ahead of us on whatevere came after what we're interested in).
+		 * ahead of us on whatever came after what we're interested in).
 		 */
 		int			len = _state->lex->prev_token_terminator - _state->result_start;
 
@@ -808,18 +1127,134 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
 
 }
 
+Datum
+jsonb_extract_path(PG_FUNCTION_ARGS)
+{
+	return get_jsonb_path_all(fcinfo, false);
+}
+
+Datum
+jsonb_extract_path_text(PG_FUNCTION_ARGS)
+{
+	return get_jsonb_path_all(fcinfo, true);
+}
+
+static inline Datum
+get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	   *pathtext;
+	bool	   *pathnulls;
+	int			npath;
+	int			i;
+	Jsonb	   *res;
+	bool		have_object = false,
+				have_array = false;
+	JsonbValue *jbvp;
+	JsonbValue	tv;
+
+
+	if (array_contains_nulls(path))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call function with null path elements")));
+
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &pathtext, &pathnulls, &npath);
+
+	if (JB_ROOT_IS_OBJECT(jb))
+		have_object = true;
+	else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
+		have_array = true;
+
+	jbvp = (JsonbValue *) VARDATA(jb);
+
+	for (i = 0; i < npath; i++)
+	{
+		if (have_object)
+		{
+			jbvp = findUncompressedJsonbValue((char *) jbvp, JB_FLAG_OBJECT, NULL,
+											  VARDATA_ANY(pathtext[i]),
+											  VARSIZE_ANY_EXHDR(pathtext[i]));
+		}
+		else if (have_array)
+		{
+			long		lindex;
+			uint32		index;
+			char	   *indextext = TextDatumGetCString(pathtext[i]);
+			char	   *endptr;
+
+			lindex = strtol(indextext, &endptr, 10);
+			if (*endptr != '\0' || lindex > INT_MAX || lindex < 0)
+				PG_RETURN_NULL();
+			index = (uint32) lindex;
+			jbvp = getJsonbValue((char *) jbvp, JB_FLAG_ARRAY, index);
+		}
+		else
+		{
+			if (i == 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot call extract path from a scalar")));
+			PG_RETURN_NULL();
+		}
+		if (jbvp == NULL)
+			PG_RETURN_NULL();
+		if (i == npath - 1)
+			break;
+		if (jbvp->type == jbvBinary)
+		{
+			JsonbIterator *it = JsonbIteratorInit(jbvp->binary.data);
+			int			r;
+
+			r = JsonbIteratorGet(&it, &tv, true);
+			jbvp = (JsonbValue *) jbvp->binary.data;
+			have_object = r == WJB_BEGIN_OBJECT;
+			have_array = r == WJB_BEGIN_ARRAY;
+		}
+		else
+		{
+			have_object = jbvp->type == jbvHash;
+			have_array = jbvp->type == jbvArray;
+		}
+	}
+
+	if (as_text)
+	{
+		if (jbvp->type == jbvString)
+			PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->string.val, jbvp->string.len));
+		else if (jbvp->type == jbvNull)
+			PG_RETURN_NULL();
+	}
+
+	res = JsonbValueToJsonb(jbvp);
+
+	if (as_text)
+	{
+		PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(res)) ? NULL : VARDATA(res), VARSIZE(res))));
+	}
+	else
+	{
+		/* not text mode - just hand back the jsonb */
+		PG_RETURN_JSONB(res);
+	}
+}
+
 /*
  * SQL function json_array_length(json) -> int
  */
 Datum
 json_array_length(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
 	AlenState  *state;
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(AlenState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -839,6 +1274,23 @@ json_array_length(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(state->count);
 }
 
+Datum
+jsonb_array_length(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a scalar")));
+	else if (!JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a non-array")));
+
+	PG_RETURN_INT32(JB_ROOT_COUNT(jb));
+}
+
 /*
  * These next two check ensure that the json is an array (since it can't be
  * a scalar or an object).
@@ -895,24 +1347,43 @@ json_each(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_each(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, false);
+}
+
+Datum
 json_each_text(PG_FUNCTION_ARGS)
 {
 	return each_worker(fcinfo, true);
 }
 
+Datum
+jsonb_each_text(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, true);
+}
+
 static inline Datum
-each_worker(PG_FUNCTION_ARGS, bool as_text)
+each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	JsonLexContext *lex = makeJsonLexContext(json, true);
-	JsonSemAction *sem;
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
 	ReturnSetInfo *rsi;
-	MemoryContext old_cxt;
+	Tuplestorestate *tuple_store;
 	TupleDesc	tupdesc;
-	EachState  *state;
-
-	state = palloc0(sizeof(EachState));
-	sem = palloc0(sizeof(JsonSemAction));
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt,
+				tmp_cxt;
+	bool		skipNested = false;
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+
+	if (!JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_each%s on a non-object",
+						as_text ? "_text" : "")));
 
 	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 
@@ -929,20 +1400,150 @@ each_worker(PG_FUNCTION_ARGS, bool as_text)
 
 	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
 
-	/* make these in a sufficiently long-lived memory context */
 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
 
-	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
-	BlessTupleDesc(state->ret_tdesc);
-	state->tuple_store =
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
 							  false, work_mem);
 
 	MemoryContextSwitchTo(old_cxt);
 
-	sem->semstate = (void *) state;
-	sem->array_start = each_array_start;
-	sem->scalar = each_scalar;
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			text	   *key;
+			HeapTuple	tuple;
+			Datum		values[2];
+			bool		nulls[2] = {false, false};
+
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			key = cstring_to_text_with_len(v.string.val, v.string.len);
+
+			/*
+			 * The next thing the iterator fetches should be the value, no
+			 * matter what shape it is.
+			 */
+			r = JsonbIteratorGet(&it, &v, skipNested);
+
+			values[0] = PointerGetDatum(key);
+
+			if (as_text)
+			{
+				if (v.type == jbvNull)
+				{
+					/* a json null is an sql null in text mode */
+					nulls[1] = true;
+					values[1] = (Datum) NULL;
+				}
+				else
+				{
+					text	   *sv;
+
+					if (v.type == jbvString)
+					{
+						/* in text mode scalar strings should be dequoted */
+						sv = cstring_to_text_with_len(v.string.val, v.string.len);
+					}
+					else
+					{
+						/* turn anything else into a json string */
+						StringInfo	jtext = makeStringInfo();
+						Jsonb	   *jb = JsonbValueToJsonb(&v);
+
+						(void) JsonbToCString(jtext, VARDATA(jb), 2 * v.size);
+						sv = cstring_to_text_with_len(jtext->data, jtext->len);
+					}
+
+					values[1] = PointerGetDatum(sv);
+				}
+			}
+			else
+			{
+				/* not in text mode, just return the Jsonb */
+				Jsonb	   *val = JsonbValueToJsonb(&v);
+
+				values[1] = PointerGetDatum(val);
+			}
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();
+}
+
+
+static inline Datum
+each_worker(PG_FUNCTION_ARGS, bool as_text)
+{
+	text	   *json;
+	JsonLexContext *lex;
+	JsonSemAction *sem;
+	ReturnSetInfo *rsi;
+	MemoryContext old_cxt;
+	TupleDesc	tupdesc;
+	EachState  *state;
+
+	json = PG_GETARG_TEXT_P(0);
+
+	lex = makeJsonLexContext(json, true);
+	state = palloc0(sizeof(EachState));
+	sem = palloc0(sizeof(JsonSemAction));
+
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
+
+	/* make these in a sufficiently long-lived memory context */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(state->ret_tdesc);
+	state->tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	sem->semstate = (void *) state;
+	sem->array_start = each_array_start;
+	sem->scalar = each_scalar;
 	sem->object_field_start = each_object_field_start;
 	sem->object_field_end = each_object_field_end;
 
@@ -1067,19 +1668,114 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
  *
  * a lot of this processing is similar to the json_each* functions
  */
+
 Datum
-json_array_elements(PG_FUNCTION_ARGS)
+jsonb_array_elements(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	ReturnSetInfo *rsi;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt,
+				tmp_cxt;
+	bool		skipNested = false;
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from a scalar")));
+	else if (!JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from an object")));
 
-	/* elements doesn't need any escaped strings, so use false here */
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	/* it's a simple type, so don't use get_call_result_type() */
+	tupdesc = rsi->expectedDesc;
+
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			HeapTuple	tuple;
+			Datum		values[1];
+			bool		nulls[1] = {false};
+			Jsonb	   *val;
+
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			val = JsonbValueToJsonb(&v);
+			values[0] = PointerGetDatum(val);
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();
+}
+
+Datum
+json_array_elements(PG_FUNCTION_ARGS)
+{
+	text	   *json;
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	ElementsState *state;
 
+	json = PG_GETARG_TEXT_P(0);
+
+	/* elements doesn't need any escaped strings, so use false here */
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(ElementsState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -1211,15 +1907,24 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
  * which is in turn partly adapted from record_out.
  *
  * The json is decomposed into a hash table, in which each
- * field in the record is then looked up by name.
+ * field in the record is then looked up by name. For jsonb
+ * we fetch the values direct from the object.
  */
 Datum
+jsonb_populate_record(PG_FUNCTION_ARGS)
+{
+	return json_populate_record(fcinfo);
+}
+
+Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid			jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
+	Jsonb	   *jb = NULL;
 	bool		use_json_as_text;
-	HTAB	   *json_hash;
+	HTAB	   *json_hash = NULL;
 	HeapTupleHeader rec;
 	Oid			tupType;
 	int32		tupTypmod;
@@ -1231,15 +1936,15 @@ json_populate_record(PG_FUNCTION_ARGS)
 	int			i;
 	Datum	   *values;
 	bool	   *nulls;
-	char		fname[NAMEDATALEN];
-	JsonHashEntry *hashentry;
+
+	Assert(jtype == JSONOID || jtype == JSONBOID);
 
 	use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
 	if (!type_is_rowtype(argtype))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
-		errmsg("first argument of json_populate_record must be a row type")));
+				 errmsg("first argument of json%s_populate_record must be a row type", jtype == JSONBOID ? "b" : "")));
 
 	if (PG_ARGISNULL(0))
 	{
@@ -1268,18 +1973,31 @@ json_populate_record(PG_FUNCTION_ARGS)
 		tupTypmod = HeapTupleHeaderGetTypMod(rec);
 	}
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
 
-	json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
+		json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
 
-	/*
-	 * if the input json is empty, we can only skip the rest if we were passed
-	 * in a non-null record, since otherwise there may be issues with domain
-	 * nulls.
-	 */
-	if (hash_get_num_entries(json_hash) == 0 && rec)
-		PG_RETURN_POINTER(rec);
+		/*
+		 * if the input json is empty, we can only skip the rest if we were
+		 * passed in a non-null record, since otherwise there may be issues
+		 * with domain nulls.
+		 */
+		if (hash_get_num_entries(json_hash) == 0 && rec)
+			PG_RETURN_POINTER(rec);
+
+	}
+	else
+	{
+		jb = PG_GETARG_JSONB(1);
 
+		/* same logic as for json */
+		if (JB_ISEMPTY(jb) && rec)
+			PG_RETURN_POINTER(rec);
+
+	}
 
 	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 	ncolumns = tupdesc->natts;
@@ -1342,7 +2060,9 @@ json_populate_record(PG_FUNCTION_ARGS)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
-		char	   *value;
+		JsonbValue *v = NULL;
+		char		fname[NAMEDATALEN];
+		JsonHashEntry *hashentry = NULL;
 
 		/* Ignore dropped columns in datatype */
 		if (tupdesc->attrs[i]->attisdropped)
@@ -1351,9 +2071,22 @@ json_populate_record(PG_FUNCTION_ARGS)
 			continue;
 		}
 
-		memset(fname, 0, NAMEDATALEN);
-		strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
-		hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+		if (jtype == JSONOID)
+		{
+
+			memset(fname, 0, NAMEDATALEN);
+			strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
+			hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+		}
+		else
+		{
+			if (!JB_ISEMPTY(jb))
+			{
+				char	   *key = NameStr(tupdesc->attrs[i]->attname);
+
+				v = findUncompressedJsonbValue(VARDATA(jb), JB_FLAG_OBJECT, NULL, key, strlen(key));
+			}
+		}
 
 		/*
 		 * we can't just skip here if the key wasn't found since we might have
@@ -1363,7 +2096,8 @@ json_populate_record(PG_FUNCTION_ARGS)
 		 * then every field which we don't populate needs to be run through
 		 * the input function just in case it's a domain type.
 		 */
-		if (hashentry == NULL && rec)
+		if (((jtype == JSONOID && hashentry == NULL) ||
+			 (jtype == JSONBOID && v == NULL)) && rec)
 			continue;
 
 		/*
@@ -1378,7 +2112,8 @@ json_populate_record(PG_FUNCTION_ARGS)
 						  fcinfo->flinfo->fn_mcxt);
 			column_info->column_type = column_type;
 		}
-		if (hashentry == NULL || hashentry->isnull)
+		if ((jtype == JSONOID && (hashentry == NULL || hashentry->isnull)) ||
+			(jtype == JSONBOID && (v == NULL || v->type == jbvNull)))
 		{
 			/*
 			 * need InputFunctionCall to happen even for nulls, so that domain
@@ -1388,12 +2123,39 @@ json_populate_record(PG_FUNCTION_ARGS)
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = true;
+
 		}
 		else
 		{
-			value = hashentry->val;
 
-			values[i] = InputFunctionCall(&column_info->proc, value,
+			char	   *s = NULL;
+
+			if (jtype == JSONOID)
+			{
+				/* already done the hard work in the json case */
+				s = hashentry->val;
+			}
+			else
+			{
+				if (v->type == jbvString)
+					s = pnstrdup(v->string.val, v->string.len);
+				else if (v->type == jbvBool)
+					s = pnstrdup((v->boolean) ? "t" : "f", 1);
+				else if (v->type == jbvNumeric)
+					s = DatumGetCString(DirectFunctionCall1(numeric_out,
+											   PointerGetDatum(v->numeric)));
+				else if (!use_json_as_text)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("cannot populate with a nested object unless use_json_as_text is true")));
+				else if (v->type == jbvBinary)
+					s = JsonbToCString(NULL, v->binary.data, v->binary.len);
+				else
+					/* not expected to happen */
+					elog(ERROR, "Wrong jsonb");
+			}
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = false;
@@ -1559,10 +2321,140 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
  * per object in the array.
  */
 Datum
+jsonb_populate_recordset(PG_FUNCTION_ARGS)
+{
+	return json_populate_recordset(fcinfo);
+}
+
+static inline void
+make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state)
+{
+	Datum	   *values;
+	bool	   *nulls;
+	int			i;
+	RecordIOData *my_extra = state->my_extra;
+	int			ncolumns = my_extra->ncolumns;
+	TupleDesc	tupdesc = state->ret_tdesc;
+	HeapTupleHeader rec = state->rec;
+	HeapTuple	rettuple;
+
+	values = (Datum *) palloc(ncolumns * sizeof(Datum));
+	nulls = (bool *) palloc(ncolumns * sizeof(bool));
+
+	if (state->rec)
+	{
+		HeapTupleData tuple;
+
+		/* Build a temporary HeapTuple control structure */
+		tuple.t_len = HeapTupleHeaderGetDatumLength(state->rec);
+		ItemPointerSetInvalid(&(tuple.t_self));
+		tuple.t_tableOid = InvalidOid;
+		tuple.t_data = state->rec;
+
+		/* Break down the tuple into fields */
+		heap_deform_tuple(&tuple, tupdesc, values, nulls);
+	}
+	else
+	{
+		for (i = 0; i < ncolumns; ++i)
+		{
+			values[i] = (Datum) 0;
+			nulls[i] = true;
+		}
+	}
+
+	for (i = 0; i < ncolumns; ++i)
+	{
+		ColumnIOData *column_info = &my_extra->columns[i];
+		Oid			column_type = tupdesc->attrs[i]->atttypid;
+		JsonbValue *v = NULL;
+
+		/* Ignore dropped columns in datatype */
+		if (tupdesc->attrs[i]->attisdropped)
+		{
+			nulls[i] = true;
+			continue;
+		}
+
+		if (!JB_ISEMPTY(element))
+		{
+			char	   *key = NameStr(tupdesc->attrs[i]->attname);
+
+			v = findUncompressedJsonbValue(VARDATA(element), JB_FLAG_OBJECT, NULL, key, strlen(key));
+		}
+
+		/*
+		 * we can't just skip here if the key wasn't found since we might have
+		 * a domain to deal with. If we were passed in a non-null record
+		 * datum, we assume that the existing values are valid (if they're
+		 * not, then it's not our fault), but if we were passed in a null,
+		 * then every field which we don't populate needs to be run through
+		 * the input function just in case it's a domain type.
+		 */
+		if (v == NULL && rec)
+			continue;
+
+		/*
+		 * Prepare to convert the column value from text
+		 */
+		if (column_info->column_type != column_type)
+		{
+			getTypeInputInfo(column_type,
+							 &column_info->typiofunc,
+							 &column_info->typioparam);
+			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
+						  state->fn_mcxt);
+			column_info->column_type = column_type;
+		}
+		if (v == NULL || v->type == jbvNull)
+		{
+			/*
+			 * need InputFunctionCall to happen even for nulls, so that domain
+			 * checks are done
+			 */
+			values[i] = InputFunctionCall(&column_info->proc, NULL,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = true;
+		}
+		else
+		{
+			char	   *s = NULL;
+
+			if (v->type == jbvString)
+				s = pnstrdup(v->string.val, v->string.len);
+			else if (v->type == jbvBool)
+				s = pnstrdup((v->boolean) ? "t" : "f", 1);
+			else if (v->type == jbvNumeric)
+				s = DatumGetCString(DirectFunctionCall1(numeric_out,
+											   PointerGetDatum(v->numeric)));
+			else if (!state->use_json_as_text)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot populate with a nested object unless use_json_as_text is true")));
+			else if (v->type == jbvBinary)
+				s = JsonbToCString(NULL, v->binary.data, v->binary.len);
+			else
+				/* not expected to happen */
+				elog(ERROR, "Wrong jsonb");
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = false;
+		}
+	}
+
+	rettuple = heap_form_tuple(tupdesc, values, nulls);
+
+	tuplestore_puttuple(state->tuple_store, rettuple);
+}
+
+Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
-	text	   *json;
+	Oid			jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	bool		use_json_as_text;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
@@ -1572,8 +2464,6 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	RecordIOData *my_extra;
 	int			ncolumns;
-	JsonLexContext *lex;
-	JsonSemAction *sem;
 	PopulateRecordsetState *state;
 
 	use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
@@ -1602,27 +2492,10 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 	 */
 	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
 
-	state = palloc0(sizeof(PopulateRecordsetState));
-	sem = palloc0(sizeof(JsonSemAction));
-
-
-	/* make these in a sufficiently long-lived memory context */
-	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
-	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
-	BlessTupleDesc(state->ret_tdesc);
-	state->tuple_store =
-		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
-							  false, work_mem);
-
-	MemoryContextSwitchTo(old_cxt);
-
 	/* if the json is null send back an empty set */
 	if (PG_ARGISNULL(1))
 		PG_RETURN_NULL();
 
-	json = PG_GETARG_TEXT_P(1);
-
 	if (PG_ARGISNULL(0))
 		rec = NULL;
 	else
@@ -1632,8 +2505,6 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 	tupTypmod = tupdesc->tdtypmod;
 	ncolumns = tupdesc->natts;
 
-	lex = makeJsonLexContext(json, true);
-
 	/*
 	 * We arrange to look up the needed I/O info just once per series of
 	 * calls, assuming the record type doesn't change underneath us.
@@ -1662,23 +2533,81 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	sem->semstate = (void *) state;
-	sem->array_start = populate_recordset_array_start;
-	sem->array_element_start = populate_recordset_array_element_start;
-	sem->scalar = populate_recordset_scalar;
-	sem->object_field_start = populate_recordset_object_field_start;
-	sem->object_field_end = populate_recordset_object_field_end;
-	sem->object_start = populate_recordset_object_start;
-	sem->object_end = populate_recordset_object_end;
+	state = palloc0(sizeof(PopulateRecordsetState));
 
-	state->lex = lex;
+	/* make these in a sufficiently long-lived memory context */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+	state->ret_tdesc = CreateTupleDescCopy(tupdesc);;
+	BlessTupleDesc(state->ret_tdesc);
+	state->tuple_store = tuplestore_begin_heap(rsi->allowedModes &
+											   SFRM_Materialize_Random,
+											   false, work_mem);
+	MemoryContextSwitchTo(old_cxt);
 
 	state->my_extra = my_extra;
 	state->rec = rec;
 	state->use_json_as_text = use_json_as_text;
 	state->fn_mcxt = fcinfo->flinfo->fn_mcxt;
 
-	pg_parse_json(lex, sem);
+
+	if (jtype == JSONOID)
+	{
+		text	   *json = PG_GETARG_TEXT_P(1);
+		JsonLexContext *lex;
+		JsonSemAction *sem;
+
+		sem = palloc0(sizeof(JsonSemAction));
+
+		lex = makeJsonLexContext(json, true);
+
+		sem->semstate = (void *) state;
+		sem->array_start = populate_recordset_array_start;
+		sem->array_element_start = populate_recordset_array_element_start;
+		sem->scalar = populate_recordset_scalar;
+		sem->object_field_start = populate_recordset_object_field_start;
+		sem->object_field_end = populate_recordset_object_field_end;
+		sem->object_start = populate_recordset_object_start;
+		sem->object_end = populate_recordset_object_end;
+
+		state->lex = lex;
+
+		pg_parse_json(lex, sem);
+
+	}
+	else
+	{
+		Jsonb	   *jb;
+		JsonbIterator *it;
+		JsonbValue	v;
+		bool		skipNested = false;
+		int			r;
+
+		Assert(jtype == JSONBOID);
+		jb = PG_GETARG_JSONB(1);
+
+		if (JB_ROOT_IS_SCALAR(jb) || !JB_ROOT_IS_ARRAY(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("cannot call jsonb_populate_recordset on non-array")));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+
+			if (r == WJB_ELEM)
+			{
+				Jsonb	   *element = JsonbValueToJsonb(&v);
+
+				if (!JB_ROOT_IS_OBJECT(element))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("jsonb_populate_recordset argument must be an array of objects")));
+				make_row_from_rec_and_jsonb(element, state);
+			}
+		}
+	}
 
 	rsi->setResult = state->tuple_store;
 	rsi->setDesc = state->ret_tdesc;
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6aa4890..143a451 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1753,6 +1753,18 @@ DATA(insert OID = 3966 (  "#>"	   PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
 DESCR("get value from json with path elements");
 DATA(insert OID = 3967 (  "#>>"    PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
 DESCR("get value from json as text with path elements");
+DATA(insert OID = 3211 (  "->"	   PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3204 (  "->>"    PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3212 (  "->"	   PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3205 (  "->>"    PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3213 (  "#>"	   PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3206 (  "#>>"    PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
+DESCR("get value from jsonb as text with path elements");
 
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ad9774c..1e12d8e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4459,6 +4459,44 @@ DESCR("I/O");
 DATA(insert OID = 3774 (  regdictionarysend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3769" _null_ _null_ _null_ _null_ regdictionarysend _null_ _null_ _null_ ));
 DESCR("I/O");
 
+/* jsonb */
+DATA(insert OID =  3806 (  jsonb_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2275" _null_ _null_ _null_ _null_ jsonb_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3805 (  jsonb_recv		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3804 (  jsonb_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3802" _null_ _null_ _null_ _null_ jsonb_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
+DESCR("I/O");
+
+DATA(insert OID = 3969 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
+DATA(insert OID = 3214 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
+DATA(insert OID = 3180 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
+DATA(insert OID = 3195 (  jsonb_array_element_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element_text _null_ _null_ _null_ ));
+DATA(insert OID = 3196 (  jsonb_extract_path			PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 3802 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3199 (  jsonb_extract_path_op		PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DATA(insert OID = 3200 (  jsonb_extract_path_text	PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3197 (  jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DATA(insert OID = 3198 (  jsonb_array_elements		PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3207 (  jsonb_array_length			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "3802" _null_ _null_ _null_ _null_ jsonb_array_length _null_ _null_ _null_ ));
+DESCR("length of jsonb array");
+DATA(insert OID = 3201 (  jsonb_object_keys			PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_object_keys _null_ _null_ _null_ ));
+DESCR("get jsonb object keys");
+DATA(insert OID = 3208 (  jsonb_each				   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,3802}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3202 (  jsonb_each_text		   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,25}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each_text _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3209 (  jsonb_populate_record	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_record _null_ _null_ _null_ ));
+DESCR("get record fields from a jsonb object");
+DATA(insert OID = 3203 (  jsonb_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ ));
+DESCR("get set of records with fields from a jsonb array of objects");
+DATA(insert OID = 3210 (  jsonb_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a jsonb value");
+
+
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in			PGNSP PGUID 12 1  0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 3fc20c6..7fb8999 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -600,6 +600,12 @@ DATA(insert OID = 3645 ( _tsquery		PGNSP PGUID -1 f b A f t \054 0 3615 0 array_
 DATA(insert OID = 3735 ( _regconfig		PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 
+/* jsonb */
+DATA(insert OID = 3802 ( jsonb			PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DESCR("Binary JSON");
+#define JSONBOID 3802
+DATA(insert OID = 3807 ( _jsonb			PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 9982e59..3610fc8 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		PG_RETURN_DATUM(_result); \
 	} while (0)
 
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+	do { \
+		ReturnSetInfo	   *rsi; \
+		(_funcctx)->call_cntr++; \
+		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+		rsi->isDone = ExprMultipleResult; \
+		PG_RETURN_NULL(); \
+	} while (0)
+
 #define  SRF_RETURN_DONE(_funcctx) \
 	do { \
 		ReturnSetInfo	   *rsi; \
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index 25bfafb..c2fc7ee 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -50,4 +50,18 @@ extern Datum json_array_elements(PG_FUNCTION_ARGS);
 extern Datum json_populate_record(PG_FUNCTION_ARGS);
 extern Datum json_populate_recordset(PG_FUNCTION_ARGS);
 
+extern Datum jsonb_object_field(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_field_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_keys(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_length(PG_FUNCTION_ARGS);
+extern Datum jsonb_each(PG_FUNCTION_ARGS);
+extern Datum jsonb_each_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_elements(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_record(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index 0000000..50509e2
--- /dev/null
+++ b/src/include/utils/jsonb.h
@@ -0,0 +1,241 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.h
+ *	  Declarations for JSONB data type support.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/include/utils/jsonb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __JSONB_H__
+#define __JSONB_H__
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "utils/array.h"
+#include "utils/numeric.h"
+
+/*
+ * JEntry: there is one of these for each key _and_ value in an jsonb
+ *
+ * the position offset points to the _end_ so that we can get the length
+ * by subtraction from the previous entry.	the ISFIRST flag lets us tell
+ * whether there is a previous entry.
+ */
+typedef struct
+{
+	uint32		entry;
+}	JEntry;
+
+#define JENTRY_ISFIRST		0x80000000
+#define JENTRY_ISSTRING		(0x00000000)		/* keep binary compatibility */
+#define JENTRY_ISNUMERIC	(0x10000000)
+#define JENTRY_ISNEST		(0x20000000)
+#define JENTRY_ISNULL		(0x40000000)		/* keep binary compatibility */
+#define JENTRY_ISBOOL		(0x10000000 | 0x20000000)
+#define JENTRY_ISFALSE		JENTRY_ISBOOL
+#define JENTRY_ISTRUE		(0x10000000 | 0x20000000 | 0x40000000)
+
+/* JENTRY_ISOBJECT, JENTRY_ISARRAY and JENTRY_ISCALAR is only used in send/recv */
+#define JENTRY_ISOBJECT		(0x20000000)
+#define JENTRY_ISARRAY		(0x20000000 | 0x40000000)
+#define JENTRY_ISCALAR		(0x10000000 | 0x40000000)
+
+#define JENTRY_POSMASK	0x0FFFFFFF
+#define JENTRY_TYPEMASK (~(JENTRY_POSMASK | JENTRY_ISFIRST))
+
+/* note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(he_)		(((he_).entry & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISNEST(he_)			(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST)
+#define JBE_ISNULL(he_)			(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL(he_)			(((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
+#define JBE_ISBOOL_TRUE(he_)	(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
+#define JBE_ISBOOL_FALSE(he_)	(JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
+
+#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK)
+#define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1]))
+#define JBE_LEN(he_) (JBE_ISFIRST(he_)	\
+					  ? JBE_ENDPOS(he_) \
+					  : JBE_ENDPOS(he_) - JBE_ENDPOS((&(he_))[-1]))
+
+/*
+ * determined by the size of "endpos" (ie JENTRY_POSMASK)
+ */
+#define JSONB_MAX_STRING_LEN		JENTRY_POSMASK
+
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* header of hash or array jsonb type */
+	/* array of JEntry follows */
+} Jsonb;
+
+/*
+ * it's not possible to get more than 2^28 items into an jsonb.
+ */
+#define JB_FLAG_UNUSED			0x80000000
+#define JB_FLAG_ARRAY			0x40000000
+#define JB_FLAG_OBJECT			0x20000000
+#define JB_FLAG_SCALAR			0x10000000
+
+#define JB_COUNT_MASK			0x0FFFFFFF
+
+#define JB_ISEMPTY(jbp_)		(VARSIZE(jbp_) <= VARHDRSZ)
+#define JB_ROOT_COUNT(jbp_)		(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_COUNT_MASK))
+#define JB_ROOT_IS_OBJECT(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_OBJECT))
+#define JB_ROOT_IS_ARRAY(jbp_)	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_ARRAY))
+#define JB_ROOT_IS_SCALAR(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_SCALAR))
+
+#define DatumGetJsonb(d)	((Jsonb*) PG_DETOAST_DATUM(d))
+#define JsonbGetDatum(p)	PointerGetDatum(p)
+
+#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
+
+typedef struct JsonbPair JsonbPair;
+typedef struct JsonbValue JsonbValue;
+
+struct JsonbValue
+{
+	enum
+	{
+		jbvNull,
+		jbvString,
+		jbvNumeric,
+		jbvBool,
+		jbvArray,
+		jbvHash,
+		jbvBinary				/* binary form of jbvArray/jbvHash */
+	}			type;
+
+	uint32		size;			/* estimation size of node (including
+								 * subnodes) */
+
+	union
+	{
+		Numeric numeric;
+		bool		boolean;
+		struct
+		{
+			uint32		len;
+			char	   *val;	/* could be not null-terminated */
+		}			string;
+
+		struct
+		{
+			int			nelems;
+			JsonbValue *elems;
+			bool		scalar; /* scalar actually shares representation with
+								 * array */
+		}			array;
+
+		struct
+		{
+			int			npairs;
+			JsonbPair  *pairs;
+		}			hash;
+
+		struct
+		{
+			uint32		len;
+			char	   *data;
+		}			binary;
+	};
+
+};
+
+struct JsonbPair
+{
+	JsonbValue	key;
+	JsonbValue	value;
+	uint32		order;			/* to keep order of pairs with equal key */
+};
+
+/*
+ * jsonb support functios
+ */
+
+#define WJB_KEY				(0x001)
+#define WJB_VALUE			(0x002)
+#define WJB_ELEM			(0x004)
+#define WJB_BEGIN_ARRAY		(0x008)
+#define WJB_END_ARRAY		(0x010)
+#define WJB_BEGIN_OBJECT	(0x020)
+#define WJB_END_OBJECT		(0x040)
+
+typedef void (*walk_jsonb_cb) (void * /* arg */ , JsonbValue * /* value */ ,
+								   uint32 /* flags */ , uint32 /* level */ );
+extern void walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg);
+
+extern int	compareJsonbStringValue(const void *a, const void *b, void *arg);
+extern int	compareJsonbPair(const void *a, const void *b, void *arg);
+
+extern int	compareJsonbBinaryValue(char *a, char *b);
+extern int	compareJsonbValue(JsonbValue *a, JsonbValue *b);
+
+extern JsonbValue *findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key);
+extern JsonbValue *findUncompressedJsonbValue(char *buffer, uint32 flags,
+						   uint32 *lowbound, char *key, uint32 keylen);
+
+extern JsonbValue *getJsonbValue(char *buffer, uint32 flags, int32 i);
+
+typedef struct ToJsonbState
+{
+	JsonbValue	v;
+	uint32		size;
+	struct ToJsonbState *next;
+}	ToJsonbState;
+
+extern JsonbValue *pushJsonbValue(ToJsonbState ** state, int r /* WJB_* */ , JsonbValue *v);
+
+extern void uniqueJsonbValue(JsonbValue *v);
+
+extern uint32 compressJsonb(JsonbValue *v, char *buffer);
+
+typedef struct JsonbIterator
+{
+	uint32		type;
+	uint32		nelems;
+	JEntry	   *array;
+	bool		isScalar;
+	char	   *data;
+	char	   *buffer;			/* unparsed buffer */
+
+	int			i;
+
+	/*
+	 * enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB
+	 * with possiblity of decoding. See optimization in JsonbIteratorGet()
+	 */
+	enum
+	{
+		jbi_start = 0x00,
+		jbi_key = 0x01,
+		jbi_value = 0x02,
+		jbi_elem = 0x04
+	} state;
+
+	struct JsonbIterator *next;
+} JsonbIterator;
+
+extern JsonbIterator *JsonbIteratorInit(char *buffer);
+extern int /* WJB_* */ JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested);
+
+extern Datum jsonb_in(PG_FUNCTION_ARGS);
+extern Datum jsonb_out(PG_FUNCTION_ARGS);
+extern Datum jsonb_recv(PG_FUNCTION_ARGS);
+extern Datum jsonb_send(PG_FUNCTION_ARGS);
+
+extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
+
+extern char *JsonbToCString(StringInfo out, char *in, int estimated_len);
+extern Jsonb *JsonbValueToJsonb(JsonbValue *v);
+
+#endif   /* __JSONB_H__ */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 0000000..cb6b4a3
--- /dev/null
+++ b/src/test/regress/expected/jsonb.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ octet_length 
+--------------
+            5
+(1 row)
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ correct_in_utf8 
+-----------------
+              10
+(1 row)
+
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+   correct_in_utf8    
+----------------------
+ the Copyright © sign
+(1 row)
+
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 0000000..8fae7c2
--- /dev/null
+++ b/src/test/regress/expected/jsonb_1.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT octet_length('"\uaBcD"'::jsonb::text);
+                            ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: ...
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc3...
+                                   ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a'...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5758b07..51238be 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,8 +98,7 @@ test: event_trigger
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
-
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 78348f5..e414ec1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -121,6 +121,7 @@ test: xmlmap
 test: functional_deps
 test: advisory_lock
 test: json
+test: jsonb
 test: indirect_toast
 test: plancache
 test: limit
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 0000000..38959a8
--- /dev/null
+++ b/src/test/regress/sql/jsonb.sql
@@ -0,0 +1,265 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+SELECT '"abc"'::jsonb;			-- OK
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+SELECT '0'::jsonb;				-- OK
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+SELECT '0.1'::jsonb;				-- OK
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+SELECT '1e100'::jsonb;			-- OK
+SELECT '1.3e100'::jsonb;			-- OK
+SELECT '1f2'::jsonb;				-- ERROR
+SELECT '0.x1'::jsonb;			-- ERROR
+SELECT '1.3ex100'::jsonb;		-- ERROR
+
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+SELECT '[1,2]'::jsonb;			-- OK
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+SELECT '{"abc":1}'::jsonb;		-- OK
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+SELECT 'false'::jsonb;			-- OK
+SELECT 'null'::jsonb;			-- OK
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+SELECT ''::jsonb;				-- ERROR, no value
+SELECT '    '::jsonb;			-- ERROR, no value
+
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+
+
+-- jsonb extraction functions
+
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+
+-- nulls
+
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+
+
+-- array length
+
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT jsonb_array_length('[]');
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT jsonb_array_length('4');
+
+-- each
+
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+-- extract_path, extract_path_as_text
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- extract_path nulls
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- extract_path operators
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+
+-- array_elements
+
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+
+-- populate_recordset
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+
+-- using the default use_json_as_text argument
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+
+
+-- handling of unicode surrogate pairs
+
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+
+--handling of simple unicode escapes
+
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
#3Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#2)
Re: jsonb and nested hstore

On Mon, Jan 27, 2014 at 9:43 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and compressed
them. The jsonb patch should work standalone. The nested hstore patch
depends on it.

All the jsonb functions now use the jsonb API - there is no more turning
jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up some
jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Don't have time to fire it up this morning, but a quick scan of the
patch turned up a few minor things:

* see a comment typo, line 290 'jsonn':
* line 332: 'bogus input' -- is this up to error reporting standards?
How about "value 'x' must be one of array, object, numeric, string,
bool"?
* line 357: "jsonb's key could be only a string" prefer non
possessive: jsonb keys must be a string
* line 374, 389: ditto 332
* line 513: is panic appropriate here?
* line 599: ditto
* line 730: odd phrasing in comment, also commenting on this function
is a little light
* line 807: slightly prefer 'with respect to'
* line 888: another PANIC: these maybe correct, seems odd to halt
server on corrupted datum though*
* line 1150: hm, is the jsonb internal hash structure documented?
Aside: why didn't we use standard hash table (performance maybe)?
* line 1805-6: poor phrasing. How about: "it will order and make
unique the hash keys. Otherwise we believe that pushed keys are
ordered and unique. (Don't like verbed 'unqiue').
* line 1860: "no break here: "

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#3)
Re: jsonb and nested hstore

On 01/28/2014 09:38 AM, Merlin Moncure wrote:

On Mon, Jan 27, 2014 at 9:43 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and compressed
them. The jsonb patch should work standalone. The nested hstore patch
depends on it.

All the jsonb functions now use the jsonb API - there is no more turning
jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up some
jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Don't have time to fire it up this morning, but a quick scan of the
patch turned up a few minor things:

* see a comment typo, line 290 'jsonn':
* line 332: 'bogus input' -- is this up to error reporting standards?
How about "value 'x' must be one of array, object, numeric, string,
bool"?
* line 357: "jsonb's key could be only a string" prefer non
possessive: jsonb keys must be a string
* line 374, 389: ditto 332
* line 513: is panic appropriate here?
* line 599: ditto
* line 730: odd phrasing in comment, also commenting on this function
is a little light
* line 807: slightly prefer 'with respect to'
* line 888: another PANIC: these maybe correct, seems odd to halt
server on corrupted datum though*
* line 1150: hm, is the jsonb internal hash structure documented?
Aside: why didn't we use standard hash table (performance maybe)?
* line 1805-6: poor phrasing. How about: "it will order and make
unique the hash keys. Otherwise we believe that pushed keys are
ordered and unique. (Don't like verbed 'unqiue').
* line 1860: "no break here: "

Looks like this review is against jsonb-5, not jsonb-6.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#4)
Re: jsonb and nested hstore

Looks like this review is against jsonb-5, not jsonb-6.

oh yep -- shoot, sorry for the noise.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andrew Dunstan (#2)
Re: jsonb and nested hstore

Andrew Dunstan wrote:

<para>
+    There are two JSON data types: <type>json</type> and <type>jsonb</type>.
+    Both accept identical sets of values as input. The difference is primarily
+    a matter of efficiency. The <type>json</type> data type stores an exact
+    copy of the the input text, and the processing functions have to reparse
+    it to precess it, while the <type>jsonb</type> is stored in a decomposed
+    form that makes it slightly less efficient to input but very much faster
+    to process, since it never needs reparsing.
+   </para>

typo "precess"
duplicated word "of the the input"

+         </indexterm><indexterm>
+          <primary>jsonb_each</primary>
+         </indexterm><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>

This SGML nesting is odd and hard to read. Please place opening tags in
separate lines (or at least not immediately following a closing tag). I
am not sure whether the mentions of jsonb_each vs. json_each there are
correct or typos. This also occurs in other places.

Expands the object in <replaceable>from_json</replaceable> to a row whose columns match
the record type defined by base. Conversion will be best
effort; columns in base with no corresponding key in <replaceable>from_json</replaceable>
-         will be left null. If a column is specified more than once, the last value is used.
+         will be left null. When processing <type>json</type>, if a column is 
+         specified more than once, the last value is used.

Maybe you also need to specify what happens with jsonb?

diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..107ebf0
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,544 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group

2014. Why "Portions", if we don't attribute any portion to UCB?

+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/backend/utils/adt/jsonb_support.c

Typo'ed name here.

+#include "postgres.h"

+

+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"

Misplaced prototype?

+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);

Not sure about the jsonb_1.out file. Is that only due to encoding
differences? What happens if you run it in a completely different
encoding than whatever you tested with? (I would assume Latin-9 and
UTF8) If it fails, then I think you'll end up ripping those tests out,
so probably the _1.out file will have no value at all.

I also wonder if it'd be better to have one large .sql file that
produces the same output in all platforms that tests most of the common
stuff, so that tests that changes output in different platforms can have
smaller alternative expected files.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Andrew Dunstan
andrew@dunslane.net
In reply to: Alvaro Herrera (#6)
Re: jsonb and nested hstore

On 01/28/2014 10:50 AM, Alvaro Herrera wrote:

+         </indexterm><indexterm>
+          <primary>jsonb_each</primary>
+         </indexterm><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>

This SGML nesting is odd and hard to read. Please place opening tags in
separate lines (or at least not immediately following a closing tag). I
am not sure whether the mentions of jsonb_each vs. json_each there are
correct or typos. This also occurs in other places.

As I understand it, an <entry> tag can only contain block-level elements
like <para> if there are no inline elements (including white space).

If that's not correct I'll change it, but that's what I read here:
<http://oreilly.com/openbook/docbook/book/entry.html&gt;

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#7)
Re: jsonb and nested hstore

Andrew Dunstan <andrew@dunslane.net> writes:

On 01/28/2014 10:50 AM, Alvaro Herrera wrote:
+         </indexterm><indexterm>
+          <primary>jsonb_each</primary>
+         </indexterm><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>

This SGML nesting is odd and hard to read. Please place opening tags in
separate lines (or at least not immediately following a closing tag). I
am not sure whether the mentions of jsonb_each vs. json_each there are
correct or typos. This also occurs in other places.

As I understand it, an <entry> tag can only contain block-level elements
like <para> if there are no inline elements (including white space).

Practically every existing use of <indexterm> is freer than this in its
use of whitespace. It sounds to me like maybe you are trying to put the
<indexterm> inside something it shouldn't go inside of.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tom Lane (#8)
Re: jsonb and nested hstore

Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 01/28/2014 10:50 AM, Alvaro Herrera wrote:
+         </indexterm><indexterm>
+          <primary>jsonb_each</primary>
+         </indexterm><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>

This SGML nesting is odd and hard to read. Please place opening tags in
separate lines (or at least not immediately following a closing tag). I
am not sure whether the mentions of jsonb_each vs. json_each there are
correct or typos. This also occurs in other places.

As I understand it, an <entry> tag can only contain block-level elements
like <para> if there are no inline elements (including white space).

Practically every existing use of <indexterm> is freer than this in its
use of whitespace. It sounds to me like maybe you are trying to put the
<indexterm> inside something it shouldn't go inside of.

FWIW I was just talking about formatting of the SGML source so that it
is easier to read.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#8)
Re: jsonb and nested hstore

On 01/28/2014 11:09 AM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 01/28/2014 10:50 AM, Alvaro Herrera wrote:
+         </indexterm><indexterm>
+          <primary>jsonb_each</primary>
+         </indexterm><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>

This SGML nesting is odd and hard to read. Please place opening tags in
separate lines (or at least not immediately following a closing tag). I
am not sure whether the mentions of jsonb_each vs. json_each there are
correct or typos. This also occurs in other places.

As I understand it, an <entry> tag can only contain block-level elements
like <para> if there are no inline elements (including white space).

Practically every existing use of <indexterm> is freer than this in its
use of whitespace. It sounds to me like maybe you are trying to put the
<indexterm> inside something it shouldn't go inside of.

The problem is not the indexterm element, it's the space that might
exist outside it. Are we using block level elements like <para> inside
entry elements anywhere else? If not, then your observation is not
relevant. If there are no block level elements then AIUI we can space
things out how we like inside the entry element.

If you can show me how else legally to get a line break inside an entry
element I'm very interested. I tried several things before I found this
way of making it work.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#9)
Re: jsonb and nested hstore

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Tom Lane wrote:

Practically every existing use of <indexterm> is freer than this in its
use of whitespace. It sounds to me like maybe you are trying to put the
<indexterm> inside something it shouldn't go inside of.

FWIW I was just talking about formatting of the SGML source so that it
is easier to read.

Yeah, me too. I'm just suggesting that maybe Andrew needs to move the
indexterm so that he can format it more readably.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#10)
Re: jsonb and nested hstore

Andrew Dunstan <andrew@dunslane.net> writes:

The problem is not the indexterm element, it's the space that might
exist outside it. Are we using block level elements like <para> inside
entry elements anywhere else?

Probably not, and I wonder why you're trying to. Whole paras inside
a table entry (this is a table no?) don't sound like they are going
to lead to nice-looking results.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#11)
Re: jsonb and nested hstore

On 01/28/2014 11:27 AM, Tom Lane wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Tom Lane wrote:

Practically every existing use of <indexterm> is freer than this in its
use of whitespace. It sounds to me like maybe you are trying to put the
<indexterm> inside something it shouldn't go inside of.

FWIW I was just talking about formatting of the SGML source so that it
is easier to read.

Yeah, me too. I'm just suggesting that maybe Andrew needs to move the
indexterm so that he can format it more readably.

Hmm. Maybe I could put them inside the para elements. So we'd have:

<entry><para>
<indexterm>
</indexterm>
para text
</para><para>
<indexterm>
</indexterm>
para text
</para></entry>

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#12)
Re: jsonb and nested hstore

On 01/28/2014 11:29 AM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

The problem is not the indexterm element, it's the space that might
exist outside it. Are we using block level elements like <para> inside
entry elements anywhere else?

Probably not, and I wonder why you're trying to. Whole paras inside
a table entry (this is a table no?) don't sound like they are going
to lead to nice-looking results.

See <http://developer.postgresql.org/~adunstan/functions-json.html&gt;

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#14)
Re: jsonb and nested hstore

On Tue, Jan 28, 2014 at 10:46 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/28/2014 11:29 AM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

The problem is not the indexterm element, it's the space that might
exist outside it. Are we using block level elements like <para> inside
entry elements anywhere else?

Probably not, and I wonder why you're trying to. Whole paras inside
a table entry (this is a table no?) don't sound like they are going
to lead to nice-looking results.

See <http://developer.postgresql.org/~adunstan/functions-json.html&gt;

yeah. note: I think the json documentation needs *major* overhaul. too
much is going in inside the function listings where there really
should be a big breakout discussing the "big picture" of json/jsonb
with examples of various use cases. I want to give it a shot but
unfortunately can not commit to do that by the end of the 'fest.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 01/28/2014 09:58 AM, Merlin Moncure wrote:

yeah. note: I think the json documentation needs *major* overhaul. too
much is going in inside the function listings where there really
should be a big breakout discussing the "big picture" of json/jsonb
with examples of various use cases. I want to give it a shot but
unfortunately can not commit to do that by the end of the 'fest.

FWIW, I've promised Andrew that I'll overhaul this by the end of beta.
Given that we have all of beta for doc refinements.

In addition to this, the JSON vs JSONB datatype page really needs
expansion and clarification.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#16)
Re: jsonb and nested hstore

On Tue, Jan 28, 2014 at 12:09 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 01/28/2014 09:58 AM, Merlin Moncure wrote:

yeah. note: I think the json documentation needs *major* overhaul. too
much is going in inside the function listings where there really
should be a big breakout discussing the "big picture" of json/jsonb
with examples of various use cases. I want to give it a shot but
unfortunately can not commit to do that by the end of the 'fest.

FWIW, I've promised Andrew that I'll overhaul this by the end of beta.
Given that we have all of beta for doc refinements.

In addition to this, the JSON vs JSONB datatype page really needs
expansion and clarification.

right: exactly. I'd be happy to help (such as I can) ...I wanted to
see if jsonb to make it in on this 'fest (doc issues notwithstanding);
it hasn't been formally reviewed yet AFAICT. So my thinking here is
to get docs to minimum acceptable standards in the short term and
focus on the structural code issues for the 'fest (if jsonb slips then
it's moot obviously).

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 01/28/2014 10:29 AM, Merlin Moncure wrote:

In addition to this, the JSON vs JSONB datatype page really needs
expansion and clarification.

right: exactly. I'd be happy to help (such as I can) ...I wanted to
see if jsonb to make it in on this 'fest (doc issues notwithstanding);
it hasn't been formally reviewed yet AFAICT. So my thinking here is
to get docs to minimum acceptable standards in the short term and
focus on the structural code issues for the 'fest (if jsonb slips then
it's moot obviously).

Well, having reviewed the docs before Andrew sent them in, I felt they
already *were* "minimum acceptable". Certainly they're as complete as
the original JSON docs were.

Or is this just about whitespace and line breaks?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Josh Berkus (#18)
Re: jsonb and nested hstore

Josh Berkus escribi�:

Or is this just about whitespace and line breaks?

If the docs are going to be rehauled, please ignore my whitespace
comments.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Josh Berkus
josh@agliodbs.com
In reply to: Tom Lane (#8)
Re: jsonb and nested hstore

On 01/28/2014 10:56 AM, Alvaro Herrera wrote:

Josh Berkus escribió:

Or is this just about whitespace and line breaks?

If the docs are going to be rehauled, please ignore my whitespace
comments.

I'm sure you'll find plenty to criticize in my version. ;-)

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#2)
1 attachment(s)
Re: jsonb and nested hstore

On 01/27/2014 10:43 PM, Andrew Dunstan wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and
compressed them. The jsonb patch should work standalone. The nested
hstore patch depends on it.

All the jsonb functions now use the jsonb API - there is no more
turning jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up
some jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Further update of jsonb portion.

Only change in functionality is the addition of casts between jsonb and
json.

The other changes are the merge with the new json functions code, and
rearrangement of the docs changes to make them less ugly. Essentially I
moved the indexterm tags right out of the table as is done in some other
parts pf the docs. That makes the entry tags much clearer to read.

cheers

andrew

Attachments:

jsonb-7.patchtext/x-patch; name=jsonb-7.patchDownload
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 6bf4cf6..12832cb 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -143,6 +143,12 @@
       </row>
 
       <row>
+       <entry><type>jsonb</type></entry>
+       <entry></entry>
+       <entry>JSON data, decomposed</entry>
+      </row>
+
+      <row>
        <entry><type>line</type></entry>
        <entry></entry>
        <entry>infinite line on a plane</entry>
@@ -4225,27 +4231,58 @@ SET xmloption TO { DOCUMENT | CONTENT };
   </sect1>
 
   <sect1 id="datatype-json">
-   <title><acronym>JSON</> Type</title>
+   <title><acronym>JSON</> Types</title>
 
    <indexterm zone="datatype-json">
     <primary>JSON</primary>
    </indexterm>
 
+   <indexterm zone="datatype-json">
+    <primary>JSONB</primary>
+   </indexterm>
+
    <para>
-    The <type>json</type> data type can be used to store JSON (JavaScript
-    Object Notation) data, as specified in <ulink
-    url="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</ulink>.  Such
-    data can also be stored as <type>text</type>, but the
-    <type>json</type> data type has the advantage of checking that each
-    stored value is a valid JSON value.  There are also related support
+    JSON data types are for storing JSON (JavaScript Object Notation)
+    data, as specified in <ulink url="http://www.ietf.org/rfc/rfc4627.txt"
+    >RFC 4627</ulink>. Such data can also be stored as <type>text</type>,
+    but the JSON data types have the advantage of checking that each
+    stored value is a valid JSON value. There are also related support
     functions available; see <xref linkend="functions-json">.
    </para>
 
    <para>
+    There are two JSON data types: <type>json</type> and <type>jsonb</type>.
+    Both accept identical sets of values as input. The difference is primarily
+    a matter of efficiency. The <type>json</type> data type stores an exact
+    copy of the the input text, and the processing functions have to reparse
+    it to precess it, while the <type>jsonb</type> is stored in a decomposed
+    form that makes it slightly less efficient to input but very much faster
+    to process, since it never needs reparsing.
+   </para>
+
+   <para>
+    The other difference between the types is that the <type>json</type> type
+    is guaranteed to contain an exact copy of the input, including
+    preservation of semantically insignificant white space, and the order of
+    keys within JSON objects. Also, because the exact text is kept, if a JSON
+    object within the value contains the same key more than once, all the
+    key/value pairs are kept. In that case, the processing functions consider
+    the last value as the operative one. By contrast, <type>jsonb</type>
+    does not preserve white space, does not preserve the order of object keys,
+    and does not keep duplicate object keys. Only the last value for a key
+    specified in the input is kept.
+   </para>
+
+   <para>
+    In general, most applications will find it advantageous to store JSON data
+    as <type>jsonb</type>, unless they have quite specialised needs.
+   </para>
+
+   <para>
     <productname>PostgreSQL</productname> allows only one server encoding
-    per database.  It is therefore not possible for JSON to conform rigidly
-    to the specification unless the server encoding is UTF-8.  Attempts to
-    directly include characters which cannot be represented in the server
+    per database.  It is therefore not possible for the JSON types to conform
+    rigidly to the specification unless the server encoding is UTF-8. Attempts
+    to directly include characters which cannot be represented in the server
     encoding will fail; conversely, characters which can be represented in
     the server encoding but not in UTF-8 will be allowed.
     <literal>\uXXXX</literal> escapes are allowed regardless of the server
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index cd886ab..864c091 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10040,13 +10040,46 @@ table2-mapping
      </tgroup>
    </table>
 
-  <para>
-   <xref linkend="functions-json-table"> shows the functions that are available
-   for creating and manipulating JSON (see <xref linkend="datatype-json">) data.
+  <note>
+   <para>
+    The operators above can take either <type>json</type> or <type>jsonb</type>
+    values as their left hand operands. In general they work much faster with
+    <type>jsonb</type>.
+   </para>
+  </note>
+
+  <!-- 
+     The release notes contain a reference to "functions-json-table". Since
+     that table is now split in two, the id has been parked here so we don't
+     have to change the release notes.
+  -->
+  <para id="functions-json-table">
+   <xref linkend="functions-json-creation-table"> shows the functions that are
+   available for creating <type>json</type> values.
+   (see <xref linkend="datatype-json">)
   </para>
 
-  <table id="functions-json-table">
-    <title>JSON Support Functions</title>
+  <indexterm>
+   <primary>array_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>row_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_array</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_object</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_object</primary>
+  </indexterm>
+
+  <table id="functions-json-creation-table">
+    <title>JSON Creation Functions</title>
     <tgroup cols="5">
      <thead>
       <row>
@@ -10060,9 +10093,6 @@ table2-mapping
      <tbody>
       <row>
        <entry>
-         <indexterm>
-          <primary>array_to_json</primary>
-         </indexterm>
          <literal>array_to_json(anyarray [, pretty_bool])</literal>
        </entry>
        <entry><type>json</type></entry>
@@ -10076,9 +10106,6 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>row_to_json</primary>
-         </indexterm>
          <literal>row_to_json(record [, pretty_bool])</literal>
        </entry>
        <entry><type>json</type></entry>
@@ -10091,9 +10118,6 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>to_json</primary>
-         </indexterm>
          <literal>to_json(anyelement)</literal>
        </entry>
        <entry><type>json</type></entry>
@@ -10109,11 +10133,174 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>json_array_length</primary>
-         </indexterm>
-         <literal>json_array_length(json)</literal>
+         <literal>json_build_array(VARIADIC "any")</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         Builds a heterogeneously-typed json array out of a variadic argument list.
        </entry>
+       <entry><literal>SELECT json_build_array(1,2,'3',4,5);</literal></entry>
+       <entry>
+<programlisting>
+ json_build_array
+-------------------
+ [1, 2, "3", 4, 5]
+ </programlisting>
+       </entry>
+      </row>
+      <row>
+       <entry>
+         <literal>json_build_object(VARIADIC "any")</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         Builds a JSON array out of a variadic argument list.
+         By convention, the object is 
+         constructed out of alternating name/value arguments.
+       </entry>
+       <entry><literal>SELECT json_build_object('foo',1,'bar',2);</literal></entry>
+       <entry>
+<programlisting>
+   json_build_object
+------------------------
+ {"foo" : 1, "bar" : 2}
+ </programlisting>
+       </entry>
+      </row>
+      <row>
+       <entry>
+         <literal>json_object(text[])</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         Builds a JSON object out of a text array.  The array must have either
+         exactly one dimension with an even number of members, in which case
+         they are taken as alternating name/value pairs, or two dimensions
+         such that each inner array has exactly two elements, which
+         are taken as a name/value pair.
+       </entry>
+       <entry><literal>select * from json_object('{a, 1, b, "def", c, 3.5}')  or <literal>select json_object('{{a, 1},{b, "def"},{c, 3.5}}')</literal></literal></entry>
+       <entry>
+<programlisting>
+              json_object
+---------------------------------------
+ {"a" : "1", "b" : "def", "c" : "3.5"}
+ </programlisting>
+       </entry>
+      </row>
+      <row>
+       <entry>
+         <literal>json_object(keys text[], values text[])</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         The two-argument form of JSON object takes keys and values pairwise from two separate
+         arrays. In all other respects it is identical to the one-argument form.
+       </entry>
+       <entry><literal>select json_object('{a, b}', '{1,2}');</literal></entry>
+       <entry>
+<programlisting>
+      json_object
+------------------------
+ {"a" : "1", "b" : "2"}
+ </programlisting>
+       </entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+
+  <para>
+   <xref linkend="functions-json-processing-table"> shows the functions that
+   are available for processing <type>json</type> and <type>jsonb</type> values.
+   (see <xref linkend="datatype-json">)
+  </para>
+
+  <indexterm>
+   <primary>json_array_length</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_array_length</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_each</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_each</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_each_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_each_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_extract_path</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_extract_path</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_extract_path_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_extract_path_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_object_keys</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_object_keys</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_populate_record</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_populate_record</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_populate_recordset</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_populate_recordset</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_array_elements</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_array_elements</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_typeof</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_typeof</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_to_record</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_to_recordset</primary>
+  </indexterm>
+
+  <table id="functions-json-processing-table">
+    <title>JSON Processing Functions</title>
+    <tgroup cols="5">
+     <thead>
+      <row>
+       <entry>Function</entry>
+       <entry>Return Type</entry>
+       <entry>Description</entry>
+       <entry>Example</entry>
+       <entry>Example Result</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry><para><literal>json_array_length(json)</literal>
+         </para><para><literal>jsonb_array_length(jsonb)</literal>
+       </para></entry>
        <entry><type>int</type></entry>
        <entry>
          Returns the number of elements in the outermost JSON array.
@@ -10122,13 +10309,12 @@ table2-mapping
        <entry><literal>5</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_each</primary>
-         </indexterm>
-         <literal>json_each(json)</literal>
-       </entry>
-       <entry><type>SETOF key text, value json</type></entry>
+       <entry><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>
+       <entry><para><literal>SETOF key text, value json</literal>
+         </para><para><literal>SETOF key text, value jsonb</literal>
+       </para></entry>
        <entry>
          Expands the outermost JSON object into a set of key/value pairs.
        </entry>
@@ -10143,12 +10329,9 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_each_text</primary>
-         </indexterm>
-         <literal>json_each_text(from_json json)</literal>
-       </entry>
+       <entry><para><literal>json_each_text(from_json json)</literal>
+         </para><para><literal>jsonb_each_text(from_json jsonb)</literal>
+       </para></entry>
        <entry><type>SETOF key text, value text</type></entry>
        <entry>
          Expands the outermost JSON object into a set of key/value pairs. The
@@ -10165,13 +10348,11 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_extract_path</primary>
-         </indexterm>
-         <literal>json_extract_path(from_json json, VARIADIC path_elems text[])</literal>
-       </entry>
-       <entry><type>json</type></entry>
+       <entry><para><literal>json_extract_path(from_json json, VARIADIC path_elems text[])</literal>
+        </para><para><literal>jsonb_extract_path(from_jsonb jsonb, VARIADIC path_elems text[])</literal>
+       </para></entry>
+       <entry><para><type>json</type></para><para><type>jsonb</type>
+       </para></entry>
        <entry>
          Returns JSON value pointed to by <parameter>path_elems</parameter>.
        </entry>
@@ -10179,12 +10360,9 @@ table2-mapping
        <entry><literal>{"f5":99,"f6":"foo"}</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_extract_path_text</primary>
-         </indexterm>
-         <literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
-       </entry>
+       <entry><para><literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
+         </para><para><literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
+       </para></entry>
        <entry><type>text</type></entry>
        <entry>
          Returns JSON value pointed to by <parameter>path_elems</parameter>.
@@ -10193,12 +10371,9 @@ table2-mapping
        <entry><literal>foo</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_object_keys</primary>
-         </indexterm>
-         <literal>json_object_keys(json)</literal>
-       </entry>
+       <entry><para><literal>json_object_keys(json)</literal>
+         </para><para><literal>jsonb_object_keys(jsonb)</literal>
+       </para></entry>
        <entry><type>SETOF text</type></entry>
        <entry>
           Returns set of keys in the JSON object.  Only the <quote>outer</quote> object will be displayed.
@@ -10214,18 +10389,16 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_populate_record</primary>
-         </indexterm>
-         <literal>json_populate_record(base anyelement, from_json json, [, use_json_as_text bool=false]</literal>
-       </entry>
+       <entry><para><literal>json_populate_record(base anyelement, from_json json, [, use_json_as_text bool=false])</literal>
+         </para><para><literal>jsonb_populate_record(base anyelement, from_json jsonb, [, use_json_as_text bool=false])</literal>
+       </para></entry>
        <entry><type>anyelement</type></entry>
        <entry>
          Expands the object in <replaceable>from_json</replaceable> to a row whose columns match
          the record type defined by base. Conversion will be best
          effort; columns in base with no corresponding key in <replaceable>from_json</replaceable>
-         will be left null. If a column is specified more than once, the last value is used.
+         will be left null. When processing <type>json</type>, if a column is 
+         specified more than once, the last value is used.
        </entry>
        <entry><literal>select * from json_populate_record(null::x, '{"a":1,"b":2}')</literal></entry>
        <entry>
@@ -10237,19 +10410,17 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_populate_recordset</primary>
-         </indexterm>
-         <literal>json_populate_recordset(base anyelement, from_json json, [, use_json_as_text bool=false]</literal>
-       </entry>
+       <entry><para><literal>json_populate_recordset(base anyelement, from_json json, [, use_json_as_text bool=false])</literal>
+         </para><para><literal>jsonb_populate_recordset(base anyelement, from_json jsonb, [, use_json_as_text bool=false])</literal>
+       </para></entry>
        <entry><type>SETOF anyelement</type></entry>
        <entry>
          Expands the outermost set of objects in <replaceable>from_json</replaceable> to a set
          whose columns match the record type defined by base.
          Conversion will be best effort; columns in base with no
          corresponding key in <replaceable>from_json</replaceable> will be left null.
-         If a column is specified more than once, the last value is used.
+         When processing <type>json</type>, if a column is specified more 
+         than once, the last value is used.
        </entry>
        <entry><literal>select * from json_populate_recordset(null::x, '[{"a":1,"b":2},{"a":3,"b":4}]')</literal></entry>
        <entry>
@@ -10262,13 +10433,12 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_array_elements</primary>
-         </indexterm>
-         <literal>json_array_elements(json)</literal>
-       </entry>
-       <entry><type>SETOF json</type></entry>
+       <entry><para><literal>json_array_elements(json)</literal>
+         </para><para><literal>jsonb_array_elements(jsonb)</literal>
+       </para></entry>
+       <entry><para><type>SETOF json</type>
+         </para><para><type>SETOF jsonb</type>
+       </para></entry>
        <entry>
          Expands a JSON array to a set of JSON values.
        </entry>
@@ -10284,12 +10454,9 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_typeof</primary>
-         </indexterm>
-         <literal>json_typeof(json)</literal>
-       </entry>
+       <entry><para><literal>json_typeof(json)</literal>
+         </para><para><literal>jsonb_typeof(jsonb)</literal>
+       </para></entry>
        <entry><type>text</type></entry>
        <entry>
          Returns the type of the outermost JSON value as a text string.  The types are
@@ -10302,98 +10469,11 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>json_build_array</primary>
-         </indexterm>
-         <literal>json_build_array(VARIADIC "any")</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         Builds a heterogeneously-typed json array out of a variadic argument list.
-       </entry>
-       <entry><literal>SELECT json_build_array(1,2,'3',4,5);</literal></entry>
-       <entry>
-<programlisting>
- json_build_array
--------------------
- [1, 2, "3", 4, 5]
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <indexterm>
-          <primary>json_build_object</primary>
-         </indexterm>
-         <literal>json_build_object(VARIADIC "any")</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         Builds a JSON array out of a variadic argument list.
-         By convention, the object is 
-         constructed out of alternating name/value arguments.
-       </entry>
-       <entry><literal>SELECT json_build_object('foo',1,'bar',2);</literal></entry>
-       <entry>
-<programlisting>
-   json_build_object
-------------------------
- {"foo" : 1, "bar" : 2}
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <indexterm>
-          <primary>json_object</primary>
-         </indexterm>
-         <literal>json_object(text[])</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         Builds a JSON object out of a text array.  The array must have either
-         exactly one dimension with an even number of members, in which case
-         they are taken as alternating name/value pairs, or two dimensions
-         such that each inner array has exactly two elements, which
-         are taken as a name/value pair.
-       </entry>
-       <entry><literal>select * from json_object('{a, 1, b, "def", c, 3.5}')  or <literal>select * from json_object('{{a, 1},{b, "def"},{c, 3.5}}')</literal></literal></entry>
-       <entry>
-<programlisting>
-              json_object
----------------------------------------
- {"a" : "1", "b" : "def", "c" : "3.5"}
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <literal>json_object(keys text[], values text[])</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         The two-argument form of JSON object takes keys and values pairwise from two separate
-         arrays. In all other respects it is identical to the one-argument form.
-       </entry>
-       <entry><literal>select * from json_object('{a, b}', '{1,2}');</literal></entry>
-       <entry>
-<programlisting>
-      json_object
-------------------------
- {"a" : "1", "b" : "2"}
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <indexterm>
-          <primary>json_to_record</primary>
-         </indexterm>
          <literal>json_to_record(json, nested_as_text bool)</literal>
        </entry>
        <entry><type>record</type></entry>
        <entry>
-         json_to_record returns an arbitrary record from a JSON object.  As with all functions 
+         Returns an arbitrary record from a JSON object.  As with all functions 
          returning 'record', the caller must explicitly define the structure of the record 
          when making the call. The input JSON must be an object, not a scalar or an array.
          If nested_as_text is true, the function coerces nested complex elements to text.
@@ -10410,14 +10490,11 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>json_to_recordset</primary>
-         </indexterm>
          <literal>json_to_recordset(json, nested_as_text bool)</literal>
        </entry>
        <entry><type>setof record</type></entry>
        <entry>
-         json_to_recordset returns an arbitrary set of records from a JSON object.  As with 
+         Returns an arbitrary set of records from a JSON object.  As with 
          json_to_record, the structure of the record must be explicitly defined when making the
          call.  However, with json_to_recordset the input JSON must be an array containing 
          objects.  nested_as_text works as with json_to_record.
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 277af61..db04a2e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -807,3 +807,11 @@ CREATE OR REPLACE FUNCTION
 CREATE OR REPLACE FUNCTION
   json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
   RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'jsonb_populate_recordset';
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index f170661..db0c434 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1274,7 +1274,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 			pfree(outputstr);
 			break;
 		case TYPCATEGORY_JSON:
-			/* JSON will already be escaped */
+			/* JSON and JSONB will already be escaped */
 			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			appendStringInfoString(result, outputstr);
 			pfree(outputstr);
@@ -1406,7 +1406,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 		tcategory = TYPCATEGORY_JSON_CAST;
 	else if (element_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (element_type == JSONOID)
+	else if (element_type == JSONOID || element_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(element_type);
@@ -1501,7 +1501,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
 			tcategory = TYPCATEGORY_ARRAY;
 		else if (tupdesc->attrs[i]->atttypid == RECORDOID)
 			tcategory = TYPCATEGORY_COMPOSITE;
-		else if (tupdesc->attrs[i]->atttypid == JSONOID)
+		else if (tupdesc->attrs[i]->atttypid == JSONOID ||
+				 tupdesc->attrs[i]->atttypid == JSONBOID)
 			tcategory = TYPCATEGORY_JSON;
 		else
 			tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
@@ -1689,7 +1690,7 @@ to_json(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1783,7 +1784,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -2346,12 +2347,15 @@ escape_json(StringInfo buf, const char *str)
 Datum
 json_typeof(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonTokenType tok;
 	char	   *type;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
+
 	/* Lex exactly one token from the input and check its type. */
 	json_lex(lex);
 	tok = lex_peek(lex);
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..107ebf0
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,544 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+
+static size_t
+checkStringLen(size_t len)
+{
+	if (len > JSONB_MAX_STRING_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for jsonb string")));
+	return len;
+}
+
+typedef struct JsonbInState
+{
+	ToJsonbState *state;
+	JsonbValue *res;
+}	JsonbInState;
+
+
+/*
+ * for jsonb we always want the de-escaped value - that's what's in token
+ */
+
+static void
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+
+		case JSON_TOKEN_STRING:
+			v.type = jbvString;
+			v.string.len = token ? checkStringLen(strlen(token)) : 0;
+			v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+			v.size += v.string.len;
+			break;
+		case JSON_TOKEN_NUMBER:
+			v.type = jbvNumeric;
+			v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+
+			v.size += VARSIZE_ANY(v.numeric) +sizeof(JEntry) /* alignment */ ;
+			break;
+		case JSON_TOKEN_TRUE:
+			v.type = jbvBool;
+			v.boolean = true;
+			break;
+		case JSON_TOKEN_FALSE:
+			v.type = jbvBool;
+			v.boolean = false;
+			break;
+		case JSON_TOKEN_NULL:
+			v.type = jbvNull;
+			break;
+		default:				/* nothing else should be here in fact */
+			break;
+	}
+
+	if (_state->state == NULL)
+	{
+		/* single scalar */
+		JsonbValue	va;
+
+		va.type = jbvArray;
+		va.array.scalar = true;
+		va.array.nelems = 1;
+
+		_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, &va);
+		_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+		_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+	}
+	else
+	{
+		JsonbValue *o = &_state->state->v;
+
+		switch (o->type)
+		{
+			case jbvArray:
+				_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+				break;
+			case jbvHash:
+				_state->res = pushJsonbValue(&_state->state, WJB_VALUE, &v);
+				break;
+			default:
+				elog(ERROR, "Wrong state");
+		}
+	}
+}
+
+static void
+jsonb_in_object_start(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_OBJECT, NULL);
+}
+
+static void
+jsonb_in_object_end(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_OBJECT, NULL);
+}
+
+static void
+jsonb_in_array_start(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, NULL);
+}
+
+static void
+jsonb_in_array_end(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+}
+
+static void
+jsonb_in_object_field_start(void *state, char *fname, bool isnull)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.type = jbvString;
+	v.string.len = fname ? checkStringLen(strlen(fname)) : 0;
+	v.string.val = fname ? pnstrdup(fname, v.string.len) : NULL;
+	v.size = sizeof(JEntry) + v.string.len;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_KEY, &v);
+}
+
+Datum
+jsonb_in(PG_FUNCTION_ARGS)
+{
+	char	   *json = PG_GETARG_CSTRING(0);
+	text	   *result = cstring_to_text(json);
+	JsonLexContext *lex;
+	JsonbInState state;
+	JsonSemAction sem;
+
+	memset(&state, 0, sizeof(state));
+	memset(&sem, 0, sizeof(sem));
+	lex = makeJsonLexContext(result, true);
+
+	sem.semstate = (void *) &state;
+
+	sem.object_start = jsonb_in_object_start;
+	sem.array_start = jsonb_in_array_start;
+	sem.object_end = jsonb_in_object_end;
+	sem.array_end = jsonb_in_array_end;
+	sem.scalar = jsonb_in_scalar;
+	sem.object_field_start = jsonb_in_object_field_start;
+
+	pg_parse_json(lex, &sem);
+
+	/* after parsing, the item membar has the composed jsonn structure */
+	PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
+}
+
+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);
+
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	uint32		hentry = c & JENTRY_TYPEMASK;
+
+	if (hentry == JENTRY_ISNULL)
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+	}
+	else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	{
+		recvJsonb(buf, v, level + 1, (uint32) c);
+	}
+	else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	{
+		v->type = jbvBool;
+		v->size = sizeof(JEntry);
+		v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	}
+	else if (hentry == JENTRY_ISNUMERIC)
+	{
+		v->type = jbvNumeric;
+		v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+									   Int32GetDatum(0), Int32GetDatum(-1)));
+
+		v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);
+	}
+	else if (hentry == JENTRY_ISSTRING)
+	{
+		v->type = jbvString;
+		v->string.val = pq_getmsgtext(buf, c, &c);
+		v->string.len = checkStringLen(c);
+		v->size = sizeof(JEntry) + v->string.len;
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
+
+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	uint32		hentry;
+	uint32		i;
+
+	hentry = header & JENTRY_TYPEMASK;
+
+	v->size = 3 * sizeof(JEntry);
+	if (hentry == JENTRY_ISOBJECT)
+	{
+		v->type = jbvHash;
+		v->hash.npairs = header & JB_COUNT_MASK;
+		if (v->hash.npairs > 0)
+		{
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+			for (i = 0; i < v->hash.npairs; i++)
+			{
+				recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != jbvString)
+					elog(ERROR, "jsonb's key could be only a string");
+
+				recvJsonbValue(buf, &v->hash.pairs[i].value, level, pq_getmsgint(buf, 4));
+
+				v->size += v->hash.pairs[i].key.size + v->hash.pairs[i].value.size;
+			}
+
+			uniqueJsonbValue(v);
+		}
+	}
+	else if (hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	{
+		v->type = jbvArray;
+		v->array.nelems = header & JB_COUNT_MASK;
+		v->array.scalar = (hentry == JENTRY_ISCALAR) ? true : false;
+
+		if (v->array.scalar && v->array.nelems != 1)
+			elog(ERROR, "bogus input");
+
+		if (v->array.nelems > 0)
+		{
+			v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+			for (i = 0; i < v->array.nelems; i++)
+			{
+				recvJsonbValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4));
+				v->size += v->array.elems[i].size;
+			}
+		}
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
+
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	JsonbValue	v;
+
+	recvJsonb(buf, &v, 0, pq_getmsgint(buf, 4));
+
+	PG_RETURN_POINTER(JsonbValueToJsonb(&v));
+}
+
+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	switch (v->type)
+	{
+		case jbvNull:
+			appendBinaryStringInfo(out, "null", 4);
+			break;
+		case jbvString:
+			escape_json(out, pnstrdup(v->string.val, v->string.len));
+			break;
+		case jbvBool:
+			if (v->boolean)
+				appendBinaryStringInfo(out, "true", 4);
+			else
+				appendBinaryStringInfo(out, "false", 5);
+			break;
+		case jbvNumeric:
+			appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+			break;
+		default:
+			elog(PANIC, "Unknown type");
+	}
+}
+
+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	bool		first = true;
+	JsonbIterator *it;
+	int			type;
+	JsonbValue	v;
+	int			level = 0;
+
+	if (out == NULL)
+		out = makeStringInfo();
+
+	if (in == NULL)
+	{
+		appendStringInfoString(out, "");
+		return out->data;
+	}
+
+	enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
+
+	it = JsonbIteratorInit(in);
+
+	while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	{
+reout:
+		switch (type)
+		{
+			case WJB_BEGIN_ARRAY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				if (v.array.scalar == false)
+					appendStringInfoChar(out, '[');
+				level++;
+				break;
+			case WJB_BEGIN_OBJECT:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+				appendStringInfoCharMacro(out, '{');
+
+				level++;
+				break;
+			case WJB_KEY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				putEscapedValue(out, &v);
+				appendBinaryStringInfo(out, ": ", 2);
+
+				type = JsonbIteratorGet(&it, &v, false);
+				if (type == WJB_VALUE)
+				{
+					first = false;
+					putEscapedValue(out, &v);
+				}
+				else
+				{
+					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+					goto reout;
+				}
+				break;
+			case WJB_ELEM:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				else
+					first = false;
+
+				putEscapedValue(out, &v);
+				break;
+			case WJB_END_ARRAY:
+				level--;
+				if (v.array.scalar == false)
+					appendStringInfoChar(out, ']');
+				first = false;
+				break;
+			case WJB_END_OBJECT:
+				level--;
+				appendStringInfoCharMacro(out, '}');
+				first = false;
+				break;
+			default:
+				elog(PANIC, "Wrong flags");
+		}
+	}
+
+	Assert(level == 0);
+
+	return out->data;
+}
+
+Datum
+jsonb_out(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	PG_RETURN_CSTRING(out);
+}
+
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *in = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+
+	pq_begintypsend(&buf);
+
+	if (JB_ISEMPTY(in))
+	{
+		pq_sendint(&buf, 0, 4);
+	}
+	else
+	{
+		JsonbIterator *it;
+		int			type;
+		JsonbValue	v;
+		uint32		flag;
+		bytea	   *nbuf;
+
+		enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */ );
+
+		it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+		{
+			switch (type)
+			{
+				case WJB_BEGIN_ARRAY:
+					flag = (v.array.scalar) ? JENTRY_ISCALAR : JENTRY_ISARRAY;
+					pq_sendint(&buf, v.array.nelems | flag, 4);
+					break;
+				case WJB_BEGIN_OBJECT:
+					pq_sendint(&buf, v.hash.npairs | JENTRY_ISOBJECT, 4);
+					break;
+				case WJB_KEY:
+					pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+					pq_sendtext(&buf, v.string.val, v.string.len);
+					break;
+				case WJB_ELEM:
+				case WJB_VALUE:
+					switch (v.type)
+					{
+						case jbvNull:
+							pq_sendint(&buf, JENTRY_ISNULL, 4);
+							break;
+						case jbvString:
+							pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+							pq_sendtext(&buf, v.string.val, v.string.len);
+							break;
+						case jbvBool:
+							pq_sendint(&buf, (v.boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE, 4);
+							break;
+						case jbvNumeric:
+							nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
+							pq_sendint(&buf, VARSIZE_ANY(nbuf) | JENTRY_ISNUMERIC, 4);
+							pq_sendbytes(&buf, (char *) nbuf, VARSIZE_ANY(nbuf));
+							break;
+						default:
+							elog(PANIC, "Wrong type: %u", v.type);
+					}
+					break;
+				case WJB_END_ARRAY:
+				case WJB_END_OBJECT:
+					break;
+				default:
+					elog(PANIC, "Wrong flags");
+			}
+		}
+	}
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *in = PG_GETARG_JSONB(0);
+	JsonbIterator *it;
+	JsonbValue	v;
+	char	   *result;
+
+	if (JB_ROOT_IS_OBJECT(in))
+		result = "object";
+	else if (JB_ROOT_IS_ARRAY(in) && !JB_ROOT_IS_SCALAR(in))
+		result = "array";
+	else
+	{
+		Assert(JB_ROOT_IS_SCALAR(in));
+
+		it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		/*
+		 * a root scalar is stored as an array of one element, so we get the
+		 * array and then its first (and only) member.
+		 */
+		(void) JsonbIteratorGet(&it, &v, true);
+		(void) JsonbIteratorGet(&it, &v, true);
+		switch (v.type)
+		{
+			case jbvNull:
+				result = "null";
+				break;
+			case jbvString:
+				result = "string";
+				break;
+			case jbvBool:
+				result = "boolean";
+				break;
+			case jbvNumeric:
+				result = "number";
+				break;
+			default:
+				elog(ERROR, "Wrong jsonb scalar type: %u", v.type);
+		}
+	}
+
+	PG_RETURN_TEXT_P(cstring_to_text(result));
+}
diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c
new file mode 100644
index 0000000..79da6eb
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_support.c
@@ -0,0 +1,1261 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_support.c
+ *	  Support functions for jsonb
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+/*
+ * turn a JsonbValue into a Jsonb
+ */
+Jsonb *
+JsonbValueToJsonb(JsonbValue *v)
+{
+	Jsonb	   *out;
+
+	if (v == NULL)
+	{
+		out = NULL;
+	}
+	else if (v->type == jbvString || v->type == jbvBool ||
+			 v->type == jbvNumeric || v->type == jbvNull)
+	{
+		/* scalar value */
+
+		ToJsonbState *state = NULL;
+		JsonbValue *res;
+		uint32		sz;
+		JsonbValue	scalarArray;
+
+		scalarArray.type = jbvArray;
+		scalarArray.array.scalar = true;
+		scalarArray.array.nelems = 1;
+
+		pushJsonbValue(&state, WJB_BEGIN_ARRAY, &scalarArray);
+		pushJsonbValue(&state, WJB_ELEM, v);
+		res = pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+		out = palloc(VARHDRSZ + res->size);
+		sz = compressJsonb(res, VARDATA(out));
+		Assert(sz <= res->size);
+		SET_VARSIZE(out, sz + VARHDRSZ);
+	}
+	else if (v->type == jbvHash || v->type == jbvArray)
+	{
+		uint32		sz;
+
+		out = palloc(VARHDRSZ + v->size);
+		sz = compressJsonb(v, VARDATA(out));
+		Assert(sz <= v->size);
+		SET_VARSIZE(out, VARHDRSZ + sz);
+	}
+	else
+	{
+		out = palloc(VARHDRSZ + v->binary.len);
+
+		Assert(v->type == jbvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+/*
+ * Sort and unique pairs in hash-like JsonbValue
+ */
+void
+uniqueJsonbValue(JsonbValue *v)
+{
+	bool		hasNonUniq = false;
+
+	Assert(v->type == jbvHash);
+
+	if (v->hash.npairs > 1)
+		qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs),
+				  compareJsonbPair, &hasNonUniq);
+
+	if (hasNonUniq)
+	{
+		JsonbPair  *ptr = v->hash.pairs + 1,
+				   *res = v->hash.pairs;
+
+		while (ptr - v->hash.pairs < v->hash.npairs)
+		{
+			if (ptr->key.string.len == res->key.string.len &&
+				memcmp(ptr->key.string.val, res->key.string.val,
+					   ptr->key.string.len) == 0)
+			{
+				v->size -= ptr->key.size + ptr->value.size;
+			}
+			else
+			{
+				res++;
+				if (ptr != res)
+					memcpy(res, ptr, sizeof(*res));
+			}
+			ptr++;
+		}
+
+		v->hash.npairs = res + 1 - v->hash.pairs;
+	}
+}
+
+/****************************************************************************
+ *						   Compare Functions								*
+ ****************************************************************************/
+
+/*
+ * Compare two jbvString JsonbValue values, third argument
+ * 'arg', if it's not null, should be a pointer to bool
+ * value which will be set to true if strings are equal and
+ * untouched otherwise.
+ */
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue *va = a;
+	const JsonbValue *vb = b;
+	int			res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool *) arg = true;
+	}
+	else
+	{
+		res = (va->string.len > vb->string.len) ? 1 : -1;
+	}
+
+	return res;
+}
+
+/*
+ * qsort helper to compare JsonbPair values, third argument
+ * arg will be trasferred as is to subsequent
+ * compareJsonbStringValue() call. Pairs with equals keys are
+ * ordered with respect of order field.
+ */
+int
+compareJsonbPair(const void *a, const void *b, void *arg)
+{
+	const JsonbPair *pa = a;
+	const JsonbPair *pb = b;
+	int			res;
+
+	res = compareJsonbStringValue(&pa->key, &pb->key, arg);
+
+	/*
+	 * guarantee keeping order of equal pair. Unique algorithm will prefer
+	 * first element as value
+	 */
+
+	if (res == 0)
+		res = (pa->order > pb->order) ? -1 : 1;
+
+	return res;
+}
+
+/*
+ * some constant order of JsonbValue
+ */
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{
+	if (a->type == b->type)
+	{
+		switch (a->type)
+		{
+			case jbvNull:
+				return 0;
+			case jbvString:
+				return compareJsonbStringValue(a, b, NULL);
+			case jbvBool:
+				if (a->boolean == b->boolean)
+					return 0;
+				return (a->boolean > b->boolean) ? 1 : -1;
+			case jbvNumeric:
+				return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(a->numeric),
+											   PointerGetDatum(b->numeric)));
+			case jbvArray:
+				if (a->array.nelems == b->array.nelems)
+				{
+					int			i,
+								r;
+
+					for (i = 0; i < a->array.nelems; i++)
+						if ((r = compareJsonbValue(a->array.elems + i,
+												   b->array.elems + i)) != 0)
+							return r;
+
+					return 0;
+				}
+
+				return (a->array.nelems > b->array.nelems) ? 1 : -1;
+			case jbvHash:
+				if (a->hash.npairs == b->hash.npairs)
+				{
+					int			i,
+								r;
+
+					for (i = 0; i < a->hash.npairs; i++)
+					{
+						if ((r = compareJsonbStringValue(&a->hash.pairs[i].key,
+													   &b->hash.pairs[i].key,
+														 NULL)) != 0)
+							return r;
+						if ((r = compareJsonbValue(&a->hash.pairs[i].value,
+											  &b->hash.pairs[i].value)) != 0)
+							return r;
+					}
+
+					return 0;
+				}
+
+				return (a->hash.npairs > b->hash.npairs) ? 1 : -1;
+			case jbvBinary:
+				return compareJsonbBinaryValue(a->binary.data, b->binary.data);
+			default:
+				elog(PANIC, "unknown JsonbValue->type: %d", a->type);
+		}
+	}
+
+	return (a->type > b->type) ? 1 : -1;
+}
+
+/*
+ * Some order for Jsonb values
+ */
+int
+compareJsonbBinaryValue(char *a, char *b)
+{
+	JsonbIterator *it1,
+			   *it2;
+	int			res = 0;
+
+	it1 = JsonbIteratorInit(a);
+	it2 = JsonbIteratorInit(b);
+
+	while (res == 0)
+	{
+		JsonbValue	v1,
+					v2;
+		int			r1,
+					r2;
+
+		r1 = JsonbIteratorGet(&it1, &v1, false);
+		r2 = JsonbIteratorGet(&it2, &v2, false);
+
+		if (r1 == r2)
+		{
+			if (r1 == 0)
+				break;			/* equal */
+
+			if (v1.type == v2.type)
+			{
+				switch (v1.type)
+				{
+					case jbvString:
+						res = compareJsonbStringValue(&v1, &v2, NULL);
+						break;
+					case jbvBool:
+						if (v1.boolean == v2.boolean)
+							res = 0;
+						else
+							res = (v1.boolean > v2.boolean) ? 1 : -1;
+						break;
+					case jbvNumeric:
+						res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(v1.numeric),
+											   PointerGetDatum(v2.numeric)));
+						break;
+					case jbvArray:
+						if (v1.array.nelems != v2.array.nelems)
+							res = (v1.array.nelems > v2.array.nelems) ? 1 : -1;
+						break;
+					case jbvHash:
+						if (v1.hash.npairs != v2.hash.npairs)
+							res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1;
+						break;
+					default:
+						break;
+				}
+			}
+			else
+			{
+				res = (v1.type > v2.type) ? 1 : -1;		/* dummy order */
+			}
+		}
+		else
+		{
+			res = (r1 > r2) ? 1 : -1;	/* dummy order */
+		}
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *			find string key in hash or element by value in array			*
+ ****************************************************************************/
+JsonbValue *
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK));
+		int			i;
+
+		for (i = (lowbound) ? *lowbound : 0; i < (header & JB_COUNT_MASK); i++)
+		{
+			JEntry	   *e = array + i;
+
+			if (JBE_ISNULL(*e) && key->type == jbvNull)
+			{
+				r.type = jbvNull;
+				if (lowbound)
+					*lowbound = i;
+				r.size = sizeof(JEntry);
+
+				return &r;
+			}
+			else if (JBE_ISSTRING(*e) && key->type == jbvString)
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e),
+						   key->string.len) == 0)
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*e);
+					r.string.len = key->string.len;
+					r.size = sizeof(JEntry) + r.string.len;
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+			{
+				if ((JBE_ISBOOL_TRUE(*e) && key->boolean == true) ||
+					(JBE_ISBOOL_FALSE(*e) && key->boolean == false))
+				{
+					r = *key;
+					r.size = sizeof(JEntry);
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
+			{
+				if (DatumGetBool(DirectFunctionCall2(numeric_eq,
+							   PointerGetDatum(data + INTALIGN(JBE_OFF(*e))),
+									 PointerGetDatum(key->numeric))) == true)
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+		}
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+		uint32		stopLow = lowbound ? *lowbound : 0,
+					stopHigh = (header & JB_COUNT_MASK),
+					stopMiddle;
+
+		if (key->type != jbvString)
+			return NULL;
+
+		while (stopLow < stopHigh)
+		{
+			int			difference;
+			JEntry	   *e;
+
+			stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+			e = array + stopMiddle * 2;
+
+			if (key->string.len == JBE_LEN(*e))
+				difference = memcmp(data + JBE_OFF(*e), key->string.val,
+									key->string.len);
+			else
+				difference = (JBE_LEN(*e) > key->string.len) ? 1 : -1;
+
+			if (difference == 0)
+			{
+				JEntry	   *v = e + 1;
+
+				if (lowbound)
+					*lowbound = stopMiddle + 1;
+
+				if (JBE_ISSTRING(*v))
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*v);
+					r.string.len = JBE_LEN(*v);
+					r.size = sizeof(JEntry) + r.string.len;
+				}
+				else if (JBE_ISBOOL(*v))
+				{
+					r.type = jbvBool;
+					r.boolean = (JBE_ISBOOL_TRUE(*v)) ? true : false;
+					r.size = sizeof(JEntry);
+				}
+				else if (JBE_ISNUMERIC(*v))
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*v)));
+
+					r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+				}
+				else if (JBE_ISNULL(*v))
+				{
+					r.type = jbvNull;
+					r.size = sizeof(JEntry);
+				}
+				else
+				{
+					r.type = jbvBinary;
+					r.binary.data = data + INTALIGN(JBE_OFF(*v));
+					r.binary.len = JBE_LEN(*v) -
+						(INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
+					r.size = 2 * sizeof(JEntry) + r.binary.len;
+				}
+
+				return &r;
+			}
+			else if (difference < 0)
+			{
+				stopLow = stopMiddle + 1;
+			}
+			else
+			{
+				stopHigh = stopMiddle;
+			}
+		}
+
+		if (lowbound)
+			*lowbound = stopLow;
+	}
+
+	return NULL;
+}
+
+/*
+ * Just wrapped for findUncompressedJsonbValueByValue()
+ * with simple string key representation
+ */
+JsonbValue *
+findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound,
+						   char *key, uint32 keylen)
+{
+	JsonbValue	v;
+
+	if (key == NULL)
+	{
+		v.type = jbvNull;
+	}
+	else
+	{
+		v.type = jbvString;
+		v.string.val = key;
+		v.string.len = keylen;
+	}
+
+	return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v);
+}
+
+/*
+ * Get i-th value of array or hash. if i < 0 then it counts from
+ * the end of array/hash. Note: returns pointer to statically
+ * allocated JsonbValue.
+ */
+JsonbValue *
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;
+	JEntry	   *array,
+			   *e;
+	char	   *data;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (i >= 0)
+	{
+		if (i >= (header & JB_COUNT_MASK))
+			return NULL;
+	}
+	else
+	{
+		if (-i > (header & JB_COUNT_MASK))
+			return NULL;
+
+		i = (header & JB_COUNT_MASK) + i;
+	}
+
+	array = (JEntry *) (buffer + sizeof(header));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		e = array + i;
+		data = (char *) (array + (header & JB_COUNT_MASK));
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		e = array + i * 2 + 1;
+		data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+	}
+	else
+	{
+		return NULL;
+	}
+
+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+		r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2 * sizeof(JEntry);
+	}
+
+	return &r;
+}
+
+/****************************************************************************
+ *					  Walk on tree representation of jsonb					*
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level)
+{
+	int			i;
+
+	switch (v->type)
+	{
+		case jbvArray:
+			cb(cb_arg, v, WJB_BEGIN_ARRAY, level);
+			for (i = 0; i < v->array.nelems; i++)
+			{
+				if (v->array.elems[i].type == jbvNull ||
+					v->array.elems[i].type == jbvString ||
+					v->array.elems[i].type == jbvBool ||
+					v->array.elems[i].type == jbvNumeric ||
+					v->array.elems[i].type == jbvBinary)
+					cb(cb_arg, v->array.elems + i, WJB_ELEM, level);
+				else
+					walkUncompressedJsonbDo(v->array.elems + i, cb, cb_arg,
+											level + 1);
+			}
+			cb(cb_arg, v, WJB_END_ARRAY, level);
+			break;
+		case jbvHash:
+			cb(cb_arg, v, WJB_BEGIN_OBJECT, level);
+
+			for (i = 0; i < v->hash.npairs; i++)
+			{
+				cb(cb_arg, &v->hash.pairs[i].key, WJB_KEY, level);
+
+				if (v->hash.pairs[i].value.type == jbvNull ||
+					v->hash.pairs[i].value.type == jbvString ||
+					v->hash.pairs[i].value.type == jbvBool ||
+					v->hash.pairs[i].value.type == jbvNumeric ||
+					v->hash.pairs[i].value.type == jbvBinary)
+					cb(cb_arg, &v->hash.pairs[i].value, WJB_VALUE, level);
+				else
+					walkUncompressedJsonbDo(&v->hash.pairs[i].value, cb, cb_arg,
+											level + 1);
+			}
+
+			cb(cb_arg, v, WJB_END_OBJECT, level);
+			break;
+		default:
+			elog(PANIC, "impossible JsonbValue->type: %d", v->type);
+	}
+}
+
+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *						   Iteration over binary jsonb						*
+ ****************************************************************************/
+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{
+	uint32		header = *(uint32 *) buffer;
+
+	it->type = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+	it->nelems = header & JB_COUNT_MASK;
+	it->buffer = buffer;
+
+
+	buffer += sizeof(uint32);
+	it->array = (JEntry *) buffer;
+
+	it->state = jbi_start;
+
+	switch (it->type)
+	{
+		case JB_FLAG_ARRAY:
+			it->data = buffer + it->nelems * sizeof(JEntry);
+			it->isScalar = (header & JB_FLAG_SCALAR) ? true : false;
+			Assert(it->isScalar == false || it->nelems == 1);
+			break;
+		case JB_FLAG_OBJECT:
+			it->data = buffer + it->nelems * sizeof(JEntry) * 2;
+			break;
+		default:
+			elog(PANIC, "impossible type: %08x", it->type);
+	}
+}
+
+JsonbIterator *
+JsonbIteratorInit(char *buffer)
+{
+	JsonbIterator *it = palloc(sizeof(*it));
+
+	parseBuffer(it, buffer);
+	it->next = NULL;
+
+	return it;
+}
+
+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry * e, bool skipNested)
+{
+	if (JBE_ISSTRING(*e))
+	{
+		v->type = jbvString;
+		v->string.val = (*it)->data + JBE_OFF(*e);
+		v->string.len = JBE_LEN(*e);
+		v->size = sizeof(JEntry) + v->string.len;
+
+		return false;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		v->type = jbvBool;
+		v->boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		v->type = jbvNumeric;
+		v->numeric = (Numeric) ((*it)->data + INTALIGN(JBE_OFF(*e)));
+
+		v->size = 2 * sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+		return false;
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (skipNested)
+	{
+		v->type = jbvBinary;
+		v->binary.data = (*it)->data + INTALIGN(JBE_OFF(*e));
+		v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		v->size = v->binary.len + 2 * sizeof(JEntry);
+
+		return false;
+	}
+	else
+	{
+		JsonbIterator *nit = palloc(sizeof(*nit));
+
+		parseBuffer(nit, (*it)->data + INTALIGN(JBE_OFF(*e)));
+		nit->next = *it;
+		*it = nit;
+
+		return true;
+	}
+}
+
+static JsonbIterator *
+up(JsonbIterator *it)
+{
+	JsonbIterator *v = it->next;
+
+	pfree(it);
+
+	return v;
+}
+
+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int			res;
+
+	if (*it == NULL)
+		return 0;
+
+	/*
+	 * Encode all possible states by one integer. That's possible because enum
+	 * members of JsonbIterator->state uses different bits than
+	 * JB_FLAG_ARRAY/JB_FLAG_OBJECT. See definition of JsonbIterator
+	 */
+
+	switch ((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:
+			(*it)->state = jbi_elem;
+			(*it)->i = 0;
+			v->type = jbvArray;
+			v->array.nelems = (*it)->nelems;
+			res = WJB_BEGIN_ARRAY;
+			v->array.scalar = (*it)->isScalar;
+			break;
+		case JB_FLAG_ARRAY | jbi_elem:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_ARRAY;
+			}
+			else if (formAnswer(it, v, &(*it)->array[(*it)->i++], skipNested))
+			{
+				res = JsonbIteratorGet(it, v, skipNested);
+			}
+			else
+			{
+				res = WJB_ELEM;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_start:
+			(*it)->state = jbi_key;
+			(*it)->i = 0;
+			v->type = jbvHash;
+			v->hash.npairs = (*it)->nelems;
+			res = WJB_BEGIN_OBJECT;
+			break;
+		case JB_FLAG_OBJECT | jbi_key:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_OBJECT;
+			}
+			else
+			{
+				formAnswer(it, v, &(*it)->array[(*it)->i * 2], false);
+				(*it)->state = jbi_value;
+				res = WJB_KEY;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_value:
+			(*it)->state = jbi_key;
+			if (formAnswer(it, v, &(*it)->array[((*it)->i++) * 2 + 1], skipNested))
+				res = JsonbIteratorGet(it, v, skipNested);
+			else
+				res = WJB_VALUE;
+			break;
+		default:
+			elog(PANIC, "unknown state %08x", (*it)->type & (*it)->state);
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *		  Transformation from tree to binary representation of jsonb		*
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	   *begin;
+	char	   *ptr;
+
+	struct
+	{
+		uint32		i;
+		uint32	   *header;
+		JEntry	   *array;
+		char	   *begin;
+	}		   *levelstate, *lptr, *pptr;
+
+	uint32		maxlevel;
+
+}	CompressState;
+
+#define curLevelState	state->lptr
+#define prevLevelState	state->pptr
+
+static void
+putJEntryString(CompressState * state, JsonbValue *value, uint32 level, uint32 i)
+{
+	curLevelState = state->levelstate + level;
+
+	if (i == 0)
+		curLevelState->array[0].entry = JENTRY_ISFIRST;
+	else
+		curLevelState->array[i].entry = 0;
+
+	switch (value->type)
+	{
+		case jbvNull:
+			curLevelState->array[i].entry |= JENTRY_ISNULL;
+
+			if (i > 0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvString:
+			memcpy(state->ptr, value->string.val, value->string.len);
+			state->ptr += value->string.len;
+
+			if (i == 0)
+				curLevelState->array[i].entry |= value->string.len;
+			else
+				curLevelState->array[i].entry |=
+					(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+					value->string.len;
+			break;
+		case jbvBool:
+			curLevelState->array[i].entry |= (value->boolean) ?
+				JENTRY_ISTRUE : JENTRY_ISFALSE;
+
+			if (i > 0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvNumeric:
+			{
+				int			addlen = INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin);
+				int			numlen = VARSIZE_ANY(value->numeric);
+
+				switch (addlen)
+				{
+					case 3:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 2:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 1:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->numeric, numlen);
+				state->ptr += numlen;
+
+				curLevelState->array[i].entry |= JENTRY_ISNUMERIC;
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + numlen;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + numlen;
+				break;
+			}
+		case jbvBinary:
+			{
+				int			addlen = INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin);
+
+				switch (addlen)
+				{
+					case 3:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 2:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 1:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->binary.data, value->binary.len);
+				state->ptr += value->binary.len;
+
+				curLevelState->array[i].entry |= JENTRY_ISNEST;
+
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + value->binary.len;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + value->binary.len;
+			}
+			break;
+		default:
+			elog(PANIC, "Unsupported JsonbValue type: %d", value->type);
+	}
+}
+
+static void
+compressCallback(void *arg, JsonbValue *value, uint32 flags, uint32 level)
+{
+	CompressState *state = arg;
+
+	if (level == state->maxlevel)
+	{
+		state->maxlevel *= 2;
+		state->levelstate = repalloc(state->levelstate,
+							   sizeof(*state->levelstate) * state->maxlevel);
+	}
+
+	curLevelState = state->levelstate + level;
+
+	if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
+	{
+		Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_BEGIN_OBJECT) && value->type == jbvHash));
+
+		curLevelState->begin = state->ptr;
+
+		switch (INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin))
+		{
+			case 3:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 2:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 1:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 0:
+			default:
+				break;
+		}
+
+		curLevelState->header = (uint32 *) state->ptr;
+		state->ptr += sizeof(*curLevelState->header);
+
+		curLevelState->array = (JEntry *) state->ptr;
+		curLevelState->i = 0;
+
+		if (value->type == jbvArray)
+		{
+			*curLevelState->header = value->array.nelems | JB_FLAG_ARRAY;
+			state->ptr += sizeof(JEntry) * value->array.nelems;
+
+			if (value->array.scalar)
+			{
+				Assert(value->array.nelems == 1);
+				Assert(level == 0);
+				*curLevelState->header |= JB_FLAG_SCALAR;
+			}
+		}
+		else
+		{
+			*curLevelState->header = value->hash.npairs | JB_FLAG_OBJECT;
+			state->ptr += sizeof(JEntry) * value->hash.npairs * 2;
+		}
+	}
+	else if (flags & WJB_ELEM)
+	{
+		putJEntryString(state, value, level, curLevelState->i);
+		curLevelState->i++;
+	}
+	else if (flags & WJB_KEY)
+	{
+		Assert(value->type == jbvString);
+
+		putJEntryString(state, value, level, curLevelState->i * 2);
+	}
+	else if (flags & WJB_VALUE)
+	{
+		putJEntryString(state, value, level, curLevelState->i * 2 + 1);
+		curLevelState->i++;
+	}
+	else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
+	{
+		uint32		len,
+					i;
+
+		Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_END_OBJECT) && value->type == jbvHash));
+		if (level == 0)
+			return;
+
+		len = state->ptr - (char *) curLevelState->begin;
+
+		prevLevelState = curLevelState - 1;
+
+		if (*prevLevelState->header & JB_FLAG_ARRAY)
+		{
+			i = prevLevelState->i;
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			if (i == 0)
+				prevLevelState->array[0].entry |= JENTRY_ISFIRST | len;
+			else
+				prevLevelState->array[i].entry |=
+					(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else if (*prevLevelState->header & JB_FLAG_OBJECT)
+		{
+			i = 2 * prevLevelState->i + 1;		/* VALUE, not a KEY */
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			prevLevelState->array[i].entry |=
+				(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else
+		{
+			elog(PANIC, "Wrong parent");
+		}
+
+		Assert(state->ptr - curLevelState->begin <= value->size);
+		prevLevelState->i++;
+	}
+	else
+	{
+		elog(PANIC, "Wrong flags");
+	}
+}
+
+/*
+ * puts JsonbValue tree into preallocated buffer
+ */
+uint32
+compressJsonb(JsonbValue *v, char *buffer)
+{
+	uint32		l = 0;
+	CompressState state;
+
+	state.begin = state.ptr = buffer;
+	state.maxlevel = 8;
+	state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+
+	walkUncompressedJsonb(v, compressCallback, &state);
+
+	l = state.ptr - buffer;
+	Assert(l <= v->size);
+
+	return l;
+}
+
+/****************************************************************************
+ *					Iteration-like forming jsonb							*
+ ****************************************************************************/
+static ToJsonbState *
+pushState(ToJsonbState ** state)
+{
+	ToJsonbState *ns = palloc(sizeof(*ns));
+
+	ns->next = *state;
+	return ns;
+}
+
+static void
+appendArray(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *a = &state->v;
+
+	Assert(a->type == jbvArray);
+
+	if (a->array.nelems >= state->size)
+	{
+		state->size *= 2;
+		a->array.elems = repalloc(a->array.elems,
+								  sizeof(*a->array.elems) * state->size);
+	}
+
+	a->array.elems[a->array.nelems++] = *v;
+
+	a->size += v->size;
+}
+
+static void
+appendKey(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	if (h->hash.npairs >= state->size)
+	{
+		state->size *= 2;
+		h->hash.pairs = repalloc(h->hash.pairs,
+								 sizeof(*h->hash.pairs) * state->size);
+	}
+
+	h->hash.pairs[h->hash.npairs].key = *v;
+	h->hash.pairs[h->hash.npairs].order = h->hash.npairs;
+
+	h->size += v->size;
+}
+
+static void
+appendValue(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	h->hash.pairs[h->hash.npairs++].value = *v;
+
+	h->size += v->size;
+}
+
+/*
+ * Pushes the value into state. With r = WJB_END_OBJECT and v = NULL
+ * it will order and unique hash's keys otherwise we believe that
+ * pushed keys was ordered and unique.
+ * Initial state of ToJsonbState is NULL.
+ */
+JsonbValue *
+pushJsonbValue(ToJsonbState ** state, int r /* WJB_* */ , JsonbValue *v)
+{
+	JsonbValue *h = NULL;
+
+	switch (r)
+	{
+		case WJB_BEGIN_ARRAY:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvArray;
+			(*state)->v.size = 3 * sizeof(JEntry);
+			(*state)->v.array.nelems = 0;
+			(*state)->v.array.scalar = (v && v->array.scalar) ? true : false;
+			(*state)->size = (v && v->type == jbvArray && v->array.nelems > 0)
+				? v->array.nelems : 4;
+			(*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) *
+											 (*state)->size);
+			break;
+		case WJB_BEGIN_OBJECT:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvHash;
+			(*state)->v.size = 3 * sizeof(JEntry);
+			(*state)->v.hash.npairs = 0;
+			(*state)->size = (v && v->type == jbvHash && v->hash.npairs > 0) ?
+				v->hash.npairs : 4;
+			(*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) *
+											(*state)->size);
+			break;
+		case WJB_ELEM:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric ||
+				   v->type == jbvBinary);
+			appendArray(*state, v);
+			break;
+		case WJB_KEY:
+			Assert(v->type == jbvString);
+			appendKey(*state, v);
+			break;
+		case WJB_VALUE:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric ||
+				   v->type == jbvBinary);
+			appendValue(*state, v);
+			break;
+		case WJB_END_OBJECT:
+			h = &(*state)->v;
+			/* v != NULL => we believe that keys was already sorted */
+			if (v == NULL)
+				uniqueJsonbValue(h);
+
+			/*
+			 * no break here - end of hash requres some extra work but rest is
+			 * the same as for array
+			 */
+		case WJB_END_ARRAY:
+			h = &(*state)->v;
+
+			/*
+			 * pop stack and push current array/hash as value in parent
+			 * array/hash
+			 */
+			*state = (*state)->next;
+			if (*state)
+			{
+				switch ((*state)->v.type)
+				{
+					case jbvArray:
+						appendArray(*state, h);
+						break;
+					case jbvHash:
+						appendValue(*state, h);
+						break;
+					default:
+						elog(PANIC, "wrong parent type: %d", (*state)->v.type);
+				}
+			}
+			break;
+		default:
+			elog(PANIC, "wrong type: %08x", r);
+	}
+
+	return h;
+}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 60ed0bb..fe6b20d 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -51,6 +52,7 @@ static inline Datum get_path_all(PG_FUNCTION_ARGS, bool as_text);
 static inline text *get_worker(text *json, char *field, int elem_index,
 		   char **tpath, int *ipath, int npath,
 		   bool normalize_results);
+static inline Datum get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text);
 
 /* semantic action functions for json_array_length */
 static void alen_object_start(void *state);
@@ -59,6 +61,7 @@ static void alen_array_element_start(void *state, bool isnull);
 
 /* common worker for json_each* functions */
 static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text);
+static inline Datum each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text);
 
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
@@ -219,6 +222,9 @@ typedef struct PopulateRecordsetState
 	MemoryContext fn_mcxt;		/* used to stash IO funcs */
 } PopulateRecordsetState;
 
+/* turn a jsonb object into a record */
+static inline void make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state);
+
 /*
  * SQL function json_object-keys
  *
@@ -226,12 +232,89 @@ typedef struct PopulateRecordsetState
  *
  * This SRF operates in value-per-call mode. It processes the
  * object during the first call, and the keys are simply stashed
- * in an array, whise size is expanded as necessary. This is probably
+ * in an array, whose size is expanded as necessary. This is probably
  * safe enough for a list of keys of a single object, since they are
  * limited in size to NAMEDATALEN and the number of keys is unlikely to
  * be so huge that it has major memory implications.
  */
 
+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext oldcontext;
+		Jsonb	   *jb = PG_GETARG_JSONB(0);
+		bool		skipNested = false;
+		JsonbIterator *it;
+		JsonbValue	v;
+		int			r = 0;
+
+		if (JB_ROOT_IS_SCALAR(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on a scalar")));
+		else if (JB_ROOT_IS_ARRAY(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on an array")));
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc(sizeof(OkeysState));
+
+		state->result_size = JB_ROOT_COUNT(jb);
+		state->result_count = 0;
+		state->sent_count = 0;
+		state->result = palloc(state->result_size * sizeof(char *));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+
+			if (r == WJB_KEY)
+			{
+				char	   *cstr;
+
+				cstr = palloc(v.string.len + 1 * sizeof(char));
+				memcpy(cstr, v.string.val, v.string.len);
+				cstr[v.string.len] = '\0';
+				state->result[state->result_count++] = cstr;
+			}
+		}
+
+
+		MemoryContextSwitchTo(oldcontext);
+		funcctx->user_fctx = (void *) state;
+
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	state = (OkeysState *) funcctx->user_fctx;
+
+	if (state->sent_count < state->result_count)
+	{
+		char	   *nxt = state->result[state->sent_count++];
+
+		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+	}
+
+	/* cleanup to reduce or eliminate memory leaks */
+	for (i = 0; i < state->result_count; i++)
+		pfree(state->result[i]);
+	pfree(state->result);
+	pfree(state);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
@@ -344,9 +427,9 @@ okeys_scalar(void *state, char *token, JsonTokenType tokentype)
 }
 
 /*
- * json getter functions
+ * json and jsonb getter functions
  * these implement the -> ->> #> and #>> operators
- * and the json_extract_path*(json, text, ...) functions
+ * and the json{b?}_extract_path*(json, text, ...) functions
  */
 
 
@@ -367,6 +450,51 @@ json_object_field(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *key = text_to_cstring(PG_GETARG_TEXT_P(1));
+	int			klen = strlen(key);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field on a scalar")));
+	else if (JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field on an array")));
+
+	Assert(JB_ROOT_IS_OBJECT(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				/*
+				 * The next thing the iterator fetches should be the value, no
+				 * matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_object_field_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -383,6 +511,74 @@ json_object_field_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field_text(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *key = text_to_cstring(PG_GETARG_TEXT_P(1));
+	int			klen = strlen(key);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field_text on a scalar")));
+	else if (JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field_text on an array")));
+
+	Assert(JB_ROOT_IS_OBJECT(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				text	   *result;
+
+				/*
+				 * The next thing the iterator fetches should be the value, no
+				 * matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+
+				/*
+				 * if it's a scalar string it needs to be de-escaped,
+				 * otherwise just return the text
+				 */
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL();
+				}
+				else
+				{
+					StringInfo	jtext = makeStringInfo();
+					Jsonb	   *tjb = JsonbValueToJsonb(&v);
+
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_array_element(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -398,6 +594,44 @@ json_array_element(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+	int			element_number = 0;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element on a scalar")));
+	else if (JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element on an object")));
+
+	Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			if (element_number++ == element)
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_array_element_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -413,6 +647,69 @@ json_array_element_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element_text(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+	int			element_number = 0;
+
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element_text on a scalar")));
+	else if (JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("cannot call jsonb_array_element_text on an object")));
+
+	Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			if (element_number++ == element)
+			{
+				/*
+				 * if it's a scalar string it needs to be de-escaped,
+				 * otherwise just return the text
+				 */
+				text	   *result;
+
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL();
+				}
+				else
+				{
+					StringInfo	jtext = makeStringInfo();
+					Jsonb	   *tjb = JsonbValueToJsonb(&v);
+
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_extract_path(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, false);
@@ -430,7 +727,8 @@ json_extract_path_text(PG_FUNCTION_ARGS)
 static inline Datum
 get_path_all(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
@@ -442,6 +740,19 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	long		ind;
 	char	   *endptr;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb	   *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -480,9 +791,17 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
+	{
+		if (val_type == JSONOID || as_text)
+			PG_RETURN_TEXT_P(result);
+		else
+			PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	}
 	else
+	{
+		/* null is null regardless */
 		PG_RETURN_NULL();
+	}
 }
 
 /*
@@ -662,7 +981,7 @@ get_object_field_end(void *state, char *fname, bool isnull)
 		/*
 		 * make a text object from the string from the prevously noted json
 		 * start up to the end of the previous token (the lexer is by now
-		 * ahead of us on whatevere came after what we're interested in).
+		 * ahead of us on whatever came after what we're interested in).
 		 */
 		int			len = _state->lex->prev_token_terminator - _state->result_start;
 
@@ -816,18 +1135,134 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
 
 }
 
+Datum
+jsonb_extract_path(PG_FUNCTION_ARGS)
+{
+	return get_jsonb_path_all(fcinfo, false);
+}
+
+Datum
+jsonb_extract_path_text(PG_FUNCTION_ARGS)
+{
+	return get_jsonb_path_all(fcinfo, true);
+}
+
+static inline Datum
+get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	   *pathtext;
+	bool	   *pathnulls;
+	int			npath;
+	int			i;
+	Jsonb	   *res;
+	bool		have_object = false,
+				have_array = false;
+	JsonbValue *jbvp;
+	JsonbValue	tv;
+
+
+	if (array_contains_nulls(path))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call function with null path elements")));
+
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &pathtext, &pathnulls, &npath);
+
+	if (JB_ROOT_IS_OBJECT(jb))
+		have_object = true;
+	else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
+		have_array = true;
+
+	jbvp = (JsonbValue *) VARDATA(jb);
+
+	for (i = 0; i < npath; i++)
+	{
+		if (have_object)
+		{
+			jbvp = findUncompressedJsonbValue((char *) jbvp, JB_FLAG_OBJECT, NULL,
+											  VARDATA_ANY(pathtext[i]),
+											  VARSIZE_ANY_EXHDR(pathtext[i]));
+		}
+		else if (have_array)
+		{
+			long		lindex;
+			uint32		index;
+			char	   *indextext = TextDatumGetCString(pathtext[i]);
+			char	   *endptr;
+
+			lindex = strtol(indextext, &endptr, 10);
+			if (*endptr != '\0' || lindex > INT_MAX || lindex < 0)
+				PG_RETURN_NULL();
+			index = (uint32) lindex;
+			jbvp = getJsonbValue((char *) jbvp, JB_FLAG_ARRAY, index);
+		}
+		else
+		{
+			if (i == 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot call extract path from a scalar")));
+			PG_RETURN_NULL();
+		}
+		if (jbvp == NULL)
+			PG_RETURN_NULL();
+		if (i == npath - 1)
+			break;
+		if (jbvp->type == jbvBinary)
+		{
+			JsonbIterator *it = JsonbIteratorInit(jbvp->binary.data);
+			int			r;
+
+			r = JsonbIteratorGet(&it, &tv, true);
+			jbvp = (JsonbValue *) jbvp->binary.data;
+			have_object = r == WJB_BEGIN_OBJECT;
+			have_array = r == WJB_BEGIN_ARRAY;
+		}
+		else
+		{
+			have_object = jbvp->type == jbvHash;
+			have_array = jbvp->type == jbvArray;
+		}
+	}
+
+	if (as_text)
+	{
+		if (jbvp->type == jbvString)
+			PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->string.val, jbvp->string.len));
+		else if (jbvp->type == jbvNull)
+			PG_RETURN_NULL();
+	}
+
+	res = JsonbValueToJsonb(jbvp);
+
+	if (as_text)
+	{
+		PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(res)) ? NULL : VARDATA(res), VARSIZE(res))));
+	}
+	else
+	{
+		/* not text mode - just hand back the jsonb */
+		PG_RETURN_JSONB(res);
+	}
+}
+
 /*
  * SQL function json_array_length(json) -> int
  */
 Datum
 json_array_length(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
 	AlenState  *state;
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(AlenState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -847,6 +1282,23 @@ json_array_length(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(state->count);
 }
 
+Datum
+jsonb_array_length(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a scalar")));
+	else if (!JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a non-array")));
+
+	PG_RETURN_INT32(JB_ROOT_COUNT(jb));
+}
+
 /*
  * These next two check ensure that the json is an array (since it can't be
  * a scalar or an object).
@@ -903,24 +1355,43 @@ json_each(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_each(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, false);
+}
+
+Datum
 json_each_text(PG_FUNCTION_ARGS)
 {
 	return each_worker(fcinfo, true);
 }
 
+Datum
+jsonb_each_text(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, true);
+}
+
 static inline Datum
-each_worker(PG_FUNCTION_ARGS, bool as_text)
+each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	JsonLexContext *lex = makeJsonLexContext(json, true);
-	JsonSemAction *sem;
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
 	ReturnSetInfo *rsi;
-	MemoryContext old_cxt;
+	Tuplestorestate *tuple_store;
 	TupleDesc	tupdesc;
-	EachState  *state;
-
-	state = palloc0(sizeof(EachState));
-	sem = palloc0(sizeof(JsonSemAction));
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt,
+				tmp_cxt;
+	bool		skipNested = false;
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+
+	if (!JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_each%s on a non-object",
+						as_text ? "_text" : "")));
 
 	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 
@@ -937,10 +1408,140 @@ each_worker(PG_FUNCTION_ARGS, bool as_text)
 
 	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
 
-	/* make these in a sufficiently long-lived memory context */
 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
 
-	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			text	   *key;
+			HeapTuple	tuple;
+			Datum		values[2];
+			bool		nulls[2] = {false, false};
+
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			key = cstring_to_text_with_len(v.string.val, v.string.len);
+
+			/*
+			 * The next thing the iterator fetches should be the value, no
+			 * matter what shape it is.
+			 */
+			r = JsonbIteratorGet(&it, &v, skipNested);
+
+			values[0] = PointerGetDatum(key);
+
+			if (as_text)
+			{
+				if (v.type == jbvNull)
+				{
+					/* a json null is an sql null in text mode */
+					nulls[1] = true;
+					values[1] = (Datum) NULL;
+				}
+				else
+				{
+					text	   *sv;
+
+					if (v.type == jbvString)
+					{
+						/* in text mode scalar strings should be dequoted */
+						sv = cstring_to_text_with_len(v.string.val, v.string.len);
+					}
+					else
+					{
+						/* turn anything else into a json string */
+						StringInfo	jtext = makeStringInfo();
+						Jsonb	   *jb = JsonbValueToJsonb(&v);
+
+						(void) JsonbToCString(jtext, VARDATA(jb), 2 * v.size);
+						sv = cstring_to_text_with_len(jtext->data, jtext->len);
+					}
+
+					values[1] = PointerGetDatum(sv);
+				}
+			}
+			else
+			{
+				/* not in text mode, just return the Jsonb */
+				Jsonb	   *val = JsonbValueToJsonb(&v);
+
+				values[1] = PointerGetDatum(val);
+			}
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();
+}
+
+
+static inline Datum
+each_worker(PG_FUNCTION_ARGS, bool as_text)
+{
+	text	   *json;
+	JsonLexContext *lex;
+	JsonSemAction *sem;
+	ReturnSetInfo *rsi;
+	MemoryContext old_cxt;
+	TupleDesc	tupdesc;
+	EachState  *state;
+
+	json = PG_GETARG_TEXT_P(0);
+
+	lex = makeJsonLexContext(json, true);
+	state = palloc0(sizeof(EachState));
+	sem = palloc0(sizeof(JsonSemAction));
+
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
+
+	/* make these in a sufficiently long-lived memory context */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
 	BlessTupleDesc(state->ret_tdesc);
 	state->tuple_store =
 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
@@ -1075,19 +1676,114 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
  *
  * a lot of this processing is similar to the json_each* functions
  */
+
 Datum
-json_array_elements(PG_FUNCTION_ARGS)
+jsonb_array_elements(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	ReturnSetInfo *rsi;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt,
+				tmp_cxt;
+	bool		skipNested = false;
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from a scalar")));
+	else if (!JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from an object")));
 
-	/* elements doesn't need any escaped strings, so use false here */
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	/* it's a simple type, so don't use get_call_result_type() */
+	tupdesc = rsi->expectedDesc;
+
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			HeapTuple	tuple;
+			Datum		values[1];
+			bool		nulls[1] = {false};
+			Jsonb	   *val;
+
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			val = JsonbValueToJsonb(&v);
+			values[0] = PointerGetDatum(val);
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();
+}
+
+Datum
+json_array_elements(PG_FUNCTION_ARGS)
+{
+	text	   *json;
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	ElementsState *state;
 
+	json = PG_GETARG_TEXT_P(0);
+
+	/* elements doesn't need any escaped strings, so use false here */
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(ElementsState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -1219,9 +1915,16 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
  * which is in turn partly adapted from record_out.
  *
  * The json is decomposed into a hash table, in which each
- * field in the record is then looked up by name.
+ * field in the record is then looked up by name. For jsonb
+ * we fetch the values direct from the object.
  */
 Datum
+jsonb_populate_record(PG_FUNCTION_ARGS)
+{
+	return populate_record_worker(fcinfo, true);
+}
+
+Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
 	return populate_record_worker(fcinfo, true);
@@ -1236,12 +1939,15 @@ json_to_record(PG_FUNCTION_ARGS)
 static inline Datum
 populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 {
+	Oid			argtype;
+	Oid			jtype = get_fn_expr_argtype(fcinfo->flinfo, have_record_arg ? 1 : 0);
 	text	   *json;
+	Jsonb	   *jb = NULL;
 	bool		use_json_as_text;
-	HTAB	   *json_hash;
+	HTAB	   *json_hash = NULL;
 	HeapTupleHeader rec = NULL;
-	Oid			tupType;
-	int32		tupTypmod;
+	Oid			tupType = InvalidOid;
+	int32		tupTypmod = -1;
 	TupleDesc	tupdesc;
 	HeapTupleData tuple;
 	HeapTuple	rettuple;
@@ -1250,66 +1956,66 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	int			i;
 	Datum	   *values;
 	bool	   *nulls;
-	char		fname[NAMEDATALEN];
-	JsonHashEntry *hashentry;
-
-	if (have_record_arg)
-	{
-		Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
-		use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
+	Assert(jtype == JSONOID || jtype == JSONBOID);
 
-		if (!type_is_rowtype(argtype))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("first argument of json_populate_record must be a row type")));
-
-		if (PG_ARGISNULL(0))
-		{
-			if (PG_ARGISNULL(1))
-				PG_RETURN_NULL();
+	use_json_as_text = PG_ARGISNULL(have_record_arg ? 2 : 1) ? false : 
+		PG_GETARG_BOOL(have_record_arg ? 2 : 1);
 
-			/*
-			 * have no tuple to look at, so the only source of type info is
-			 * the argtype. The lookup_rowtype_tupdesc call below will error
-			 * out if we don't have a known composite type oid here.
-			 */
-			tupType = argtype;
-			tupTypmod = -1;
-		}
-		else
-		{
-			rec = PG_GETARG_HEAPTUPLEHEADER(0);
-
-			if (PG_ARGISNULL(1))
-				PG_RETURN_POINTER(rec);
-
-			/* Extract type info from the tuple itself */
-			tupType = HeapTupleHeaderGetTypeId(rec);
-			tupTypmod = HeapTupleHeaderGetTypMod(rec);
-		}
-
-		json = PG_GETARG_TEXT_P(1);
+	if (have_record_arg)
+	{
+		 argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+
+		 if (!type_is_rowtype(argtype))
+			 ereport(ERROR,
+					 (errcode(ERRCODE_DATATYPE_MISMATCH),
+					  errmsg("first argument of json%s_populate_record must be a row type", jtype == JSONBOID ? "b" : "")));
+
+		 if (PG_ARGISNULL(0))
+		 {
+			 if (PG_ARGISNULL(1))
+				 PG_RETURN_NULL();
+			 
+			 /*
+			  * have no tuple to look at, so the only source of type info is the
+			  * argtype. The lookup_rowtype_tupdesc call below will error out if we
+			  * don't have a known composite type oid here.
+			  */
+			 tupType = argtype;
+			 tupTypmod = -1;
+		 }
+		 else
+		 {
+			 rec = PG_GETARG_HEAPTUPLEHEADER(0);
+
+			 if (PG_ARGISNULL(1))
+				 PG_RETURN_POINTER(rec);
+			 
+			 /* Extract type info from the tuple itself */
+			 tupType = HeapTupleHeaderGetTypeId(rec);
+			 tupTypmod = HeapTupleHeaderGetTypMod(rec);
+		 }
+		 
+		 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 	}
 	else
-	{
-		/* json_to_record case */
-
-		use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
-
-		if (PG_ARGISNULL(0))
-			PG_RETURN_NULL();
+	{       /* json{b}_to_record case */
+		
+        use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
 
-		json = PG_GETARG_TEXT_P(0);
+        if (PG_ARGISNULL(0))
+            PG_RETURN_NULL();
 
 		get_call_result_type(fcinfo, NULL, &tupdesc);
 	}
 
-	json_hash = get_json_object_as_hash(json, "json_populate_record",
-										use_json_as_text);
-
-	if (have_record_arg)
+	if (jtype == JSONOID)
 	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(have_record_arg ? 1 : 0);
+
+		json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
+
 		/*
 		 * if the input json is empty, we can only skip the rest if we were
 		 * passed in a non-null record, since otherwise there may be issues
@@ -1318,8 +2024,15 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 		if (hash_get_num_entries(json_hash) == 0 && rec)
 			PG_RETURN_POINTER(rec);
 
+	}
+	else
+	{
+		jb = PG_GETARG_JSONB(have_record_arg ? 1 : 0);
+
+		/* same logic as for json */
+		if (JB_ISEMPTY(jb) && rec)
+			PG_RETURN_POINTER(rec);
 
-		tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 	}
 
 	ncolumns = tupdesc->natts;
@@ -1382,7 +2095,9 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
-		char	   *value;
+		JsonbValue *v = NULL;
+		char		fname[NAMEDATALEN];
+		JsonHashEntry *hashentry = NULL;
 
 		/* Ignore dropped columns in datatype */
 		if (tupdesc->attrs[i]->attisdropped)
@@ -1391,9 +2106,22 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 			continue;
 		}
 
-		memset(fname, 0, NAMEDATALEN);
-		strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
-		hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+		if (jtype == JSONOID)
+		{
+
+			memset(fname, 0, NAMEDATALEN);
+			strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
+			hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+		}
+		else
+		{
+			if (!JB_ISEMPTY(jb))
+			{
+				char	   *key = NameStr(tupdesc->attrs[i]->attname);
+
+				v = findUncompressedJsonbValue(VARDATA(jb), JB_FLAG_OBJECT, NULL, key, strlen(key));
+			}
+		}
 
 		/*
 		 * we can't just skip here if the key wasn't found since we might have
@@ -1403,7 +2131,8 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 		 * then every field which we don't populate needs to be run through
 		 * the input function just in case it's a domain type.
 		 */
-		if (hashentry == NULL && rec)
+		if (((jtype == JSONOID && hashentry == NULL) ||
+			 (jtype == JSONBOID && v == NULL)) && rec)
 			continue;
 
 		/*
@@ -1418,7 +2147,8 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 						  fcinfo->flinfo->fn_mcxt);
 			column_info->column_type = column_type;
 		}
-		if (hashentry == NULL || hashentry->isnull)
+		if ((jtype == JSONOID && (hashentry == NULL || hashentry->isnull)) ||
+			(jtype == JSONBOID && (v == NULL || v->type == jbvNull)))
 		{
 			/*
 			 * need InputFunctionCall to happen even for nulls, so that domain
@@ -1428,12 +2158,39 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = true;
+
 		}
 		else
 		{
-			value = hashentry->val;
 
-			values[i] = InputFunctionCall(&column_info->proc, value,
+			char	   *s = NULL;
+
+			if (jtype == JSONOID)
+			{
+				/* already done the hard work in the json case */
+				s = hashentry->val;
+			}
+			else
+			{
+				if (v->type == jbvString)
+					s = pnstrdup(v->string.val, v->string.len);
+				else if (v->type == jbvBool)
+					s = pnstrdup((v->boolean) ? "t" : "f", 1);
+				else if (v->type == jbvNumeric)
+					s = DatumGetCString(DirectFunctionCall1(numeric_out,
+											   PointerGetDatum(v->numeric)));
+				else if (!use_json_as_text)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("cannot populate with a nested object unless use_json_as_text is true")));
+				else if (v->type == jbvBinary)
+					s = JsonbToCString(NULL, v->binary.data, v->binary.len);
+				else
+					/* not expected to happen */
+					elog(ERROR, "Wrong jsonb");
+			}
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = false;
@@ -1599,6 +2356,136 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
  * per object in the array.
  */
 Datum
+jsonb_populate_recordset(PG_FUNCTION_ARGS)
+{
+	return populate_recordset_worker(fcinfo, true);
+}
+
+static inline void
+make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state)
+{
+	Datum	   *values;
+	bool	   *nulls;
+	int			i;
+	RecordIOData *my_extra = state->my_extra;
+	int			ncolumns = my_extra->ncolumns;
+	TupleDesc	tupdesc = state->ret_tdesc;
+	HeapTupleHeader rec = state->rec;
+	HeapTuple	rettuple;
+
+	values = (Datum *) palloc(ncolumns * sizeof(Datum));
+	nulls = (bool *) palloc(ncolumns * sizeof(bool));
+
+	if (state->rec)
+	{
+		HeapTupleData tuple;
+
+		/* Build a temporary HeapTuple control structure */
+		tuple.t_len = HeapTupleHeaderGetDatumLength(state->rec);
+		ItemPointerSetInvalid(&(tuple.t_self));
+		tuple.t_tableOid = InvalidOid;
+		tuple.t_data = state->rec;
+
+		/* Break down the tuple into fields */
+		heap_deform_tuple(&tuple, tupdesc, values, nulls);
+	}
+	else
+	{
+		for (i = 0; i < ncolumns; ++i)
+		{
+			values[i] = (Datum) 0;
+			nulls[i] = true;
+		}
+	}
+
+	for (i = 0; i < ncolumns; ++i)
+	{
+		ColumnIOData *column_info = &my_extra->columns[i];
+		Oid			column_type = tupdesc->attrs[i]->atttypid;
+		JsonbValue *v = NULL;
+
+		/* Ignore dropped columns in datatype */
+		if (tupdesc->attrs[i]->attisdropped)
+		{
+			nulls[i] = true;
+			continue;
+		}
+
+		if (!JB_ISEMPTY(element))
+		{
+			char	   *key = NameStr(tupdesc->attrs[i]->attname);
+
+			v = findUncompressedJsonbValue(VARDATA(element), JB_FLAG_OBJECT, NULL, key, strlen(key));
+		}
+
+		/*
+		 * we can't just skip here if the key wasn't found since we might have
+		 * a domain to deal with. If we were passed in a non-null record
+		 * datum, we assume that the existing values are valid (if they're
+		 * not, then it's not our fault), but if we were passed in a null,
+		 * then every field which we don't populate needs to be run through
+		 * the input function just in case it's a domain type.
+		 */
+		if (v == NULL && rec)
+			continue;
+
+		/*
+		 * Prepare to convert the column value from text
+		 */
+		if (column_info->column_type != column_type)
+		{
+			getTypeInputInfo(column_type,
+							 &column_info->typiofunc,
+							 &column_info->typioparam);
+			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
+						  state->fn_mcxt);
+			column_info->column_type = column_type;
+		}
+		if (v == NULL || v->type == jbvNull)
+		{
+			/*
+			 * need InputFunctionCall to happen even for nulls, so that domain
+			 * checks are done
+			 */
+			values[i] = InputFunctionCall(&column_info->proc, NULL,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = true;
+		}
+		else
+		{
+			char	   *s = NULL;
+
+			if (v->type == jbvString)
+				s = pnstrdup(v->string.val, v->string.len);
+			else if (v->type == jbvBool)
+				s = pnstrdup((v->boolean) ? "t" : "f", 1);
+			else if (v->type == jbvNumeric)
+				s = DatumGetCString(DirectFunctionCall1(numeric_out,
+											   PointerGetDatum(v->numeric)));
+			else if (!state->use_json_as_text)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot populate with a nested object unless use_json_as_text is true")));
+			else if (v->type == jbvBinary)
+				s = JsonbToCString(NULL, v->binary.data, v->binary.len);
+			else
+				/* not expected to happen */
+				elog(ERROR, "Wrong jsonb");
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = false;
+		}
+	}
+
+	rettuple = heap_form_tuple(tupdesc, values, nulls);
+
+	tuplestore_puttuple(state->tuple_store, rettuple);
+}
+
+Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
 	return populate_recordset_worker(fcinfo, true);
@@ -1617,7 +2504,7 @@ static inline Datum
 populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 {
 	Oid			argtype;
-	text	   *json;
+	Oid			jtype = get_fn_expr_argtype(fcinfo->flinfo, have_record_arg ? 1 : 0);
 	bool		use_json_as_text;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
@@ -1627,27 +2514,25 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	TupleDesc	tupdesc;
 	RecordIOData *my_extra;
 	int			ncolumns;
-	JsonLexContext *lex;
-	JsonSemAction *sem;
 	PopulateRecordsetState *state;
 
-	if (have_record_arg)
-	{
-		argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+    if (have_record_arg)
+    {
+        argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
-		use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
+        use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
-		if (!type_is_rowtype(argtype))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("first argument of json_populate_recordset must be a row type")));
-	}
-	else
-	{
-		argtype = InvalidOid;
+        if (!type_is_rowtype(argtype))
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg("first argument of json_populate_recordset must be a row type")));
+    }
+    else
+    {
+        argtype = InvalidOid;
 
-		use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
-	}
+        use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
+    }
 
 	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 
@@ -1664,33 +2549,17 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 
 	/*
 	 * get the tupdesc from the result set info - it must be a record type
-	 * because we already checked that arg1 is a record type.
+	 * because we already checked that arg1 is a record type, or we're
+	 * in a to_record function which returns a setof record.
 	 */
 	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
 
-	state = palloc0(sizeof(PopulateRecordsetState));
-	sem = palloc0(sizeof(JsonSemAction));
-
-
-	/* make these in a sufficiently long-lived memory context */
-	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
-	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
-	BlessTupleDesc(state->ret_tdesc);
-	state->tuple_store =
-		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
-							  false, work_mem);
-
-	MemoryContextSwitchTo(old_cxt);
-
 	/* if the json is null send back an empty set */
 	if (have_record_arg)
 	{
 		if (PG_ARGISNULL(1))
 			PG_RETURN_NULL();
-
-		json = PG_GETARG_TEXT_P(1);
-
+		
 		if (PG_ARGISNULL(0))
 			rec = NULL;
 		else
@@ -1698,11 +2567,9 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	}
 	else
 	{
-		if (PG_ARGISNULL(0))
+		if (PG_ARGISNULL(1))
 			PG_RETURN_NULL();
 
-		json = PG_GETARG_TEXT_P(0);
-
 		rec = NULL;
 	}
 
@@ -1710,8 +2577,6 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	tupTypmod = tupdesc->tdtypmod;
 	ncolumns = tupdesc->natts;
 
-	lex = makeJsonLexContext(json, true);
-
 	/*
 	 * We arrange to look up the needed I/O info just once per series of
 	 * calls, assuming the record type doesn't change underneath us.
@@ -1740,29 +2605,86 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	sem->semstate = (void *) state;
-	sem->array_start = populate_recordset_array_start;
-	sem->array_element_start = populate_recordset_array_element_start;
-	sem->scalar = populate_recordset_scalar;
-	sem->object_field_start = populate_recordset_object_field_start;
-	sem->object_field_end = populate_recordset_object_field_end;
-	sem->object_start = populate_recordset_object_start;
-	sem->object_end = populate_recordset_object_end;
+	state = palloc0(sizeof(PopulateRecordsetState));
 
-	state->lex = lex;
+	/* make these in a sufficiently long-lived memory context */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+	state->ret_tdesc = CreateTupleDescCopy(tupdesc);;
+	BlessTupleDesc(state->ret_tdesc);
+	state->tuple_store = tuplestore_begin_heap(rsi->allowedModes &
+											   SFRM_Materialize_Random,
+											   false, work_mem);
+	MemoryContextSwitchTo(old_cxt);
 
 	state->my_extra = my_extra;
 	state->rec = rec;
 	state->use_json_as_text = use_json_as_text;
 	state->fn_mcxt = fcinfo->flinfo->fn_mcxt;
 
-	pg_parse_json(lex, sem);
+
+	if (jtype == JSONOID)
+	{
+		text	   *json = PG_GETARG_TEXT_P(have_record_arg ? 1 : 0);
+		JsonLexContext *lex;
+		JsonSemAction *sem;
+
+		sem = palloc0(sizeof(JsonSemAction));
+
+		lex = makeJsonLexContext(json, true);
+
+		sem->semstate = (void *) state;
+		sem->array_start = populate_recordset_array_start;
+		sem->array_element_start = populate_recordset_array_element_start;
+		sem->scalar = populate_recordset_scalar;
+		sem->object_field_start = populate_recordset_object_field_start;
+		sem->object_field_end = populate_recordset_object_field_end;
+		sem->object_start = populate_recordset_object_start;
+		sem->object_end = populate_recordset_object_end;
+
+		state->lex = lex;
+
+		pg_parse_json(lex, sem);
+
+	}
+	else
+	{
+		Jsonb	   *jb;
+		JsonbIterator *it;
+		JsonbValue	v;
+		bool		skipNested = false;
+		int			r;
+
+		Assert(jtype == JSONBOID);
+		jb = PG_GETARG_JSONB(have_record_arg ? 1 : 0);
+
+		if (JB_ROOT_IS_SCALAR(jb) || !JB_ROOT_IS_ARRAY(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("cannot call jsonb_populate_recordset on non-array")));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+
+			if (r == WJB_ELEM)
+			{
+				Jsonb	   *element = JsonbValueToJsonb(&v);
+
+				if (!JB_ROOT_IS_OBJECT(element))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("jsonb_populate_recordset argument must be an array of objects")));
+				make_row_from_rec_and_jsonb(element, state);
+			}
+		}
+	}
 
 	rsi->setResult = state->tuple_store;
 	rsi->setDesc = state->ret_tdesc;
 
 	PG_RETURN_NULL();
-
 }
 
 static void
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 3544d0a..e037957 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -359,4 +359,8 @@ DATA(insert ( 1560 1560 1685 i f ));
 DATA(insert ( 1562 1562 1687 i f ));
 DATA(insert ( 1700 1700 1703 i f ));
 
+/* json to/from jsonb */
+DATA(insert ( 114 3802 0 e i ));
+DATA(insert ( 3802 114 0 e i ));
+
 #endif   /* PG_CAST_H */
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6aa4890..08f2f1e 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1753,6 +1753,18 @@ DATA(insert OID = 3966 (  "#>"	   PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
 DESCR("get value from json with path elements");
 DATA(insert OID = 3967 (  "#>>"    PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
 DESCR("get value from json as text with path elements");
+DATA(insert OID = 3211 (  "->"	   PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3225 (  "->>"    PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3212 (  "->"	   PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3226 (  "->>"    PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3213 (  "#>"	   PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3206 (  "#>>"    PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
+DESCR("get value from jsonb as text with path elements");
 
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b7c0d8f..8cb59df 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4483,6 +4483,44 @@ DESCR("I/O");
 DATA(insert OID = 3774 (  regdictionarysend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3769" _null_ _null_ _null_ _null_ regdictionarysend _null_ _null_ _null_ ));
 DESCR("I/O");
 
+/* jsonb */
+DATA(insert OID =  3806 (  jsonb_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2275" _null_ _null_ _null_ _null_ jsonb_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3805 (  jsonb_recv		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3804 (  jsonb_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3802" _null_ _null_ _null_ _null_ jsonb_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
+DESCR("I/O");
+
+DATA(insert OID = 3969 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
+DATA(insert OID = 3214 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
+DATA(insert OID = 3215 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
+DATA(insert OID = 3216 (  jsonb_array_element_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element_text _null_ _null_ _null_ ));
+DATA(insert OID = 3217 (  jsonb_extract_path			PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 3802 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3220 (  jsonb_extract_path_op		PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DATA(insert OID = 3221 (  jsonb_extract_path_text	PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3218 (  jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DATA(insert OID = 3219 (  jsonb_array_elements		PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3207 (  jsonb_array_length			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "3802" _null_ _null_ _null_ _null_ jsonb_array_length _null_ _null_ _null_ ));
+DESCR("length of jsonb array");
+DATA(insert OID = 3222 (  jsonb_object_keys			PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_object_keys _null_ _null_ _null_ ));
+DESCR("get jsonb object keys");
+DATA(insert OID = 3208 (  jsonb_each				   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,3802}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3223 (  jsonb_each_text		   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,25}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each_text _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3209 (  jsonb_populate_record	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_record _null_ _null_ _null_ ));
+DESCR("get record fields from a jsonb object");
+DATA(insert OID = 3224 (  jsonb_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ ));
+DESCR("get set of records with fields from a jsonb array of objects");
+DATA(insert OID = 3210 (  jsonb_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a jsonb value");
+
+
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in			PGNSP PGUID 12 1  0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 3fc20c6..7fb8999 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -600,6 +600,12 @@ DATA(insert OID = 3645 ( _tsquery		PGNSP PGUID -1 f b A f t \054 0 3615 0 array_
 DATA(insert OID = 3735 ( _regconfig		PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 
+/* jsonb */
+DATA(insert OID = 3802 ( jsonb			PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DESCR("Binary JSON");
+#define JSONBOID 3802
+DATA(insert OID = 3807 ( _jsonb			PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 9982e59..3610fc8 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		PG_RETURN_DATUM(_result); \
 	} while (0)
 
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+	do { \
+		ReturnSetInfo	   *rsi; \
+		(_funcctx)->call_cntr++; \
+		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+		rsi->isDone = ExprMultipleResult; \
+		PG_RETURN_NULL(); \
+	} while (0)
+
 #define  SRF_RETURN_DONE(_funcctx) \
 	do { \
 		ReturnSetInfo	   *rsi; \
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index ed96a62..8b7f4b1 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -63,4 +63,18 @@ extern Datum json_populate_recordset(PG_FUNCTION_ARGS);
 extern Datum json_to_record(PG_FUNCTION_ARGS);
 extern Datum json_to_recordset(PG_FUNCTION_ARGS);
 
+extern Datum jsonb_object_field(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_field_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_keys(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_length(PG_FUNCTION_ARGS);
+extern Datum jsonb_each(PG_FUNCTION_ARGS);
+extern Datum jsonb_each_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_elements(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_record(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index 0000000..50509e2
--- /dev/null
+++ b/src/include/utils/jsonb.h
@@ -0,0 +1,241 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.h
+ *	  Declarations for JSONB data type support.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/include/utils/jsonb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __JSONB_H__
+#define __JSONB_H__
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "utils/array.h"
+#include "utils/numeric.h"
+
+/*
+ * JEntry: there is one of these for each key _and_ value in an jsonb
+ *
+ * the position offset points to the _end_ so that we can get the length
+ * by subtraction from the previous entry.	the ISFIRST flag lets us tell
+ * whether there is a previous entry.
+ */
+typedef struct
+{
+	uint32		entry;
+}	JEntry;
+
+#define JENTRY_ISFIRST		0x80000000
+#define JENTRY_ISSTRING		(0x00000000)		/* keep binary compatibility */
+#define JENTRY_ISNUMERIC	(0x10000000)
+#define JENTRY_ISNEST		(0x20000000)
+#define JENTRY_ISNULL		(0x40000000)		/* keep binary compatibility */
+#define JENTRY_ISBOOL		(0x10000000 | 0x20000000)
+#define JENTRY_ISFALSE		JENTRY_ISBOOL
+#define JENTRY_ISTRUE		(0x10000000 | 0x20000000 | 0x40000000)
+
+/* JENTRY_ISOBJECT, JENTRY_ISARRAY and JENTRY_ISCALAR is only used in send/recv */
+#define JENTRY_ISOBJECT		(0x20000000)
+#define JENTRY_ISARRAY		(0x20000000 | 0x40000000)
+#define JENTRY_ISCALAR		(0x10000000 | 0x40000000)
+
+#define JENTRY_POSMASK	0x0FFFFFFF
+#define JENTRY_TYPEMASK (~(JENTRY_POSMASK | JENTRY_ISFIRST))
+
+/* note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(he_)		(((he_).entry & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISNEST(he_)			(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST)
+#define JBE_ISNULL(he_)			(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL(he_)			(((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
+#define JBE_ISBOOL_TRUE(he_)	(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
+#define JBE_ISBOOL_FALSE(he_)	(JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
+
+#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK)
+#define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1]))
+#define JBE_LEN(he_) (JBE_ISFIRST(he_)	\
+					  ? JBE_ENDPOS(he_) \
+					  : JBE_ENDPOS(he_) - JBE_ENDPOS((&(he_))[-1]))
+
+/*
+ * determined by the size of "endpos" (ie JENTRY_POSMASK)
+ */
+#define JSONB_MAX_STRING_LEN		JENTRY_POSMASK
+
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* header of hash or array jsonb type */
+	/* array of JEntry follows */
+} Jsonb;
+
+/*
+ * it's not possible to get more than 2^28 items into an jsonb.
+ */
+#define JB_FLAG_UNUSED			0x80000000
+#define JB_FLAG_ARRAY			0x40000000
+#define JB_FLAG_OBJECT			0x20000000
+#define JB_FLAG_SCALAR			0x10000000
+
+#define JB_COUNT_MASK			0x0FFFFFFF
+
+#define JB_ISEMPTY(jbp_)		(VARSIZE(jbp_) <= VARHDRSZ)
+#define JB_ROOT_COUNT(jbp_)		(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_COUNT_MASK))
+#define JB_ROOT_IS_OBJECT(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_OBJECT))
+#define JB_ROOT_IS_ARRAY(jbp_)	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_ARRAY))
+#define JB_ROOT_IS_SCALAR(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_SCALAR))
+
+#define DatumGetJsonb(d)	((Jsonb*) PG_DETOAST_DATUM(d))
+#define JsonbGetDatum(p)	PointerGetDatum(p)
+
+#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
+
+typedef struct JsonbPair JsonbPair;
+typedef struct JsonbValue JsonbValue;
+
+struct JsonbValue
+{
+	enum
+	{
+		jbvNull,
+		jbvString,
+		jbvNumeric,
+		jbvBool,
+		jbvArray,
+		jbvHash,
+		jbvBinary				/* binary form of jbvArray/jbvHash */
+	}			type;
+
+	uint32		size;			/* estimation size of node (including
+								 * subnodes) */
+
+	union
+	{
+		Numeric numeric;
+		bool		boolean;
+		struct
+		{
+			uint32		len;
+			char	   *val;	/* could be not null-terminated */
+		}			string;
+
+		struct
+		{
+			int			nelems;
+			JsonbValue *elems;
+			bool		scalar; /* scalar actually shares representation with
+								 * array */
+		}			array;
+
+		struct
+		{
+			int			npairs;
+			JsonbPair  *pairs;
+		}			hash;
+
+		struct
+		{
+			uint32		len;
+			char	   *data;
+		}			binary;
+	};
+
+};
+
+struct JsonbPair
+{
+	JsonbValue	key;
+	JsonbValue	value;
+	uint32		order;			/* to keep order of pairs with equal key */
+};
+
+/*
+ * jsonb support functios
+ */
+
+#define WJB_KEY				(0x001)
+#define WJB_VALUE			(0x002)
+#define WJB_ELEM			(0x004)
+#define WJB_BEGIN_ARRAY		(0x008)
+#define WJB_END_ARRAY		(0x010)
+#define WJB_BEGIN_OBJECT	(0x020)
+#define WJB_END_OBJECT		(0x040)
+
+typedef void (*walk_jsonb_cb) (void * /* arg */ , JsonbValue * /* value */ ,
+								   uint32 /* flags */ , uint32 /* level */ );
+extern void walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg);
+
+extern int	compareJsonbStringValue(const void *a, const void *b, void *arg);
+extern int	compareJsonbPair(const void *a, const void *b, void *arg);
+
+extern int	compareJsonbBinaryValue(char *a, char *b);
+extern int	compareJsonbValue(JsonbValue *a, JsonbValue *b);
+
+extern JsonbValue *findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key);
+extern JsonbValue *findUncompressedJsonbValue(char *buffer, uint32 flags,
+						   uint32 *lowbound, char *key, uint32 keylen);
+
+extern JsonbValue *getJsonbValue(char *buffer, uint32 flags, int32 i);
+
+typedef struct ToJsonbState
+{
+	JsonbValue	v;
+	uint32		size;
+	struct ToJsonbState *next;
+}	ToJsonbState;
+
+extern JsonbValue *pushJsonbValue(ToJsonbState ** state, int r /* WJB_* */ , JsonbValue *v);
+
+extern void uniqueJsonbValue(JsonbValue *v);
+
+extern uint32 compressJsonb(JsonbValue *v, char *buffer);
+
+typedef struct JsonbIterator
+{
+	uint32		type;
+	uint32		nelems;
+	JEntry	   *array;
+	bool		isScalar;
+	char	   *data;
+	char	   *buffer;			/* unparsed buffer */
+
+	int			i;
+
+	/*
+	 * enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB
+	 * with possiblity of decoding. See optimization in JsonbIteratorGet()
+	 */
+	enum
+	{
+		jbi_start = 0x00,
+		jbi_key = 0x01,
+		jbi_value = 0x02,
+		jbi_elem = 0x04
+	} state;
+
+	struct JsonbIterator *next;
+} JsonbIterator;
+
+extern JsonbIterator *JsonbIteratorInit(char *buffer);
+extern int /* WJB_* */ JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested);
+
+extern Datum jsonb_in(PG_FUNCTION_ARGS);
+extern Datum jsonb_out(PG_FUNCTION_ARGS);
+extern Datum jsonb_recv(PG_FUNCTION_ARGS);
+extern Datum jsonb_send(PG_FUNCTION_ARGS);
+
+extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
+
+extern char *JsonbToCString(StringInfo out, char *in, int estimated_len);
+extern Jsonb *JsonbValueToJsonb(JsonbValue *v);
+
+#endif   /* __JSONB_H__ */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 0000000..cb6b4a3
--- /dev/null
+++ b/src/test/regress/expected/jsonb.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ octet_length 
+--------------
+            5
+(1 row)
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ correct_in_utf8 
+-----------------
+              10
+(1 row)
+
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+   correct_in_utf8    
+----------------------
+ the Copyright © sign
+(1 row)
+
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 0000000..8fae7c2
--- /dev/null
+++ b/src/test/regress/expected/jsonb_1.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT octet_length('"\uaBcD"'::jsonb::text);
+                            ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: ...
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc3...
+                                   ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a'...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5758b07..51238be 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,8 +98,7 @@ test: event_trigger
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
-
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 78348f5..e414ec1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -121,6 +121,7 @@ test: xmlmap
 test: functional_deps
 test: advisory_lock
 test: json
+test: jsonb
 test: indirect_toast
 test: plancache
 test: limit
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 0000000..38959a8
--- /dev/null
+++ b/src/test/regress/sql/jsonb.sql
@@ -0,0 +1,265 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+SELECT '"abc"'::jsonb;			-- OK
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+SELECT '0'::jsonb;				-- OK
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+SELECT '0.1'::jsonb;				-- OK
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+SELECT '1e100'::jsonb;			-- OK
+SELECT '1.3e100'::jsonb;			-- OK
+SELECT '1f2'::jsonb;				-- ERROR
+SELECT '0.x1'::jsonb;			-- ERROR
+SELECT '1.3ex100'::jsonb;		-- ERROR
+
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+SELECT '[1,2]'::jsonb;			-- OK
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+SELECT '{"abc":1}'::jsonb;		-- OK
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+SELECT 'false'::jsonb;			-- OK
+SELECT 'null'::jsonb;			-- OK
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+SELECT ''::jsonb;				-- ERROR, no value
+SELECT '    '::jsonb;			-- ERROR, no value
+
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+
+
+-- jsonb extraction functions
+
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+
+-- nulls
+
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+
+
+-- array length
+
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT jsonb_array_length('[]');
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT jsonb_array_length('4');
+
+-- each
+
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+-- extract_path, extract_path_as_text
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- extract_path nulls
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- extract_path operators
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+
+-- array_elements
+
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+
+-- populate_recordset
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+
+-- using the default use_json_as_text argument
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+
+
+-- handling of unicode surrogate pairs
+
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+
+--handling of simple unicode escapes
+
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
#22Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#21)
Re: jsonb and nested hstore

On Wed, Jan 29, 2014 at 12:03 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Only change in functionality is the addition of casts between jsonb and
json.

The other changes are the merge with the new json functions code, and
rearrangement of the docs changes to make them less ugly. Essentially I
moved the indexterm tags right out of the table as is done in some other
parts pf the docs. That makes the entry tags much clearer to read.

I think the opening paragraphs contrasting json/jsonb be needs
refinement. json is going to be slightly faster than jsonb for input
*and* output. For example, in one application I store fairly large
json objects containing pre-compiled static polygon data that is
simply flipped up to google maps. This case will likely be pessimal
for jsonb. For the next paragaph, I'd like to expand it a bit on
'specialized needs' and boil it down to specific uses cases.
Basically, json will likely be more compact in most cases and slightly
faster for input/output; jsonb would be preferred in any context
where processing, or searching or extensive server side parsing is
employed.

If you agree, I'd be happy to do that...

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 01/29/2014 12:46 PM, Merlin Moncure wrote:

I think the opening paragraphs contrasting json/jsonb be needs
refinement. json is going to be slightly faster than jsonb for input
*and* output. For example, in one application I store fairly large
json objects containing pre-compiled static polygon data that is
simply flipped up to google maps. This case will likely be pessimal
for jsonb. For the next paragaph, I'd like to expand it a bit on
'specialized needs' and boil it down to specific uses cases.
Basically, json will likely be more compact in most cases and slightly
faster for input/output; jsonb would be preferred in any context
where processing, or searching or extensive server side parsing is
employed.

If you agree, I'd be happy to do that...

Please take a stab at it, I'll be happy to revise it.

I was working on doing a two-column table comparison chart; I still
think that's the best way to go.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#21)
1 attachment(s)
Re: jsonb and nested hstore

On 01/29/2014 01:03 PM, Andrew Dunstan wrote:

On 01/27/2014 10:43 PM, Andrew Dunstan wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and
compressed them. The jsonb patch should work standalone. The nested
hstore patch depends on it.

All the jsonb functions now use the jsonb API - there is no more
turning jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up
some jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Further update of jsonb portion.

Only change in functionality is the addition of casts between jsonb
and json.

The other changes are the merge with the new json functions code, and
rearrangement of the docs changes to make them less ugly. Essentially
I moved the indexterm tags right out of the table as is done in some
other parts pf the docs. That makes the entry tags much clearer to read.

Updated to apply cleanly after recent commits.

cheers

andrew

Attachments:

jsonb-8.patchtext/x-patch; name=jsonb-8.patchDownload
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 6bf4cf6..12832cb 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -143,6 +143,12 @@
       </row>
 
       <row>
+       <entry><type>jsonb</type></entry>
+       <entry></entry>
+       <entry>JSON data, decomposed</entry>
+      </row>
+
+      <row>
        <entry><type>line</type></entry>
        <entry></entry>
        <entry>infinite line on a plane</entry>
@@ -4225,27 +4231,58 @@ SET xmloption TO { DOCUMENT | CONTENT };
   </sect1>
 
   <sect1 id="datatype-json">
-   <title><acronym>JSON</> Type</title>
+   <title><acronym>JSON</> Types</title>
 
    <indexterm zone="datatype-json">
     <primary>JSON</primary>
    </indexterm>
 
+   <indexterm zone="datatype-json">
+    <primary>JSONB</primary>
+   </indexterm>
+
    <para>
-    The <type>json</type> data type can be used to store JSON (JavaScript
-    Object Notation) data, as specified in <ulink
-    url="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</ulink>.  Such
-    data can also be stored as <type>text</type>, but the
-    <type>json</type> data type has the advantage of checking that each
-    stored value is a valid JSON value.  There are also related support
+    JSON data types are for storing JSON (JavaScript Object Notation)
+    data, as specified in <ulink url="http://www.ietf.org/rfc/rfc4627.txt"
+    >RFC 4627</ulink>. Such data can also be stored as <type>text</type>,
+    but the JSON data types have the advantage of checking that each
+    stored value is a valid JSON value. There are also related support
     functions available; see <xref linkend="functions-json">.
    </para>
 
    <para>
+    There are two JSON data types: <type>json</type> and <type>jsonb</type>.
+    Both accept identical sets of values as input. The difference is primarily
+    a matter of efficiency. The <type>json</type> data type stores an exact
+    copy of the the input text, and the processing functions have to reparse
+    it to precess it, while the <type>jsonb</type> is stored in a decomposed
+    form that makes it slightly less efficient to input but very much faster
+    to process, since it never needs reparsing.
+   </para>
+
+   <para>
+    The other difference between the types is that the <type>json</type> type
+    is guaranteed to contain an exact copy of the input, including
+    preservation of semantically insignificant white space, and the order of
+    keys within JSON objects. Also, because the exact text is kept, if a JSON
+    object within the value contains the same key more than once, all the
+    key/value pairs are kept. In that case, the processing functions consider
+    the last value as the operative one. By contrast, <type>jsonb</type>
+    does not preserve white space, does not preserve the order of object keys,
+    and does not keep duplicate object keys. Only the last value for a key
+    specified in the input is kept.
+   </para>
+
+   <para>
+    In general, most applications will find it advantageous to store JSON data
+    as <type>jsonb</type>, unless they have quite specialised needs.
+   </para>
+
+   <para>
     <productname>PostgreSQL</productname> allows only one server encoding
-    per database.  It is therefore not possible for JSON to conform rigidly
-    to the specification unless the server encoding is UTF-8.  Attempts to
-    directly include characters which cannot be represented in the server
+    per database.  It is therefore not possible for the JSON types to conform
+    rigidly to the specification unless the server encoding is UTF-8. Attempts
+    to directly include characters which cannot be represented in the server
     encoding will fail; conversely, characters which can be represented in
     the server encoding but not in UTF-8 will be allowed.
     <literal>\uXXXX</literal> escapes are allowed regardless of the server
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 9816163..1f9e0a19 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10040,13 +10040,46 @@ table2-mapping
      </tgroup>
    </table>
 
-  <para>
-   <xref linkend="functions-json-table"> shows the functions that are available
-   for creating and manipulating JSON (see <xref linkend="datatype-json">) data.
+  <note>
+   <para>
+    The operators above can take either <type>json</type> or <type>jsonb</type>
+    values as their left hand operands. In general they work much faster with
+    <type>jsonb</type>.
+   </para>
+  </note>
+
+  <!-- 
+     The release notes contain a reference to "functions-json-table". Since
+     that table is now split in two, the id has been parked here so we don't
+     have to change the release notes.
+  -->
+  <para id="functions-json-table">
+   <xref linkend="functions-json-creation-table"> shows the functions that are
+   available for creating <type>json</type> values.
+   (see <xref linkend="datatype-json">)
   </para>
 
-  <table id="functions-json-table">
-    <title>JSON Support Functions</title>
+  <indexterm>
+   <primary>array_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>row_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_array</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_object</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_object</primary>
+  </indexterm>
+
+  <table id="functions-json-creation-table">
+    <title>JSON Creation Functions</title>
     <tgroup cols="5">
      <thead>
       <row>
@@ -10060,9 +10093,6 @@ table2-mapping
      <tbody>
       <row>
        <entry>
-         <indexterm>
-          <primary>array_to_json</primary>
-         </indexterm>
          <literal>array_to_json(anyarray [, pretty_bool])</literal>
        </entry>
        <entry><type>json</type></entry>
@@ -10076,9 +10106,6 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>row_to_json</primary>
-         </indexterm>
          <literal>row_to_json(record [, pretty_bool])</literal>
        </entry>
        <entry><type>json</type></entry>
@@ -10091,9 +10118,6 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>to_json</primary>
-         </indexterm>
          <literal>to_json(anyelement)</literal>
        </entry>
        <entry><type>json</type></entry>
@@ -10109,11 +10133,177 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>json_array_length</primary>
-         </indexterm>
-         <literal>json_array_length(json)</literal>
+         <literal>json_build_array(VARIADIC "any")</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         Builds a heterogeneously-typed json array out of a variadic argument list.
+       </entry>
+       <entry><literal>SELECT json_build_array(1,2,'3',4,5);</literal></entry>
+       <entry>
+<programlisting>
+ json_build_array
+-------------------
+ [1, 2, "3", 4, 5]
+ </programlisting>
+       </entry>
+      </row>
+      <row>
+       <entry>
+         <literal>json_build_object(VARIADIC "any")</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         Builds a JSON array out of a variadic argument list.
+         By convention, the object is 
+         constructed out of alternating name/value arguments.
+       </entry>
+       <entry><literal>SELECT json_build_object('foo',1,'bar',2);</literal></entry>
+       <entry>
+<programlisting>
+   json_build_object
+------------------------
+ {"foo" : 1, "bar" : 2}
+ </programlisting>
+       </entry>
+      </row>
+      <row>
+       <entry>
+         <literal>json_object(text[])</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         Builds a JSON object out of a text array.  The array must have either
+         exactly one dimension with an even number of members, in which case
+         they are taken as alternating name/value pairs, or two dimensions
+         such that each inner array has exactly two elements, which
+         are taken as a name/value pair.
        </entry>
+       <entry><literal>select * from json_object('{a, 1, b, "def", c, 3.5}')  or <literal>select json_object('{{a, 1},{b, "def"},{c, 3.5}}')</literal></literal></entry>
+       <entry>
+<programlisting>
+              json_object
+---------------------------------------
+ {"a" : "1", "b" : "def", "c" : "3.5"}
+ </programlisting>
+       </entry>
+      </row>
+      <row>
+       <entry>
+         <literal>json_object(keys text[], values text[])</literal>
+       </entry>
+       <entry><type>json</type></entry>
+       <entry>
+         The two-argument form of JSON object takes keys and values pairwise from two separate
+         arrays. In all other respects it is identical to the one-argument form.
+       </entry>
+       <entry><literal>select json_object('{a, b}', '{1,2}');</literal></entry>
+       <entry>
+<programlisting>
+      json_object
+------------------------
+ {"a" : "1", "b" : "2"}
+ </programlisting>
+       </entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+
+  <para>
+   <xref linkend="functions-json-processing-table"> shows the functions that
+   are available for processing <type>json</type> and <type>jsonb</type> values.
+   (see <xref linkend="datatype-json">)
+  </para>
+
+  <indexterm>
+   <primary>json_array_length</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_array_length</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_each</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_each</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_each_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_each_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_extract_path</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_extract_path</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_extract_path_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_extract_path_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_object_keys</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_object_keys</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_populate_record</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_populate_record</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_populate_recordset</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_populate_recordset</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_array_elements</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_array_elements</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_array_elements_text</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_typeof</primary>
+  </indexterm>
+  <indexterm>
+   <primary>jsonb_typeof</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_to_record</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_to_recordset</primary>
+  </indexterm>
+
+  <table id="functions-json-processing-table">
+    <title>JSON Processing Functions</title>
+    <tgroup cols="5">
+     <thead>
+      <row>
+       <entry>Function</entry>
+       <entry>Return Type</entry>
+       <entry>Description</entry>
+       <entry>Example</entry>
+       <entry>Example Result</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry><para><literal>json_array_length(json)</literal>
+         </para><para><literal>jsonb_array_length(jsonb)</literal>
+       </para></entry>
        <entry><type>int</type></entry>
        <entry>
          Returns the number of elements in the outermost JSON array.
@@ -10122,13 +10312,12 @@ table2-mapping
        <entry><literal>5</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_each</primary>
-         </indexterm>
-         <literal>json_each(json)</literal>
-       </entry>
-       <entry><type>SETOF key text, value json</type></entry>
+       <entry><para><literal>json_each(json)</literal>
+         </para><para><literal>jsonb_each(jsonb)</literal>
+       </para></entry>
+       <entry><para><literal>SETOF key text, value json</literal>
+         </para><para><literal>SETOF key text, value jsonb</literal>
+       </para></entry>
        <entry>
          Expands the outermost JSON object into a set of key/value pairs.
        </entry>
@@ -10143,12 +10332,9 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_each_text</primary>
-         </indexterm>
-         <literal>json_each_text(from_json json)</literal>
-       </entry>
+       <entry><para><literal>json_each_text(from_json json)</literal>
+         </para><para><literal>jsonb_each_text(from_json jsonb)</literal>
+       </para></entry>
        <entry><type>SETOF key text, value text</type></entry>
        <entry>
          Expands the outermost JSON object into a set of key/value pairs. The
@@ -10165,13 +10351,11 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_extract_path</primary>
-         </indexterm>
-         <literal>json_extract_path(from_json json, VARIADIC path_elems text[])</literal>
-       </entry>
-       <entry><type>json</type></entry>
+       <entry><para><literal>json_extract_path(from_json json, VARIADIC path_elems text[])</literal>
+        </para><para><literal>jsonb_extract_path(from_jsonb jsonb, VARIADIC path_elems text[])</literal>
+       </para></entry>
+       <entry><para><type>json</type></para><para><type>jsonb</type>
+       </para></entry>
        <entry>
          Returns JSON value pointed to by <parameter>path_elems</parameter>.
        </entry>
@@ -10179,12 +10363,9 @@ table2-mapping
        <entry><literal>{"f5":99,"f6":"foo"}</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_extract_path_text</primary>
-         </indexterm>
-         <literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
-       </entry>
+       <entry><para><literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
+         </para><para><literal>json_extract_path_text(from_json json, VARIADIC path_elems text[])</literal>
+       </para></entry>
        <entry><type>text</type></entry>
        <entry>
          Returns JSON value pointed to by <parameter>path_elems</parameter>.
@@ -10193,12 +10374,9 @@ table2-mapping
        <entry><literal>foo</literal></entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_object_keys</primary>
-         </indexterm>
-         <literal>json_object_keys(json)</literal>
-       </entry>
+       <entry><para><literal>json_object_keys(json)</literal>
+         </para><para><literal>jsonb_object_keys(jsonb)</literal>
+       </para></entry>
        <entry><type>SETOF text</type></entry>
        <entry>
           Returns set of keys in the JSON object.  Only the <quote>outer</quote> object will be displayed.
@@ -10214,18 +10392,16 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_populate_record</primary>
-         </indexterm>
-         <literal>json_populate_record(base anyelement, from_json json, [, use_json_as_text bool=false]</literal>
-       </entry>
+       <entry><para><literal>json_populate_record(base anyelement, from_json json, [, use_json_as_text bool=false])</literal>
+         </para><para><literal>jsonb_populate_record(base anyelement, from_json jsonb, [, use_json_as_text bool=false])</literal>
+       </para></entry>
        <entry><type>anyelement</type></entry>
        <entry>
          Expands the object in <replaceable>from_json</replaceable> to a row whose columns match
          the record type defined by base. Conversion will be best
          effort; columns in base with no corresponding key in <replaceable>from_json</replaceable>
-         will be left null. If a column is specified more than once, the last value is used.
+         will be left null. When processing <type>json</type>, if a column is 
+         specified more than once, the last value is used.
        </entry>
        <entry><literal>select * from json_populate_record(null::x, '{"a":1,"b":2}')</literal></entry>
        <entry>
@@ -10237,19 +10413,17 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_populate_recordset</primary>
-         </indexterm>
-         <literal>json_populate_recordset(base anyelement, from_json json, [, use_json_as_text bool=false]</literal>
-       </entry>
+       <entry><para><literal>json_populate_recordset(base anyelement, from_json json, [, use_json_as_text bool=false])</literal>
+         </para><para><literal>jsonb_populate_recordset(base anyelement, from_json jsonb, [, use_json_as_text bool=false])</literal>
+       </para></entry>
        <entry><type>SETOF anyelement</type></entry>
        <entry>
          Expands the outermost set of objects in <replaceable>from_json</replaceable> to a set
          whose columns match the record type defined by base.
          Conversion will be best effort; columns in base with no
          corresponding key in <replaceable>from_json</replaceable> will be left null.
-         If a column is specified more than once, the last value is used.
+         When processing <type>json</type>, if a column is specified more 
+         than once, the last value is used.
        </entry>
        <entry><literal>select * from json_populate_recordset(null::x, '[{"a":1,"b":2},{"a":3,"b":4}]')</literal></entry>
        <entry>
@@ -10262,13 +10436,12 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_array_elements</primary>
-         </indexterm>
-         <literal>json_array_elements(json)</literal>
-       </entry>
-       <entry><type>SETOF json</type></entry>
+       <entry><para><literal>json_array_elements(json)</literal>
+         </para><para><literal>jsonb_array_elements(jsonb)</literal>
+       </para></entry>
+       <entry><para><type>SETOF json</type>
+         </para><para><type>SETOF jsonb</type>
+       </para></entry>
        <entry>
          Expands a JSON array to a set of JSON values.
        </entry>
@@ -10285,9 +10458,6 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>json_array_elements_text</primary>
-         </indexterm>
          <literal>json_array_elements_text(json)</literal>
        </entry>
        <entry><type>SETOF json</type></entry>
@@ -10305,12 +10475,9 @@ table2-mapping
        </entry>
       </row>
       <row>
-       <entry>
-         <indexterm>
-          <primary>json_typeof</primary>
-         </indexterm>
-         <literal>json_typeof(json)</literal>
-       </entry>
+       <entry><para><literal>json_typeof(json)</literal>
+         </para><para><literal>jsonb_typeof(jsonb)</literal>
+       </para></entry>
        <entry><type>text</type></entry>
        <entry>
          Returns the type of the outermost JSON value as a text string.  The types are
@@ -10323,98 +10490,11 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>json_build_array</primary>
-         </indexterm>
-         <literal>json_build_array(VARIADIC "any")</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         Builds a heterogeneously-typed json array out of a variadic argument list.
-       </entry>
-       <entry><literal>SELECT json_build_array(1,2,'3',4,5);</literal></entry>
-       <entry>
-<programlisting>
- json_build_array
--------------------
- [1, 2, "3", 4, 5]
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <indexterm>
-          <primary>json_build_object</primary>
-         </indexterm>
-         <literal>json_build_object(VARIADIC "any")</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         Builds a JSON array out of a variadic argument list.
-         By convention, the object is 
-         constructed out of alternating name/value arguments.
-       </entry>
-       <entry><literal>SELECT json_build_object('foo',1,'bar',2);</literal></entry>
-       <entry>
-<programlisting>
-   json_build_object
-------------------------
- {"foo" : 1, "bar" : 2}
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <indexterm>
-          <primary>json_object</primary>
-         </indexterm>
-         <literal>json_object(text[])</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         Builds a JSON object out of a text array.  The array must have either
-         exactly one dimension with an even number of members, in which case
-         they are taken as alternating name/value pairs, or two dimensions
-         such that each inner array has exactly two elements, which
-         are taken as a name/value pair.
-       </entry>
-       <entry><literal>select * from json_object('{a, 1, b, "def", c, 3.5}')  or <literal>select * from json_object('{{a, 1},{b, "def"},{c, 3.5}}')</literal></literal></entry>
-       <entry>
-<programlisting>
-              json_object
----------------------------------------
- {"a" : "1", "b" : "def", "c" : "3.5"}
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <literal>json_object(keys text[], values text[])</literal>
-       </entry>
-       <entry><type>json</type></entry>
-       <entry>
-         The two-argument form of JSON object takes keys and values pairwise from two separate
-         arrays. In all other respects it is identical to the one-argument form.
-       </entry>
-       <entry><literal>select * from json_object('{a, b}', '{1,2}');</literal></entry>
-       <entry>
-<programlisting>
-      json_object
-------------------------
- {"a" : "1", "b" : "2"}
- </programlisting>
-       </entry>
-      </row>
-      <row>
-       <entry>
-         <indexterm>
-          <primary>json_to_record</primary>
-         </indexterm>
          <literal>json_to_record(json, nested_as_text bool)</literal>
        </entry>
        <entry><type>record</type></entry>
        <entry>
-         json_to_record returns an arbitrary record from a JSON object.  As with all functions 
+         Returns an arbitrary record from a JSON object.  As with all functions 
          returning 'record', the caller must explicitly define the structure of the record 
          when making the call. The input JSON must be an object, not a scalar or an array.
          If nested_as_text is true, the function coerces nested complex elements to text.
@@ -10431,14 +10511,11 @@ table2-mapping
       </row>
       <row>
        <entry>
-         <indexterm>
-          <primary>json_to_recordset</primary>
-         </indexterm>
          <literal>json_to_recordset(json, nested_as_text bool)</literal>
        </entry>
        <entry><type>setof record</type></entry>
        <entry>
-         json_to_recordset returns an arbitrary set of records from a JSON object.  As with 
+         Returns an arbitrary set of records from a JSON object.  As with 
          json_to_record, the structure of the record must be explicitly defined when making the
          call.  However, with json_to_recordset the input JSON must be an array containing 
          objects.  nested_as_text works as with json_to_record.
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 277af61..db04a2e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -807,3 +807,11 @@ CREATE OR REPLACE FUNCTION
 CREATE OR REPLACE FUNCTION
   json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
   RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'jsonb_populate_recordset';
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index f170661..db0c434 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1274,7 +1274,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 			pfree(outputstr);
 			break;
 		case TYPCATEGORY_JSON:
-			/* JSON will already be escaped */
+			/* JSON and JSONB will already be escaped */
 			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			appendStringInfoString(result, outputstr);
 			pfree(outputstr);
@@ -1406,7 +1406,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 		tcategory = TYPCATEGORY_JSON_CAST;
 	else if (element_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (element_type == JSONOID)
+	else if (element_type == JSONOID || element_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(element_type);
@@ -1501,7 +1501,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
 			tcategory = TYPCATEGORY_ARRAY;
 		else if (tupdesc->attrs[i]->atttypid == RECORDOID)
 			tcategory = TYPCATEGORY_COMPOSITE;
-		else if (tupdesc->attrs[i]->atttypid == JSONOID)
+		else if (tupdesc->attrs[i]->atttypid == JSONOID ||
+				 tupdesc->attrs[i]->atttypid == JSONBOID)
 			tcategory = TYPCATEGORY_JSON;
 		else
 			tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
@@ -1689,7 +1690,7 @@ to_json(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1783,7 +1784,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -2346,12 +2347,15 @@ escape_json(StringInfo buf, const char *str)
 Datum
 json_typeof(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonTokenType tok;
 	char	   *type;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
+
 	/* Lex exactly one token from the input and check its type. */
 	json_lex(lex);
 	tok = lex_peek(lex);
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..107ebf0
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,544 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+
+static size_t
+checkStringLen(size_t len)
+{
+	if (len > JSONB_MAX_STRING_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for jsonb string")));
+	return len;
+}
+
+typedef struct JsonbInState
+{
+	ToJsonbState *state;
+	JsonbValue *res;
+}	JsonbInState;
+
+
+/*
+ * for jsonb we always want the de-escaped value - that's what's in token
+ */
+
+static void
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+
+		case JSON_TOKEN_STRING:
+			v.type = jbvString;
+			v.string.len = token ? checkStringLen(strlen(token)) : 0;
+			v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+			v.size += v.string.len;
+			break;
+		case JSON_TOKEN_NUMBER:
+			v.type = jbvNumeric;
+			v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+
+			v.size += VARSIZE_ANY(v.numeric) +sizeof(JEntry) /* alignment */ ;
+			break;
+		case JSON_TOKEN_TRUE:
+			v.type = jbvBool;
+			v.boolean = true;
+			break;
+		case JSON_TOKEN_FALSE:
+			v.type = jbvBool;
+			v.boolean = false;
+			break;
+		case JSON_TOKEN_NULL:
+			v.type = jbvNull;
+			break;
+		default:				/* nothing else should be here in fact */
+			break;
+	}
+
+	if (_state->state == NULL)
+	{
+		/* single scalar */
+		JsonbValue	va;
+
+		va.type = jbvArray;
+		va.array.scalar = true;
+		va.array.nelems = 1;
+
+		_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, &va);
+		_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+		_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+	}
+	else
+	{
+		JsonbValue *o = &_state->state->v;
+
+		switch (o->type)
+		{
+			case jbvArray:
+				_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+				break;
+			case jbvHash:
+				_state->res = pushJsonbValue(&_state->state, WJB_VALUE, &v);
+				break;
+			default:
+				elog(ERROR, "Wrong state");
+		}
+	}
+}
+
+static void
+jsonb_in_object_start(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_OBJECT, NULL);
+}
+
+static void
+jsonb_in_object_end(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_OBJECT, NULL);
+}
+
+static void
+jsonb_in_array_start(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, NULL);
+}
+
+static void
+jsonb_in_array_end(void *state)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+}
+
+static void
+jsonb_in_object_field_start(void *state, char *fname, bool isnull)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.type = jbvString;
+	v.string.len = fname ? checkStringLen(strlen(fname)) : 0;
+	v.string.val = fname ? pnstrdup(fname, v.string.len) : NULL;
+	v.size = sizeof(JEntry) + v.string.len;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_KEY, &v);
+}
+
+Datum
+jsonb_in(PG_FUNCTION_ARGS)
+{
+	char	   *json = PG_GETARG_CSTRING(0);
+	text	   *result = cstring_to_text(json);
+	JsonLexContext *lex;
+	JsonbInState state;
+	JsonSemAction sem;
+
+	memset(&state, 0, sizeof(state));
+	memset(&sem, 0, sizeof(sem));
+	lex = makeJsonLexContext(result, true);
+
+	sem.semstate = (void *) &state;
+
+	sem.object_start = jsonb_in_object_start;
+	sem.array_start = jsonb_in_array_start;
+	sem.object_end = jsonb_in_object_end;
+	sem.array_end = jsonb_in_array_end;
+	sem.scalar = jsonb_in_scalar;
+	sem.object_field_start = jsonb_in_object_field_start;
+
+	pg_parse_json(lex, &sem);
+
+	/* after parsing, the item membar has the composed jsonn structure */
+	PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
+}
+
+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);
+
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	uint32		hentry = c & JENTRY_TYPEMASK;
+
+	if (hentry == JENTRY_ISNULL)
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+	}
+	else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	{
+		recvJsonb(buf, v, level + 1, (uint32) c);
+	}
+	else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	{
+		v->type = jbvBool;
+		v->size = sizeof(JEntry);
+		v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	}
+	else if (hentry == JENTRY_ISNUMERIC)
+	{
+		v->type = jbvNumeric;
+		v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+									   Int32GetDatum(0), Int32GetDatum(-1)));
+
+		v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);
+	}
+	else if (hentry == JENTRY_ISSTRING)
+	{
+		v->type = jbvString;
+		v->string.val = pq_getmsgtext(buf, c, &c);
+		v->string.len = checkStringLen(c);
+		v->size = sizeof(JEntry) + v->string.len;
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
+
+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	uint32		hentry;
+	uint32		i;
+
+	hentry = header & JENTRY_TYPEMASK;
+
+	v->size = 3 * sizeof(JEntry);
+	if (hentry == JENTRY_ISOBJECT)
+	{
+		v->type = jbvHash;
+		v->hash.npairs = header & JB_COUNT_MASK;
+		if (v->hash.npairs > 0)
+		{
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+			for (i = 0; i < v->hash.npairs; i++)
+			{
+				recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != jbvString)
+					elog(ERROR, "jsonb's key could be only a string");
+
+				recvJsonbValue(buf, &v->hash.pairs[i].value, level, pq_getmsgint(buf, 4));
+
+				v->size += v->hash.pairs[i].key.size + v->hash.pairs[i].value.size;
+			}
+
+			uniqueJsonbValue(v);
+		}
+	}
+	else if (hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	{
+		v->type = jbvArray;
+		v->array.nelems = header & JB_COUNT_MASK;
+		v->array.scalar = (hentry == JENTRY_ISCALAR) ? true : false;
+
+		if (v->array.scalar && v->array.nelems != 1)
+			elog(ERROR, "bogus input");
+
+		if (v->array.nelems > 0)
+		{
+			v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+			for (i = 0; i < v->array.nelems; i++)
+			{
+				recvJsonbValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4));
+				v->size += v->array.elems[i].size;
+			}
+		}
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
+
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	JsonbValue	v;
+
+	recvJsonb(buf, &v, 0, pq_getmsgint(buf, 4));
+
+	PG_RETURN_POINTER(JsonbValueToJsonb(&v));
+}
+
+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	switch (v->type)
+	{
+		case jbvNull:
+			appendBinaryStringInfo(out, "null", 4);
+			break;
+		case jbvString:
+			escape_json(out, pnstrdup(v->string.val, v->string.len));
+			break;
+		case jbvBool:
+			if (v->boolean)
+				appendBinaryStringInfo(out, "true", 4);
+			else
+				appendBinaryStringInfo(out, "false", 5);
+			break;
+		case jbvNumeric:
+			appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+			break;
+		default:
+			elog(PANIC, "Unknown type");
+	}
+}
+
+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	bool		first = true;
+	JsonbIterator *it;
+	int			type;
+	JsonbValue	v;
+	int			level = 0;
+
+	if (out == NULL)
+		out = makeStringInfo();
+
+	if (in == NULL)
+	{
+		appendStringInfoString(out, "");
+		return out->data;
+	}
+
+	enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
+
+	it = JsonbIteratorInit(in);
+
+	while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	{
+reout:
+		switch (type)
+		{
+			case WJB_BEGIN_ARRAY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				if (v.array.scalar == false)
+					appendStringInfoChar(out, '[');
+				level++;
+				break;
+			case WJB_BEGIN_OBJECT:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+				appendStringInfoCharMacro(out, '{');
+
+				level++;
+				break;
+			case WJB_KEY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				putEscapedValue(out, &v);
+				appendBinaryStringInfo(out, ": ", 2);
+
+				type = JsonbIteratorGet(&it, &v, false);
+				if (type == WJB_VALUE)
+				{
+					first = false;
+					putEscapedValue(out, &v);
+				}
+				else
+				{
+					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+					goto reout;
+				}
+				break;
+			case WJB_ELEM:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				else
+					first = false;
+
+				putEscapedValue(out, &v);
+				break;
+			case WJB_END_ARRAY:
+				level--;
+				if (v.array.scalar == false)
+					appendStringInfoChar(out, ']');
+				first = false;
+				break;
+			case WJB_END_OBJECT:
+				level--;
+				appendStringInfoCharMacro(out, '}');
+				first = false;
+				break;
+			default:
+				elog(PANIC, "Wrong flags");
+		}
+	}
+
+	Assert(level == 0);
+
+	return out->data;
+}
+
+Datum
+jsonb_out(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	PG_RETURN_CSTRING(out);
+}
+
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *in = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+
+	pq_begintypsend(&buf);
+
+	if (JB_ISEMPTY(in))
+	{
+		pq_sendint(&buf, 0, 4);
+	}
+	else
+	{
+		JsonbIterator *it;
+		int			type;
+		JsonbValue	v;
+		uint32		flag;
+		bytea	   *nbuf;
+
+		enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */ );
+
+		it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+		{
+			switch (type)
+			{
+				case WJB_BEGIN_ARRAY:
+					flag = (v.array.scalar) ? JENTRY_ISCALAR : JENTRY_ISARRAY;
+					pq_sendint(&buf, v.array.nelems | flag, 4);
+					break;
+				case WJB_BEGIN_OBJECT:
+					pq_sendint(&buf, v.hash.npairs | JENTRY_ISOBJECT, 4);
+					break;
+				case WJB_KEY:
+					pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+					pq_sendtext(&buf, v.string.val, v.string.len);
+					break;
+				case WJB_ELEM:
+				case WJB_VALUE:
+					switch (v.type)
+					{
+						case jbvNull:
+							pq_sendint(&buf, JENTRY_ISNULL, 4);
+							break;
+						case jbvString:
+							pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+							pq_sendtext(&buf, v.string.val, v.string.len);
+							break;
+						case jbvBool:
+							pq_sendint(&buf, (v.boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE, 4);
+							break;
+						case jbvNumeric:
+							nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
+							pq_sendint(&buf, VARSIZE_ANY(nbuf) | JENTRY_ISNUMERIC, 4);
+							pq_sendbytes(&buf, (char *) nbuf, VARSIZE_ANY(nbuf));
+							break;
+						default:
+							elog(PANIC, "Wrong type: %u", v.type);
+					}
+					break;
+				case WJB_END_ARRAY:
+				case WJB_END_OBJECT:
+					break;
+				default:
+					elog(PANIC, "Wrong flags");
+			}
+		}
+	}
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *in = PG_GETARG_JSONB(0);
+	JsonbIterator *it;
+	JsonbValue	v;
+	char	   *result;
+
+	if (JB_ROOT_IS_OBJECT(in))
+		result = "object";
+	else if (JB_ROOT_IS_ARRAY(in) && !JB_ROOT_IS_SCALAR(in))
+		result = "array";
+	else
+	{
+		Assert(JB_ROOT_IS_SCALAR(in));
+
+		it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		/*
+		 * a root scalar is stored as an array of one element, so we get the
+		 * array and then its first (and only) member.
+		 */
+		(void) JsonbIteratorGet(&it, &v, true);
+		(void) JsonbIteratorGet(&it, &v, true);
+		switch (v.type)
+		{
+			case jbvNull:
+				result = "null";
+				break;
+			case jbvString:
+				result = "string";
+				break;
+			case jbvBool:
+				result = "boolean";
+				break;
+			case jbvNumeric:
+				result = "number";
+				break;
+			default:
+				elog(ERROR, "Wrong jsonb scalar type: %u", v.type);
+		}
+	}
+
+	PG_RETURN_TEXT_P(cstring_to_text(result));
+}
diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c
new file mode 100644
index 0000000..79da6eb
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_support.c
@@ -0,0 +1,1261 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_support.c
+ *	  Support functions for jsonb
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+/*
+ * turn a JsonbValue into a Jsonb
+ */
+Jsonb *
+JsonbValueToJsonb(JsonbValue *v)
+{
+	Jsonb	   *out;
+
+	if (v == NULL)
+	{
+		out = NULL;
+	}
+	else if (v->type == jbvString || v->type == jbvBool ||
+			 v->type == jbvNumeric || v->type == jbvNull)
+	{
+		/* scalar value */
+
+		ToJsonbState *state = NULL;
+		JsonbValue *res;
+		uint32		sz;
+		JsonbValue	scalarArray;
+
+		scalarArray.type = jbvArray;
+		scalarArray.array.scalar = true;
+		scalarArray.array.nelems = 1;
+
+		pushJsonbValue(&state, WJB_BEGIN_ARRAY, &scalarArray);
+		pushJsonbValue(&state, WJB_ELEM, v);
+		res = pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+		out = palloc(VARHDRSZ + res->size);
+		sz = compressJsonb(res, VARDATA(out));
+		Assert(sz <= res->size);
+		SET_VARSIZE(out, sz + VARHDRSZ);
+	}
+	else if (v->type == jbvHash || v->type == jbvArray)
+	{
+		uint32		sz;
+
+		out = palloc(VARHDRSZ + v->size);
+		sz = compressJsonb(v, VARDATA(out));
+		Assert(sz <= v->size);
+		SET_VARSIZE(out, VARHDRSZ + sz);
+	}
+	else
+	{
+		out = palloc(VARHDRSZ + v->binary.len);
+
+		Assert(v->type == jbvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+/*
+ * Sort and unique pairs in hash-like JsonbValue
+ */
+void
+uniqueJsonbValue(JsonbValue *v)
+{
+	bool		hasNonUniq = false;
+
+	Assert(v->type == jbvHash);
+
+	if (v->hash.npairs > 1)
+		qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs),
+				  compareJsonbPair, &hasNonUniq);
+
+	if (hasNonUniq)
+	{
+		JsonbPair  *ptr = v->hash.pairs + 1,
+				   *res = v->hash.pairs;
+
+		while (ptr - v->hash.pairs < v->hash.npairs)
+		{
+			if (ptr->key.string.len == res->key.string.len &&
+				memcmp(ptr->key.string.val, res->key.string.val,
+					   ptr->key.string.len) == 0)
+			{
+				v->size -= ptr->key.size + ptr->value.size;
+			}
+			else
+			{
+				res++;
+				if (ptr != res)
+					memcpy(res, ptr, sizeof(*res));
+			}
+			ptr++;
+		}
+
+		v->hash.npairs = res + 1 - v->hash.pairs;
+	}
+}
+
+/****************************************************************************
+ *						   Compare Functions								*
+ ****************************************************************************/
+
+/*
+ * Compare two jbvString JsonbValue values, third argument
+ * 'arg', if it's not null, should be a pointer to bool
+ * value which will be set to true if strings are equal and
+ * untouched otherwise.
+ */
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue *va = a;
+	const JsonbValue *vb = b;
+	int			res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool *) arg = true;
+	}
+	else
+	{
+		res = (va->string.len > vb->string.len) ? 1 : -1;
+	}
+
+	return res;
+}
+
+/*
+ * qsort helper to compare JsonbPair values, third argument
+ * arg will be trasferred as is to subsequent
+ * compareJsonbStringValue() call. Pairs with equals keys are
+ * ordered with respect of order field.
+ */
+int
+compareJsonbPair(const void *a, const void *b, void *arg)
+{
+	const JsonbPair *pa = a;
+	const JsonbPair *pb = b;
+	int			res;
+
+	res = compareJsonbStringValue(&pa->key, &pb->key, arg);
+
+	/*
+	 * guarantee keeping order of equal pair. Unique algorithm will prefer
+	 * first element as value
+	 */
+
+	if (res == 0)
+		res = (pa->order > pb->order) ? -1 : 1;
+
+	return res;
+}
+
+/*
+ * some constant order of JsonbValue
+ */
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{
+	if (a->type == b->type)
+	{
+		switch (a->type)
+		{
+			case jbvNull:
+				return 0;
+			case jbvString:
+				return compareJsonbStringValue(a, b, NULL);
+			case jbvBool:
+				if (a->boolean == b->boolean)
+					return 0;
+				return (a->boolean > b->boolean) ? 1 : -1;
+			case jbvNumeric:
+				return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(a->numeric),
+											   PointerGetDatum(b->numeric)));
+			case jbvArray:
+				if (a->array.nelems == b->array.nelems)
+				{
+					int			i,
+								r;
+
+					for (i = 0; i < a->array.nelems; i++)
+						if ((r = compareJsonbValue(a->array.elems + i,
+												   b->array.elems + i)) != 0)
+							return r;
+
+					return 0;
+				}
+
+				return (a->array.nelems > b->array.nelems) ? 1 : -1;
+			case jbvHash:
+				if (a->hash.npairs == b->hash.npairs)
+				{
+					int			i,
+								r;
+
+					for (i = 0; i < a->hash.npairs; i++)
+					{
+						if ((r = compareJsonbStringValue(&a->hash.pairs[i].key,
+													   &b->hash.pairs[i].key,
+														 NULL)) != 0)
+							return r;
+						if ((r = compareJsonbValue(&a->hash.pairs[i].value,
+											  &b->hash.pairs[i].value)) != 0)
+							return r;
+					}
+
+					return 0;
+				}
+
+				return (a->hash.npairs > b->hash.npairs) ? 1 : -1;
+			case jbvBinary:
+				return compareJsonbBinaryValue(a->binary.data, b->binary.data);
+			default:
+				elog(PANIC, "unknown JsonbValue->type: %d", a->type);
+		}
+	}
+
+	return (a->type > b->type) ? 1 : -1;
+}
+
+/*
+ * Some order for Jsonb values
+ */
+int
+compareJsonbBinaryValue(char *a, char *b)
+{
+	JsonbIterator *it1,
+			   *it2;
+	int			res = 0;
+
+	it1 = JsonbIteratorInit(a);
+	it2 = JsonbIteratorInit(b);
+
+	while (res == 0)
+	{
+		JsonbValue	v1,
+					v2;
+		int			r1,
+					r2;
+
+		r1 = JsonbIteratorGet(&it1, &v1, false);
+		r2 = JsonbIteratorGet(&it2, &v2, false);
+
+		if (r1 == r2)
+		{
+			if (r1 == 0)
+				break;			/* equal */
+
+			if (v1.type == v2.type)
+			{
+				switch (v1.type)
+				{
+					case jbvString:
+						res = compareJsonbStringValue(&v1, &v2, NULL);
+						break;
+					case jbvBool:
+						if (v1.boolean == v2.boolean)
+							res = 0;
+						else
+							res = (v1.boolean > v2.boolean) ? 1 : -1;
+						break;
+					case jbvNumeric:
+						res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(v1.numeric),
+											   PointerGetDatum(v2.numeric)));
+						break;
+					case jbvArray:
+						if (v1.array.nelems != v2.array.nelems)
+							res = (v1.array.nelems > v2.array.nelems) ? 1 : -1;
+						break;
+					case jbvHash:
+						if (v1.hash.npairs != v2.hash.npairs)
+							res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1;
+						break;
+					default:
+						break;
+				}
+			}
+			else
+			{
+				res = (v1.type > v2.type) ? 1 : -1;		/* dummy order */
+			}
+		}
+		else
+		{
+			res = (r1 > r2) ? 1 : -1;	/* dummy order */
+		}
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *			find string key in hash or element by value in array			*
+ ****************************************************************************/
+JsonbValue *
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK));
+		int			i;
+
+		for (i = (lowbound) ? *lowbound : 0; i < (header & JB_COUNT_MASK); i++)
+		{
+			JEntry	   *e = array + i;
+
+			if (JBE_ISNULL(*e) && key->type == jbvNull)
+			{
+				r.type = jbvNull;
+				if (lowbound)
+					*lowbound = i;
+				r.size = sizeof(JEntry);
+
+				return &r;
+			}
+			else if (JBE_ISSTRING(*e) && key->type == jbvString)
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e),
+						   key->string.len) == 0)
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*e);
+					r.string.len = key->string.len;
+					r.size = sizeof(JEntry) + r.string.len;
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+			{
+				if ((JBE_ISBOOL_TRUE(*e) && key->boolean == true) ||
+					(JBE_ISBOOL_FALSE(*e) && key->boolean == false))
+				{
+					r = *key;
+					r.size = sizeof(JEntry);
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
+			{
+				if (DatumGetBool(DirectFunctionCall2(numeric_eq,
+							   PointerGetDatum(data + INTALIGN(JBE_OFF(*e))),
+									 PointerGetDatum(key->numeric))) == true)
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+		}
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+		uint32		stopLow = lowbound ? *lowbound : 0,
+					stopHigh = (header & JB_COUNT_MASK),
+					stopMiddle;
+
+		if (key->type != jbvString)
+			return NULL;
+
+		while (stopLow < stopHigh)
+		{
+			int			difference;
+			JEntry	   *e;
+
+			stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+			e = array + stopMiddle * 2;
+
+			if (key->string.len == JBE_LEN(*e))
+				difference = memcmp(data + JBE_OFF(*e), key->string.val,
+									key->string.len);
+			else
+				difference = (JBE_LEN(*e) > key->string.len) ? 1 : -1;
+
+			if (difference == 0)
+			{
+				JEntry	   *v = e + 1;
+
+				if (lowbound)
+					*lowbound = stopMiddle + 1;
+
+				if (JBE_ISSTRING(*v))
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*v);
+					r.string.len = JBE_LEN(*v);
+					r.size = sizeof(JEntry) + r.string.len;
+				}
+				else if (JBE_ISBOOL(*v))
+				{
+					r.type = jbvBool;
+					r.boolean = (JBE_ISBOOL_TRUE(*v)) ? true : false;
+					r.size = sizeof(JEntry);
+				}
+				else if (JBE_ISNUMERIC(*v))
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*v)));
+
+					r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+				}
+				else if (JBE_ISNULL(*v))
+				{
+					r.type = jbvNull;
+					r.size = sizeof(JEntry);
+				}
+				else
+				{
+					r.type = jbvBinary;
+					r.binary.data = data + INTALIGN(JBE_OFF(*v));
+					r.binary.len = JBE_LEN(*v) -
+						(INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
+					r.size = 2 * sizeof(JEntry) + r.binary.len;
+				}
+
+				return &r;
+			}
+			else if (difference < 0)
+			{
+				stopLow = stopMiddle + 1;
+			}
+			else
+			{
+				stopHigh = stopMiddle;
+			}
+		}
+
+		if (lowbound)
+			*lowbound = stopLow;
+	}
+
+	return NULL;
+}
+
+/*
+ * Just wrapped for findUncompressedJsonbValueByValue()
+ * with simple string key representation
+ */
+JsonbValue *
+findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound,
+						   char *key, uint32 keylen)
+{
+	JsonbValue	v;
+
+	if (key == NULL)
+	{
+		v.type = jbvNull;
+	}
+	else
+	{
+		v.type = jbvString;
+		v.string.val = key;
+		v.string.len = keylen;
+	}
+
+	return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v);
+}
+
+/*
+ * Get i-th value of array or hash. if i < 0 then it counts from
+ * the end of array/hash. Note: returns pointer to statically
+ * allocated JsonbValue.
+ */
+JsonbValue *
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;
+	JEntry	   *array,
+			   *e;
+	char	   *data;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (i >= 0)
+	{
+		if (i >= (header & JB_COUNT_MASK))
+			return NULL;
+	}
+	else
+	{
+		if (-i > (header & JB_COUNT_MASK))
+			return NULL;
+
+		i = (header & JB_COUNT_MASK) + i;
+	}
+
+	array = (JEntry *) (buffer + sizeof(header));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		e = array + i;
+		data = (char *) (array + (header & JB_COUNT_MASK));
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		e = array + i * 2 + 1;
+		data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+	}
+	else
+	{
+		return NULL;
+	}
+
+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+		r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2 * sizeof(JEntry);
+	}
+
+	return &r;
+}
+
+/****************************************************************************
+ *					  Walk on tree representation of jsonb					*
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level)
+{
+	int			i;
+
+	switch (v->type)
+	{
+		case jbvArray:
+			cb(cb_arg, v, WJB_BEGIN_ARRAY, level);
+			for (i = 0; i < v->array.nelems; i++)
+			{
+				if (v->array.elems[i].type == jbvNull ||
+					v->array.elems[i].type == jbvString ||
+					v->array.elems[i].type == jbvBool ||
+					v->array.elems[i].type == jbvNumeric ||
+					v->array.elems[i].type == jbvBinary)
+					cb(cb_arg, v->array.elems + i, WJB_ELEM, level);
+				else
+					walkUncompressedJsonbDo(v->array.elems + i, cb, cb_arg,
+											level + 1);
+			}
+			cb(cb_arg, v, WJB_END_ARRAY, level);
+			break;
+		case jbvHash:
+			cb(cb_arg, v, WJB_BEGIN_OBJECT, level);
+
+			for (i = 0; i < v->hash.npairs; i++)
+			{
+				cb(cb_arg, &v->hash.pairs[i].key, WJB_KEY, level);
+
+				if (v->hash.pairs[i].value.type == jbvNull ||
+					v->hash.pairs[i].value.type == jbvString ||
+					v->hash.pairs[i].value.type == jbvBool ||
+					v->hash.pairs[i].value.type == jbvNumeric ||
+					v->hash.pairs[i].value.type == jbvBinary)
+					cb(cb_arg, &v->hash.pairs[i].value, WJB_VALUE, level);
+				else
+					walkUncompressedJsonbDo(&v->hash.pairs[i].value, cb, cb_arg,
+											level + 1);
+			}
+
+			cb(cb_arg, v, WJB_END_OBJECT, level);
+			break;
+		default:
+			elog(PANIC, "impossible JsonbValue->type: %d", v->type);
+	}
+}
+
+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *						   Iteration over binary jsonb						*
+ ****************************************************************************/
+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{
+	uint32		header = *(uint32 *) buffer;
+
+	it->type = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+	it->nelems = header & JB_COUNT_MASK;
+	it->buffer = buffer;
+
+
+	buffer += sizeof(uint32);
+	it->array = (JEntry *) buffer;
+
+	it->state = jbi_start;
+
+	switch (it->type)
+	{
+		case JB_FLAG_ARRAY:
+			it->data = buffer + it->nelems * sizeof(JEntry);
+			it->isScalar = (header & JB_FLAG_SCALAR) ? true : false;
+			Assert(it->isScalar == false || it->nelems == 1);
+			break;
+		case JB_FLAG_OBJECT:
+			it->data = buffer + it->nelems * sizeof(JEntry) * 2;
+			break;
+		default:
+			elog(PANIC, "impossible type: %08x", it->type);
+	}
+}
+
+JsonbIterator *
+JsonbIteratorInit(char *buffer)
+{
+	JsonbIterator *it = palloc(sizeof(*it));
+
+	parseBuffer(it, buffer);
+	it->next = NULL;
+
+	return it;
+}
+
+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry * e, bool skipNested)
+{
+	if (JBE_ISSTRING(*e))
+	{
+		v->type = jbvString;
+		v->string.val = (*it)->data + JBE_OFF(*e);
+		v->string.len = JBE_LEN(*e);
+		v->size = sizeof(JEntry) + v->string.len;
+
+		return false;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		v->type = jbvBool;
+		v->boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		v->type = jbvNumeric;
+		v->numeric = (Numeric) ((*it)->data + INTALIGN(JBE_OFF(*e)));
+
+		v->size = 2 * sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+		return false;
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (skipNested)
+	{
+		v->type = jbvBinary;
+		v->binary.data = (*it)->data + INTALIGN(JBE_OFF(*e));
+		v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		v->size = v->binary.len + 2 * sizeof(JEntry);
+
+		return false;
+	}
+	else
+	{
+		JsonbIterator *nit = palloc(sizeof(*nit));
+
+		parseBuffer(nit, (*it)->data + INTALIGN(JBE_OFF(*e)));
+		nit->next = *it;
+		*it = nit;
+
+		return true;
+	}
+}
+
+static JsonbIterator *
+up(JsonbIterator *it)
+{
+	JsonbIterator *v = it->next;
+
+	pfree(it);
+
+	return v;
+}
+
+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int			res;
+
+	if (*it == NULL)
+		return 0;
+
+	/*
+	 * Encode all possible states by one integer. That's possible because enum
+	 * members of JsonbIterator->state uses different bits than
+	 * JB_FLAG_ARRAY/JB_FLAG_OBJECT. See definition of JsonbIterator
+	 */
+
+	switch ((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:
+			(*it)->state = jbi_elem;
+			(*it)->i = 0;
+			v->type = jbvArray;
+			v->array.nelems = (*it)->nelems;
+			res = WJB_BEGIN_ARRAY;
+			v->array.scalar = (*it)->isScalar;
+			break;
+		case JB_FLAG_ARRAY | jbi_elem:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_ARRAY;
+			}
+			else if (formAnswer(it, v, &(*it)->array[(*it)->i++], skipNested))
+			{
+				res = JsonbIteratorGet(it, v, skipNested);
+			}
+			else
+			{
+				res = WJB_ELEM;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_start:
+			(*it)->state = jbi_key;
+			(*it)->i = 0;
+			v->type = jbvHash;
+			v->hash.npairs = (*it)->nelems;
+			res = WJB_BEGIN_OBJECT;
+			break;
+		case JB_FLAG_OBJECT | jbi_key:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_OBJECT;
+			}
+			else
+			{
+				formAnswer(it, v, &(*it)->array[(*it)->i * 2], false);
+				(*it)->state = jbi_value;
+				res = WJB_KEY;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_value:
+			(*it)->state = jbi_key;
+			if (formAnswer(it, v, &(*it)->array[((*it)->i++) * 2 + 1], skipNested))
+				res = JsonbIteratorGet(it, v, skipNested);
+			else
+				res = WJB_VALUE;
+			break;
+		default:
+			elog(PANIC, "unknown state %08x", (*it)->type & (*it)->state);
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *		  Transformation from tree to binary representation of jsonb		*
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	   *begin;
+	char	   *ptr;
+
+	struct
+	{
+		uint32		i;
+		uint32	   *header;
+		JEntry	   *array;
+		char	   *begin;
+	}		   *levelstate, *lptr, *pptr;
+
+	uint32		maxlevel;
+
+}	CompressState;
+
+#define curLevelState	state->lptr
+#define prevLevelState	state->pptr
+
+static void
+putJEntryString(CompressState * state, JsonbValue *value, uint32 level, uint32 i)
+{
+	curLevelState = state->levelstate + level;
+
+	if (i == 0)
+		curLevelState->array[0].entry = JENTRY_ISFIRST;
+	else
+		curLevelState->array[i].entry = 0;
+
+	switch (value->type)
+	{
+		case jbvNull:
+			curLevelState->array[i].entry |= JENTRY_ISNULL;
+
+			if (i > 0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvString:
+			memcpy(state->ptr, value->string.val, value->string.len);
+			state->ptr += value->string.len;
+
+			if (i == 0)
+				curLevelState->array[i].entry |= value->string.len;
+			else
+				curLevelState->array[i].entry |=
+					(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+					value->string.len;
+			break;
+		case jbvBool:
+			curLevelState->array[i].entry |= (value->boolean) ?
+				JENTRY_ISTRUE : JENTRY_ISFALSE;
+
+			if (i > 0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvNumeric:
+			{
+				int			addlen = INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin);
+				int			numlen = VARSIZE_ANY(value->numeric);
+
+				switch (addlen)
+				{
+					case 3:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 2:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 1:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->numeric, numlen);
+				state->ptr += numlen;
+
+				curLevelState->array[i].entry |= JENTRY_ISNUMERIC;
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + numlen;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + numlen;
+				break;
+			}
+		case jbvBinary:
+			{
+				int			addlen = INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin);
+
+				switch (addlen)
+				{
+					case 3:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 2:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 1:
+						*state->ptr = '\0';
+						state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->binary.data, value->binary.len);
+				state->ptr += value->binary.len;
+
+				curLevelState->array[i].entry |= JENTRY_ISNEST;
+
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + value->binary.len;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + value->binary.len;
+			}
+			break;
+		default:
+			elog(PANIC, "Unsupported JsonbValue type: %d", value->type);
+	}
+}
+
+static void
+compressCallback(void *arg, JsonbValue *value, uint32 flags, uint32 level)
+{
+	CompressState *state = arg;
+
+	if (level == state->maxlevel)
+	{
+		state->maxlevel *= 2;
+		state->levelstate = repalloc(state->levelstate,
+							   sizeof(*state->levelstate) * state->maxlevel);
+	}
+
+	curLevelState = state->levelstate + level;
+
+	if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
+	{
+		Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_BEGIN_OBJECT) && value->type == jbvHash));
+
+		curLevelState->begin = state->ptr;
+
+		switch (INTALIGN(state->ptr - state->begin) -
+				(state->ptr - state->begin))
+		{
+			case 3:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 2:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 1:
+				*state->ptr = '\0';
+				state->ptr++;
+			case 0:
+			default:
+				break;
+		}
+
+		curLevelState->header = (uint32 *) state->ptr;
+		state->ptr += sizeof(*curLevelState->header);
+
+		curLevelState->array = (JEntry *) state->ptr;
+		curLevelState->i = 0;
+
+		if (value->type == jbvArray)
+		{
+			*curLevelState->header = value->array.nelems | JB_FLAG_ARRAY;
+			state->ptr += sizeof(JEntry) * value->array.nelems;
+
+			if (value->array.scalar)
+			{
+				Assert(value->array.nelems == 1);
+				Assert(level == 0);
+				*curLevelState->header |= JB_FLAG_SCALAR;
+			}
+		}
+		else
+		{
+			*curLevelState->header = value->hash.npairs | JB_FLAG_OBJECT;
+			state->ptr += sizeof(JEntry) * value->hash.npairs * 2;
+		}
+	}
+	else if (flags & WJB_ELEM)
+	{
+		putJEntryString(state, value, level, curLevelState->i);
+		curLevelState->i++;
+	}
+	else if (flags & WJB_KEY)
+	{
+		Assert(value->type == jbvString);
+
+		putJEntryString(state, value, level, curLevelState->i * 2);
+	}
+	else if (flags & WJB_VALUE)
+	{
+		putJEntryString(state, value, level, curLevelState->i * 2 + 1);
+		curLevelState->i++;
+	}
+	else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
+	{
+		uint32		len,
+					i;
+
+		Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_END_OBJECT) && value->type == jbvHash));
+		if (level == 0)
+			return;
+
+		len = state->ptr - (char *) curLevelState->begin;
+
+		prevLevelState = curLevelState - 1;
+
+		if (*prevLevelState->header & JB_FLAG_ARRAY)
+		{
+			i = prevLevelState->i;
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			if (i == 0)
+				prevLevelState->array[0].entry |= JENTRY_ISFIRST | len;
+			else
+				prevLevelState->array[i].entry |=
+					(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else if (*prevLevelState->header & JB_FLAG_OBJECT)
+		{
+			i = 2 * prevLevelState->i + 1;		/* VALUE, not a KEY */
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			prevLevelState->array[i].entry |=
+				(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else
+		{
+			elog(PANIC, "Wrong parent");
+		}
+
+		Assert(state->ptr - curLevelState->begin <= value->size);
+		prevLevelState->i++;
+	}
+	else
+	{
+		elog(PANIC, "Wrong flags");
+	}
+}
+
+/*
+ * puts JsonbValue tree into preallocated buffer
+ */
+uint32
+compressJsonb(JsonbValue *v, char *buffer)
+{
+	uint32		l = 0;
+	CompressState state;
+
+	state.begin = state.ptr = buffer;
+	state.maxlevel = 8;
+	state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+
+	walkUncompressedJsonb(v, compressCallback, &state);
+
+	l = state.ptr - buffer;
+	Assert(l <= v->size);
+
+	return l;
+}
+
+/****************************************************************************
+ *					Iteration-like forming jsonb							*
+ ****************************************************************************/
+static ToJsonbState *
+pushState(ToJsonbState ** state)
+{
+	ToJsonbState *ns = palloc(sizeof(*ns));
+
+	ns->next = *state;
+	return ns;
+}
+
+static void
+appendArray(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *a = &state->v;
+
+	Assert(a->type == jbvArray);
+
+	if (a->array.nelems >= state->size)
+	{
+		state->size *= 2;
+		a->array.elems = repalloc(a->array.elems,
+								  sizeof(*a->array.elems) * state->size);
+	}
+
+	a->array.elems[a->array.nelems++] = *v;
+
+	a->size += v->size;
+}
+
+static void
+appendKey(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	if (h->hash.npairs >= state->size)
+	{
+		state->size *= 2;
+		h->hash.pairs = repalloc(h->hash.pairs,
+								 sizeof(*h->hash.pairs) * state->size);
+	}
+
+	h->hash.pairs[h->hash.npairs].key = *v;
+	h->hash.pairs[h->hash.npairs].order = h->hash.npairs;
+
+	h->size += v->size;
+}
+
+static void
+appendValue(ToJsonbState * state, JsonbValue *v)
+{
+	JsonbValue *h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	h->hash.pairs[h->hash.npairs++].value = *v;
+
+	h->size += v->size;
+}
+
+/*
+ * Pushes the value into state. With r = WJB_END_OBJECT and v = NULL
+ * it will order and unique hash's keys otherwise we believe that
+ * pushed keys was ordered and unique.
+ * Initial state of ToJsonbState is NULL.
+ */
+JsonbValue *
+pushJsonbValue(ToJsonbState ** state, int r /* WJB_* */ , JsonbValue *v)
+{
+	JsonbValue *h = NULL;
+
+	switch (r)
+	{
+		case WJB_BEGIN_ARRAY:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvArray;
+			(*state)->v.size = 3 * sizeof(JEntry);
+			(*state)->v.array.nelems = 0;
+			(*state)->v.array.scalar = (v && v->array.scalar) ? true : false;
+			(*state)->size = (v && v->type == jbvArray && v->array.nelems > 0)
+				? v->array.nelems : 4;
+			(*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) *
+											 (*state)->size);
+			break;
+		case WJB_BEGIN_OBJECT:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvHash;
+			(*state)->v.size = 3 * sizeof(JEntry);
+			(*state)->v.hash.npairs = 0;
+			(*state)->size = (v && v->type == jbvHash && v->hash.npairs > 0) ?
+				v->hash.npairs : 4;
+			(*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) *
+											(*state)->size);
+			break;
+		case WJB_ELEM:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric ||
+				   v->type == jbvBinary);
+			appendArray(*state, v);
+			break;
+		case WJB_KEY:
+			Assert(v->type == jbvString);
+			appendKey(*state, v);
+			break;
+		case WJB_VALUE:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric ||
+				   v->type == jbvBinary);
+			appendValue(*state, v);
+			break;
+		case WJB_END_OBJECT:
+			h = &(*state)->v;
+			/* v != NULL => we believe that keys was already sorted */
+			if (v == NULL)
+				uniqueJsonbValue(h);
+
+			/*
+			 * no break here - end of hash requres some extra work but rest is
+			 * the same as for array
+			 */
+		case WJB_END_ARRAY:
+			h = &(*state)->v;
+
+			/*
+			 * pop stack and push current array/hash as value in parent
+			 * array/hash
+			 */
+			*state = (*state)->next;
+			if (*state)
+			{
+				switch ((*state)->v.type)
+				{
+					case jbvArray:
+						appendArray(*state, h);
+						break;
+					case jbvHash:
+						appendValue(*state, h);
+						break;
+					default:
+						elog(PANIC, "wrong parent type: %d", (*state)->v.type);
+				}
+			}
+			break;
+		default:
+			elog(PANIC, "wrong type: %08x", r);
+	}
+
+	return h;
+}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 16d584f..e1fb83a 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -51,6 +52,7 @@ static inline Datum get_path_all(PG_FUNCTION_ARGS, bool as_text);
 static inline text *get_worker(text *json, char *field, int elem_index,
 		   char **tpath, int *ipath, int npath,
 		   bool normalize_results);
+static inline Datum get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text);
 
 /* semantic action functions for json_array_length */
 static void alen_object_start(void *state);
@@ -59,6 +61,7 @@ static void alen_array_element_start(void *state, bool isnull);
 
 /* common worker for json_each* functions */
 static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text);
+static inline Datum each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text);
 
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
@@ -225,6 +228,9 @@ typedef struct PopulateRecordsetState
 	MemoryContext fn_mcxt;		/* used to stash IO funcs */
 } PopulateRecordsetState;
 
+/* turn a jsonb object into a record */
+static inline void make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state);
+
 /*
  * SQL function json_object-keys
  *
@@ -232,12 +238,89 @@ typedef struct PopulateRecordsetState
  *
  * This SRF operates in value-per-call mode. It processes the
  * object during the first call, and the keys are simply stashed
- * in an array, whise size is expanded as necessary. This is probably
+ * in an array, whose size is expanded as necessary. This is probably
  * safe enough for a list of keys of a single object, since they are
  * limited in size to NAMEDATALEN and the number of keys is unlikely to
  * be so huge that it has major memory implications.
  */
 
+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext oldcontext;
+		Jsonb	   *jb = PG_GETARG_JSONB(0);
+		bool		skipNested = false;
+		JsonbIterator *it;
+		JsonbValue	v;
+		int			r = 0;
+
+		if (JB_ROOT_IS_SCALAR(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on a scalar")));
+		else if (JB_ROOT_IS_ARRAY(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on an array")));
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc(sizeof(OkeysState));
+
+		state->result_size = JB_ROOT_COUNT(jb);
+		state->result_count = 0;
+		state->sent_count = 0;
+		state->result = palloc(state->result_size * sizeof(char *));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+
+			if (r == WJB_KEY)
+			{
+				char	   *cstr;
+
+				cstr = palloc(v.string.len + 1 * sizeof(char));
+				memcpy(cstr, v.string.val, v.string.len);
+				cstr[v.string.len] = '\0';
+				state->result[state->result_count++] = cstr;
+			}
+		}
+
+
+		MemoryContextSwitchTo(oldcontext);
+		funcctx->user_fctx = (void *) state;
+
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	state = (OkeysState *) funcctx->user_fctx;
+
+	if (state->sent_count < state->result_count)
+	{
+		char	   *nxt = state->result[state->sent_count++];
+
+		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+	}
+
+	/* cleanup to reduce or eliminate memory leaks */
+	for (i = 0; i < state->result_count; i++)
+		pfree(state->result[i]);
+	pfree(state->result);
+	pfree(state);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
@@ -350,9 +433,9 @@ okeys_scalar(void *state, char *token, JsonTokenType tokentype)
 }
 
 /*
- * json getter functions
+ * json and jsonb getter functions
  * these implement the -> ->> #> and #>> operators
- * and the json_extract_path*(json, text, ...) functions
+ * and the json{b?}_extract_path*(json, text, ...) functions
  */
 
 
@@ -373,6 +456,51 @@ json_object_field(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *key = text_to_cstring(PG_GETARG_TEXT_P(1));
+	int			klen = strlen(key);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field on a scalar")));
+	else if (JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field on an array")));
+
+	Assert(JB_ROOT_IS_OBJECT(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				/*
+				 * The next thing the iterator fetches should be the value, no
+				 * matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_object_field_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -389,6 +517,74 @@ json_object_field_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field_text(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	char	   *key = text_to_cstring(PG_GETARG_TEXT_P(1));
+	int			klen = strlen(key);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field_text on a scalar")));
+	else if (JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_object_field_text on an array")));
+
+	Assert(JB_ROOT_IS_OBJECT(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				text	   *result;
+
+				/*
+				 * The next thing the iterator fetches should be the value, no
+				 * matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+
+				/*
+				 * if it's a scalar string it needs to be de-escaped,
+				 * otherwise just return the text
+				 */
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL();
+				}
+				else
+				{
+					StringInfo	jtext = makeStringInfo();
+					Jsonb	   *tjb = JsonbValueToJsonb(&v);
+
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_array_element(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -404,6 +600,44 @@ json_array_element(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+	int			element_number = 0;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element on a scalar")));
+	else if (JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element on an object")));
+
+	Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			if (element_number++ == element)
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_array_element_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -419,6 +653,69 @@ json_array_element_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element_text(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+	bool		skipNested = false;
+	int			element_number = 0;
+
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_array_element_text on a scalar")));
+	else if (JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("cannot call jsonb_array_element_text on an object")));
+
+	Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			if (element_number++ == element)
+			{
+				/*
+				 * if it's a scalar string it needs to be de-escaped,
+				 * otherwise just return the text
+				 */
+				text	   *result;
+
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL();
+				}
+				else
+				{
+					StringInfo	jtext = makeStringInfo();
+					Jsonb	   *tjb = JsonbValueToJsonb(&v);
+
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL();
+}
+
+Datum
 json_extract_path(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, false);
@@ -436,7 +733,8 @@ json_extract_path_text(PG_FUNCTION_ARGS)
 static inline Datum
 get_path_all(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
@@ -448,6 +746,19 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	long		ind;
 	char	   *endptr;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb	   *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -486,9 +797,17 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
+	{
+		if (val_type == JSONOID || as_text)
+			PG_RETURN_TEXT_P(result);
+		else
+			PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	}
 	else
+	{
+		/* null is null regardless */
 		PG_RETURN_NULL();
+	}
 }
 
 /*
@@ -668,7 +987,7 @@ get_object_field_end(void *state, char *fname, bool isnull)
 		/*
 		 * make a text object from the string from the prevously noted json
 		 * start up to the end of the previous token (the lexer is by now
-		 * ahead of us on whatevere came after what we're interested in).
+		 * ahead of us on whatever came after what we're interested in).
 		 */
 		int			len = _state->lex->prev_token_terminator - _state->result_start;
 
@@ -822,18 +1141,134 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
 
 }
 
+Datum
+jsonb_extract_path(PG_FUNCTION_ARGS)
+{
+	return get_jsonb_path_all(fcinfo, false);
+}
+
+Datum
+jsonb_extract_path_text(PG_FUNCTION_ARGS)
+{
+	return get_jsonb_path_all(fcinfo, true);
+}
+
+static inline Datum
+get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	   *pathtext;
+	bool	   *pathnulls;
+	int			npath;
+	int			i;
+	Jsonb	   *res;
+	bool		have_object = false,
+				have_array = false;
+	JsonbValue *jbvp;
+	JsonbValue	tv;
+
+
+	if (array_contains_nulls(path))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call function with null path elements")));
+
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &pathtext, &pathnulls, &npath);
+
+	if (JB_ROOT_IS_OBJECT(jb))
+		have_object = true;
+	else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
+		have_array = true;
+
+	jbvp = (JsonbValue *) VARDATA(jb);
+
+	for (i = 0; i < npath; i++)
+	{
+		if (have_object)
+		{
+			jbvp = findUncompressedJsonbValue((char *) jbvp, JB_FLAG_OBJECT, NULL,
+											  VARDATA_ANY(pathtext[i]),
+											  VARSIZE_ANY_EXHDR(pathtext[i]));
+		}
+		else if (have_array)
+		{
+			long		lindex;
+			uint32		index;
+			char	   *indextext = TextDatumGetCString(pathtext[i]);
+			char	   *endptr;
+
+			lindex = strtol(indextext, &endptr, 10);
+			if (*endptr != '\0' || lindex > INT_MAX || lindex < 0)
+				PG_RETURN_NULL();
+			index = (uint32) lindex;
+			jbvp = getJsonbValue((char *) jbvp, JB_FLAG_ARRAY, index);
+		}
+		else
+		{
+			if (i == 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot call extract path from a scalar")));
+			PG_RETURN_NULL();
+		}
+		if (jbvp == NULL)
+			PG_RETURN_NULL();
+		if (i == npath - 1)
+			break;
+		if (jbvp->type == jbvBinary)
+		{
+			JsonbIterator *it = JsonbIteratorInit(jbvp->binary.data);
+			int			r;
+
+			r = JsonbIteratorGet(&it, &tv, true);
+			jbvp = (JsonbValue *) jbvp->binary.data;
+			have_object = r == WJB_BEGIN_OBJECT;
+			have_array = r == WJB_BEGIN_ARRAY;
+		}
+		else
+		{
+			have_object = jbvp->type == jbvHash;
+			have_array = jbvp->type == jbvArray;
+		}
+	}
+
+	if (as_text)
+	{
+		if (jbvp->type == jbvString)
+			PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->string.val, jbvp->string.len));
+		else if (jbvp->type == jbvNull)
+			PG_RETURN_NULL();
+	}
+
+	res = JsonbValueToJsonb(jbvp);
+
+	if (as_text)
+	{
+		PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(res)) ? NULL : VARDATA(res), VARSIZE(res))));
+	}
+	else
+	{
+		/* not text mode - just hand back the jsonb */
+		PG_RETURN_JSONB(res);
+	}
+}
+
 /*
  * SQL function json_array_length(json) -> int
  */
 Datum
 json_array_length(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
 	AlenState  *state;
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(AlenState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -853,6 +1288,23 @@ json_array_length(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(state->count);
 }
 
+Datum
+jsonb_array_length(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a scalar")));
+	else if (!JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a non-array")));
+
+	PG_RETURN_INT32(JB_ROOT_COUNT(jb));
+}
+
 /*
  * These next two check ensure that the json is an array (since it can't be
  * a scalar or an object).
@@ -909,24 +1361,43 @@ json_each(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_each(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, false);
+}
+
+Datum
 json_each_text(PG_FUNCTION_ARGS)
 {
 	return each_worker(fcinfo, true);
 }
 
+Datum
+jsonb_each_text(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, true);
+}
+
 static inline Datum
-each_worker(PG_FUNCTION_ARGS, bool as_text)
+each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	JsonLexContext *lex = makeJsonLexContext(json, true);
-	JsonSemAction *sem;
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
 	ReturnSetInfo *rsi;
-	MemoryContext old_cxt;
+	Tuplestorestate *tuple_store;
 	TupleDesc	tupdesc;
-	EachState  *state;
-
-	state = palloc0(sizeof(EachState));
-	sem = palloc0(sizeof(JsonSemAction));
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt,
+				tmp_cxt;
+	bool		skipNested = false;
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+
+	if (!JB_ROOT_IS_OBJECT(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot call jsonb_each%s on a non-object",
+						as_text ? "_text" : "")));
 
 	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 
@@ -943,10 +1414,140 @@ each_worker(PG_FUNCTION_ARGS, bool as_text)
 
 	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
 
-	/* make these in a sufficiently long-lived memory context */
 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
 
-	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_KEY)
+		{
+			text	   *key;
+			HeapTuple	tuple;
+			Datum		values[2];
+			bool		nulls[2] = {false, false};
+
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			key = cstring_to_text_with_len(v.string.val, v.string.len);
+
+			/*
+			 * The next thing the iterator fetches should be the value, no
+			 * matter what shape it is.
+			 */
+			r = JsonbIteratorGet(&it, &v, skipNested);
+
+			values[0] = PointerGetDatum(key);
+
+			if (as_text)
+			{
+				if (v.type == jbvNull)
+				{
+					/* a json null is an sql null in text mode */
+					nulls[1] = true;
+					values[1] = (Datum) NULL;
+				}
+				else
+				{
+					text	   *sv;
+
+					if (v.type == jbvString)
+					{
+						/* in text mode scalar strings should be dequoted */
+						sv = cstring_to_text_with_len(v.string.val, v.string.len);
+					}
+					else
+					{
+						/* turn anything else into a json string */
+						StringInfo	jtext = makeStringInfo();
+						Jsonb	   *jb = JsonbValueToJsonb(&v);
+
+						(void) JsonbToCString(jtext, VARDATA(jb), 2 * v.size);
+						sv = cstring_to_text_with_len(jtext->data, jtext->len);
+					}
+
+					values[1] = PointerGetDatum(sv);
+				}
+			}
+			else
+			{
+				/* not in text mode, just return the Jsonb */
+				Jsonb	   *val = JsonbValueToJsonb(&v);
+
+				values[1] = PointerGetDatum(val);
+			}
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();
+}
+
+
+static inline Datum
+each_worker(PG_FUNCTION_ARGS, bool as_text)
+{
+	text	   *json;
+	JsonLexContext *lex;
+	JsonSemAction *sem;
+	ReturnSetInfo *rsi;
+	MemoryContext old_cxt;
+	TupleDesc	tupdesc;
+	EachState  *state;
+
+	json = PG_GETARG_TEXT_P(0);
+
+	lex = makeJsonLexContext(json, true);
+	state = palloc0(sizeof(EachState));
+	sem = palloc0(sizeof(JsonSemAction));
+
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
+
+	/* make these in a sufficiently long-lived memory context */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
 	BlessTupleDesc(state->ret_tdesc);
 	state->tuple_store =
 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
@@ -1081,6 +1682,99 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
  *
  * a lot of this processing is similar to the json_each* functions
  */
+
+Datum
+jsonb_array_elements(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	ReturnSetInfo *rsi;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt,
+				tmp_cxt;
+	bool		skipNested = false;
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			r = 0;
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from a scalar")));
+	else if (!JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from an object")));
+
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	/* it's a simple type, so don't use get_call_result_type() */
+	tupdesc = rsi->expectedDesc;
+
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+
+		if (r == WJB_ELEM)
+		{
+			HeapTuple	tuple;
+			Datum		values[1];
+			bool		nulls[1] = {false};
+			Jsonb	   *val;
+
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			val = JsonbValueToJsonb(&v);
+			values[0] = PointerGetDatum(val);
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();
+}
+
 Datum
 json_array_elements(PG_FUNCTION_ARGS)
 {
@@ -1262,9 +1956,16 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
  * which is in turn partly adapted from record_out.
  *
  * The json is decomposed into a hash table, in which each
- * field in the record is then looked up by name.
+ * field in the record is then looked up by name. For jsonb
+ * we fetch the values direct from the object.
  */
 Datum
+jsonb_populate_record(PG_FUNCTION_ARGS)
+{
+	return populate_record_worker(fcinfo, true);
+}
+
+Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
 	return populate_record_worker(fcinfo, true);
@@ -1279,12 +1980,15 @@ json_to_record(PG_FUNCTION_ARGS)
 static inline Datum
 populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 {
+	Oid			argtype;
+	Oid			jtype = get_fn_expr_argtype(fcinfo->flinfo, have_record_arg ? 1 : 0);
 	text	   *json;
+	Jsonb	   *jb = NULL;
 	bool		use_json_as_text;
-	HTAB	   *json_hash;
+	HTAB	   *json_hash = NULL;
 	HeapTupleHeader rec = NULL;
-	Oid			tupType;
-	int32		tupTypmod;
+	Oid			tupType = InvalidOid;
+	int32		tupTypmod = -1;
 	TupleDesc	tupdesc;
 	HeapTupleData tuple;
 	HeapTuple	rettuple;
@@ -1293,66 +1997,66 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	int			i;
 	Datum	   *values;
 	bool	   *nulls;
-	char		fname[NAMEDATALEN];
-	JsonHashEntry *hashentry;
-
-	if (have_record_arg)
-	{
-		Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
-
-		use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
-		if (!type_is_rowtype(argtype))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("first argument of json_populate_record must be a row type")));
+	Assert(jtype == JSONOID || jtype == JSONBOID);
 
-		if (PG_ARGISNULL(0))
-		{
-			if (PG_ARGISNULL(1))
-				PG_RETURN_NULL();
+	use_json_as_text = PG_ARGISNULL(have_record_arg ? 2 : 1) ? false : 
+		PG_GETARG_BOOL(have_record_arg ? 2 : 1);
 
-			/*
-			 * have no tuple to look at, so the only source of type info is
-			 * the argtype. The lookup_rowtype_tupdesc call below will error
-			 * out if we don't have a known composite type oid here.
-			 */
-			tupType = argtype;
-			tupTypmod = -1;
-		}
-		else
-		{
-			rec = PG_GETARG_HEAPTUPLEHEADER(0);
-
-			if (PG_ARGISNULL(1))
-				PG_RETURN_POINTER(rec);
-
-			/* Extract type info from the tuple itself */
-			tupType = HeapTupleHeaderGetTypeId(rec);
-			tupTypmod = HeapTupleHeaderGetTypMod(rec);
-		}
-
-		json = PG_GETARG_TEXT_P(1);
+	if (have_record_arg)
+	{
+		 argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+
+		 if (!type_is_rowtype(argtype))
+			 ereport(ERROR,
+					 (errcode(ERRCODE_DATATYPE_MISMATCH),
+					  errmsg("first argument of json%s_populate_record must be a row type", jtype == JSONBOID ? "b" : "")));
+
+		 if (PG_ARGISNULL(0))
+		 {
+			 if (PG_ARGISNULL(1))
+				 PG_RETURN_NULL();
+			 
+			 /*
+			  * have no tuple to look at, so the only source of type info is the
+			  * argtype. The lookup_rowtype_tupdesc call below will error out if we
+			  * don't have a known composite type oid here.
+			  */
+			 tupType = argtype;
+			 tupTypmod = -1;
+		 }
+		 else
+		 {
+			 rec = PG_GETARG_HEAPTUPLEHEADER(0);
+
+			 if (PG_ARGISNULL(1))
+				 PG_RETURN_POINTER(rec);
+			 
+			 /* Extract type info from the tuple itself */
+			 tupType = HeapTupleHeaderGetTypeId(rec);
+			 tupTypmod = HeapTupleHeaderGetTypMod(rec);
+		 }
+		 
+		 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 	}
 	else
-	{
-		/* json_to_record case */
-
-		use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
+	{       /* json{b}_to_record case */
+		
+        use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
 
-		if (PG_ARGISNULL(0))
-			PG_RETURN_NULL();
-
-		json = PG_GETARG_TEXT_P(0);
+        if (PG_ARGISNULL(0))
+            PG_RETURN_NULL();
 
 		get_call_result_type(fcinfo, NULL, &tupdesc);
 	}
 
-	json_hash = get_json_object_as_hash(json, "json_populate_record",
-										use_json_as_text);
-
-	if (have_record_arg)
+	if (jtype == JSONOID)
 	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(have_record_arg ? 1 : 0);
+
+		json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
+
 		/*
 		 * if the input json is empty, we can only skip the rest if we were
 		 * passed in a non-null record, since otherwise there may be issues
@@ -1361,8 +2065,15 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 		if (hash_get_num_entries(json_hash) == 0 && rec)
 			PG_RETURN_POINTER(rec);
 
+	}
+	else
+	{
+		jb = PG_GETARG_JSONB(have_record_arg ? 1 : 0);
+
+		/* same logic as for json */
+		if (JB_ISEMPTY(jb) && rec)
+			PG_RETURN_POINTER(rec);
 
-		tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 	}
 
 	ncolumns = tupdesc->natts;
@@ -1425,7 +2136,9 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
-		char	   *value;
+		JsonbValue *v = NULL;
+		char		fname[NAMEDATALEN];
+		JsonHashEntry *hashentry = NULL;
 
 		/* Ignore dropped columns in datatype */
 		if (tupdesc->attrs[i]->attisdropped)
@@ -1434,9 +2147,22 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 			continue;
 		}
 
-		memset(fname, 0, NAMEDATALEN);
-		strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
-		hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+		if (jtype == JSONOID)
+		{
+
+			memset(fname, 0, NAMEDATALEN);
+			strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
+			hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+		}
+		else
+		{
+			if (!JB_ISEMPTY(jb))
+			{
+				char	   *key = NameStr(tupdesc->attrs[i]->attname);
+
+				v = findUncompressedJsonbValue(VARDATA(jb), JB_FLAG_OBJECT, NULL, key, strlen(key));
+			}
+		}
 
 		/*
 		 * we can't just skip here if the key wasn't found since we might have
@@ -1446,7 +2172,8 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 		 * then every field which we don't populate needs to be run through
 		 * the input function just in case it's a domain type.
 		 */
-		if (hashentry == NULL && rec)
+		if (((jtype == JSONOID && hashentry == NULL) ||
+			 (jtype == JSONBOID && v == NULL)) && rec)
 			continue;
 
 		/*
@@ -1461,7 +2188,8 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 						  fcinfo->flinfo->fn_mcxt);
 			column_info->column_type = column_type;
 		}
-		if (hashentry == NULL || hashentry->isnull)
+		if ((jtype == JSONOID && (hashentry == NULL || hashentry->isnull)) ||
+			(jtype == JSONBOID && (v == NULL || v->type == jbvNull)))
 		{
 			/*
 			 * need InputFunctionCall to happen even for nulls, so that domain
@@ -1471,12 +2199,39 @@ populate_record_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = true;
+
 		}
 		else
 		{
-			value = hashentry->val;
 
-			values[i] = InputFunctionCall(&column_info->proc, value,
+			char	   *s = NULL;
+
+			if (jtype == JSONOID)
+			{
+				/* already done the hard work in the json case */
+				s = hashentry->val;
+			}
+			else
+			{
+				if (v->type == jbvString)
+					s = pnstrdup(v->string.val, v->string.len);
+				else if (v->type == jbvBool)
+					s = pnstrdup((v->boolean) ? "t" : "f", 1);
+				else if (v->type == jbvNumeric)
+					s = DatumGetCString(DirectFunctionCall1(numeric_out,
+											   PointerGetDatum(v->numeric)));
+				else if (!use_json_as_text)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("cannot populate with a nested object unless use_json_as_text is true")));
+				else if (v->type == jbvBinary)
+					s = JsonbToCString(NULL, v->binary.data, v->binary.len);
+				else
+					/* not expected to happen */
+					elog(ERROR, "Wrong jsonb");
+			}
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = false;
@@ -1642,6 +2397,136 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
  * per object in the array.
  */
 Datum
+jsonb_populate_recordset(PG_FUNCTION_ARGS)
+{
+	return populate_recordset_worker(fcinfo, true);
+}
+
+static inline void
+make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state)
+{
+	Datum	   *values;
+	bool	   *nulls;
+	int			i;
+	RecordIOData *my_extra = state->my_extra;
+	int			ncolumns = my_extra->ncolumns;
+	TupleDesc	tupdesc = state->ret_tdesc;
+	HeapTupleHeader rec = state->rec;
+	HeapTuple	rettuple;
+
+	values = (Datum *) palloc(ncolumns * sizeof(Datum));
+	nulls = (bool *) palloc(ncolumns * sizeof(bool));
+
+	if (state->rec)
+	{
+		HeapTupleData tuple;
+
+		/* Build a temporary HeapTuple control structure */
+		tuple.t_len = HeapTupleHeaderGetDatumLength(state->rec);
+		ItemPointerSetInvalid(&(tuple.t_self));
+		tuple.t_tableOid = InvalidOid;
+		tuple.t_data = state->rec;
+
+		/* Break down the tuple into fields */
+		heap_deform_tuple(&tuple, tupdesc, values, nulls);
+	}
+	else
+	{
+		for (i = 0; i < ncolumns; ++i)
+		{
+			values[i] = (Datum) 0;
+			nulls[i] = true;
+		}
+	}
+
+	for (i = 0; i < ncolumns; ++i)
+	{
+		ColumnIOData *column_info = &my_extra->columns[i];
+		Oid			column_type = tupdesc->attrs[i]->atttypid;
+		JsonbValue *v = NULL;
+
+		/* Ignore dropped columns in datatype */
+		if (tupdesc->attrs[i]->attisdropped)
+		{
+			nulls[i] = true;
+			continue;
+		}
+
+		if (!JB_ISEMPTY(element))
+		{
+			char	   *key = NameStr(tupdesc->attrs[i]->attname);
+
+			v = findUncompressedJsonbValue(VARDATA(element), JB_FLAG_OBJECT, NULL, key, strlen(key));
+		}
+
+		/*
+		 * we can't just skip here if the key wasn't found since we might have
+		 * a domain to deal with. If we were passed in a non-null record
+		 * datum, we assume that the existing values are valid (if they're
+		 * not, then it's not our fault), but if we were passed in a null,
+		 * then every field which we don't populate needs to be run through
+		 * the input function just in case it's a domain type.
+		 */
+		if (v == NULL && rec)
+			continue;
+
+		/*
+		 * Prepare to convert the column value from text
+		 */
+		if (column_info->column_type != column_type)
+		{
+			getTypeInputInfo(column_type,
+							 &column_info->typiofunc,
+							 &column_info->typioparam);
+			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
+						  state->fn_mcxt);
+			column_info->column_type = column_type;
+		}
+		if (v == NULL || v->type == jbvNull)
+		{
+			/*
+			 * need InputFunctionCall to happen even for nulls, so that domain
+			 * checks are done
+			 */
+			values[i] = InputFunctionCall(&column_info->proc, NULL,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = true;
+		}
+		else
+		{
+			char	   *s = NULL;
+
+			if (v->type == jbvString)
+				s = pnstrdup(v->string.val, v->string.len);
+			else if (v->type == jbvBool)
+				s = pnstrdup((v->boolean) ? "t" : "f", 1);
+			else if (v->type == jbvNumeric)
+				s = DatumGetCString(DirectFunctionCall1(numeric_out,
+											   PointerGetDatum(v->numeric)));
+			else if (!state->use_json_as_text)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot populate with a nested object unless use_json_as_text is true")));
+			else if (v->type == jbvBinary)
+				s = JsonbToCString(NULL, v->binary.data, v->binary.len);
+			else
+				/* not expected to happen */
+				elog(ERROR, "Wrong jsonb");
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = false;
+		}
+	}
+
+	rettuple = heap_form_tuple(tupdesc, values, nulls);
+
+	tuplestore_puttuple(state->tuple_store, rettuple);
+}
+
+Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
 	return populate_recordset_worker(fcinfo, true);
@@ -1660,7 +2545,7 @@ static inline Datum
 populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 {
 	Oid			argtype;
-	text	   *json;
+	Oid			jtype = get_fn_expr_argtype(fcinfo->flinfo, have_record_arg ? 1 : 0);
 	bool		use_json_as_text;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
@@ -1670,27 +2555,25 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	TupleDesc	tupdesc;
 	RecordIOData *my_extra;
 	int			ncolumns;
-	JsonLexContext *lex;
-	JsonSemAction *sem;
 	PopulateRecordsetState *state;
 
-	if (have_record_arg)
-	{
-		argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+    if (have_record_arg)
+    {
+        argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
-		use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
+        use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
-		if (!type_is_rowtype(argtype))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("first argument of json_populate_recordset must be a row type")));
-	}
-	else
-	{
-		argtype = InvalidOid;
+        if (!type_is_rowtype(argtype))
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg("first argument of json_populate_recordset must be a row type")));
+    }
+    else
+    {
+        argtype = InvalidOid;
 
-		use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
-	}
+        use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
+    }
 
 	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 
@@ -1707,33 +2590,17 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 
 	/*
 	 * get the tupdesc from the result set info - it must be a record type
-	 * because we already checked that arg1 is a record type.
+	 * because we already checked that arg1 is a record type, or we're
+	 * in a to_record function which returns a setof record.
 	 */
 	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
 
-	state = palloc0(sizeof(PopulateRecordsetState));
-	sem = palloc0(sizeof(JsonSemAction));
-
-
-	/* make these in a sufficiently long-lived memory context */
-	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
-	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
-	BlessTupleDesc(state->ret_tdesc);
-	state->tuple_store =
-		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
-							  false, work_mem);
-
-	MemoryContextSwitchTo(old_cxt);
-
 	/* if the json is null send back an empty set */
 	if (have_record_arg)
 	{
 		if (PG_ARGISNULL(1))
 			PG_RETURN_NULL();
-
-		json = PG_GETARG_TEXT_P(1);
-
+		
 		if (PG_ARGISNULL(0))
 			rec = NULL;
 		else
@@ -1741,11 +2608,9 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	}
 	else
 	{
-		if (PG_ARGISNULL(0))
+		if (PG_ARGISNULL(1))
 			PG_RETURN_NULL();
 
-		json = PG_GETARG_TEXT_P(0);
-
 		rec = NULL;
 	}
 
@@ -1753,8 +2618,6 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 	tupTypmod = tupdesc->tdtypmod;
 	ncolumns = tupdesc->natts;
 
-	lex = makeJsonLexContext(json, true);
-
 	/*
 	 * We arrange to look up the needed I/O info just once per series of
 	 * calls, assuming the record type doesn't change underneath us.
@@ -1783,29 +2646,86 @@ populate_recordset_worker(PG_FUNCTION_ARGS, bool have_record_arg)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	sem->semstate = (void *) state;
-	sem->array_start = populate_recordset_array_start;
-	sem->array_element_start = populate_recordset_array_element_start;
-	sem->scalar = populate_recordset_scalar;
-	sem->object_field_start = populate_recordset_object_field_start;
-	sem->object_field_end = populate_recordset_object_field_end;
-	sem->object_start = populate_recordset_object_start;
-	sem->object_end = populate_recordset_object_end;
+	state = palloc0(sizeof(PopulateRecordsetState));
 
-	state->lex = lex;
+	/* make these in a sufficiently long-lived memory context */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+	state->ret_tdesc = CreateTupleDescCopy(tupdesc);;
+	BlessTupleDesc(state->ret_tdesc);
+	state->tuple_store = tuplestore_begin_heap(rsi->allowedModes &
+											   SFRM_Materialize_Random,
+											   false, work_mem);
+	MemoryContextSwitchTo(old_cxt);
 
 	state->my_extra = my_extra;
 	state->rec = rec;
 	state->use_json_as_text = use_json_as_text;
 	state->fn_mcxt = fcinfo->flinfo->fn_mcxt;
 
-	pg_parse_json(lex, sem);
+
+	if (jtype == JSONOID)
+	{
+		text	   *json = PG_GETARG_TEXT_P(have_record_arg ? 1 : 0);
+		JsonLexContext *lex;
+		JsonSemAction *sem;
+
+		sem = palloc0(sizeof(JsonSemAction));
+
+		lex = makeJsonLexContext(json, true);
+
+		sem->semstate = (void *) state;
+		sem->array_start = populate_recordset_array_start;
+		sem->array_element_start = populate_recordset_array_element_start;
+		sem->scalar = populate_recordset_scalar;
+		sem->object_field_start = populate_recordset_object_field_start;
+		sem->object_field_end = populate_recordset_object_field_end;
+		sem->object_start = populate_recordset_object_start;
+		sem->object_end = populate_recordset_object_end;
+
+		state->lex = lex;
+
+		pg_parse_json(lex, sem);
+
+	}
+	else
+	{
+		Jsonb	   *jb;
+		JsonbIterator *it;
+		JsonbValue	v;
+		bool		skipNested = false;
+		int			r;
+
+		Assert(jtype == JSONBOID);
+		jb = PG_GETARG_JSONB(have_record_arg ? 1 : 0);
+
+		if (JB_ROOT_IS_SCALAR(jb) || !JB_ROOT_IS_ARRAY(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("cannot call jsonb_populate_recordset on non-array")));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while ((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+
+			if (r == WJB_ELEM)
+			{
+				Jsonb	   *element = JsonbValueToJsonb(&v);
+
+				if (!JB_ROOT_IS_OBJECT(element))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("jsonb_populate_recordset argument must be an array of objects")));
+				make_row_from_rec_and_jsonb(element, state);
+			}
+		}
+	}
 
 	rsi->setResult = state->tuple_store;
 	rsi->setDesc = state->ret_tdesc;
 
 	PG_RETURN_NULL();
-
 }
 
 static void
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 3544d0a..e037957 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -359,4 +359,8 @@ DATA(insert ( 1560 1560 1685 i f ));
 DATA(insert ( 1562 1562 1687 i f ));
 DATA(insert ( 1700 1700 1703 i f ));
 
+/* json to/from jsonb */
+DATA(insert ( 114 3802 0 e i ));
+DATA(insert ( 3802 114 0 e i ));
+
 #endif   /* PG_CAST_H */
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6aa4890..08f2f1e 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1753,6 +1753,18 @@ DATA(insert OID = 3966 (  "#>"	   PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
 DESCR("get value from json with path elements");
 DATA(insert OID = 3967 (  "#>>"    PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
 DESCR("get value from json as text with path elements");
+DATA(insert OID = 3211 (  "->"	   PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3225 (  "->>"    PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3212 (  "->"	   PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3226 (  "->>"    PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3213 (  "#>"	   PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3206 (  "#>>"    PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
+DESCR("get value from jsonb as text with path elements");
 
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9fc61eb..a2227b3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4485,6 +4485,44 @@ DESCR("I/O");
 DATA(insert OID = 3774 (  regdictionarysend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3769" _null_ _null_ _null_ _null_ regdictionarysend _null_ _null_ _null_ ));
 DESCR("I/O");
 
+/* jsonb */
+DATA(insert OID =  3806 (  jsonb_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2275" _null_ _null_ _null_ _null_ jsonb_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3805 (  jsonb_recv		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3804 (  jsonb_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3802" _null_ _null_ _null_ _null_ jsonb_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
+DESCR("I/O");
+
+DATA(insert OID = 3227 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
+DATA(insert OID = 3214 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
+DATA(insert OID = 3215 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
+DATA(insert OID = 3216 (  jsonb_array_element_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element_text _null_ _null_ _null_ ));
+DATA(insert OID = 3217 (  jsonb_extract_path			PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 3802 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3220 (  jsonb_extract_path_op		PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DATA(insert OID = 3221 (  jsonb_extract_path_text	PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3218 (  jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DATA(insert OID = 3219 (  jsonb_array_elements		PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3207 (  jsonb_array_length			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "3802" _null_ _null_ _null_ _null_ jsonb_array_length _null_ _null_ _null_ ));
+DESCR("length of jsonb array");
+DATA(insert OID = 3222 (  jsonb_object_keys			PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_object_keys _null_ _null_ _null_ ));
+DESCR("get jsonb object keys");
+DATA(insert OID = 3208 (  jsonb_each				   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,3802}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3223 (  jsonb_each_text		   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,25}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each_text _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3209 (  jsonb_populate_record	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_record _null_ _null_ _null_ ));
+DESCR("get record fields from a jsonb object");
+DATA(insert OID = 3224 (  jsonb_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ ));
+DESCR("get set of records with fields from a jsonb array of objects");
+DATA(insert OID = 3210 (  jsonb_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a jsonb value");
+
+
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in			PGNSP PGUID 12 1  0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 3fc20c6..7fb8999 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -600,6 +600,12 @@ DATA(insert OID = 3645 ( _tsquery		PGNSP PGUID -1 f b A f t \054 0 3615 0 array_
 DATA(insert OID = 3735 ( _regconfig		PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 
+/* jsonb */
+DATA(insert OID = 3802 ( jsonb			PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DESCR("Binary JSON");
+#define JSONBOID 3802
+DATA(insert OID = 3807 ( _jsonb			PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 9982e59..3610fc8 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		PG_RETURN_DATUM(_result); \
 	} while (0)
 
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+	do { \
+		ReturnSetInfo	   *rsi; \
+		(_funcctx)->call_cntr++; \
+		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+		rsi->isDone = ExprMultipleResult; \
+		PG_RETURN_NULL(); \
+	} while (0)
+
 #define  SRF_RETURN_DONE(_funcctx) \
 	do { \
 		ReturnSetInfo	   *rsi; \
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index baf751e..6af287d 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -64,4 +64,18 @@ extern Datum json_populate_recordset(PG_FUNCTION_ARGS);
 extern Datum json_to_record(PG_FUNCTION_ARGS);
 extern Datum json_to_recordset(PG_FUNCTION_ARGS);
 
+extern Datum jsonb_object_field(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_field_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_keys(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_length(PG_FUNCTION_ARGS);
+extern Datum jsonb_each(PG_FUNCTION_ARGS);
+extern Datum jsonb_each_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_elements(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_record(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index 0000000..50509e2
--- /dev/null
+++ b/src/include/utils/jsonb.h
@@ -0,0 +1,241 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.h
+ *	  Declarations for JSONB data type support.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/include/utils/jsonb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __JSONB_H__
+#define __JSONB_H__
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "utils/array.h"
+#include "utils/numeric.h"
+
+/*
+ * JEntry: there is one of these for each key _and_ value in an jsonb
+ *
+ * the position offset points to the _end_ so that we can get the length
+ * by subtraction from the previous entry.	the ISFIRST flag lets us tell
+ * whether there is a previous entry.
+ */
+typedef struct
+{
+	uint32		entry;
+}	JEntry;
+
+#define JENTRY_ISFIRST		0x80000000
+#define JENTRY_ISSTRING		(0x00000000)		/* keep binary compatibility */
+#define JENTRY_ISNUMERIC	(0x10000000)
+#define JENTRY_ISNEST		(0x20000000)
+#define JENTRY_ISNULL		(0x40000000)		/* keep binary compatibility */
+#define JENTRY_ISBOOL		(0x10000000 | 0x20000000)
+#define JENTRY_ISFALSE		JENTRY_ISBOOL
+#define JENTRY_ISTRUE		(0x10000000 | 0x20000000 | 0x40000000)
+
+/* JENTRY_ISOBJECT, JENTRY_ISARRAY and JENTRY_ISCALAR is only used in send/recv */
+#define JENTRY_ISOBJECT		(0x20000000)
+#define JENTRY_ISARRAY		(0x20000000 | 0x40000000)
+#define JENTRY_ISCALAR		(0x10000000 | 0x40000000)
+
+#define JENTRY_POSMASK	0x0FFFFFFF
+#define JENTRY_TYPEMASK (~(JENTRY_POSMASK | JENTRY_ISFIRST))
+
+/* note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(he_)		(((he_).entry & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISNEST(he_)			(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST)
+#define JBE_ISNULL(he_)			(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL(he_)			(((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
+#define JBE_ISBOOL_TRUE(he_)	(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
+#define JBE_ISBOOL_FALSE(he_)	(JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
+
+#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK)
+#define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1]))
+#define JBE_LEN(he_) (JBE_ISFIRST(he_)	\
+					  ? JBE_ENDPOS(he_) \
+					  : JBE_ENDPOS(he_) - JBE_ENDPOS((&(he_))[-1]))
+
+/*
+ * determined by the size of "endpos" (ie JENTRY_POSMASK)
+ */
+#define JSONB_MAX_STRING_LEN		JENTRY_POSMASK
+
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* header of hash or array jsonb type */
+	/* array of JEntry follows */
+} Jsonb;
+
+/*
+ * it's not possible to get more than 2^28 items into an jsonb.
+ */
+#define JB_FLAG_UNUSED			0x80000000
+#define JB_FLAG_ARRAY			0x40000000
+#define JB_FLAG_OBJECT			0x20000000
+#define JB_FLAG_SCALAR			0x10000000
+
+#define JB_COUNT_MASK			0x0FFFFFFF
+
+#define JB_ISEMPTY(jbp_)		(VARSIZE(jbp_) <= VARHDRSZ)
+#define JB_ROOT_COUNT(jbp_)		(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_COUNT_MASK))
+#define JB_ROOT_IS_OBJECT(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_OBJECT))
+#define JB_ROOT_IS_ARRAY(jbp_)	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_ARRAY))
+#define JB_ROOT_IS_SCALAR(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_SCALAR))
+
+#define DatumGetJsonb(d)	((Jsonb*) PG_DETOAST_DATUM(d))
+#define JsonbGetDatum(p)	PointerGetDatum(p)
+
+#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
+
+typedef struct JsonbPair JsonbPair;
+typedef struct JsonbValue JsonbValue;
+
+struct JsonbValue
+{
+	enum
+	{
+		jbvNull,
+		jbvString,
+		jbvNumeric,
+		jbvBool,
+		jbvArray,
+		jbvHash,
+		jbvBinary				/* binary form of jbvArray/jbvHash */
+	}			type;
+
+	uint32		size;			/* estimation size of node (including
+								 * subnodes) */
+
+	union
+	{
+		Numeric numeric;
+		bool		boolean;
+		struct
+		{
+			uint32		len;
+			char	   *val;	/* could be not null-terminated */
+		}			string;
+
+		struct
+		{
+			int			nelems;
+			JsonbValue *elems;
+			bool		scalar; /* scalar actually shares representation with
+								 * array */
+		}			array;
+
+		struct
+		{
+			int			npairs;
+			JsonbPair  *pairs;
+		}			hash;
+
+		struct
+		{
+			uint32		len;
+			char	   *data;
+		}			binary;
+	};
+
+};
+
+struct JsonbPair
+{
+	JsonbValue	key;
+	JsonbValue	value;
+	uint32		order;			/* to keep order of pairs with equal key */
+};
+
+/*
+ * jsonb support functios
+ */
+
+#define WJB_KEY				(0x001)
+#define WJB_VALUE			(0x002)
+#define WJB_ELEM			(0x004)
+#define WJB_BEGIN_ARRAY		(0x008)
+#define WJB_END_ARRAY		(0x010)
+#define WJB_BEGIN_OBJECT	(0x020)
+#define WJB_END_OBJECT		(0x040)
+
+typedef void (*walk_jsonb_cb) (void * /* arg */ , JsonbValue * /* value */ ,
+								   uint32 /* flags */ , uint32 /* level */ );
+extern void walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg);
+
+extern int	compareJsonbStringValue(const void *a, const void *b, void *arg);
+extern int	compareJsonbPair(const void *a, const void *b, void *arg);
+
+extern int	compareJsonbBinaryValue(char *a, char *b);
+extern int	compareJsonbValue(JsonbValue *a, JsonbValue *b);
+
+extern JsonbValue *findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key);
+extern JsonbValue *findUncompressedJsonbValue(char *buffer, uint32 flags,
+						   uint32 *lowbound, char *key, uint32 keylen);
+
+extern JsonbValue *getJsonbValue(char *buffer, uint32 flags, int32 i);
+
+typedef struct ToJsonbState
+{
+	JsonbValue	v;
+	uint32		size;
+	struct ToJsonbState *next;
+}	ToJsonbState;
+
+extern JsonbValue *pushJsonbValue(ToJsonbState ** state, int r /* WJB_* */ , JsonbValue *v);
+
+extern void uniqueJsonbValue(JsonbValue *v);
+
+extern uint32 compressJsonb(JsonbValue *v, char *buffer);
+
+typedef struct JsonbIterator
+{
+	uint32		type;
+	uint32		nelems;
+	JEntry	   *array;
+	bool		isScalar;
+	char	   *data;
+	char	   *buffer;			/* unparsed buffer */
+
+	int			i;
+
+	/*
+	 * enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB
+	 * with possiblity of decoding. See optimization in JsonbIteratorGet()
+	 */
+	enum
+	{
+		jbi_start = 0x00,
+		jbi_key = 0x01,
+		jbi_value = 0x02,
+		jbi_elem = 0x04
+	} state;
+
+	struct JsonbIterator *next;
+} JsonbIterator;
+
+extern JsonbIterator *JsonbIteratorInit(char *buffer);
+extern int /* WJB_* */ JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested);
+
+extern Datum jsonb_in(PG_FUNCTION_ARGS);
+extern Datum jsonb_out(PG_FUNCTION_ARGS);
+extern Datum jsonb_recv(PG_FUNCTION_ARGS);
+extern Datum jsonb_send(PG_FUNCTION_ARGS);
+
+extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
+
+extern char *JsonbToCString(StringInfo out, char *in, int estimated_len);
+extern Jsonb *JsonbValueToJsonb(JsonbValue *v);
+
+#endif   /* __JSONB_H__ */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 0000000..cb6b4a3
--- /dev/null
+++ b/src/test/regress/expected/jsonb.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ octet_length 
+--------------
+            5
+(1 row)
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ correct_in_utf8 
+-----------------
+              10
+(1 row)
+
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+   correct_in_utf8    
+----------------------
+ the Copyright © sign
+(1 row)
+
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 0000000..8fae7c2
--- /dev/null
+++ b/src/test/regress/expected/jsonb_1.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT octet_length('"\uaBcD"'::jsonb::text);
+                            ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: ...
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot populate with a nested object unless use_json_as_text is true
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc3...
+                                   ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a'...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5758b07..51238be 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,8 +98,7 @@ test: event_trigger
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
-
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 78348f5..e414ec1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -121,6 +121,7 @@ test: xmlmap
 test: functional_deps
 test: advisory_lock
 test: json
+test: jsonb
 test: indirect_toast
 test: plancache
 test: limit
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 0000000..38959a8
--- /dev/null
+++ b/src/test/regress/sql/jsonb.sql
@@ -0,0 +1,265 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+SELECT '"abc"'::jsonb;			-- OK
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+SELECT '0'::jsonb;				-- OK
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+SELECT '0.1'::jsonb;				-- OK
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+SELECT '1e100'::jsonb;			-- OK
+SELECT '1.3e100'::jsonb;			-- OK
+SELECT '1f2'::jsonb;				-- ERROR
+SELECT '0.x1'::jsonb;			-- ERROR
+SELECT '1.3ex100'::jsonb;		-- ERROR
+
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+SELECT '[1,2]'::jsonb;			-- OK
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+SELECT '{"abc":1}'::jsonb;		-- OK
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+SELECT 'false'::jsonb;			-- OK
+SELECT 'null'::jsonb;			-- OK
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+SELECT ''::jsonb;				-- ERROR, no value
+SELECT '    '::jsonb;			-- ERROR, no value
+
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+
+
+-- jsonb extraction functions
+
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+
+-- nulls
+
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+
+
+-- array length
+
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT jsonb_array_length('[]');
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT jsonb_array_length('4');
+
+-- each
+
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+-- extract_path, extract_path_as_text
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- extract_path nulls
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- extract_path operators
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+
+-- array_elements
+
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+
+-- populate_recordset
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+
+-- using the default use_json_as_text argument
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+
+
+-- handling of unicode surrogate pairs
+
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+
+--handling of simple unicode escapes
+
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
#25Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#24)
Re: jsonb and nested hstore

On Wed, Jan 29, 2014 at 3:56 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/29/2014 01:03 PM, Andrew Dunstan wrote:

On 01/27/2014 10:43 PM, Andrew Dunstan wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and compressed
them. The jsonb patch should work standalone. The nested hstore patch
depends on it.

All the jsonb functions now use the jsonb API - there is no more turning
jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up some
jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Further update of jsonb portion.

Only change in functionality is the addition of casts between jsonb and
json.

The other changes are the merge with the new json functions code, and
rearrangement of the docs changes to make them less ugly. Essentially I
moved the indexterm tags right out of the table as is done in some other
parts pf the docs. That makes the entry tags much clearer to read.

Updated to apply cleanly after recent commits.

ok, great. This is really fabulous. So far most everything feels
natural and good.

I see something odd in terms of the jsonb use case coverage. One of
the major headaches with json deserialization presently is that
there's no easy way to easily move a complex (record- or array-
containing) json structure into a row object. For example,

create table bar(a int, b int[]);
postgres=# select jsonb_populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::jsonb, false);
ERROR: cannot populate with a nested object unless use_json_as_text is true

If find the use_json_as_text argument here to be pretty useless
(unlike in the json_build to_record variants where it least provides
some hope for an escape hatch) for handling this since it will just
continue to fail:

postgres=# select jsonb_populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::jsonb, true);
ERROR: missing "]" in array dimensions

OTOH, the nested hstore handles this no questions asked:

postgres=# select * from populate_record(null::bar, '"a"=>1,
"b"=>{1,2}'::hstore);
a | b
---+-------
1 | {1,2}

So, if you need to convert a complex json to a row type, the only
effective way to do that is like this:
postgres=# select* from populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::json::hstore);
a | b
---+-------
1 | {1,2}

Not a big deal really. But it makes me wonder (now that we have the
internal capability of properly mapping to a record) why *both* the
json/jsonb populate record variants shouldn't point to what the nested
hstore behavior is when the 'as_text' flag is false. That would
demolish the error and remove the dependency on hstore in order to do
effective rowtype mapping. In an ideal world the json_build
'to_record' variants would behave similarly I think although there's
no existing hstore analog so I'm assuming it's a non-trival amount of
work.

Now, if we're agreed on that, I then also wonder if the 'as_text'
argument needs to exist at all for the populate functions except for
backwards compatibility on the json side (not jsonb). For non-complex
structures it does best effort casting anyways so the flag is moot.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#25)
Re: jsonb and nested hstore

On 01/29/2014 05:37 PM, Merlin Moncure wrote:

On Wed, Jan 29, 2014 at 3:56 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/29/2014 01:03 PM, Andrew Dunstan wrote:

On 01/27/2014 10:43 PM, Andrew Dunstan wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and compressed
them. The jsonb patch should work standalone. The nested hstore patch
depends on it.

All the jsonb functions now use the jsonb API - there is no more turning
jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up some
jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Further update of jsonb portion.

Only change in functionality is the addition of casts between jsonb and
json.

The other changes are the merge with the new json functions code, and
rearrangement of the docs changes to make them less ugly. Essentially I
moved the indexterm tags right out of the table as is done in some other
parts pf the docs. That makes the entry tags much clearer to read.

Updated to apply cleanly after recent commits.

ok, great. This is really fabulous. So far most everything feels
natural and good.

I see something odd in terms of the jsonb use case coverage. One of
the major headaches with json deserialization presently is that
there's no easy way to easily move a complex (record- or array-
containing) json structure into a row object. For example,

create table bar(a int, b int[]);
postgres=# select jsonb_populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::jsonb, false);
ERROR: cannot populate with a nested object unless use_json_as_text is true

If find the use_json_as_text argument here to be pretty useless
(unlike in the json_build to_record variants where it least provides
some hope for an escape hatch) for handling this since it will just
continue to fail:

postgres=# select jsonb_populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::jsonb, true);
ERROR: missing "]" in array dimensions

OTOH, the nested hstore handles this no questions asked:

postgres=# select * from populate_record(null::bar, '"a"=>1,
"b"=>{1,2}'::hstore);
a | b
---+-------
1 | {1,2}

So, if you need to convert a complex json to a row type, the only
effective way to do that is like this:
postgres=# select* from populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::json::hstore);
a | b
---+-------
1 | {1,2}

Not a big deal really. But it makes me wonder (now that we have the
internal capability of properly mapping to a record) why *both* the
json/jsonb populate record variants shouldn't point to what the nested
hstore behavior is when the 'as_text' flag is false. That would
demolish the error and remove the dependency on hstore in order to do
effective rowtype mapping. In an ideal world the json_build
'to_record' variants would behave similarly I think although there's
no existing hstore analog so I'm assuming it's a non-trival amount of
work.

Now, if we're agreed on that, I then also wonder if the 'as_text'
argument needs to exist at all for the populate functions except for
backwards compatibility on the json side (not jsonb). For non-complex
structures it does best effort casting anyways so the flag is moot.

Well, I could certainly look at making the populate_record{set} and
to_record{set} logic handle types that are arrays or composites inside
the record. It might not be terribly hard to do - not sure.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 01/29/2014 02:37 PM, Merlin Moncure wrote:

create table bar(a int, b int[]);
postgres=# select jsonb_populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::jsonb, false);
ERROR: cannot populate with a nested object unless use_json_as_text is true

Hmmm. What about just making any impossibly complex objects type JSON?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#26)
Re: jsonb and nested hstore

ok, great. This is really fabulous. So far most everything feels
natural and good.

I see something odd in terms of the jsonb use case coverage. One of
the major headaches with json deserialization presently is that
there's no easy way to easily move a complex (record- or array-
containing) json structure into a row object. For example,

create table bar(a int, b int[]);
postgres=# select jsonb_populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::jsonb, false);
ERROR: cannot populate with a nested object unless use_json_as_text
is true

If find the use_json_as_text argument here to be pretty useless
(unlike in the json_build to_record variants where it least provides
some hope for an escape hatch) for handling this since it will just
continue to fail:

postgres=# select jsonb_populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::jsonb, true);
ERROR: missing "]" in array dimensions

OTOH, the nested hstore handles this no questions asked:

postgres=# select * from populate_record(null::bar, '"a"=>1,
"b"=>{1,2}'::hstore);
a | b
---+-------
1 | {1,2}

So, if you need to convert a complex json to a row type, the only
effective way to do that is like this:
postgres=# select* from populate_record(null::bar, '{"a": 1, "b":
[1,2]}'::json::hstore);
a | b
---+-------
1 | {1,2}

Not a big deal really. But it makes me wonder (now that we have the
internal capability of properly mapping to a record) why *both* the
json/jsonb populate record variants shouldn't point to what the nested
hstore behavior is when the 'as_text' flag is false. That would
demolish the error and remove the dependency on hstore in order to do
effective rowtype mapping. In an ideal world the json_build
'to_record' variants would behave similarly I think although there's
no existing hstore analog so I'm assuming it's a non-trival amount of
work.

Now, if we're agreed on that, I then also wonder if the 'as_text'
argument needs to exist at all for the populate functions except for
backwards compatibility on the json side (not jsonb). For non-complex
structures it does best effort casting anyways so the flag is moot.

Well, I could certainly look at making the populate_record{set} and
to_record{set} logic handle types that are arrays or composites inside
the record. It might not be terribly hard to do - not sure.

A quick analysis suggests that this is fixable with fairly minimal
disturbance in the jsonb case. In the json case it would probably
involve reparsing the inner json. That's probably doable, because the
routines are all reentrant, but not likely to be terribly efficient. It
will also be a deal more work.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#28)
Re: jsonb and nested hstore

On Thu, Jan 30, 2014 at 9:50 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

Now, if we're agreed on that, I then also wonder if the 'as_text'
argument needs to exist at all for the populate functions except for
backwards compatibility on the json side (not jsonb). For non-complex
structures it does best effort casting anyways so the flag is moot.

Well, I could certainly look at making the populate_record{set} and
to_record{set} logic handle types that are arrays or composites inside the
record. It might not be terribly hard to do - not sure.

A quick analysis suggests that this is fixable with fairly minimal
disturbance in the jsonb case. In the json case it would probably involve
reparsing the inner json. That's probably doable, because the routines are
all reentrant, but not likely to be terribly efficient. It will also be a
deal more work.

Right. Also the text json functions are already in the wild anyways
-- that's not in the scope of this patch so if they need to be fixed
that could be done later.

ISTM then the right course of action is to point jsonb 'populate'
variants at hstore implementation, not the text json one and remove
the 'as text' argument. Being able to ditch that argument is the main
reason why I think this should be handled now (not forcing hstore
dependency to handle complex json is gravy).

People handling json as text would then invoke a ::jsonb cast trading
off performance for flexibility which is perfectly fine. If you
agree, perhaps we can HINT the error in certain places that return
"ERROR: cannot call json_populate_record on a nested object" that the
jsonb variant can be used as a workaround.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#29)
Re: jsonb and nested hstore

On 01/30/2014 12:34 PM, Merlin Moncure wrote:

On Thu, Jan 30, 2014 at 9:50 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

Now, if we're agreed on that, I then also wonder if the 'as_text'
argument needs to exist at all for the populate functions except for
backwards compatibility on the json side (not jsonb). For non-complex
structures it does best effort casting anyways so the flag is moot.

Well, I could certainly look at making the populate_record{set} and
to_record{set} logic handle types that are arrays or composites inside the
record. It might not be terribly hard to do - not sure.

A quick analysis suggests that this is fixable with fairly minimal
disturbance in the jsonb case. In the json case it would probably involve
reparsing the inner json. That's probably doable, because the routines are
all reentrant, but not likely to be terribly efficient. It will also be a
deal more work.

Right. Also the text json functions are already in the wild anyways
-- that's not in the scope of this patch so if they need to be fixed
that could be done later.

ISTM then the right course of action is to point jsonb 'populate'
variants at hstore implementation, not the text json one and remove
the 'as text' argument. Being able to ditch that argument is the main
reason why I think this should be handled now (not forcing hstore
dependency to handle complex json is gravy).

We can't reference any hstore code in jsonb. There is no guarantee that
hstore will even be loaded.

We'd have to move that code from hstore to jsonb_support.c and then make
hstore refer to it.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Andrew Dunstan (#30)
Re: jsonb and nested hstore

On 01/30/2014 06:45 PM, Andrew Dunstan wrote:

On 01/30/2014 12:34 PM, Merlin Moncure wrote:

On Thu, Jan 30, 2014 at 9:50 AM, Andrew Dunstan <andrew@dunslane.net>
wrote:

Now, if we're agreed on that, I then also wonder if the 'as_text'
argument needs to exist at all for the populate functions except for
backwards compatibility on the json side (not jsonb). For
non-complex
structures it does best effort casting anyways so the flag is moot.

Well, I could certainly look at making the populate_record{set} and
to_record{set} logic handle types that are arrays or composites
inside the
record. It might not be terribly hard to do - not sure.

A quick analysis suggests that this is fixable with fairly minimal
disturbance in the jsonb case.

As row_to_json() works with arbitrarily complex nested types (for
example row having a field
of type array of another (table)type containing arrays of third type) it
would be really nice if
you can get the result back into that row without too much hassle.

and it should be ok to treat json as "source type" and require it to be
translated to jsonb
for more complex operations

In the json case it would probably involve
reparsing the inner json. That's probably doable, because the
routines are
all reentrant, but not likely to be terribly efficient. It will also
be a
deal more work.

Right. Also the text json functions are already in the wild anyways
-- that's not in the scope of this patch so if they need to be fixed
that could be done later.

ISTM then the right course of action is to point jsonb 'populate'
variants at hstore implementation, not the text json one and remove
the 'as text' argument. Being able to ditch that argument is the main
reason why I think this should be handled now (not forcing hstore
dependency to handle complex json is gravy).

We can't reference any hstore code in jsonb. There is no guarantee
that hstore will even be loaded.

We'd have to move that code from hstore to jsonb_support.c and then
make hstore refer to it.

Or just copy it and leave hstore alone - the code duplication is not
terribly huge
here and hstore might still want to develop independently.

Cheers

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic O�

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Andrew Dunstan
andrew@dunslane.net
In reply to: Hannu Krosing (#31)
Re: jsonb and nested hstore

On 01/30/2014 01:03 PM, Hannu Krosing wrote:

On 01/30/2014 06:45 PM, Andrew Dunstan wrote:

On 01/30/2014 12:34 PM, Merlin Moncure wrote:

On Thu, Jan 30, 2014 at 9:50 AM, Andrew Dunstan <andrew@dunslane.net>
wrote:

Now, if we're agreed on that, I then also wonder if the 'as_text'
argument needs to exist at all for the populate functions except for
backwards compatibility on the json side (not jsonb). For
non-complex
structures it does best effort casting anyways so the flag is moot.

Well, I could certainly look at making the populate_record{set} and
to_record{set} logic handle types that are arrays or composites
inside the
record. It might not be terribly hard to do - not sure.

A quick analysis suggests that this is fixable with fairly minimal
disturbance in the jsonb case.

As row_to_json() works with arbitrarily complex nested types (for
example row having a field
of type array of another (table)type containing arrays of third type) it
would be really nice if
you can get the result back into that row without too much hassle.

and it should be ok to treat json as "source type" and require it to be
translated to jsonb
for more complex operations

Might be possible.

In the json case it would probably involve
reparsing the inner json. That's probably doable, because the
routines are
all reentrant, but not likely to be terribly efficient. It will also
be a
deal more work.

Right. Also the text json functions are already in the wild anyways
-- that's not in the scope of this patch so if they need to be fixed
that could be done later.

ISTM then the right course of action is to point jsonb 'populate'
variants at hstore implementation, not the text json one and remove
the 'as text' argument. Being able to ditch that argument is the main
reason why I think this should be handled now (not forcing hstore
dependency to handle complex json is gravy).

We can't reference any hstore code in jsonb. There is no guarantee
that hstore will even be loaded.

We'd have to move that code from hstore to jsonb_support.c and then
make hstore refer to it.

Or just copy it and leave hstore alone - the code duplication is not
terribly huge
here and hstore might still want to develop independently.

We have gone to great deal of trouble to make jsonb and nested hstore
more or less incarnations of the same thing. The new hstore relies
heavily on the new jsonb. So what you're suggesting is the opposite of
what's been developed these last months.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#32)
Re: jsonb and nested hstore

Andrew Dunstan <andrew@dunslane.net> writes:

On 01/30/2014 01:03 PM, Hannu Krosing wrote:

On 01/30/2014 06:45 PM, Andrew Dunstan wrote:

We'd have to move that code from hstore to jsonb_support.c and then
make hstore refer to it.

Or just copy it and leave hstore alone - the code duplication is not
terribly huge here and hstore might still want to develop independently.

We have gone to great deal of trouble to make jsonb and nested hstore
more or less incarnations of the same thing. The new hstore relies
heavily on the new jsonb. So what you're suggesting is the opposite of
what's been developed these last months.

If so, why would you be resistant to pushing more code out of hstore
and into jsonb?

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#33)
Re: jsonb and nested hstore

On 01/30/2014 01:50 PM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 01/30/2014 01:03 PM, Hannu Krosing wrote:

On 01/30/2014 06:45 PM, Andrew Dunstan wrote:

We'd have to move that code from hstore to jsonb_support.c and then
make hstore refer to it.

Or just copy it and leave hstore alone - the code duplication is not
terribly huge here and hstore might still want to develop independently.

We have gone to great deal of trouble to make jsonb and nested hstore
more or less incarnations of the same thing. The new hstore relies
heavily on the new jsonb. So what you're suggesting is the opposite of
what's been developed these last months.

If so, why would you be resistant to pushing more code out of hstore
and into jsonb?

I'm not. Above I suggested exactly that. I was simply opposed to Hannu's
suggestion that instead of making hstore refer to the adopted code we
maintain two copies of code that does essentially the same thing.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#24)
2 attachment(s)
Re: jsonb and nested hstore

On 01/29/2014 04:56 PM, Andrew Dunstan wrote:

On 01/29/2014 01:03 PM, Andrew Dunstan wrote:

On 01/27/2014 10:43 PM, Andrew Dunstan wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and
compressed them. The jsonb patch should work standalone. The nested
hstore patch depends on it.

All the jsonb functions now use the jsonb API - there is no more
turning jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up
some jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Further update of jsonb portion.

Only change in functionality is the addition of casts between jsonb
and json.

The other changes are the merge with the new json functions code, and
rearrangement of the docs changes to make them less ugly. Essentially
I moved the indexterm tags right out of the table as is done in some
other parts pf the docs. That makes the entry tags much clearer to read.

Updated to apply cleanly after recent commits.

Updated patches for both pieces. Included is some tidying done by
Teodor, and fixes for remaining whitespace issues. This now passes "git
diff --check master" cleanly for me.

cheers

andrew

Attachments:

jsonb-9.patch.gzapplication/x-gzip; name=jsonb-9.patch.gzDownload
nested-hstore-9.patch.gzapplication/x-gzip; name=nested-hstore-9.patch.gzDownload
����Rnested-hstore-9.patch�\�s7����+P��J20f�>9v��W�8e'U�J�$�)���d��k��C����������_��xph��������3;N�}s��LGc�����N�������@��&��1���*�(UB4���6��f��U�����[i��"6��`�=���O��I<�^^]���o�����;���r����X=�v�����7>�5��Z��"3���{�J�����\���yk�7C�Y�y�����?>ra�]rN*�y����_^�G���|tus��O��W��;������F����wF���;���b�O~���w����������~x4�l�Y�w&o���&��;�5\��}����C���\�!�����_�BM\�R�3i�_>y����x��;d�'S������4��&�������OE�>v��tl:���;��Z���d9a�~p>�_=����O�����_|�_�����.�o��*a(2%k1C���s�8�����Y���vp�<����������������>�����M�v�t��M��!P*���=��}\m���7�^�O�=}E�����#r�j����-x�/u8�~�������%i�m�+@o�)�:]SC����Y���r��P[�`��th���3e�0�9��lx=`\Y�QE��Ec!h��m��]�A������G����ODy��q�	�������=k���f�����&q��`���AMY����B�QeD�(�Mf���O�Vh�_^
��N'd�����?����w��3r5|�.��r��'����
�iGc��W#H�are/���w��>���Lz���C
����z8�a�2
�lx1�C����"b>�WW~h��<���t
����^���T�Q��
X)F.����x��!����HqJ~%p�g��+4�NO�_����
����:&��������B����\U��:T7a�h��X����C�s�+�������/��:��i���Lo@�����z
>���~HiM[�^��<�����S����,i)���J"W�
V�U:�"�D�2-3�^�R�,Ixg�"�(Q�l��%���d%r�]YI�4JXnP��[�P"7w4G	js%�$�390
�*m(���)s&�x^�s>Ih���Q�r�y��� �0R2��\VH�'�L��������"n�)�\��V���&	A���QU�:�-h2�2�p��.�J"3�sj�^aU��D|�TT0��i����<IX�y��^a(X/��+d@����He��4R����Pgg���C��56��&	����
	�����S�Y�O�2'�J�+WfT�^	�S�a�p�d�f���1�$��G	e2+8�g���,7S��$QPn���������Up6���%I"������*�e��e`�����.s�DJv���$��+���>8�(O���a�,�^��2�!>e0�����Nd�yAs[j��JC�a�KEK]��g��K�������k9��d���D��R��4�#�z�UV1��A:���+N�H�P��Rz��eZ��_�i8��XX!?�4���a&��D�
c�)<�?X,�?h��C|�t�;���e�
�` ��J��p�$����S����XJs��k2�?�vAK��f��t���X��?P?,�����P�������B�N+
1L��
�r�7�h	�q��ja%��u����R�����r�4�g<��e��-��?d�
#9��T�B���J9A75��2���CI^Bd�q�(�����\&�C�&�C|�7q3�q�fK�VV'~�������22���0�����y�����<��M�E��%���A�4�-��5[�q�N9�3�/�JK�?�����B*C(1�TS�=G�rGJ[*�D;�[���@)�JO�q�%��<$~�\q�K�[P%R(Q*��qS^�|�G�N0�#>E�2�F	�t��D���6�#��tP�P�����!"^`�!i�]�G�����2���p������e��c��<��Zy��d�H
#J�gJ$~8�8b�TC��8g�fB�O0* &���������8gL��B	�J���k3Y�z
L���a0y	�k�MB\<��$,�y^�^c�9���Ls��&%!��a���aQ�7n�����Kk2���H�F|�tJ��a�)����q^{���&2h��*�#>�TZg%�G�`�$,�<�h�74&?��s�y��GS�H�������k�q�
��h��&@�w��%��������:��A��q������.����T�������E�?
f<�	J������G�Xar���q���#>!P�r�M)���c�Y,�F&~A�H��� ����fBp�.D�G*h��)���OE�I�/+�ze6�C�\)��+��%JU�\����.�C{��h�������j�B����	����D{�7&�g�nC��A	�!����k������K�0+�:��F;
E����������*���\�r����r�)��q�NAp)��k�)?`�>e���`�5�&5�Y#������PD~�q?.YW)�6.S.&��U��������I�_q�������?ON��zz?n�����t�*^A��;w~���p���;���G�q�%v/�hLNbc���-rm`�}�h���f��4*ZZU����a9z=��=��c�-���-��m[��
��E�~�"�m�����_��0nl%������kZ%�D&����qz����T�?L�xX�N���7)���A�e���	X�r�6��������>��C����_U[a���?\y����z����R�8c�����U�d��s���-��1��zR�\���	d���c��}w�|pt|�^%t�A���G��9ad<z� ��� � ��o��	<�^�@kR	BG��LcsY#���F��[?]�/��/������%����e�����	X��&I�8:����-b@��c��_��o@S`����7P-�
�����$J3��P����ln\TG���SI��F����?j�#��#nI|�_�I,[���#c�<�7m0��`rd�l��$�����hl�[4y��-����Zjt�;���m���!i���x?��k;\_�>���`;_�����9��
����[I{����-^���o��-�U�k����]���(�_�F�q��F-��)���VoqF���x3��tq2@N�x�?��UE�|��~�������/�����Y��������3�g=7V,��xz���y��4;~��~����Q��E��\�x�.x<���'�>t��0����|{}X��_pwv�,8��>�/�8��]..�I|����N��R�J j����#p����K��/<Y��WJy�\���p�Y<:EX,w+�����i���hU�����sS�bpR Z�
V3z�#FQ�I��C���T�,K-��s�a��3K����m����Kq,V�m�����/2���/v*]�k�u���o�^���no��5e`��{H��4�]����~l��.~�N�]��'f�{�qi*����OQ|��_v��[^g�������F�=���t�V����J�x�eAk5A����1y�$����8�`{�EB��A8^)���c�j��]�V��m<�����a�Lo�aKD����;��;����H����	x|<}�@�K��v�n�����a�v+l����B-@QY�b;k|����l���T���o�w�����vw�uK�[r~�J�TV�J��z���G�#�%nm�W����'���'����f��.,�{���a�ogMvmgE��j����~���Z��������q6�'�����^��/�Qg�u���?k��w*V3���q���ri�mEy��daw������d�������n2�9�ywJ�xtIN������s�p
��:`pv�^���8=%�Nt��l�����������kBP�K��f�sW?%�d����������x��v�5���(�H1��'S�`DO�i���d8���g�8�z0��b�Y�A�T��k�����*w9�3]�K$n�A6��K?��q�
S��b��'~<���������l'��<[�R�F�s���:0����3Z�d�M�y��_�%�h]	J0��[��0�8�Wi&E+_{�lM������]|r���\`^��Wv�K0{ya���OB� PE���6�6}���@fU|	2[��������kyL��<�����R���60@v�I$������X�������'??�]L����ex��GW�z��w��K��=2.�P������W�o���|���a.+����=���'^��q�y�?�s����{�C��V��,G���������d�����!o6RW��L`q��������s2_��E����IOw�����}��t���QF�*���"��Uj������|E���X�u G�3.��E!�*��=������A����?�f��P������<jU x`��x���ZY�������vW�0Y��5���0�CT���5�af<\��� ��]M ��Mk��������O�����s�Z�f��<�yz��oV��'+o���TQ��_�����G*2����pe����.����#M���j���)���#�}&d�c�9M!����[�.����M������1;�<��^���{�hZMJ�%@`V7�j�Wt~�1����j
���������OEh�Y��h4�����r���G$R� �6]���Q[C�dz2�d~����hr�,��~	Z�����J\�=�to���,]f��`4��Y)�3f�����s V�r)	��p�����������I�3L��I`i�$pr�n������lap<���"���������M�����_����C��kHX23�2�!a���<m��xpl�v�L�o�"��n��`f'���n�T*�J�R��[S��6�Vm�F_������]-8���n�i��E-5i����zW�h�bQ�?�^�?
f��/�Dd�gS����[�U��W���V��\{�u�$���v�}���n��B`��F�i��8���
8�/��-s�W#fr���>���Ljd��6������5��D�8������]���
�6�c�N�eq������&{�����/"3_KZd�������2%D1JH��x�3�_���.
/��9��Bk��SZ
|bs������w�R(�(�Dv��	4	b,�F�k���O�Z������#�=%�
�
�tV��s��/��g8y�#�~uo��k����[�,,<��#+�n|�H��X������a�g�=��%�W�D_�q������&����������6|�I���)$/��}���[���a�a����W$M[����v�k�Me{T���3��0���51��p�zL��@����;���
����n�kl���kt��L/0Fn�e�S�t�'���}
�����\�r��/=�+ZO��m�vU��T������4��Q���&���f�jp]�������x���}���s��E��s����o���l��B�'���7�LEg�w1�:�B���pY;��N�q>f��N|��p�C�d������v�I���}���A�E�)@�@%p��P��b
��)�l��"#l�i.fv��.����l�� ��}�H��7�$ra�1�)�'&�����L���( ���d7��G)4( ��q��j�,���I�}�Cc&~hL����NW`\��t�D0Y���FQt�s'�V|�DK4 \�r���,���7O��r�nI�7O�����������sR�%�ks�2z�M%�WD
���ET']s
q���/>AR�tr�J��Bx�q��[������A_rs1L��y}������Sk.b�-���ir��;�d,}��@�-B�C=M��)[����<�������W�z�a����g�Z���
RmQP�����C�0���b�$?�+��n����]���.Nk�_��zS�� &d3�m�m���o���^�xoc��N����`�'����?<?�1��$j�$�������o��~S�V�g/^�g/�^F5������C�LA�$�J{��������$�dw&������|��v��]l�����Dm\�dJ�\ ��^�
���Q��k�V�	�j�5i���|u��������a,��)tf�_�_���*����$�	`�����\�RN�}�8�Ol����������
���H���,a5-�C.snE=j�h���
��!|�gm���������-n����sL�$@��X
�r��
H���Z\43J��.���g1��:PJ�_���&*&%.��������*��qt]������o
#*�v������,��	�.����(��~����)�����NF"�f%6�����!Wd��������*I���je���-����m,7�bT��[I��>���o��h0:1�?rB�\u?z����'^%c�|�����o�c����P(��{|/Z1>ZK���,�����2�rq�ns�a�R_��f�x�����9qo�1����LX]/��������J�E�����c�O^>"dH�.�(����i�s�B�b�_
��tt����D2w����{�;�����U�l��O^f�M�_���J�U�]��Nn�YfW��3)#~#p���j�
��������dD��f���u�Qd-��]��P�{^DR+Z\U�Un�]�����a2s�P�������b���9����]p�)������}W��Y�UG������}���
&D[��NR�������}��Ct�q�������t���,�Nc?����wx�������������	�nc	
���6����B�6��4���Oh�u2��nE����	n�Nj�N�������V�bv~��D5��r�f��/��o&���������G�Fu��'+'���qO.]q�����v���;�Vt���t{�	���?���,O��-P�E�\�~�e������R��l��Q{#�=�5+(���W�1����-;{�`����./����^]3��l���.[~V�n��VCaMnc������Q��V6�3���������
:�FH��\]:}n�Y���������*3��U��Vy�����<��z� ���,��� ���w.�l�I9�������P3�?�
�Cs9�C�K���b��-��_E�5��k�@����� �-����by���f&st���9��
x�y�K�f&'.}��-f2��fq3��W&����-���c�y1�����D�d���lsJ�lV�S|53e�"��������s�&��~�&��l�P�Q���
�=`90�1��T�M�}y�=�C���c|sK�|�9����5�`sl�sBm�a^l�9T����2����9��vK��^m{; �n����ih*2���C�����In����Yio���O��o���c��Z	�z��p���z��xM����]����"{cE2�������H���7V%go�J��X��-2��7���������B�����GK�C=����O[j/s��>F^�����
��������
�
��4��������y ���<�s0R�9x%���r�_���
0+I���U��K�l��*��b0�4�����0���\�+�v�pL.���y�=CS~�Z,_#~�~wc7l������vg�4�-���B;�U�o���ji�\1u�+&O� }����b��^~��f	��"�7��ho���,��Wpt���� �$��a/�oB�s��iA���rL�E�D���D��^��o�������h�+ps0	_#a"�l|��5���:%�w��m�;Gc�T�������������b�xW�����m���6��~!4��G�r��?-v�T��5��ZJ��IQ0��}�8�m�xs���q:+����v��n�^n�0�r�n�;��.G�fq
��x{U���2���m-���j������tQ\�/.����I3�}�u�������L�����&S>�"�RU�rt���C�y)W��f������|�O�_�����gu�t�����2�����+"�	z|R ����(���(�lr-c������)��}e��A�X_�:���B������Z�)�N_�H�����/E�oV���hK-t�$�*��|��!�����My�V�f��IS�_�i�)�W��5�Uo��&��-����6��y�������
XF�w��W�G�&���R�/��0���9USa�'�z~��p��#4g�\�t%��Y!�7��`2����wm�USi����ZE�����-g��$Q�=W���%������$)�s��L!�&�6�K�i�k�����9\�i�E�J����M�M�t_��G]���?�'��tQ�)�h<;��E��h��1�����:�	.���yHZ��K���|#�3�����.��W��bV	�����VEdujV���X�s�Z���Y�����*���a�,D�R���N���1b�<���/�����j{��� �����?���';�/MS�h���rt]6i�/��DCo�4�*�����U��%�Q[��[���"/�#O��&A��9+I^��
\m���oW�����k����_�����_w���l:�\V��um�{|(��P;x����Bt��Dw�� F36��O�?{e�Kx�+���8�'Aw�����g/~:~�	~����u1��uo]�3�zb����|�N��GV'��Xo�a�����o��{����G��70�F<6���0������>�O�u��N/�.W���8<e��O�����N�1'��Fj#����o�9?j���qI���qU
O������Ow����N�|���;z����hgk�4E/�$U?��Y3A�U���z�@���o�x�6��_���R����M���������Ho�6n�����J������_��fNe����I�`3���@�:�3���.^����rz@�F�3�K���`�U��ujT�p?������MB���O�J�W���.��IX�n�����p��8����i|)�w��?�#V� -��s�C�i�:��a�{	�I��Rc�.�t_���8[��W'|�5����7)�������kqb4�!�"�8G2��������;���2;����2��y��2;�y���iWvg:o;_yg:��g�1��s�6���s�6�T������kg���Zdg��3-�3]�R���Kwx
�LCg��4�e�������t�����m���3��R�\dA���%�Sw�r��ug���������s	�c��luI�����w~7�qn0b��{�v��K�����!�9��G[t��x��V{o}�0F����K�(�_��o���{��{]z4<����A�����]p.��W�g��R�ulMu	�������W ��,�s����c ��L��M/�I(����1�w��)��z�Ax&2@-�5L'��u��x*=g7�����V�����~o�Q���ztwU=��[ Y�A���Tf]�U�'���BE��B��b�WW|�Oy\��v�J�NZ���2�{�OP}�������d�f����`4{b6=vHIu��������������b����a�� �#BW�P/�!��`��i#u�?}����4t�9�PJ��F���bW�/.v%�
��F��h��b�����^�����:��:'�%{�p����z�bo��\����U,p{�f������-�,�m����-A������� ����x1{�U��x1���q=�z\]�014'A�����{��b�������RF$)�%?�elF��������������o������/Z���fS���\�D��cj���*;V�^T��"�!���y��+����[�G9O��0X��J��R=���������t<�������;�^QZ�b�%���Z
k:/.��P��<��O������
�����L��]WNV��.����t��3��Y���:��i��M��+HS��%e�7N��?�"��B����br�,��I��e��!���o��3��$�%P���D_�S��2���&�����k�W1��������#i����R>6��$���{�~_4�'���ou�#��������Z��]N�ps|>��2k������8��������6���b��67w�[w����A���^pq��k�F�P��hny[�����i���-2�V��1�Os
����o)����s�nY��&1�G����/���(M�����ATI�v*�kLzU�=��v� y��l?��AH�#d�p�;(1��`i:��{
�<�K��41�s�PfK���Y�8����-�~.��t�������Z
�A�2���J��l.
�V��gh�[��Zi���2�V�=M�)!dt�jg
	�����<�����M�k�YD�L�5�2�L�X$���c����39<��J�I��������@]
�����4��X�hk�B�5r�[Jt5ay����X��k�N�5�,��B.9n��e���]c��������Seh�LX)dN`�2$���L1�t��B����$�E%I3O�4��� ��3��&��%O�&j3G�4�EJd#<�TifK�f�T�krd�y��'�������������`Y{����D���;E����Z&Q��y%Z[��<M������o�[+��Zi�B;���x��{�8���\��>������4��(_}������k�sEh�lb�sQ��3���4�"���$�>*�KYGn�y�S`&���*�Wf������o�s0dyf������g&�?*:��������h���u�����8�\q�fw_�m���+i �+) ��+)�� ����
�J
����D�����������(:$�|jH�w@W�zI�`T$(r�@X�%��zR��<�t�����1�*���"$�bR��"�P�_L0���
�
it~L]E_M����9c���x�������_Jm�����/Kc�z�

�k���K����
��fk�^`��t1���Z6�u,��|�����#�,�x.��L�f�jk���Dn)�~��^�!46D~q��G�n����{�<���{����Z�N���u;��!(���R��M=�x����7�!�#E$�7�4x�e��">��&U��rt�o��Nq�dO��X_���jy-o���jo{��m��3��o���*���(�_�S��m�����]�r�|s���0����F%��k��8���G�������T��$�f���#^}��
,B-�$N��������"��1���������g_��=���8��y��w��i]�'����O�?�q��
/"'����E���4Z�9E-}���i�	��o1��6=�
S�e��]���-�V�����Z��<.��A���\:R��d����;xd�n��.�|�
=A�G!��37Y���a��P�
c��@/��(�c��U����b��tYS��b�x Lj<5�;���%!�=��
�&����rG
A�B������fk���lo�����R�./�
�w��_����
�����]��pkC���-dh�;>���H�"�N���"�O�AO��	�����S1;�G�b��'����9:xq��������{:o`x������)����S�s�R�.�U1��z�DZm�w�3DM6���O�F?�u1����N��G��0`����/#3���y������W��~~����uo]�c��Z���?�y���xB��`z<���7G�x~�k�������G/_�qXm�Qy~�����?�=�I�����O?���>z������o^��x�k��nf�Q��QJ����=�������{�q����),�FGr�q���B�����;�1���W�sW��=��u������y7��%YWn���^�$0G'�/<A��_'�Y����p����pz��V�K24�}~����@��{A�V{,��O��������@
�R���V���V��,�5Wol]����s����c�������Hd6���E{k�-��'��5EG��aZk�������9{0���p���9���������[��_��h�sFP�T�@��G��0����|�B>�m<��4��S���@�U�M�/�����??�6
�`��f���2�C����0Z�����lc��V�o������iL�~7��xZ����5�����`�0���E4v�3�D>5�H4^�a��S�_�y	`��_?��`r�@���FT�gh�28���#
��TK��Q������;���q��<�(-��o����r����5�����8����n;jT4�b4�Ilv�(z!���p�I�`,�L�������A��t�8��-��C�(b�U�7�����z��^�e�2���O&����Tv�0����;��K�n�F����~wv�$*����LG�����E���Iw?�.Z� q��<�^:P�o���/�$���`�A�8^;9x����q�����	S���	�������W�(������(����!�������r�� �Z��3�v�&5�)�Sw�Wq�K���v6�y��������Z:�p�?,�d��c�?��G��L������fO��eM-�T2����3��6������}�m07D�)�	+��[?=�����G2�pT��0��+bf�se�`G�d�b�X7I17%��g'�;�����n���(��c�F=&l��T+f�m�
;E��X7��������.80�-�e��`A~lc���W�wZ����Mum��+�Z%���	]+Z��������Z3�����*�����*��WE���7
�EV2t�]�q\�F�.G���5c��t��f������|���nI����`/~Q�T���+F�d�:�3��Y�%S}��5���������??x�L�=�Z���7G�x�k�{���z	Cb�}}����?��HF�������K���RjZh�������,�]�p0��I�|8x'��M@��3a�1��|Z��P�7n��4��f�d��>�O!p8t�r7�[}{�>�wtZv�]e�Zn���EfGS��5��]T+ci&r�rw��8p�9�N���]�Hn.���AM�iP�u�2��@vi��_�e"�>?oG
/���+�<��#��\���2�1{A0
z��	`�	�U�6Oc-�a)M�������B��BB�(�B���:=�K*��S����M��%��6<y$��x�c���{!�����=�s3�����+0�����v$i@����$�[!�.���{|��%��P������/�
h��m��$����fS�;���9WD��[
1�oe�\==fP6�3>��g�3V�.�X�#����;X@�-��B����(��[H������[����3j�-���i�l���
�,K��|���4��r��V�T�����<�X�3�x��4gW�������Rd��.��d0���$IT�������
�L�����-��Vs�vP���M������w�/�;n6�{>��9�Du���|��d��X����O��1�\6g_Cw_��;g��.�����p��O?b��"m����P��9���'�CH�����:F��b��w1�s�<�w�y��^L?4�/�#��44��l��*C*3�Ax�)g�>��3J7;k|4��E�=I�ck���;���=?Y���E{>\���E{>\������z�c3�0���g,����\s�1zm�
���/~���D���#���s>�}c�����?F�~|���!��a�)�5{�w������#�aC��NrL����O����j�_�>�p)F�Y����[��Qo�=G���
@#����������A��O��e�x�@�~wv���8�l�l��d�����AH�N��W������+��,e0����5���}��-�\���YC����J:��mvO��������I�>tw�a��d��$�+���|�2����d�������E7��r�s�;Sy\�@�S�*�
Nh�|�����mW��;����vy�e��.�J,��~�^I��s��Xw�E��L����La�D�K!��+E���G�PS'\����wn/��,�Ly�G��~d'�QY�#����.W5\>W�ifU��i�.6}�"���lj�M�����N:3�$�|	$��d8�9o������`<�:M����]��R8D>�\��=7�q�%q�'���X<&v���.�X��z}_~>��5�E[[���1wiyO��MGiU���W�'?��,�.o�-XK���?�X�}�$�4~�2�y�'z;�f�p��%#8Y�wb�#�Z���X})(S���N�ze�r7eHq��7�L���J���{h_Xn&���sgr��%.��u]r�bvTJ��r�lq[J�����������~$�p�O���2G�\����f������]�lV�R)T�Qn����Y�m�n����gM�e�w�����d-y(��e]J��hn�
]D��V�yy���-���������<]Q��C��d_�a��;Y�m���~��<���\��]������C)��d}���F�B���<Tz����	�����Ya����W����.������]�+�r��+��_A��
��W��������-q��}^���=���������:���&���������������m�\hh��ZYl���r���?)*wz�9Aq�s���A�aX1�q�!�p��aX���r��3BJ�`���Wm:�a��7�����D����j^N��P�[
�F�NSP(��j2�G6-��B�����8
���2!5�G9�����x�9|Q�[�2���Y���"`��*2
e�N;��B.�`4\QE��i��"���0�RT��ZQE��\5�o)�����r�2����Qb������.dQ�[�x�OW��_�L���Kf�}��6�����@y�-�V�h���@)���@��b�����y}����H��~26�_Q��������4G�Ms&Y�����*ZI9Q���I';@S����!)@��zt���UV��l��d����S�����Ez'4�t��T�O2@S�-���d�s�i��,��r������K��WZ�EZ��i9a����L���a������t���&�?!ig��b#�X^�x\�9Wj+�������q�
X��q������B��Dp�t�Og+�������u��l%-0����J6~2�Q�]��'�<��d��z��V��z��V�Zg+�{�����>�R�����xp6�����IgSo�����M����Hj'b�W��Q��]�gVp6GwG����������5���Q"8�k�~��
c8K�Q�
i�+vYlT�����&��-��i�f�$�m%�_E�NR���{3�$�'y�[������|��&����Q$8���Zgk8��5�����l�dp�F��l,��l�+�������J/���l9T��l	"����������.x��)B���iB��lK����������l%������w���l)���l�Q<�%�����V�g[=���V�|zp��7] 8��q(����Xa\r����]
�f�]5���l������E���9Jgg�)'8[$�2��e3���b+	��
�Lp�T �����.�-�����R�
��
h��l�8,����(8��c��g��a�hm�b����R���m�rwTJ��r�nq�[J�������Ow��d�������-��
��5R��9dm�:~���	�}mYA�o_`8��MI
"#���DF�t��t���?%�k*�����2L��;���f/��	�g��,�����p����i6����MB-}�%���{>^w�xA�]~:�����M����
t��Y���E���G�����	��r���S#X��2V������77{��v��=�CV����R��8�-�6����Q,��j�Rh>�����x��-z��x�/�����x��P���_�.u1�SL�&5G���6�@I����5�y>�+sx �/�k8���[��ZL����t��~6=��4� ������=�Ns��s���}��O3I}JD������w��<�Cc}q|�3N�������)_C�=]k~?u���@T�g'������|6��h����F�y��;�&nm�	�!~>D/������E��� ��E'r�9�G�c�U��O��H�s�k���������k�s	*A�D%+�G��H����<��Y�g�|��f��?Cd#x?�C���4�P%�~���D�D�����Z�j�Y9�����T���Z�Zv�B+Z�|������
����dtXGt�Pd'������<>�������[�������~0�/��G"���4~������9�a�q�
�z��<<PX�N������s��+n�zV����z�>v)�:]p�>x�:�j�*Kz�����GY)��GbK<0��n����f�}=����8�w0�$�b������fF��H*.�;��f'x�f�J���oG9\8@)�
�G�o~9�E&*)��Xm�O'����R��Q���/�R���������oG���o�b��wk#*��^������x �=y���k�Zt��!�1��-�BKa�-#X���?�����*��Q�~;�F9��`%TI�;Qe|������|����3;�����G����l���D��8*�I�5`�$^��.����#��Q����qh�R�8NxSN0Z����p.�~�������o�N7a�������W\Tq�����
�JU�qV�{Pr|~r��q�$`���~/8t����� �a]��a0����i�o;��)5:�.�� �"X�N�C�e�,�jk���}��G�"]����s���_�J���(������62�U*?�MN�xi��?��q1<��V* �$���0SQ���iv6>u�����]��$�G�'�4����bf�Y�"Pj�T���x���}r�=(C����W5���G��`��Z���$���>
88}�A�Sh�m��v�@��|
@c��d��x"��'��P*���A0�m�� �.v�25��$TMQW�>��>��y8�#@�25�3*�?���(��=n&������:~q����������(V�d���h�w�/G�Tb9���2��uFXy������51�l���.��yK�K:><8��y�d�=Q���;h`I2n��/��j�4K=;<������u�zb�}�����+,�����������h����z<��Q�|n�����>��Ry����3�[�������X�����\���� D���L3�.`�^S5��Z=�g�g?3n�����P;��t�����x�k��3��	��Hj4�&0I!6������Y��������C�{\�������~u�:X�@?�_�gnm���o���n�j������
jXD�3&�"�G�������w���O�F ��~�D�05����7�I�_3��@�m{��c�\u�h�7J��e��K�$�����A=���U�CGm=�~:��=��>����I�f��_pyLH%�������!O������\h�q�p,(NR���I��SYO�����0����).��%(V6B+!�ZC�o��% ����Z�-C���`��@*TMj]�3wE�w��!�� �OP�po=�JK���b��+0����W������]����%-���[l�A��}:�vrv���h����	&���R���pB��T���8M�f�V�� y���hvL��HRv�c��s��H&�Q�f�:�F�F(����ac��|j�����l�����qo�t�)�SjF�hFK����O^������/5��Gh{��GT�>���]��x	
=2L��wU���Q�,8�N.k5Z|-T_X�Q#p���QI���x��xF��h4��N5T�&����&[I�qvE:�$�d�zEf*�+j5I��XF�b�{���A����f#o���?C�0�����]����/��Cb$|u(��j`�+������c����o���o��m���~xJ�~&�P0��j�7c���zz6�jM�����gw(��x�TN:��:�hW&�u��������HJ��C��-�9�CB�~��i�4@t��5g|��I�	��K��	��
���~��}(.��$��EfE4)pV��Ws��z�"����T��#B�����H��\h7n�R�k�r���l9>���1��(�4���:���]s���� �:��f|��I�I�e3$8�j�����'�#���]Y���;�B*��x��^������w����i�C��wn���q-Oc�k�9%�&uh.+��I��->d����5�Uk����i�
��s��)C�2��@��P��a���;��2����9�1@=�w�,����W�u�`�(h����%���D���M��nci{�f���^�����:P��p��]�cf	z`�9x~��*Be��]��������6�1���x*���D��`����E����
�,kl��l5�O�����r7
z\�v��n�;��'���b���~����������5R�j>L5O0��,E�B5��:�����^'@��gO������	\�
�4`10�8������I~�-��	j=�o|��T�y���R����D�*"@`��D�U/�U&:�J��
��$��=%��tp_����^c�Ax4�m������]�����r���x4G�<+���TQ)k�a��
��,�6"��������d��U*R�4�H5�th���4�K��Py�6x.�1��� ��E���H.uP�����|���s8��P$g�����XJ �P~
O��S���{b
�����'.�,h�O�U������9a�8>_����?�m�;=.��.�w�$>���L5����J�%9v��{D����_�Q�4��+��g��L�����|�3@�
���~oF���$P���
�:]�/J�JY����a�jNH�P�4�hm�Dpm��N����E�s�GX
�0�9"*h����\����.���|aw'YF,Q��y���h|�b�����Q8�"�1cmD����� �Q�K
LR�����h���hy��'6F4��5f��H�i(���^�fy��-��sbs"^��Cc�]���=�Od@gX�aB�\�<����;�< �R����u�������B�.�uD%)����y���:�Z��7��r�*�u
0�_����-��nz���#F<[����'�U~MG��l{�oP\M�L��2�*�[U����O���X�@��	�)��*"
Gh��6������*�R����,��7[b0g��S��v��]����v�"�^�g����u^}�	��O��<m���V�Ag0�.71��iC�O�P�]G�o�t{�\���x*}����4RhP/��u�r,���;#�������i>�������v��/���������N�������;^kK4����xD~�|���93���w/�>���:n�b��a�`��2[�*�YJ�Q�M�a�sON�(��������v�"1���7 ���
b_�,O�����T
���Aq
H2��O{�C����!���Z[�����������bS�>�ev��O���`�&|���*4���9�Y���u���c�"��a���u�@x��g�a?�!��/)2)���,��z�#���g#C�t���S��0����\������������NmLsE��d>�n��?����!,��r
���^�~���uW8wi�*�d1PD(_��IS2�z���l���q�l�0����Ed��������S�,�4�~Q��9���%tt����B��8�H*
�(V��-J1�6�`����)?e(�����:\�=v��Dw�i�8�4���?��'z ��b��O:zH_����:�o��r���I?�\����b��_�=�E�I@+��?�&f�v��w��MG������g������GDT��a���0#��$�.�s4�;0�EL������+����
��+d�Y��Q�����6&+o>�����`Z��d��o�������E�v����R�UiS�A���P��
@k��k��/������Z�����!�h��3�=��bO���"��;����-��#r����NzY�L���^��R�Q�R`��=��q{����]����wb}v��-k1��q���6���h~����u�?=8z���H~��CE�9Q
��^QQ�F�8:��|�6�O�]#?W\r_�s�����l�P�����t4���C�=:����%.�$)~�u()�����P���=�Q���������*E`�������*�Z�^��=q��u] �;xY���V�2�^����rE������K
��N#�R������@�u��*��U�M�4�Yx�Zv��b���Z�M���D�~O��q	�}��}��kX�:�[l.K��9�.�4_��L�n�M	����a,���J�2��V/g�k:����o�e{
������&������$�����,!^����M�J���j`�(��u������{�#,�$�"s4mz:�IL���ia�;[w�]j����Iq��O��-j���t�-Rq%.s����3���\��	&!VsfRL�f�'Q�t2e��������u��Vks��uo�^�)b��Y .���v��?-y�W]��K���$��M��$
�7xsy8>�59�p��6qo�s>�{����ku�������tCis���fqCi%�N�\�������~��Z��-�����O�!��V���0���?�<�g���!��C��+�%|���m�*��k�8:�?hL��N��P(J9����S&X��)X?N�g|��<Xc�dE�����l��je�@���tOk�}���U�.5���Z���q/�
��[�&��:����k�@oL7lV���XU�>��~�m>HM�*@0�+�'�LQ{Jf����gZ*�F����19�C����<pJ������`��G=Q��Mh�'!;�E?�9�`e`�����~���'����h7��j�TY������R��|  W�"L�^-G�-�c�vXbdlw�9j����%�����7{#����]�i�!/���-0!n��s
���z;��f����F�=(S�����P1���D�2�
��l
�.W��r(3p����q�z�X�� m!�&94���&V���5��n����+{����P%y�����p�����K���n	�����S��Bt��U9,��*;��ME39��=bOLH��=�&��C��+W�E�����{d����V������8����9#�B�W: ;���J
�kDI
T�2� �43�0�������!���	e��M����&7����� �Nx ��$%��.��06�M�����"_z"m|���\&�FS$��&KcM�A���Qr��*v����Q���H#j@����E�P'��F�U�xq-w|
k�����.���zDf��}2�����"5~�c-c��8�����;t@z2�������Lg
��W�Aa>&������-�E���rMI5�b)J5��B;b��hj�J�-n�$���U�a0�onF���Ml�i��+���^��5 �p����F��JL�P�L�����>�������7d��F�z�k��W�.�N��!�!�6�j����Z%��t�7P�u��>�4�/b�������!O�(��, �����X��p�S01��|����jIw��������wJ3c�Ez)bRS�h���<��<�T@�?�Mto������x�L;��#�p2��V��j�-���4�I Q������O��|�9z�y�H�0�� ���������P��}-nr2��S�H�R4����0�"2RU�@��_H�����r���Hh���#L�v2���q'�?7zUO?����������������k;�W���������������L�J�C�][qU��ET���[��������c�Y�����
������p��cM��!�J�
�����:~��	Ll����zC��"�m�wP�*��`�F�w[��6��mdn��h���g6-8@r��c7��V�������gw>k+��M��Z�*c�*4M�����\�B�|��j1L<�f����Mv-���-�{��;V9K�O�m�6U��MY���xv$�^Gl[g����sS��4�px�����$e@�_�l���-��{���������z���l���������UV��P��g>�1�"P��<�K`?b!?�
]��rqcL1���PX�U1��JE�����g���?�����'�^����J.a�D
-��m����-�����D��G��o�'�d�T�WVO��6���B�YTlUd+�&m��!0|�<��R4q��n�l�8�����O,���)��D��gvHu�Eu�
�9�����a�p����dq��D7~�qgL��
@��a��TS����Y^D5�k<q���RL{��YV�:�x��(�v��$�&�L�IiU��'�
y��%��.Q��.{�Pjk�����0����_�� ��;���t��6���iK��w��0���a�2^:L\�3�9V�u4&�����vD�#�K�"���Dj������������dW�x����w�>9�������I$��(�9�K{�p58<x�������|
�?�C�n�j��IpR�X*��6�_�1����b������</��+8�Z����j��mmnv�����i������"�}q�/���^@�e:_���>F�u�1p�;�-��r�g��o'#.���j�G�Ad&R��2r;���Z�b<��	k�A�7vV�Q��iU�N9A��pb��PWe��U����d�@0��QaN�W� %�s�cW*����u��F^�F+���.���)��X���2��YA9�3��Vci{���;�1�I���8�5"�JVP�2������
�:Kjxk���	A*e$I��d�bv����x �����6M�K������I�ymD���2����ym_6R�����={�9
��6�Eppw��i�I��Kj��-���<��V�B��Bl{mL�rw[���+i)�Ss����2�7�O��(?�i�>�i��V
�}�tDgj������1d���U�-bk\!������P����_������w��1:q(��F�����r��I#�� �]F������]��5�ROy�I���%M���4�lV��g���,�wMN��(�g�CR�`��H>���x�T�R�u�� b����4������~"��.�f��`LQ;�!�~�]����M+]]��)�?��=u��W�ZR~E1Wu��*R�C�Z��'���������� _w��t�~<t���OGx��� '���CB�DZaY��/%������E�SY�"��^�'����z����w�������gG�<x���d��p(���g��x�)"
L�����
����6]x��y�6-5h�2��v�r7��s���)!/������q�/��0��e���7u�&�s�����m�N��Jn�/����Pt��T��6(
�`�r��m���kk
Y�Kv�-�d���^�)Hj y^����1�Te�3�����`�O����R��~U�|v�k@�����������w������]��c)e���� ��V���'>^�W]|:��s������
B���l<��q,�Lp����y���8a&[����{@�������bT����xp�U=�5������J�E�A��q�����KT��	c���1fD�,zJ'��k��J�HSW��"�Y*�,�1�M����4+���k��-h�c`>�����5L�!����{q��W�w{�����s��v�$��[V��Z%���3�����_�R��)�
;3�k�n�3
�
1(��-o��yH<"-�@Q�I���e�!�^7��Ql.W��,
��c����!r���O����g��?����_��2~�1��3��W�H����z��T�r��u�����{_7u�u�\��a-���_<.�C#m�`&-�_������R��"!o�	�l?a7+�w6��Yl,�`~��j�oI�9�?�Fj���p�FwH+���%��t��$��(8G�����W�����oa�:�O�9(U�,���/'�e�F�y�5��x%+GV}0��+Yo����AYc�0&����<J�J����Z�"��l�>��J�pGE
�7�������������K�<e$"���+���N� �CB����Ec�i����s�|I�!mu3�;M=[�S�+%��m[�*�����x�E�T�OH��������e���qEsT6���vV;���5�L�-?����H&(��`;:���u7�����?��L�$��k���A���}C:)���������	�nw�w�V/��5�BYn
M>[��h��m��������P�F��(>�����ld��F�]��Em{2�\N)�B�[����4�[�m���N���??
����0�J@��t|>��2�!-�����Ut)��__�����������>�Q�E���Q���)s��e�M������3K��O���@�7����in����9��ci�0����%����G��d^9rW������4r!��&odX�8^�X��	�#����&��YV���Q�3z�	�,�f2���B�tF
���QE��t��2�g�wN���}<J�$Sn���y�V�z��'��[P8��4��Z���������Z�P!�L�
DT�YrQ�a�O����=�h�����!O��<���Qsn��\������Q�T)�������F���_?���������x�I��2z�cb���`V�c��>z�������)���	�
"%`�c*zLE8������/����odw�5[���F�,<���x60���~k���9������/��6vu���VN����6���5��<Vyk�a���k��[���L��.�
f��`��x����Juh������)��A�r5Hri��e�]c�>4�����?��B��7w�L�X90��:>0nw(8&6+a]^B�����q�p;F7k��n�Y-��0,B�ubk@07����4��s$��Xb��'Oh��F��6@����="�m�B�!m�,M�����z2���|J�=u ��s�T�a�7��+�C�=���M��l�r<:�Z�8*.`�������lT�|���d����FTj-�"�G�9Z�c�����q��2�o��$kC(*�.]x��2����}��.��O����4�?e����W�aP�i_��}k��kSTQ;���p�T��z���i�09�����F~�x*HCH�62<@Q������'dp��cZ�ml�U���=y|��g���hO�b2�,�N��2
�E�J�U�����!9��kHw�3,��t;��6:��Z���K.�{E��0O�8<O	:�t��-n��RQ57����u����_��WM_RT�Kj��T����
ir$!0h$�9��.>����gF�����`I��QOB�)n�����|/Y�V'�4C�Zd@@��Pz"�s)�2D��h�U����bc�Z�8Y�G��I<���)����~5�[z�W�f1����F(8{��u��e\��[(umN�EyW@����Kg��hAI2�7�"����o��C*=	���F7�k�4{�[��M���M9��x�.��2`�~�����	9.j�Zcc�w��)�U��0E�-f��xc��n2m����	�v-������+�r<���TJP�?9�����I^��*�����X�y�������*��h��"�Y�A^�k�Sjh�����J�����c������e�������z$�������1>~�:����h��DRsFP�F�HR�M{Eb!�����^@o^�8q�C9����l��>�P[��!	���
����7&������|�`�-�:���������R�����1�/����KI�
9M�FJ5�I����OI(�
�t�<M�9L��2
�p9#Q��k����4&����l�!U��x�t���/��^�"�������^�_�ztJt�jM�:�x����gO�B�Vy�����M0���/�|cV�����`O|���c">'�H�Np�PU�����Lyr��d$�v���]S������X7���D�L�e�TA0�q�F�ZU7�r���"y���������)��WK�5z�l#������N�R_��������D�81X��\{���j�m�����Z�y�!�B�j�%�k�h�0�W��	������,%�����W�c�+�`�����[��_O��q����+��l�#0W6
��J�Dv���T��Z�%I��Ua���H5��cUb��1��Y�W��X;U��"�P��p��(�de^3����g��#��J���j�u(��2�D(�4��B���@�k��@��Ax�;=��f-�3qI�l�N����_-���q�a8�.�����h���K�2��S�t"�<��:��s�#28�5z����{�����(�1����J�81%�f�HZ4���� ��Q��c�^dDX����Q�f�Z�z0��3�/�����~�}gssk����v�����Y�T�n�{��h���>x_�q/�tv>9��),u?�x��������?�S���0e�F?�Xx9�xk���&�Zc��}w�����g��K<�z�~�K\Q��N���
����{�`�LTrF/�1(�i�����k�������0������<����]�3��5p��)����p�+s��z����#L�B���������g6��(�z�y"T6^V���
�����1b�^���3��z}pt{gKt�����^-^�*����aZ��1��{��0&�C���<���*����=�O{QDv��y�����3��R	D��
���q(�V�x}�6�W�x�#t�I����@��,h�@�T?	����g>�s�:W�*2ieWI���U�c�(6��d8M����u����-��u
Q�����LL�4���?������??�����N>D+����	>n'��g/���9���V
�^k�����O
��1����������Fs8����,�P�f�����j�+��X��L��<lA�������7�2�^v���6�	������u�@��d��x�%��{�(���K"�
���
\Z��� �����;58k�px	����qe��Z��]Go��8d�(�;���d��0�F���������wvoM� �s��n���j�#t�����(&��� �"6�
�Ut7>+��(�Cx�"����X.����r�m6w-V�x4��X�9F�<���SO���.�.�D���aQ�)�6U��Y��1X�8����-���V2��[��|j���[�:��CR�dw���'c9���<a��a4�4�e�!kU��r�GI�qA_����'���C�����g�8V��"$'h��������_+�&�/��bhQ�:�oS{��OG���/��WvXC3=� F%Y�)LI��@�8�S�Q�`�u�P���WMf���2��uJ�a����cm��f���7�&�Ae�W���~��H�=�,7�����Q@ELd�
��%��"o?N�J��f	}�'�tJ����(��H�����_����	��Gp�<3�����`�����������;U���\Q��X�`�|��enU�+��NB�t�K�}���l,a!�V���������)E���Vb�C�����?\h�BJ��g��K�
�������MZ`��;��v�	�����	i�wu�ON�����{61���p���z�`�E��mi'����=x�;=�t�+��{�?�\+��-U��Z�_lR���<��{|��W�4�gGjd�L��!��x���F�d��?�b�����B���MC/�x�8|�PT�6��#���cZ��[������YDw���6��L�@�4�M�=jR��Z��X��R��v�a�O�-9:]�A�}�@��h�\��N&i��"����/�
�qHT�X���YPz|~r��s���?$��O��~����x!����n����'�)]*��d�3�O�N��	�>����ul5d����N�!����'����S������	b�tS���8#x��R��3��tS�\&����
y�1cK��)��v[��7����hX�gS
 �j2O��j#�C?E�R�)�G�� �
pC�j���3����OnZs�|1�|�~�j��s=�4pO�s������]��P�W�a!�C�V�(dLY����I=S�T�t�,]�Yh������E�aN9�`*S��T�	u�+��
k$:���N�L�#���'��Q P�1i��.D���[��c�;I<��W�v����x-�H
%��/z�H�/�_#�?��Y�~b�Z���e�	j�����q�u��
�`���[0:?S!��g���?0�����x����?o������|�������|:��a
�n��v����x�.�O�������{��8����-�TGF&^hH�u���������a�l�h��q�G�����`d���y���� �e���[^���
d����@�tmi�� �$�d8?^#���/��C}cG�z�.E,����;��3x
�
��S�w!�(�����{^�u_4���v��b(� S���&�Sl(�n�ui���3������g����{z2����d�p�d$#z����qT��
�9"��������0�!(���]b�u�4���E=4�z�?�;�H/&f+�2x��������D��D�����
#C8�sj�!�f�g�j-�j{"#�P��&z�?~�����/5��'�^��O�v���eQsm�����(qy]Q�A1��Zb���4*�a�/����^<~���K�b �u\���H�1Oi����N�hn������d::%D=JW��&"J&��KB�ty#b��]�i�&M���efH�'��Fi��e?��fc���&�A�ZZ1uYO��&��z��I�h�K��9�J�b�!��|W��XFt^�i3cLQA��Q���q�bJ��������RJ��5�+�%�5R�j�HU�':�X��Lz���%���I�7��|����e��J������`vb���{'M������&^�b�}6�������w���j���:�������?���w��(B)-S$2>�Q����5n�
L���x�Q������-������>�n��������6�Z%M9�,���<���k�h�\J.s7i�dCOf�����R����L"#V��s�g������i#�H���s,#���vh�`n��z�t��Q7�_
;���;����5�������CL_
l�_g|r�@�^tm���������W�-V����o��@�7��c�mp��E����"/�@B�7(*PT��<b��Q;���U^��4����{+�u;���P����I��P��W����[��k��d �c�UC�$u\���^�o�nX��7m���r��
A,�<d��85D���yw�����{����uU^p���U�k;2���S�3������1�"����C�(����W=h8n&n��I����I�1���Iz�7����ed�c��zH�1t��n��dl�1����K[��:i�-Y8q�U��4|���!F�gL����g6k�N�/+��"v�E������/u���7��������4J�/|J��W�,���b8�_d�d
N�n"\q�K�)��;2�7�S#��%!�12D
?�$^I��������NQk���nf���-�JC��6{.Xq:�[,��y�����d���%��r��l!��������6��cU
��},�h��y�^��J����v��	3*���!N��(��0�Y�}�'i��
X�2��_����k��E=��+���+UJb8���@%�{�q��)G�8'IDAz*)>1WL����d~V�'r7���c�I�r�����h|�H��m��KkW�l>YrL�N��Hh�N���Z�����c��xv�=��)�}#.�`A���y
�~���c���>��d-7�,����u��8�ml����1��,\O'��p�P�N�7'Z��iEFj�b3���p�4V���!bse{�2�g7��X���������8L�j����..Z���~�!��E0�kl�E��i�$�y$i$"|���_���C
�L���}���\G�2X|�K�%��H��������0��t��ba�k�:��"�Ch+Pbu����O���	Q����m:*
f�J������2�!U��)�/_��`�$]�|
����[��dv)x�s�-��#4���BSN����D4!�����~1Gz_�a�,Z	�����7?>y����1V�,m���1�{��y�����;Lf��dX�^)��D�@����Nf��^F{��!��se���\R�Y�6r�������
��l���l��$�ak3�?xlgf2W�db;aD[����9#v-��r��otjg�N�",��_Q9�3�i��%[5D�-��)X���)�����2p>!����v�4�m������w{)5��*�[xyhTz��^��7f=|gV}���7/��+�w�����THq M ���I�+��5����i��3;-���J���4�ZIlu�����1n�i$�4*#r��=�2���d��MN?�n"Kk_A��$�HbIH�
���B�~M��X�������2�i����x�B!�J2���������q.Ok�<�`�$C�3�4b5jz��
�Y����z���E�����o:�+�T��|�o�N4[�S�����4�5��B�k2���/Q�+SLf_�+/\6��tS�V�@BVH�%;
�d��=���	�w1s���7Po``�u��?.����;��t�zl�i�S��s'���T�x#�(�p�iF�F�Q�a���������2���(c�����@���4Q6��F�m[c��9��4=��9����Q�5��l1\��F����b�j������������F������7�m�s�mj�N�N�NN�N�|�	�e�����9S�T��w�n|�ly���Be��"4�����iL%��>�c�p��g�[�NG��&4��
�d���w���G�%nG�2Q��=o�d����w����&�������������������aZ2R���/ep2�fv�tk���ltv�q����r�wQ?���t*u�lY�(��\������eu'���z�tWk8�j�K)�z&�>��Tf)4�Y������
Z������^�s��eT���Q=H�ap��d�,��������E��4��H�2��j%4�t�f]�rov\�yQ��6@Yw�wJ���������w�e��0=��gn���2!7~#�'�"�U@U�(���qK�f�]hLmuE����r��0�'	"��]��h��j�!QH�oE�z�j�#�s[H0����:�=�tYXi��h��Z�'EZS�`q�Q��b�����T���e��f�"���
r^��a,��z��V*c-G�~�\q���,��w���;;�`�8S�	tE=����j��b�����F��zA5�A�h�.�����b������e�^Dot��'SS�`�d����
`���P�W?��'�V�i�KPom��1g�5��)Fz�a���?�A/�B�K.q����)���{��=�E��RWs_.��rt>?��W(=������-f�Tp
0��lL�����A���=�$�
���O2�o��5���%=�8�<>��~tE`���F��������q�����Ohd��{r�&����;���zgv�at/�_#����`���0��|�>�G�(��`tev2�J�e��(���;;�5��*�ps_�	�,B9����D�5)�Ve��'�Y���5h�&k�]2� �}R�()���-��5�*�G\�����'�PM.r�����6#Yd$�ffiYZ]2ms���H��7x9������;@����{�2�@�����t<�`��0�#T��P�)}��GX�S@a:��n�Z�R	�����R��5(���6�W�(�t� �UY��S-���)������e����4>|�h��_"�Z��.ci�� i�p�G�,x��=�l,��������$V�X�Q1~���?�4/��Y�O9���a}.*:/)��-�4�R����	�e��y4�RiE#�)���3F�}��f~��*���O�Yp2�^���__=y|t�����q�t/�B�X1���x6������D/WXp\a)�*'���8�5��@9�����
(���h�*b���a�U�s��<�����L�\!�]~�����Y����&R
�Z�b=a�%%�ex�2���X�wD#cD6i�2,��M��*�+����E�	���������'���rp�����?<{����:����A
�Q����� ���T/0�B����Y	�M��y	j�!�>Fs ���R�C�R�4*�?�ARt?sE���2NsJ�����2W�������1��B��������t���BE��S9��Od[d=����_����2���1��a8��"H�($�LR���T~���,�@���|���E�>(46�����?T�X������6�:�$��^�����E�����l����xr��Rr�0�L���A�V
&_�y2=�Ok����.aZfNCG���=�p�)�7��	;{g;{{�lgi�E�4�+H}��N��~j���q�OC9(��
�gAn�*K����� dwS��tA��8������NXs���L�+������t��
��������9�����b��!�:D'Y�����_^�J%�K�:��7�}��@��x��|r<BN9�/jr=�������jm��ukk�v��~I��\��������g�/$�L���EdzAnm�g�zXH��P��o�V8t
L��������%�i<*�^r��E?F=��o�!�������M�}����i=JXdxy���Co��T���g�m����`���A:<>��|��c����k�R��<�p^%���3�/B��r1IA�h�C,�G�RX
G]��O��n��>�����B��`��Q�����O���)��c�
�L�~d�9&��b����n}�=q��g�Ga0�
��D��&���d�������*�)s,���q����)/4����0���wd�>~.��r���;���*��6�H�b��B�
�����i�d
�a��8�a�s~8��hLmI�;�q�mn������q��UZ����v�wU�� 2l�P�2s2�DLT���"8��yv-*���*-������6
���B���]�{������c�K�Ugl�hrA�E��1�juVD��V6@�O5`���d�#��a�1}�u
� �F$�zN��f�����[��~����gO��o~@���xJ&��H�����#���4)�@�8�>@���)5#���"���h�H4B��c��W�<�V!�W�����2�"�s"�YDNG���=2������C}���I�����g�R��+�XV�
D�l��P��������s��ji��M����w;zrb5|��(E0e�
_�M��y��S��`�e�o0%�X�R�QR8����8�����#iO=��_����9�@�����O/�_�_=;<|����rF�+�]}�kD��S��:�
�Ic]������A1��My����@�1�+��n���r���F�������}�I�T���YSpL�����4�A�_����A}9�bXIz�FvMa�\O�FC����d0����jvK���v:�t���a���D3����hl��L��U�� 
qr�wjA��I��0!`IL�&&
���8jz�7����L�N���>�I~o��M)x]��Z�D0HK-������#we�>#o$�#�N>I�JY8�����R��3fM0���Q��D�I���Pi&)Y$��`(���v1�wS���M,/n
#��_d�K��
jG���/#�Y
fR��T;��f���
za����I/"���/~w:��v�b]�H&�!R>y� �;�&_�O'J.*�F�����'yW����C������$B�������
���Te��h-V��E3���a-�����w2�:�p�!�����
�A���!����I%pa&���w�R��,g���Y(�X
��@<g�8'���	z)	d���U��S��2"�|(C�������l=WN���;�=����s�T�(���ZVM��#k��w��L3RG�G���d�(VL�n����DQ;�����;�=��yK/�i���o��	�Yw������:���p��n����n��k'���X&��)��*c�	*�eV����p<D��A����6�b0�4M�d������X�W��A��E�f�#)���Fb��W93R�RlT��M��w��i*	�R���9p���L&-g�R%����������xrz�vj����L��/kUK�b��h	|,�5�z"�)#Jb&��M�h������rI6��� r�7��l�Q��X��P���N�D�V�z-�M�@4�LG���T�4�i(���+���U�i�?v��([���u���� �3��_S������p���l�@�ax~6A�lF�1;<�X�b+:N�jx��%�|0g
�&7�3������:�G�bL��(�%rG(&�q��/9���P� l�#�8
&C�Kg��@u�����a��v`#o���Z�^�W�H��� neP���ch1<��@5��_��k2`���r@5�f)�rt��J�C�]��o�Tl6�q[�[m��F/��y�z�4)}��VU�����?q�g %|�R���+oG($����rv0���}Z��h-�w����s�\F�|������R��������}��;<��Z>�`o�>���3�N`��Q��X�'�`8��L�q��}<��86XrF�9=�����Z�[]�7�����A�������CU�}Q���>N�x���%s�#����G���������?��|�<�F���������<����rU�9�qJ2�i]��(�6P��]��ch�s8����y@Jz�!�1N��������i�l������z��7�'���)�-�RKdQ$[�}qP�:��(�dA'�����/����O�^p���&E�1o�L�����.����U����-��so���>�Q�5.^��b��^����`#Z��8�c]4mm��i[clF��hbTM{�""��\��s�j�0��EeJI�C��D���?����B_p�]c'��1���Y�����CStp@�}3�R�V����Px���A��qF]�����P���������g��a����}E�Ut�2��:����7�QT4���l�1�f��g��_��$�����������H��CN~
�L�E��y9���8������K[Z���%����\����7Ecy)F���{u41�z�������||���zC:�A
�@
������f���z<��2���
R:K�u�h�x��w(���X�h|����JJ#P����,�{PZ3y_�|��"���z]�+���Y�t��c���g��hI��i'������4��R-n�c�Y��� �N+z����"W�����	O��!n0�x���R4Y�������>�*�r%n��J�>`"��+�`����4O���������}������rO��N�b��SN�p��F�J��'k�1���n��EdO�����4#�����x6�S
��g�/H��{��y���JN�9�����iL#���9�G64
'RIA��Z���7>�x�	0%�����7���^�#L�~����(<h�i2��=�R)�o��-7�9F^l��!R�hz����qs���X9W!b�C��v.g�6F�LM�����~O������j���'�<���MG�]\v)�=]L&t���c�6+��f��h��cl�7������)f%���i�k(��y5"�/�El��CL����������x����� ����5��l":������������@dpA6n��Y����Ck&x�!e�cl	�3�+,�9��q\+���ng�tWx��:bc�>�^�VsR+
�� ��WM�B�������w|����!��g�Cqj\�������fD�P�sn��l8�c�0l�n[F�pR���B�i��b*
��X���7�]g2~l�����j�0�g����KP�������x
`G4Z�������!�`��N2�d�K
r�G����*������ZZeQ��k��pF�Z���zu]��u��������((+����������~�p�!��^r�B<nt�P;�Jl��x},f�Kc`~���M�Xlv�)>�I�PA38C6���c@�x�w2;��� ����T�����S��h��b3���;�G���K�o=n=n�PXY�����]��&r����(����z��A�|�����O�L��h�_��
�]}��[�����o��=p��n��'�w������0h�a���A�X�g�2w��v�O=`�<�t'0�Bv���90�g��g��.�iMkfCmvr�R��ZK����	6Y���c��f�E�e���]cg�$�����jO�<+_���7e��d��Ivrr���+1
W��	=���`:�����H^Z^o~�Cc5#/_�{0!�,d3\@�o1��
�����%�w5=1���j�o���S;����a(�h�������&��"�@�Fs�����}�����f��F������(F^j*��/��m�� ��K0�	`���&�:��QA/�������!��)���{U�I�.0�,�����>T��*�:���P�%n����xhv�&��DK�f�����.d�����F�C�\g�����'��w���k��:���s��.�q�?ew��E�d�G4MMl8���<��P���/�w�A��'u4�~�d$�#��9��lxf^ �V�I�W�1^j��`�����J^�	����)��^�h�0�h`�V����+���:�)���q�k��q������M�<Q�M�"�.w[uyK{���w�vg��x�mGrY�O�1K����JK��}�=���[�V�G�A���=>����L_���?R,z��"�}�	����8c�_d�3��4N�7��%-�(�qf2�{x��S�lvP�L�����3M���S�f1x,i�����XH�
��{'#
c��xDa������SO�TF)&h+Mk��)�����<N�#3SP(-�{����{�kO�|��	
��QD�������YOWq�����!�uJw��z5�C�%�����R�_|�3�|Q�#������I����}��pHRL�&�������"n�Fo]H�dJE�y�_�&9DP�0d%��D���rle_EEY��q��e�j
F�4)��q4������W�].vEAN'	8K����S���d����,ftB��i���>�VV{�Hd>u�F�\�dC�@���0�QR�s���2rB=% 6�"L����e�^�1|���k�)Y������Y�S���F(�p(W.��c�mL��|h:|�~�]��G�x�?��LI�"�
��C���������a��Z�f���Ne;�f�����]B[3�����|��e�?vc4������;����zUS��&���:X$��3��XjX�5�#�,�b�h$��@���z(��U�X�j�J� �|�`������;�@E2�&[�	�r��YE���w��:~{(���ncu4�(�',����	��s(:W
�G0'���_�(����F�b	S�u��[k��g�98������5lr�Zq�i	��H��D��#����������b4(NK>|�R-�n�j�-S���1���F����6�{$���IF��X���GV�����aH�tDJ��Y�-U��T��Q2�<"��kf�v��k�,�X�'t��D��/u\����:F,fN��6�Q{$�	���H&�����k���O�A���;����&�c~\���h�k����Kw��?[��XQ�
h������'�Xi�6a�]�(������p�9�#e��1�Q"=P�~m`�+��#�Nq��<o5��(�����X�2�������Z����FV�����e�����g���.h����3Lv6����������ldBQ	w��_�aH���aw�5����$)��c����z;�z�E=���X�4U��SG�h�<Z(u7��C�K�M�o�y2�6���fW�N���q>k��+v���S�����L�u*�����[��d�]��q"���:*�$_���_�p�������F�:{�(]R��'���?p��7/�<u��P�wY;�t��v����y'3��xL:G����HX#�
��=88�B����9yCQ���0>���L$/���h���hDK�t��Pd��V��b���5��L��A0
����P���?��!BU��	�U�U������F��1|�i���7M�����:����1p��Cy����'�/^c���������RC�|J��
f���#p���+�u:����hU��j������;�f�
<�9���M:�~�21k��a����b�<�0�b<�tOy�0$������&9���.�A����::�lvE'���`�>���A����������m����;;;k�f3�Z:�	=G�����������m������`=���@_��\b��@VJ)���~��
���'�_���#[��6�	�>$OA� ����L����*'`�1��I�6���:��<�glv[�7l�!�j��8E9���f�f�2t��_��oW�0Y�`D�������6�)�TD/
?��~<��;��i@ �����C�
J����y�8��Xt��Q�W�TP9��e�o�N0����R��6�'�#v��b�m�W����J0�� �GAs|O�G� ���66����&�IV��������m��(�@�����#	������U��3���W����C�������h�=�!���l�����H�7J�s����������o[f��)F$��2��!7��d�x��:%E}��ju� �i"�v<���N��,����"�G�kS�Z[�XU���7C�M�/bC�ACmC�-���}��>Ap`�����0kx�����-Y�4��	����+u�����
���j��Ty����!Z�`�����g�Q��x����6X��v�S����H�F���~<F��h��1��P��{A�Jv��M�o�]�:M�VE�`����('����2d�?(���������:�y$��]�u���vr ��Qf��D���������l7us������<895�0�~�z� ������������VS����E�j	~l2�u��@r�B��P@�F(�s
P���XW|$s�EP���a5,a�TO���h�f��V�xZ
q���<S�Gf�VQ��\�F�3�Z�HFZ�n�Q�����0|�-��z��`���uSxH$\��5�d5M`�4����NH��w�p��I':��r�(` #n�d�����,V4�1��%��;��1������*/���F#��+�����)/�#+AD+����I\9]E�P: k�c��l��R���Ll?�uO�U�FX���6�(�_�g��3������3(�il`g��%�[��<(F�����a�Ag�Ts�r^����I�VBH	v�Q�\9d���UK��a��z��vy���9�?���yc�/c�$�ZV��f-2c�]��������D����U�E�s/�<S�"�@��H�?6�r��@�(1��Xv��2���/�";2���n8,�fx����c�*
�t&U�-�6��D o8�/n5�%��L3�
%����DM���.�Cu�h�����F?h*�YLQ�b\~)f��z����v{�K�I0+�<=1����,nEG���3�����g��"c��"��26���c���+Fc�k��T�U������V����k�	����;�yV��Jj]xA�������@&U9D����v��2��*^����R:�sBnk	}M���t���1�8b��'0[$uc�|?���jO�S7�"�<g��yc#S���n �R�����_���yO�������������N�!5�3����h�.�l�zM��Q�u��zY����<~����FT���q���!LHd&����`��h/��D��/��0fAOl��:�T��8��`y�'��+��bU������SAX�u�SL_
�2�8"�L��>w�����c���'/���=>|������^Sa}�����t����}�u�y���t0������4x���Qt3?��+���Nx�"{����*z*�`��s'v��U/���:'R�8����8�r�H�(��nb
CNH$����p��x���t`�Y��z�!��R`'����^	����c���^��>14����}���FC�{�Z�?zi�:�%�����>�KT�D����Nd�x���� Hy�N��z�/�b��-7���}
=�7�6g�@*$��x�.s6�!��k��f����Q�a��7-!�#V���qf�$_����T���>o��}L�6�[]"it����D��c��������
H4$E*i����{s��#*Y')5lv�T�����������������46\��6���:7&[�Mo%}ZD���,]���e�mw��zvty�Vq�vu�Y�*Q,I�v����2��BY|k��.��Y���Y���,c�^nh��
��nNu�|�Y�|iU������a�%V��f��2�04�O������'np�b3
�e	.��a��G{�����"�
�t+�c�U��?�$���_��r����|��BIh�uxJKz���	�s��l�:��4Y�]�K"�6�8����c�����,�]��
���V
`���{�L&Hc�����R���4���5~��Mz����.Yq�:|d���S�p/��)_��6�&��������NQ$�&���r������D0�������J����2fR"'-��\���/T��4�e���6_�}C���p����:�EUiu���1W.���`��:_�����(2VjH���������N� (MvsXt���U������4��J��r�(Uz�8{��9�EQ���SNL������A8��C�����Vp����{����h����D���S�X	�}��4��Q2&�@�����*\n=Q��o�DZ�!�G����I�eqJQ�d������
���r��]��B|WX����a/��0�,J6��d������9_R�9��TY;
���(�o.U�c�+8�R���V������\�\e�k��]'������uv��	��3�8���d���G�������:o��}�������&�7�w<�����
L�|�R��������_��x����i��.EH�B��1������*|0��{��SK����O�Zi�e�]��,`�:R�"�
Wp�:���5�K�a(�:
.��PT�|���{x�V������}~� ��Mzq�c���:����]���f�*7��o��"����:1�O��S�l�o^�� ���W�I�����$�Y"q]8���v1�-#+Q��{������������	�;t�,�\W�?��U6�?����*,lm���W��H��	�Lq�w�����3f�2��>O�t�����%
�f�^���pb=(�<Cq����z<����	���X��������@"���no{�X[���Z��kK��9R�'���_�,ue�{��:�Q~�q����/{�����rO]��,q��}o��6��}`�������d��uw�e����8hG�|���4�TU�@��="#�����a�V~��$[��t4
=���H�e�X�J��9�:!�p@P�aQy[S����x�O/�c���i��Dd��Z�tST��i�cF���~��\`X2v�?:3\�d��)���
{�0R�?&/��em�SGF��TPe"��c�5JI�8��B���G^���g0P����8�y���3��g�y�3��7��/M��	h?������b$3E4���SW��Q�=��o�.D%h:�m����}��YR�����(��
���C!�U��(^�P�G�#��U?L�*BO�b3��-E�D.6�q}"
���X�!�N�T����1aD�����H+�)0�d�!�.d%.�1�����4Il������/��G#���C�/ C�:Q/�aF4o4��c��
w0:W�?T d�S�����p����|uq~��S3R5a���4	z�~����P���x����P�!��O�� ��r��'��R���I��oL�l�y$�����p��9u	y���F�V��^Qt���t����JW
/�Qx���>nP:�NW��U�X&L�LKs��~l��
�8)��Z���	k�&*i�)���.�Kp~�m+m���JH��i) 0�
le%�NFRi���TRP��l��V�(��t-�A6M.����u{�|SitBw����mD��L�R�".��L�M�����7^��GVd�^r
Q�Z)������~��A����I�#����%���3����=�Q�Zm�[�R�H�E;&v����@c��5��u����(��<��������e-������a�rw���@�4�P��"�#HZ�r\�������10A5�"qPY�\���xo
�}'��(o�qt3���N3�g(�OMoG=l�(�h�c�
]�!1n_�t��7=���l:�3�x�,Z\�n�����F��h���39�	�2�Z:vQmV���` ;�&gFI,����������MEL�Z����	9�M�l�Jx�"�y-���{�8���������a��k������s�(�WfL��f*���_Q����R�Mt#(b�v�c�Uh�Ul���pk���S�N�{l���r�3���L�U������=�;���\���g�FI�ye�������/�Z��Lh�J��wo��=�V��Wn_�wU]�-�������_�����m���i�*�+La�"{Gh m�����`�.���Z�vr��/�i�?��i��I����R���bJ����vQ��c���N�����i�R.���`W�OrKF��/y�V��GC����� �^�����0M�k~�F��
�<p����h��I;���$YPb�FL�IL��I� &��w��5�y[�6�C*���eG%����2xh�x�Y�4�3������-����CM��t�L
0m�$��q���(�Sn"��n����%U{-a�������K��9$��w��obJ��<���d{�%Sk����x��������y�MBN�����(�(�9�]K,���-�?Dr���.��>���1��C�W�f5�����)�m�Q�������@mX|���+$�(�P��G����0��,�P�ccAbd����eS1��I�2f��T�Y8�7}�3��*�����6������#�BL�i�O+����;K�]��x�����,��eFC�cw�9f?B�D�o����u�K�D��F7����Y3k���� w~i��3$o�_������=�bc-Ll$0�4�*DE��������\(�e�g2�hpq%�gS,�d�>������o�h[���n�����>s,Ie��PCNpY^s�j���=A�O�(�#������C�q�(�Uv8UZ��x�������l��So&4��z�/d
��9��KD�����,)m(��������8���d��!�tPS��.C�>Q�b1����Zp�h+(0�s+r�+�Xb�B���'��1Z��
`�H���$0�30nZ�rk�]���`$8�*����P��S6�%P�k�{h�eip5en���d���hY�FL�� a� L��UY�h{9�Q�I+jTl}� ���P����Y��C������0��"��?#�N(��x1�,L�K�0��)��t�Gj��2~��SY{���1�rH,���V*~�f�f��Ft10�g�
RZ�Q���e���������c�=W^."�
�H�d5�����M�rz�L��S7�Q���H���w���F)l��,;rG���7�������n�u7�3}����7�^U��������6��������	Rnxe_c�x\&-$��H�|�d���!�w)�-��$�A2M�����y�M��/.�%i�M,m�Gv�y����� ��~#�`��#�"^q�6�4a������b�{���3O�K����KJx�E+���Y	�-��T�v��O:�K��I�����+��Ao���y�E������K��&n�1{���l2�Ag
]Y5,+������;������E��7��y���U���k+1��J�����N��6�t����k�d �������l���DW���`_��9:S�����v�7Q�l�FD{E���Kk������~����|I����+.��ECKS:�����������(<a����ki�6�8�g{�fr�\�}.k��h�{�R����4�3��#�<`[��/,��|O���x����>g{����jO�\DE��a�I�b�uP����l�;	����G?�J��,�5���2R���1��������P�����jG��Hg)�`0R�r���n4�aR8���*�1�g��1���Q]��U/�->2j8��I��i ���*�j���McT�M7��1g:�5���.#��iH�l%�����9j{Zy:�������l0=���U��F��tA�{5��+�	H��$)��m�B��Rp_�����>�%����$��J��)'���B��'�Q����[�p<m��ja�V�;)i$���k����CQ?�5��	�R��I�N� �&R*��PH
~h��:��A����|��V9���r�X�f��]��#���Y��L������G��B�m_�o2�-��t�LX�\�rHL(Zo�-?�Tw��/"W���;,����f�c>t9%���	k�*\@�	[t��e��
NF(z����c�UG�'T����mF.�-Zx�d�%yj����	���6/��)R�3<��$�=3���!��2�+;>���A�)��|���+�N�s��5(����:�O��s������,��Se��E�NuC�m�������hy�&�S�"����M#�e~a��_���Mj�U����4����0���2&���W������k;����r1LL��w���L�����.';���tN�����kv��p8�YL��eE�\�Fq��Z�� �"����ZM`U������3X`�gl��MQ3X6���4���E��V�9��TT��� OK&U�X��9���Wp�w��k�us�>Y�^4�a�`���|����df4f�^�p����
:�_�,[���v)~,c�E�5����+�m/�R�q9���^��ka��M�2w^��c�y��g�2���?���M9B�*P�(��:&��trN�8��
���"���S1�bB�~_@x�U��������u|�~&T���<�c�q������8����7V�"��:Y�"�i��?f����:e�-3O?���[����e�A�ap6�I�j�W�K�����hIpi&�����p�h2&* �����#��8���X��umG���ZD�����Ms�����e��A�*�s~�����~'�~������*�7�MKd`�h��'"~8``~4>bJ�]U3"����jA�b"������'��g=��$
e%Y�������R��kx{����/��x�R eVJ���"7-�N�@�[(�d��5'��?T�>1s�D���$-Nu�P�r�ns ��2jV����@Jd�F������f��l�	[����-�"Y���]�
O<MNA�����q���-��v����(m���?�/��n,1��d�U�HH�w#yU�5C9���������)���y�G[�?����I^����ZSX��~�������i0���+�"y������1������s�Q�	�	W����M�eE�y��+�����������n�
��n�J�x�Z�#_�K�#���@o =����5��e�y��i��3�bk����y6h�aO��a*�5w���c���!����,��?�C]���'F������Lz
�!��}5Ib��'��r��%+����u���f�1���z�M�y%3�d���Q���Q�$?'M&�EFV�f��V�D�b���]d������X1�[�!n�B�H��?���V"Hrh��	���m�}���	2������O�w��Xa��zaXc�����1��D|a
Gx��m��Ps]��0Y������7����+A�`���������u�K]-]�fG��_�Z��)���s>�v��V��lE�rq�Nh��q����b���N d�t	������;����
�`��%����P�2�uS�k�Eo����)�����7�-��Y�Z?���!E�=?H��=��]���:�����^�:�`����0E��Lm�*���4YA����pvE��a2��R�y2&u���U�v�(��X��3�l���8K8H��l��(w���|�i[x�Rc����������pR���"6y{�������{����;��J�T��i���1s^B�+������~���J��bJm��+6+�Sl��<���]���~��@v��"+�@��P��=P h��1���,��KK����<3���<G&�����Dj�l��w%r�?�6p�t1S#�x�yR�&]��).XM�V3���t�j&]���������L_��TmSu_U���V��,���>]J�qoIU��wW3��K&�+qP����k����(�}�J�w��6�y���
�ZM?��j��5�K5��)��)K���?��:����^���Mo��m9Q�V�����X�YNz���r��Ct�����E�0=�����K=�t�g�(QK�VF���dEk�[4��'�2����e����u���B����*�Z�.����������Q�!E�,������b�4skN�w]S��J���F��Op\��%��:�*�/�Q������N�h�u�/�~����
d�E�u��.��Y_�(y��t!O�"g��g��&�����[����M�y���hJ����+\��NW��J]�Sc�)nr�{�:�x��F:�}�-E�1S�����a���8v�J(�������:��`�O8{�;���i��dNg���������5='�j��T������#N3o��@�'��`F��}��_|����8�P���#6`�1�������m0�B���b�`��:0�D��M��=���=�?D��c��?�	�@QZ^&�Di\������B@��{�["=������G��*���\+��V�����Mx���E�e���y�����B-�aw�	?���������i���#,5��?��qG�\G���z��@����&I��hEiu���M��6�\��\��]�5��^J�����?�GA��Oc����FV����"���3qz��h���E�8��Bt���/���	��aH1��.o=�E3x�������������������P�}��[Q��*��.WX��Lz���*q#t�ub���W��v���IE,�[��]�2=K��G������S�����l�3C�CG�A8#O�R���Qg����8���Rz�
����s����Pq[�2���8HpI��i_�/�:���k�>f��3$b�Y�����E.���?�(^�j9��qe�$-�=��1��#��C�C�w�]����M���F����u�l�bv�y<�g��x�T��
1ni���������]og[4�[;���-�bb�Bb�]n^���w����d����6�� �nN���~��:��E��:^��,���.j��t����%�#�����BUF�[�}�8w�����Ot�S�P,��)��I���j".J//
��h�D��%B��* ��������a'�j�T��B�������vvp-�w����kI�� =�"1����uL4�CIn\B�3j���j����?P�m��^�xn_�K�|Tk]���40����-n�2n��n#�M��A����?��2)(B�������[��	��8����M{Y�)�%�R���.a��H��zO��n����0��G��8��m����B�fE�
O)�E��nF���������tO��F�7�U>ges;-������@�%��H��D>]kP���We5���|KO�bWpY��O��|���c�G�8���p���
@3�_kJ�����(%����-Z��M���H�Q�w`���E�V�31�By��p&�*��-�����S���oe�Q/]
u�{���vi���x��wm&������'�bZl?���������D'.xcM����d�A���oS4��"�PZ�	��!,{���`�� �k�'s=����������t,��fJ�g���)�[��w�d��_�3�N�{]�;q�G;
�O��+��kZ�Ap�q��w��Z��n��e�R&����:�U:�o�I��B�z���.���l�kX���3��9�U�!Q�P��?��f����K�~-$�d87�?�I���4��Nm���x���~S)�sPf�l~��������J��;P��ea[�i	:�$���3���c����<��R9
g	������}(�KA&��4�t��wCPy~�5�1���SI�!V.��^dh3������9h��}�0y4a�=~���O��� �V�qz�)=om�����b�E�C'��P��O��(�i�����`������Z����o��HB���������gG��R���(�%��so�+i2������a���5��t'����
�)�y+��z5���*�S"�21�;2pa��J�O��)��W�<n!�:Nd{N��W
�~w���o�MX}��'�]�}
7�h�M����s��K��K8��:�a�}7*�fo�B��Z�T��������A
��W1�gx���D����5��l����w0�e2,s�k����4����Q=3���7c�R[��M����qza��G�2�kT��I��������}��������Zb�t-�
�x��Cc;��0��*+1�*!rjm���k�\(�-����u���6��E�L+W���L�<W��GN;W�GEN9W'Z�5��R��
k+JGc[*#��r�8|�#��~|����	�nkQ4_�#��.=vTSR'{L���Z��I-��`�V3�^S#�Go^q+�����"�C�Y��Nr��p[;�����U^%�rO$������Q\.���?���^������(�����6��28�cr��������\NGs�����b���bm����N���������O�zJ��x����������������~\?C��u+w� ��x�����n{t:�"u����W;i �im'*QYi��i�H[�`&�S�e�k���M�������dt��cL32��$c0�jo��jT@�7}�x��KQ�f�Ige������0�=x�Q�o����($�������~�=����9t��+=����]���Y7>�����u���K�|�����������������r���PB�c����M�l��*�����f����A�2�~#�*s8JQ�g����^�q%�2���,uSfsy�LV��Y�K���bg�n��}(d���G�y|�e�RB'$X\����nb��L�Zv���N/Lp���U/aXY_d����UL�k�4Kv���-�ioq�sYmF��?�]�����
�n�^�ld��v���E��EQ��l�ptP���:(U����(�Y������j:1<[����bdQ[�
���nC�+re<�nW��w���O�^x�m�#��Lsm����a\��%�n�6R�K�k��vw[���T�6�Y������Mr�ohy����^r�Tb�=3�8U�����^O�q�����R���6�HG���["G��WT�j9�>��[���u���7�z�*������u�%mIq��jK��K����R�+�BU�W�_��+����� +"�Jx�2������x��OK%���/�6��SL(���tL{0�P�H��*ZTE�
�H�j@V��5�y���u��� |��>O���{��_SI.�t r��=������A��\�:�f,�twZ1��nF����Zn?�Z�)+�1�ERP��bY`R�R�
)q���&�fv6I��A�)����1w)�������2��E��Q��������Vu��W�Y�Z��eh5G0��P��HT�*e,�t�#f
��;pJ3�-8E�La@�����u���ayu�N�+#��7o�����R��gSV�W�
��#5v�J���$�q�dDj�@B� =�F�������B����}\GJ~�D�:��LU�b��?��7}q#j���"}�/�L+GdU;�����D�T����8�
�s�~'�J����f�3��*��b������C�s�������u�<�[��e��{%��)a\���d�YI��1�
��Z��j�J�F5+������}���FU�.g�9�"E���/�c��T��@T��;9uvLO�C��n�	���^��fD����S=��%T1D��O_�5,r�s�XA�@����|�#"���Go~�=L����$��+}��-;\�U�|��{��b!MT���bzc�E�t(��7c�h���(�k�����g>0����?��`9S3hFUa�D��N����Z�(�%	��$/�m��#��8���N�vQ�H�,�
;�5�^���lc
�;�u��+������b��VG-`�X$1<�E���S���4m�Z�\��nF*��Z#J�v!�
o��������F���m�u�9�f��S�����F�~��C6�P��V<1=mK/>���2�r�vbu����X��0M?t;"&�C�c�����`���E1��E�(*�	����M���S����Bus8��fuVmvvC���%�����[m9�B,L<�u�oM��)r����.�����[o�w�1�\�s+�cM����A��|Wm�
�` �5��_+q�G.��j'�U���	�p�&��R(U�~aE=$y�;t�9�E��K@&��x��Q�,���8v�40�p.��Q���5(��/������[>��-�%U�Yt�.bwY�����P�p'��`�DJ��f�|D:B+,�v������z,w�c���$�vA��K��tt����3���2|,�g+�]lab���,��&����:�#k�
���[�c���
I�_��r��9\ZI���R��\��]<�x5�x�Ok+���0��V*�A�K��|'��������I_�5ZJ�'���i_��D���������8��EK�����oF���XC`@�S��hx��=*2��Q�\�S����0x_�NYyW��uq"A�K�5W��\��k�,&{��-e�6J��w���;��b�LSN4c���4��mf��5W�	J%�0�!�@���
�%��Q����R����#��	�=R��|�l(v��)��_�.a�u3���K�1���XT\����[u�m�i[�['�3����^��9"7���1>�GG���'��C������f�px)�����J������M���x�{�H�x���Y����'p��nln=����y*P�v��Q�9r+1�S�K�r������JZ'5�2,��M����+�/&4��`�Q��xs
v���/�y����_�����W�X��������I0��������7_��:�����?��j���B{_�g�E�JET�/�)M1��\"�Zu�7'z�����68u�J�@�K�������Xa���3�������"f�m�V���D�,!a��u<��Z#f���_�<D�Q�m�D��������Jm�R[W�jLfy�}�FSP��j�f��uAce��cb�%9��G������P���r�x���d�2�F�%�%��H���n��n��(w/�V!�!�����rL�	�Y���#�1�K�)>��[��md��*E���>���XS��1�%�rpz�BIC�G������b}.Y'e���.;E��fb��?P��G�%h�������������V4����G9Y�K�I�@�/*�pR�+���O0�}����K�h�9���'1�]���L^��;���]��������2l��A�iV>+�X��]�j��ppz�@�'qF�X9[_���nVj�cM�O�h�����������O�?��Q����!���� m|A����>y�����i��)�%���"�n>mQ��2�0���t5��D���Xd'��3���������n��/Hp�����&%��hU$���$��#b�'��?ArR��>�0gXt�H+�����x@�~>���S��|^q�_������2~�As\�=eH����������y�;��c'��T"��J4���4��eYi.����
RcD����.+
),���kXD6�M3k�eS�U�B��|K����e@K�����Z���i�{��.=1���Z�0�V����g���M��h;�Hb)t��6K���$;tn�`����RF��Y��|�!������U������F`��5���u5������s������<�	���M�t���uo�F]�9���k#X$��!Hp\bZ[[wvv��9�,��oss��������l6��^pqYs��h��w��������v�qk�����������(>����gX��������+����'���tpr:�n]�����lo����.��L���~.~�;�<����	E\�i:>�hh�������[�7���43���+�'�0��a#"��2�,��?#-�%V���	��*}O�����=~z����q��;w��m��H�������1&��O�d�C���/2k��v�
qh��x�����������]]��,�2z�;��c8�7L��������S�8c���*X Q!�[������pt��T
�R����z�!����W���7��� �T�1����f��0��I�unp��R��������<�������DO>��Q�k2�q��'3P_^V�w�0Q�3�N������o^<��/^�P?��#��{����wo��w���]����}�n�n�n���������y�����=~���`y��������K���.�P+��������|a�-�^�������zy�['j����g�<;�|e��!��R�}�Pv#:�F�\�P��v����VG*���������z�����l
��_���[0�����������@+~?�g4�����%����,�U��@�
��������d3R8�
�'���9�U9�Y���<�{r���������e,�B�����Ke�:��,k��~��'���(����;���z#el�oi��$ �J/'�bu%��j���*I:E9Z�����k�{��N��Kr����%:2C�M�JFB�F��r����X
�,���
�w�hI�9C��K��"��%�U��f|D0�R0��?���~�C��������J�J���1�R��8�wU����<}J������U�7�������1e �����,�����Q����J�Z~���*`9�)2��{t�z���2<���km,r�:H�:8�������3��'n��p�l�|T|��Q��H<��@
��*M[UCa	{�Q8Dc��6����K{[`iq��{��kU��&�"�(4�(�l�1��o��t�f���6i�����Q��ED0����h��Q�{>�0xMELE�~����hJm��_�7��A���5��v�>v<����f�Q�Nd�a�=E���)��L��B��3F���_^?{�3�����c���mw��X���r���^�9��@Cu��[�~|����>;>��cx=B#����"���u��M���8�q����:�GN�ngv�Fr��m�J�&�����N1r2qdB%ZK:��b�z���N��M������a�'�c ��a��i�a�mX�F���$����0�B�h$���x���$:xj�sQ"���/d���	�?��.��*�W�r*�
O��~}��H.����q/���
S�b�Vy�b��-��h��Z��:��t�?>o*t�32Iaev1��pI��
���*21"��	h�}����_�V�����Q�MjP���I�&
|�)5��^g6�����l��hx>�%\LS/�]h�
O�����S�D�I��F�j��iP\�i8�I�rc��3:��;������Q��f6��g>���(�k�v����c��D��=����`d����J�\��;�`��N#�tE��n�
���C�=,J�|`�����!���Bk����������1�$��f���X�p�CC�;@=Nk�����4�4B�!����8�T��"��K�U��.�@�D����I�	l���F����q�G����9���^]w6+���t�=�>?��M��<#�������[����ln���vwK�5����'q�2x�}oE�����%�	������W;�������^o�����f�Ae-����������j�m�����zi��	R�&��w������O�����_��P�|��i�������|D��.��?�����Q������FDe��$�H�(jdw<<?Q��n����	F������-��@��xJ'�b����o=��
�|nE��%�8�F��	�� d�e2����08�m�x���x�S��u��lo��^�g�6F/��}f���w�W�
�m$�����L�����p�]�p���� �������~����e������x}pxp$�>;��(�Pd��t<,TN=��1�f��L�Ip��9�m'��V��Y����gjn��������t6Q�Okt���^�����������'!
���@���n6��n�\2D�����L6�����������S��F���#���]C��Z���V�/���Z���C��z����L�H�:sF�,��@��m��(A"X!�+t~�g�R����"�����dr��(����������6�{����4�������������n �g�D6THF����H��QF����{<���C
%z}�������'0���������|�d�z�`�4k���$���PY������k�U�_U�
�+���>�������
�(>��?��|�{���7�i��Y74���j���XzK(_Hy���lG��3]����-���w�;E|�b�
���{�\��8<x~��H�C�W���I�i��6��������)����\��}_�&Aj��]�n��F�����������)�U}TUu+���gV[��U;��~��;h����d����_|������r[�������������M�!���"���~��W�����Y:��Dy!�����9�M���B�B*�YO='��v��:i/�:��WW@��Z@���X��8�6��d�N}��A:�:�&F��8�Z�%����1�����c�Y��]��*���\(L�<Hrs���i~�e����g��Y7�*��R#��~I�U��Z|%��g��P>�*uM4���:���zQH?*H�EA��^���M<>-c9~^���� �R����.�cI�����|������Q������y\�>>x���)��������#������&>:5X�Y��'"�m_�����������>��oG�!~|����������Ww�{������������\�Aze����������9�����%��V��M�#<Z�J��/����b�W�`�B�E��'�}T�!Z�eg��5@���[��;hB�����M�!6�IM�`E|�@e*2o}?��'T}OP}�Z�^������o/�����kSN����Bko���QY�i�]u?zCl���'^,���1�G��JA������-���;�p�
t-�uL�������6���a������)�7Xi0N �/sN�#��.�D��Wm�	�.H6,�X?�Qc1Zk>��S��l�xi��=�X�h�7n�Q�i6dx�7���Y���e{�`�k�`�T�����s�@3��������
d-a�h����:��R��&J��II�,��+��Z������5��)-��rM�k�V*����;���i����d�xa��h��Q���&��Q�A������������rO��J�������-������l�����XB����C�R�r��^���M���/Vg���K��[*,�h������h���+�������]rY��^Z�w�i{i��K��Z�-�#Lq��b��Pn��2���n�)�*SxK����RH�\S7�����1usiL�\S7�1u�S7�0u�S7�0u�S'��m�w�6L�������Fsys����Aa�J�5L��KM�f�9�,5	��f��
����c{����[���q��W��
�e
wK��G��'6?9�������CN�fY��e1j�Fi'��|���D�����qu{o�?�LU����m�Yog�z��h���������(��7Jq�����,��(;n��7JO���������b���"%*�3n�;n$f��%i9����?Z���hY
��e������?*��?*��?*��?*��?*��?*���)�Q���?Z���hiJ���������?*��?*��?*��?*��?*��?*��'���f=�5����/��=�-�l��<�:s�D
�\�Zs�k�Yo{�z�ym��isnTw��x��XVOI�����}.^�����8
�MLr�������oB��q�5^ ����lG���D^����g��)��%(�%6	���5+Y*�>���p��Q��]}�--�ce�S�}+��V��f�g�-�x���Y&�W�8:�2�>��$��,T�f��%w�n���X�4/��_��Th��B�.Zo�����t�0��K��*�	#���;���7Wt�=g���'
��7�l��V�B���(Kr6�&
�ubsf�N"��[���9��[����i�ZDe���U�]�\�`�����E6�b�,�eI�fsP�!���Im��[4�k�^��,�$���J��y-�?V�UN6�wN��������v�h��*��g�t�����}�v�D#W���5C�`�q���we�-�<9I�0��6v��o��	j�
�V��;�����(s�����	��0$Hm���oW�����k����_�����_w�d��K��Jx�������.�.h���$��k-4p���_��'�:_0��x3���S&�|��Ov�����GSm�8?0�����g^6���A��rk�����	)��,��<|�FZ��j�n����]qi-�AN��7:��+�R�����E3������l�����"�����n�3/��U"�2�o�4Q:]�������\�������5�yfh>s�L_���>���L�|���|�O���6?e/��3�����s��j�������F�Yl��]����t�g1����bp���h>�#�2�9��0/����|0��1�7�I���qu�I���[��k�DeV��W��b�'�����[�?�^�n�-.�wx��O)���l�C&��������{�D��ztR(+(���y"��%)U����4��.�'S�f��Pi������o�=��=�������Q�4rD���"|0�p�����s�b�&��k~N#5#�����zPF��I>C�����>�g���]PO��0��bE�L�������V�$��9��O���Fv�-�	�jc��|�����
ci#�3���%_�1�2z�5�����HY �:���������wvvZ����q�#�F����6�YW+#%�*Bw4}����������yV����81c��>�~V����~*�R�j���3��fY�g�m��Y�������?���R�%������W[Y��`n��G���Tn��o����z���fN��mf����:�>V"��F��5����9~��l��`#oyc���F�(62���=��������1���hf�D3{$�9#���f�H43F��5���hf�D3o$ZA�������RQ��>�^���^�/�U�d��+�J�����8���a�W~�D�V��W��B�e�#W�v/R#~��5�$!�gJQ�X��&�]�5�3�xfv�_J����L��uc3o�RJ;G�_|�R��F�]41b�b�������K)����)#�/5b)����������~��c��%�����W'9��� �
�K2�Be�Z��HI.SE�x5�p���L/�+:����A7����a:��2}�L�Tm��������},����}�DJ����N*|����Qz3Xl�*/�dt����ot������������r��y���X>������j�����
�s$R��l�������U�k������������h�~k�C/\mg�j�����������Z�C��7���k9�s��>���]�SL���}�beL�����W;�N�>T{�H�������R�����F,�����V{{�w(�S��wq��o����;�o%���b��tF�G��d�[�e)�����)�wre������J��������M'���=;�xje6��F�=��]�����R"F!��&g��l�s����c���A����a ��,�B1;
�C\!���[����|Ai��������4�����>��(�����i(>
f�����2�'Sh�;�g���q8;����aS�M���0B��3�M��`x��d��8n���Sa4�D���DS����0���P��`�_CO���SL�9����?������ D��N}`&P��`x)����`�<�������|
]�no
�����!��BM"�.
&�����:G�T�p<��
�� ���aa�S���4�G!A8��C��dD|�bl�w���e` 6tl0��b6&f��F�eqb[���cr4�cB���S`����T�7Le�W����O���0��8��������p��v+��sB����f�����y��D��/����v�*�xsq�j�.������G|����^�� �OC(\}68"V��v��n2��4�o������s�4�'��t��m4G���;���fQ��z{���G���~w:]���O����>�c�����0+={a��T�D	8�7���p�C�M � ��0� I�`�/�����J��;<��j@�>�E!'Oa�@}�!��h��	����\�l�\	�=���N����i<���'������qc4e4����Xr�d�=�TS��y$|�S/��P[_����;���'���Kl

�H~*�b�Y{��|0�qp������I��'(zM��$HH��
����1wb?�	�I>>�MP��H����t�\sF�G^6�\���B��u{7Of����[%��X��0@�q����gg~�)�0]QB��"Y$`���yH����s?��(���9|�Z�b��t��"��cmY
0!����)�qC�4eJ}q�u�L0��
,��
�kI��#�-p���$��Iz�s�k����Q��1A�c��z��@�����K��Xv�PW���>A�56��IE9.��xd4���$���O�3��*����>�M��3����$8t{8	��b�����/��� 
&��
���`��AQ��� Ht �P	f� �J��s	��������@��d������,��!S�q��^=�w�����Z5����X�c��r����B~������/���K�g��C��D~���L�T�� /A�b���Y{�&��d��Z��Is��	s��;�O~<���D��>������F9����w�����Y�0C��X��(B����6���E����[k8���P�V?`����{���FF�,=?�<��%��80�Y �-,�.�|T)�A"G�����EfL�
��R&�c4�W���n�V.��3$-J���?�l#�����xBX?"��Xk��?�
���-~u
���f�����i��
��f��T��r6s�f5�+�`���8���r�Rv<
d��'f�T�6���t���o�G��p�
�����`xs&d�	��p�����2�`
�0�P�����d��@~�I�Q������J4�'3��Z���6 �p���k2o88
��]�BXC-��j\"q#3c<"=�u�����V;���Sz-�E�`:>@���'+�m�Vx2�M���\~�m�i��D������
b����M�RCpJ)6�RC�XQgM���a-~���"��"��r>�>cS<��&@�����-H�0�#Lz��A`��p����i0��R�e��"6d�'��O��I]��
��`2c�:�?�gu����V��������ZB
��N��?���ReMs�	W��V%���Z�9��:��E
7�8E�?��@���s��#n/yy`.��O-S��Ono`��g�������ek|<L�����$_�VT����\fi3C�cNV	Xv��������['�w�������'Aw�tv6�����~����GoQ�}��5�1y�@j�:�(B�-��'4|[
����\q��"��}cM�BBV����#4z����t��������-\O���C����4��,�M/U�+���
�/O�#n^v��1������NDm����m�	
�D�6�lnG��W ��o�k�������e��8�({���u��N�����������+��QM��j���{���;���h��wz�>�����i�z��w�HK����n
c����w���w����z�����3�W���.���+hO���������i�w����R`���z��]�A��7B���r���h���m�� &�����6�����<G���d�O������/��W� ��`j�h|�zD��pG�� `��h���%���E>�����B�w�=r�Ln��X���f.��
���}u�A��Lw,@p�5��-�R�ix|H7%���-�C�T���`%��nj?�R8�@5o"vvb����%(�}�|�_���n*G6��X���� \Ua��h�	fI���i@��I���Z�@��&T������c`{��wW�6�nD��_�w�o��b�ESSM ��{��r�������.R?t0�d�'s{���u�ej�4�{��C<��?���!c�}x������D�A��+?���t0�cg	izIy�:���K�Tk�����n�F�Vz�:��eV����|��6/*�O�v�����-�����w���Pb}�m�iUO������gt
�/���"�����-�t���E�b�YttQ ��x�x��)��c!>�-N�F
I���{�����l9H�@���T�v)z�oy���_�������*�J��9v��l	����M��������.�����8������8��%H��B?*��Z���r�<��ghU�4C�EG��aG7���;%������� _�I�B|=/����\�����b!J�i����'����e.~�h	+_t�t���z���y1�y�J.��%x�a����e1��,��4�WT;���}������5�wH�|�/ ��i?�8q���u+h~��L��(��j(��]�w�2?��`�/Mq�V�����o��n�&��732�"��9��o�����C��j�����������E���*��w�U�2;;����C��UC�'?B��E1�;,�t��H�]��$���;�~�w|��������]��{��t���n�����W�\�.>6W��>��s`�<&���F%���J�Mq�O��p��wq�w�{��]�w�����G���J��w��+|�=���K��A�}�����;�^kk�e�g-�1���aB$���~Tp����R&)>�4�G���\m1�����(��g�{Tf�\��TVD/F3�F ��d o�z%un{�r	���2��M�l���J�y���A3��v_Xx�{���������Sf�[|�/a~���]*���
��j:0�?������i�Fa�gb���������tV�������A�4��&��r%�cQ����Ww��mI��=���RM�,;��M}M��$���Pm+�EU��8�����c�%��HJ����!)r��������_����S~j�;��"~TZ`WE��'����������O�`x���#-�1(,[��mE���/T��8��XM��d[��-�����(5�������#�e2�$S�������%���A�K����������I���'W�X�qp�n���������+s���D
���w�����2��&�Q��^��b��	}��@�^C(����zG�P�����6RL�Kx��Sc	e�'qj?/;7�F2��S���g,�|9W*�����Q�u��w�0�O@�]v�Z��tlCh]�d���_����,��y^U]�T��~���P�dI`�d���J�`q<!P����O�4���NZ�_���G�
h��/�q��kN����I�Z1��Z�>�y���=Q8�����e�%�}��6�R>�!���X��\�������o�2Db�j[\�(gCBXS�(6��6�>CP����\�W�%v��A�hf���)D�;U���U� �Bs>9�|��&�<��rh���9(��(��5o�O��u���K���>�hoYB���aAS�=s���@u���<������d��L�Q� �o��zt�)�������9�����O@\��$�]{�����������%r����
K|!�a�4�Q�|���3C��)~{�����XN��n��EJ���1�l�!��-p$��E���������L)���C�$L�/�w���f������?��\��{�-N(��Jw
���5J��E�EU���X��k6��v�Uo�����v;��uY���n}8<(�]g��jw-;3�-����Z����n��5����I�q������kw3�H���1(BS�0:��.���I��������
�	.�����r�S!*�v���.��u�B�T��� {W�������#�Ea
�4��<aSv6[	G�����m��R�k��BE>p
9��py���X6S����-���<����UI�
Q3��^�F2W�> Y�J��M7"����F�n�q����U(���E�QQ��(�.%����S�6�����/m�J1}�< �Z�Z��h9�K����eQ��.�l��.Do�Y�_Dg�����(8�0�{�|a=S8W5P.�<���������
B��~���� �*��\�x��x�wi��H3����6�S�kg��DV�����9^��&��|�{�H�
��Z)L,�������ZU���NVb�6���A�����;T��\g��8'���2}E����}
��Z�
���)Wl8��N�p�pr�gW�SK�1�-�����/3��q"h�iG�8��Em�?�{����+��g�p�:R*���M���/�o�W�cx/>�.Ca9�=v����N���4�jboT 5�EN�	M����Qj8S�lH������Vs�|���Aq�r��@�7���,����p�;�����`EZ�����G*,��_��It�&E\|f�B�c�;\��<��U�C���z�tA��u����7!�(Gc���%�qtx��2q>I.Y:��5b-*�*�9�l:v����V2�����Z�/�2^�pd��c(��v~�:��q�����*UUl��(�/��iOTU���]H�+r��s���m��@
_7�;s%�=!R'B*v�����o�-�kD�b����R#<joY)w��Fx<����4��@-<^++L�p�����e���{=���n;��q�t��|�sS��:.�B\���Sz�p[2����Y��?[N�%�C��E���E�gI4X�
;�"�B{Xfa�^oCW�#��/�V���gw����'��I~��N���(��'-��k�����jA������R����;�D:���L�h���4|��;����M�U�����4g�KDC�����')����COTXu����	!��HC�I��$�[R���Q(kT�s��S�z�
����U�c{SU\���&��8���J��'/C�1�l4��*���FHjsd���R2��|WyS|W
�Qy�������������f����X����@A���O�%!�p����]�5��"7�����-}6���a�C���R�V%�A�4���n�z��Kg)�
��H�2�b����ME�"�-G�`n�]��i�G;^r�7�Qv���+��f
y��!�� ���b>"�PI� �����n�;�4���(�����/��_�z��:������C�m�ot�8��%6x��d6K�#��so���JA�
�d��7��].�e���0k����l)��5�����.�=(����o�������p�!2\�-h33�j��$%L5x��*�P��7��S=����#�%[������/��0V&�����k�?�l,�������x}������t�E�9���8L�������~!�o��~���7�5� �m���-�3����t���L���W�h���0h��j4�=*�gS���4CIa��|O�;9x���?����'GD�*�,�G3�}+A#�����)KcN���A��{�������+�u������f�bwZ���,�������M�5����+��d�R%�i���H*N��9l�ak�M���G�q��HTU�u����+�3��[y��H��a�epK)p8l)��o�c�E|���0B��=��Z��=
���	bR����'$Bt�v�{^���-M�\��Uy�����:��^/��8�^����e����QA�b���#Yi=�k��z\��c�G�e*����)
%�
2�p:��g^�~�����tu��~G)6v`���U
�h��i*���{/N�F5�����s�"���G�u�/�^���.
�m����s�y��	W����C�����=��Z:_�d����C�s9��!��� 4�����Y�j���l|3�f�������	(�����z(��c0'�7-��{��(Wo�ZI�nNHCO+P�>"�eH��>����l�1zE@B��|�!��zP��N�F����h�	r����0�]^�I�*������n)��"���3��$(J�r�RE'������I��3���W�c�v��u���dzu�.�k�+�A�a��B�)�l���1}��hF�\l�rf!#������h���iL��7cCE���~���|�A�&������w�5"����Q�TW�h1T��������j'�a1T���{�}[~����<T]}�}�&�!�j+f>$����
����J��Dl��7���,��/ZR]������	I�)��������B��B5%���y�=`�v�	�[Kx�[��.>��T����g��\�9����e�r`5�[/�����&��������(�e{���E�;ks�<�P�SI�bO������Hv��|#P��x~�k�<k{�^���+����_ku�(��o��o�r5]��@��)y�|�������?����2�Z�%��*f����V�l����Z�u n�8��������	�S\�NN���5K�'���z���E�����
�������{�?^���Pac�����^P�Y�e�-SvCZ�����[���5����G:u����:�����}-"�����P>"{�9�P�*p��<��c�I'Tk:���R���v:2q������E�>�
ho�����w���74��y���gr�lH��l:��OPl��h��99���0�\�c���O�-.����\�=L����+UE�Y���j��y#7�<^��6�)PG��$����D�.����T�-n�"�i�u�WD��+��5���p`��U)d��$�8�#u#���fC�k��`���H�����O�gM��R(K�c4���N�/qH�9��$7�|�ZFz0��zT�%�)��9��'t��v(����;8���3�a��h]H5_|����V���V��PvzK�LP1d��w-�K�nD+�����y\�v&Je���e)pe��%��iu+G��;a�?��Q[����d5�����U������*E'����v�����j!�`7}����������h�@L}����9!Got��|�>bhF��$����������,q$�]���`:�^�wa�'��/w�2X�[�d�W���LC:
#36Erik Rijkers
er@xs4all.nl
In reply to: Andrew Dunstan (#35)
Re: jsonb and nested hstore - small docpatch

On Thu, January 30, 2014 20:07, Andrew Dunstan wrote:

Updated patches for both pieces. Included is some tidying done by

[ nested-hstore-9.patch.gz ]

Here is a small doc-patch to Table F-6. hstore Operators

It corrects its booleans in the 'Result' column ( t and f instead of true and false ).

Thanks,

Erik Rijkers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Erik Rijkers
er@xs4all.nl
In reply to: Erik Rijkers (#36)
1 attachment(s)
Re: jsonb and nested hstore - small docpatch

On Thu, January 30, 2014 23:15, Erik Rijkers wrote:

On Thu, January 30, 2014 20:07, Andrew Dunstan wrote:

Updated patches for both pieces. Included is some tidying done by

[ nested-hstore-9.patch.gz ]

Here is a small doc-patch to Table F-6. hstore Operators

It corrects its booleans in the 'Result' column ( t and f instead of true and false ).

I mean, here it is...

Attachments:

hstore-20140130.sgml.difftext/x-patch; name=hstore-20140130.sgml.diffDownload
--- doc/src/sgml/hstore.sgml.orig	2014-01-30 22:39:52.970474354 +0100
+++ doc/src/sgml/hstore.sgml	2014-01-30 22:57:27.630698633 +0100
@@ -286,7 +286,7 @@
       <entry><type>boolean</></entry>
       <entry>get boolean value for key (<literal>NULL</> if not boolean or not present)</entry>
       <entry><literal>'a =&gt; 42.0, b =&gt; true'::hstore ?&gt; 'b'</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -294,7 +294,7 @@
       <entry><type>boolean</></entry>
       <entry>get boolean value for array index (<literal>NULL</> if not boolean or not present)</entry>
       <entry><literal>'[false,null,44]'::hstore ?&gt; 0</literal></entry>
-      <entry><literal>false</literal></entry>
+      <entry><literal>f</literal></entry>
      </row>
 
      <row>
@@ -318,7 +318,7 @@
       <entry><type>boolean</></entry>
       <entry>get boolean value for key path (<literal>NULL</> if not boolean or not present)</entry>
       <entry><literal>'foo =&gt; {bar =&gt; true}'::hstore #?&gt; '{foo,bar}'</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -366,7 +366,7 @@
       <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain key?</entry>
       <entry><literal>'a=&gt;1'::hstore ? 'a'</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -374,7 +374,7 @@
       <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain array index?</entry>
       <entry><literal>'[a,b,c]'::hstore ? 2</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -382,7 +382,7 @@
       <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain key path?</entry>
       <entry><literal>'[1, 2, {foo=&gt;hi}]'::hstore #? '{2,foo}'</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -390,7 +390,7 @@
       <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain all specified keys?</entry>
       <entry><literal>'a=&gt;1,b=&gt;2'::hstore ?&amp; ARRAY['a','b']</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -398,7 +398,7 @@
       <entry><type>boolean</></entry>
       <entry>does <type>hstore</> contain any of the specified keys?</entry>
       <entry><literal>'a=&gt;1,b=&gt;2'::hstore ?| ARRAY['b','c']</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -406,7 +406,7 @@
       <entry><type>boolean</></entry>
       <entry>does left operand contain right?</entry>
       <entry><literal>'a=&gt;b, b=&gt;1, c=&gt;NULL'::hstore @&gt; 'b=&gt;1'</literal></entry>
-      <entry><literal>true</literal></entry>
+      <entry><literal>t</literal></entry>
      </row>
 
      <row>
@@ -414,7 +414,7 @@
       <entry><type>boolean</></entry>
       <entry>is left operand contained in right?</entry>
       <entry><literal>'a=&gt;c'::hstore &lt;@ 'a=&gt;b, b=&gt;1, c=&gt;NULL'</literal></entry>
-      <entry><literal>false</literal></entry>
+      <entry><literal>f</literal></entry>
      </row>
 
      <row>
#38Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#35)
Re: jsonb and nested hstore

On Thu, Jan 30, 2014 at 1:07 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/29/2014 04:56 PM, Andrew Dunstan wrote:

On 01/29/2014 01:03 PM, Andrew Dunstan wrote:

On 01/27/2014 10:43 PM, Andrew Dunstan wrote:

On 01/26/2014 05:42 PM, Andrew Dunstan wrote:

Here is the latest set of patches for nested hstore and jsonb.

Because it's so large I've broken this into two patches and compressed
them. The jsonb patch should work standalone. The nested hstore patch
depends on it.

All the jsonb functions now use the jsonb API - there is no more
turning jsonb into text and reparsing it.

At this stage I'm going to be starting cleanup on the jsonb code
(indentation, error messages, comments etc.) as well get getting up some
jsonb docs.

Here is an update of the jsonb part of this. Charges:

* there is now documentation for jsonb
* most uses of elog() in json_funcs.c are replaced by ereport().
* indentation fixes and other tidying.

No changes in functionality.

Further update of jsonb portion.

Only change in functionality is the addition of casts between jsonb and
json.

The other changes are the merge with the new json functions code, and
rearrangement of the docs changes to make them less ugly. Essentially I
moved the indexterm tags right out of the table as is done in some other
parts pf the docs. That makes the entry tags much clearer to read.

Updated to apply cleanly after recent commits.

Updated patches for both pieces. Included is some tidying done by Teodor,
and fixes for remaining whitespace issues. This now passes "git diff --check
master" cleanly for me.

Something seems off:

postgres=# create type z as (a int, b int[]);
CREATE TYPE
postgres=# create type y as (a int, b z[]);
CREATE TYPE
postgres=# create type x as (a int, b y[]);
CREATE TYPE

-- test a complicated construction
postgres=# select row(1, array[row(1, array[row(1, array[1,2])::z])::y])::x;
row
-------------------------------------------------------------------------------------
(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

postgres=# select hstore(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x);
hstore
----------------------------------------------------------------------------------------------
"a"=>1, "b"=>"{\"(1,\\\"{\\\"\\\"(1,\\\\\\\\\\\"\\\"{1,2}\\\\\\\\\\\"\\\")\\\"\\\"}\\\")\"}"

here, the output escaping has leaked into the internal array
structures. istm we should have a json expressing the internal
structure. It does (weirdly) map back however:

postgres=# select populate_record(null::x, hstore(row(1, array[row(1,
array[row(1, array[1,2])::z])::y])::x));
populate_record
-------------------------------------------------------------------------------------
(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

OTOH, if I go via json route:

postgres=# select row_to_json(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x);
row_to_json
-----------------------------------------------
{"a":1,"b":[{"a":1,"b":[{"a":1,"b":[1,2]}]}]}

so far, so good. let's push to hstore:
postgres=# select row_to_json(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x)::jsonb::hstore;
row_to_json
-------------------------------------------------------
"a"=>1, "b"=>[{"a"=>1, "b"=>[{"a"=>1, "b"=>[1, 2]}]}]

this ISTM is the 'right' behavior. but what if we bring it back to
record object?

postgres=# select populate_record(null::x, row_to_json(row(1,
array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb::hstore);
ERROR: malformed array literal: "{{"a"=>1, "b"=>{{"a"=>1, "b"=>{1, 2}}}}}"

yikes. The situation as I read it is that (notwithstanding my comments
upthread) there is no clean way to slide rowtypes to/from hstore and
jsonb while preserving structure. IMO, the above query should work
and the populate function record above should return the internally
structured row object, not the text escaped version.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#38)
Re: jsonb and nested hstore

On 01/30/2014 07:21 PM, Merlin Moncure wrote:

Something seems off:

postgres=# create type z as (a int, b int[]);
CREATE TYPE
postgres=# create type y as (a int, b z[]);
CREATE TYPE
postgres=# create type x as (a int, b y[]);
CREATE TYPE

-- test a complicated construction
postgres=# select row(1, array[row(1, array[row(1, array[1,2])::z])::y])::x;
row
-------------------------------------------------------------------------------------
(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

postgres=# select hstore(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x);
hstore
----------------------------------------------------------------------------------------------
"a"=>1, "b"=>"{\"(1,\\\"{\\\"\\\"(1,\\\\\\\\\\\"\\\"{1,2}\\\\\\\\\\\"\\\")\\\"\\\"}\\\")\"}"

here, the output escaping has leaked into the internal array
structures. istm we should have a json expressing the internal
structure.

What has this to do with json at all? It's clearly a failure in the
hstore() function.

It does (weirdly) map back however:

postgres=# select populate_record(null::x, hstore(row(1, array[row(1,
array[row(1, array[1,2])::z])::y])::x));
populate_record
-------------------------------------------------------------------------------------
(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

OTOH, if I go via json route:

postgres=# select row_to_json(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x);
row_to_json
-----------------------------------------------
{"a":1,"b":[{"a":1,"b":[{"a":1,"b":[1,2]}]}]}

so far, so good. let's push to hstore:
postgres=# select row_to_json(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x)::jsonb::hstore;
row_to_json
-------------------------------------------------------
"a"=>1, "b"=>[{"a"=>1, "b"=>[{"a"=>1, "b"=>[1, 2]}]}]

this ISTM is the 'right' behavior. but what if we bring it back to
record object?

postgres=# select populate_record(null::x, row_to_json(row(1,
array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb::hstore);
ERROR: malformed array literal: "{{"a"=>1, "b"=>{{"a"=>1, "b"=>{1, 2}}}}}"

yikes. The situation as I read it is that (notwithstanding my comments
upthread) there is no clean way to slide rowtypes to/from hstore and
jsonb while preserving structure. IMO, the above query should work
and the populate function record above should return the internally
structured row object, not the text escaped version.

And this is a failure in populate_record().

I think we possibly need to say that handling of nested composites and
arrays is an area that needs further work. OTOH, the refusal of
json_populate_record() and json_populate_recordset() to handle these in
9.3 has not generated a flood of complaints, so I don't think it's a
tragedy, just a limitation, which should be documented if it's not
already. (And of course hstore hasn't handled nested anything before now.)

Meanwhile, maybe Teodor can fix the two hstore bugs shown here.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#39)
Re: jsonb and nested hstore

On Thu, Jan 30, 2014 at 4:52 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/30/2014 07:21 PM, Merlin Moncure wrote:

postgres=# select hstore(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x);
hstore

----------------------------------------------------------------------------------------------
"a"=>1,
"b"=>"{\"(1,\\\"{\\\"\\\"(1,\\\\\\\\\\\"\\\"{1,2}\\\\\\\\\\\"\\\")\\\"\\\"}\\\")\"}"

here, the output escaping has leaked into the internal array
structures. istm we should have a json expressing the internal
structure.

What has this to do with json at all? It's clearly a failure in the hstore()
function.

yeah -- meant to say 'hstore' there. Also I'm not sure that it's
'wrong'; it's just doing what it always did. That brings up another
point: are there any interesting cases of compatibility breakage? I'm
inclined not to care about this particular case though...

array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb::hstore);
ERROR: malformed array literal: "{{"a"=>1, "b"=>{{"a"=>1, "b"=>{1,
2}}}}}"

yikes. The situation as I read it is that (notwithstanding my comments
upthread) there is no clean way to slide rowtypes to/from hstore and
jsonb while preserving structure. IMO, the above query should work
and the populate function record above should return the internally
structured row object, not the text escaped version.

And this is a failure in populate_record().

I think we possibly need to say that handling of nested composites and
arrays is an area that needs further work. OTOH, the refusal of
json_populate_record() and json_populate_recordset() to handle these in 9.3
has not generated a flood of complaints, so I don't think it's a tragedy,
just a limitation, which should be documented if it's not already. (And of
course hstore hasn't handled nested anything before now.)

Meanwhile, maybe Teodor can fix the two hstore bugs shown here.

While not a "flood", there certainly have been complaints. See
http://postgresql.1045698.n5.nabble.com/Best-way-to-populate-nested-composite-type-from-JSON-td5770566.html
http://osdir.com/ml/postgresql-pgsql-general/2014-01/msg00205.html

But, if we had to drop this in the interests of time I'd rather see
the behavior cauterized off so that it errored out 'not supported' (as
json_populate does) that attempt to implement the wrong behavior.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Oleg Bartunov
obartunov@gmail.com
In reply to: Andrew Dunstan (#39)
Re: jsonb and nested hstore

Hmm,
neither me, nor Teodor have experience and knowledge with
populate_record() and moreover hstore here is virgin and we don't know
the right behaviour, so I think we better take it from jsonb, once
Andrew realize it. Andrew ?

On Fri, Jan 31, 2014 at 4:52 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/30/2014 07:21 PM, Merlin Moncure wrote:

Something seems off:

postgres=# create type z as (a int, b int[]);
CREATE TYPE
postgres=# create type y as (a int, b z[]);
CREATE TYPE
postgres=# create type x as (a int, b y[]);
CREATE TYPE

-- test a complicated construction
postgres=# select row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x;
row

-------------------------------------------------------------------------------------

(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

postgres=# select hstore(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x);
hstore

----------------------------------------------------------------------------------------------
"a"=>1,
"b"=>"{\"(1,\\\"{\\\"\\\"(1,\\\\\\\\\\\"\\\"{1,2}\\\\\\\\\\\"\\\")\\\"\\\"}\\\")\"}"

here, the output escaping has leaked into the internal array
structures. istm we should have a json expressing the internal
structure.

What has this to do with json at all? It's clearly a failure in the hstore()
function.

It does (weirdly) map back however:

postgres=# select populate_record(null::x, hstore(row(1, array[row(1,
array[row(1, array[1,2])::z])::y])::x));
populate_record

-------------------------------------------------------------------------------------

(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

OTOH, if I go via json route:

postgres=# select row_to_json(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x);
row_to_json
-----------------------------------------------
{"a":1,"b":[{"a":1,"b":[{"a":1,"b":[1,2]}]}]}

so far, so good. let's push to hstore:
postgres=# select row_to_json(row(1, array[row(1, array[row(1,
array[1,2])::z])::y])::x)::jsonb::hstore;
row_to_json
-------------------------------------------------------
"a"=>1, "b"=>[{"a"=>1, "b"=>[{"a"=>1, "b"=>[1, 2]}]}]

this ISTM is the 'right' behavior. but what if we bring it back to
record object?

postgres=# select populate_record(null::x, row_to_json(row(1,
array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb::hstore);
ERROR: malformed array literal: "{{"a"=>1, "b"=>{{"a"=>1, "b"=>{1,
2}}}}}"

yikes. The situation as I read it is that (notwithstanding my comments
upthread) there is no clean way to slide rowtypes to/from hstore and
jsonb while preserving structure. IMO, the above query should work
and the populate function record above should return the internally
structured row object, not the text escaped version.

And this is a failure in populate_record().

I think we possibly need to say that handling of nested composites and
arrays is an area that needs further work. OTOH, the refusal of
json_populate_record() and json_populate_recordset() to handle these in 9.3
has not generated a flood of complaints, so I don't think it's a tragedy,
just a limitation, which should be documented if it's not already. (And of
course hstore hasn't handled nested anything before now.)

Meanwhile, maybe Teodor can fix the two hstore bugs shown here.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Merlin Moncure
mmoncure@gmail.com
In reply to: Oleg Bartunov (#41)
Re: jsonb and nested hstore

On Fri, Jan 31, 2014 at 4:03 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

Hmm,
neither me, nor Teodor have experience and knowledge with
populate_record() and moreover hstore here is virgin and we don't know
the right behaviour, so I think we better take it from jsonb, once
Andrew realize it. Andrew ?

Andrew Gierth wrote the current implementation of htsore
populate_record IIRC. Unfortunately the plan for jsonb was to borrow
hstore's (I don't think hstore can use the jsonb implementation
because you'd be taking away the ability to handle internally nested
structures it currently has). Of my two complaints upthread, the
second one, not being able to populate from and internally well formed
structure, is by far the more serious one I think.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#42)
Re: jsonb and nested hstore

On 01/31/2014 08:57 AM, Merlin Moncure wrote:

On Fri, Jan 31, 2014 at 4:03 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

Hmm,
neither me, nor Teodor have experience and knowledge with
populate_record() and moreover hstore here is virgin and we don't know
the right behaviour, so I think we better take it from jsonb, once
Andrew realize it. Andrew ?

Andrew Gierth wrote the current implementation of htsore
populate_record IIRC. Unfortunately the plan for jsonb was to borrow
hstore's (I don't think hstore can use the jsonb implementation
because you'd be taking away the ability to handle internally nested
structures it currently has). Of my two complaints upthread, the
second one, not being able to populate from and internally well formed
structure, is by far the more serious one I think.

Umm, I think at least one of us is seriously confused.

I am going to look at dealing with these issues in a way that can be
used by both - at least the populate_record case.

As far as populate_record goes, there is a bit of an impedance mismatch,
since json/hstore records are heterogenous and one-dimensional, whereas
sql arrays are homogeneous and multidimensional. Right now I am thinking
I will deal with arrays up to two dimensions, because I can do that
relatively simply, and after that throw in the towel. That will surely
deal with 99.9% of use cases. Of course this would be documented.

Anyway, Let me see what I can do.

If Andrew Gierth wants to have a look at fixing the hstore() side that
might help speed things up.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#43)
Re: jsonb and nested hstore

On Fri, Jan 31, 2014 at 8:45 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/31/2014 08:57 AM, Merlin Moncure wrote:

On Fri, Jan 31, 2014 at 4:03 AM, Oleg Bartunov <obartunov@gmail.com>
wrote:

Hmm,
neither me, nor Teodor have experience and knowledge with
populate_record() and moreover hstore here is virgin and we don't know
the right behaviour, so I think we better take it from jsonb, once
Andrew realize it. Andrew ?

Andrew Gierth wrote the current implementation of htsore
populate_record IIRC. Unfortunately the plan for jsonb was to borrow
hstore's (I don't think hstore can use the jsonb implementation
because you'd be taking away the ability to handle internally nested
structures it currently has). Of my two complaints upthread, the
second one, not being able to populate from and internally well formed
structure, is by far the more serious one I think.

Umm, I think at least one of us is seriously confused.

I am going to look at dealing with these issues in a way that can be used by
both - at least the populate_record case.

As far as populate_record goes, there is a bit of an impedance mismatch,
since json/hstore records are heterogenous and one-dimensional, whereas sql
arrays are homogeneous and multidimensional. Right now I am thinking I will
deal with arrays up to two dimensions, because I can do that relatively
simply, and after that throw in the towel. That will surely deal with 99.9%
of use cases. Of course this would be documented.

Anyway, Let me see what I can do.

If Andrew Gierth wants to have a look at fixing the hstore() side that might
help speed things up.

(ah, you beat me to it.)

Disregard my statements above. It works.

postgres=# select jsonb_populate_record(null::x, hstore(row(1,
array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb);
jsonb_populate_record
-------------------------------------------------------------------------------------
(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#44)
Re: jsonb and nested hstore

On 01/31/2014 09:53 AM, Merlin Moncure wrote:

On Fri, Jan 31, 2014 at 8:45 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/31/2014 08:57 AM, Merlin Moncure wrote:

On Fri, Jan 31, 2014 at 4:03 AM, Oleg Bartunov <obartunov@gmail.com>
wrote:

Hmm,
neither me, nor Teodor have experience and knowledge with
populate_record() and moreover hstore here is virgin and we don't know
the right behaviour, so I think we better take it from jsonb, once
Andrew realize it. Andrew ?

Andrew Gierth wrote the current implementation of htsore
populate_record IIRC. Unfortunately the plan for jsonb was to borrow
hstore's (I don't think hstore can use the jsonb implementation
because you'd be taking away the ability to handle internally nested
structures it currently has). Of my two complaints upthread, the
second one, not being able to populate from and internally well formed
structure, is by far the more serious one I think.

Umm, I think at least one of us is seriously confused.

I am going to look at dealing with these issues in a way that can be used by
both - at least the populate_record case.

As far as populate_record goes, there is a bit of an impedance mismatch,
since json/hstore records are heterogenous and one-dimensional, whereas sql
arrays are homogeneous and multidimensional. Right now I am thinking I will
deal with arrays up to two dimensions, because I can do that relatively
simply, and after that throw in the towel. That will surely deal with 99.9%
of use cases. Of course this would be documented.

Anyway, Let me see what I can do.

If Andrew Gierth wants to have a look at fixing the hstore() side that might
help speed things up.

(ah, you beat me to it.)

Disregard my statements above. It works.

postgres=# select jsonb_populate_record(null::x, hstore(row(1,
array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb);
jsonb_populate_record
-------------------------------------------------------------------------------------
(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

Actually, there is a workaround to the limitations of hstore(record):

andrew=# select row_to_json(row(1,
array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb::hstore;
row_to_json
-------------------------------------------------------
"a"=>1, "b"=>[{"a"=>1, "b"=>[{"a"=>1, "b"=>[1, 2]}]}]

I think we could just document that for now, or possibly just use it
inside hstore(record) if we encounter a nested composite or array.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#46Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#45)
Re: jsonb and nested hstore

On Fri, Jan 31, 2014 at 9:26 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/31/2014 09:53 AM, Merlin Moncure wrote:

On Fri, Jan 31, 2014 at 8:45 AM, Andrew Dunstan <andrew@dunslane.net>
wrote:

On 01/31/2014 08:57 AM, Merlin Moncure wrote:

On Fri, Jan 31, 2014 at 4:03 AM, Oleg Bartunov <obartunov@gmail.com>
wrote:

Hmm,
neither me, nor Teodor have experience and knowledge with
populate_record() and moreover hstore here is virgin and we don't know
the right behaviour, so I think we better take it from jsonb, once
Andrew realize it. Andrew ?

Andrew Gierth wrote the current implementation of htsore
populate_record IIRC. Unfortunately the plan for jsonb was to borrow
hstore's (I don't think hstore can use the jsonb implementation
because you'd be taking away the ability to handle internally nested
structures it currently has). Of my two complaints upthread, the
second one, not being able to populate from and internally well formed
structure, is by far the more serious one I think.

Umm, I think at least one of us is seriously confused.

I am going to look at dealing with these issues in a way that can be used
by
both - at least the populate_record case.

As far as populate_record goes, there is a bit of an impedance mismatch,
since json/hstore records are heterogenous and one-dimensional, whereas
sql
arrays are homogeneous and multidimensional. Right now I am thinking I
will
deal with arrays up to two dimensions, because I can do that relatively
simply, and after that throw in the towel. That will surely deal with
99.9%
of use cases. Of course this would be documented.

Anyway, Let me see what I can do.

If Andrew Gierth wants to have a look at fixing the hstore() side that
might
help speed things up.

(ah, you beat me to it.)

Disregard my statements above. It works.

postgres=# select jsonb_populate_record(null::x, hstore(row(1,
array[row(1, array[row(1, array[1,2])::z])::y])::x)::jsonb);
jsonb_populate_record

-------------------------------------------------------------------------------------

(1,"{""(1,\\""{\\""\\""(1,\\\\\\\\\\""\\""{1,2}\\\\\\\\\\""\\"")\\""\\""}\\"")""}")

Actually, there is a workaround to the limitations of hstore(record):

yeah I'm ok with hstore() function as it is. That also eliminates
backwards compatibility concerns so things worked out. The only 'must
fix' 9.4 facing issue I see on the table is to make sure jsonb
populate function is forward compatible with future expectations of
behavior which to me means zeroing in on the necessity of the as_text
argument (but if you can expand coverage without jeopardizing 9.4
inclusion than great...).

For my part I'm going to continue functionally testing the rest of the
API (so far, a cursory look hasn't turned up anything else). I'm also
signing up for some documentation refinements which will be done after
you nail down these little bits but before the end of the 'fest.

IMNSHO, formal code review needs to begin ASAP (salahaldin is the
reviewer per the fest wiki)

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#46)
Re: jsonb and nested hstore

On 01/31/2014 02:48 PM, Merlin Moncure wrote:

Actually, there is a workaround to the limitations of hstore(record):

yeah I'm ok with hstore() function as it is. That also eliminates
backwards compatibility concerns so things worked out. The only 'must
fix' 9.4 facing issue I see on the table is to make sure jsonb
populate function is forward compatible with future expectations of
behavior which to me means zeroing in on the necessity of the as_text
argument (but if you can expand coverage without jeopardizing 9.4
inclusion than great...).

This isn't terribly clear. Currently, if jsonb_populate_record{set}
encounters a nested array or object when populating the record it errors
out, regardless of the type of the field, unless as_text is set (it
defaults to off). In the latter case it tries to use the array or
object's json text representation as the value to populate the field
(realistically, this only works for text, json and jsonb fields). This
is exactly the current behaviour of json_populate_record{set}. The
enhancement would be to alter the behaviour when as_text is NOT set. In
this case, we would try recursively to populate an array or composite
field with the corresponding jsonb. i.e we would be removing some
current error conditions and returning a result. But we would not be
returning a different result in any case where we now return a result. I
think that's future-proof enough.

Frankly, I think the behaviour of hstore(record) with nested composites
and arrays is sufficiently counter-intuitive, to put it mildly, that we
should at least document the workaround from my previous email.

For my part I'm going to continue functionally testing the rest of the
API (so far, a cursory look hasn't turned up anything else). I'm also
signing up for some documentation refinements which will be done after
you nail down these little bits but before the end of the 'fest.

IMNSHO, formal code review needs to begin ASAP (salahaldin is the
reviewer per the fest wiki)

Yes, or anyone else who wants to join in. I'd very much welcome a
substantial code review - I have been staring at this far too long on my
own.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#47)
Re: jsonb and nested hstore

On 01/31/2014 11:35 PM, Andrew Dunstan wrote:

Yes, or anyone else who wants to join in. I'd very much welcome a
substantial code review - I have been staring at this far too long on
my own.

I should mention that in fact by far the largest piece of this is not my
work, but Oleg and Teodor's work. I'm sure they would welcome an in
depth review too, but I realised that my message above might have given
a false impression. They deserve the credit for having come up with and
implemented this scheme for tree-ish data.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#49Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#35)
Re: jsonb and nested hstore

On 2014-01-30 14:07:42 -0500, Andrew Dunstan wrote:

+  <para id="functions-json-table">
+   <xref linkend="functions-json-creation-table"> shows the functions that are
+   available for creating <type>json</type> values.
+   (see <xref linkend="datatype-json">)
</para>
-  <table id="functions-json-table">
-    <title>JSON Support Functions</title>
+  <indexterm>
+   <primary>array_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>row_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_array</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_object</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_object</primary>
+  </indexterm>

Hm, why are you collecting the indexterms at the top in the contrast to
the previous way of collecting them at the point of documentation?

diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
tsvector.o tsvector_op.o tsvector_parser.o \
txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o

Odd, most OBJS lines are kept in alphabetical order, but that doesn't
seem to be the case here.

+/*
+ * for jsonb we always want the de-escaped value - that's what's in token
+ */
+

strange newline.

+static void
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+

...

+		default:				/* nothing else should be here in fact */
+			break;

Shouldn't this at least Assert(false) or something?

+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	uint32		hentry = c & JENTRY_TYPEMASK;
+
+	if (hentry == JENTRY_ISNULL)
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+	}
+	else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	{
+		recvJsonb(buf, v, level + 1, (uint32) c);
+	}
+	else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	{
+		v->type = jbvBool;
+		v->size = sizeof(JEntry);
+		v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	}
+	else if (hentry == JENTRY_ISNUMERIC)
+	{
+		v->type = jbvNumeric;
+		v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+									   Int32GetDatum(0), Int32GetDatum(-1)));
+
+		v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);

What's the *2 here?

+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	uint32		hentry;
+	uint32		i;

This function and recvJsonbValue call each other recursively, afaics
without any limit, shouldn't they check for the stack depth?

+	hentry = header & JENTRY_TYPEMASK;
+
+	v->size = 3 * sizeof(JEntry);

*3?

+	if (hentry == JENTRY_ISOBJECT)
+	{
+		v->type = jbvHash;
+		v->hash.npairs = header & JB_COUNT_MASK;
+		if (v->hash.npairs > 0)
+		{
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+

Hm, if I understand correctly, we're just allocating JB_COUNT_MASK
(which is 0x0FFFFFFF) * sizeof(*v->hash.pairs) bytes here, without any
crosschecks about the actual length of the data? So with a few bytes the
server can be coaxed to allocate a gigabyte of data?
Since this immediately calls another input routine, this can be done in
a nested fashion, quickly OOMing the server.

I think this and several other places really need a bit more input
sanity checking.

+			for (i = 0; i < v->hash.npairs; i++)
+			{
+				recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != jbvString)
+					elog(ERROR, "jsonb's key could be only a string");

Shouldn't that be an ereport(ERRCODE_DATATYPE_MISMATCH)? Similar in a
few other places.

+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	bool		first = true;
+	JsonbIterator *it;
+	int			type;
+	JsonbValue	v;
+	int			level = 0;
+
+	if (out == NULL)
+		out = makeStringInfo();

Such a behaviour certainly deserves a documentary comment. Generally
some more functions could use that.

+	while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	{
+reout:
+		switch (type)
+		{

...

+				{
+					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+					goto reout;

Hrmpf.

+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{

...

+}

Hm, shouldn't that be in jsonfuncs.c?

diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index a19b222..f1eacc6 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/json.h"
+#include "utils/jsonb.h"
#include "utils/jsonapi.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -51,6 +52,7 @@ static inline Datum get_path_all(PG_FUNCTION_ARGS, bool as_text);
static inline text *get_worker(text *json, char *field, int elem_index,
char **tpath, int *ipath, int npath,
bool normalize_results);
+static inline Datum get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text);

I don't see the point of using PG_FUNCTION_ARGS if you're manually
calling it like
+ return get_jsonb_path_all(fcinfo, false);

That just makes it harder if someday PG_FUNCTION_ARGS grows a second
argument or something.

+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext oldcontext;
+		Jsonb	   *jb = PG_GETARG_JSONB(0);
+		bool		skipNested = false;
+		JsonbIterator *it;
+		JsonbValue	v;
+		int			r = 0;
+
+		if (JB_ROOT_IS_SCALAR(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on a scalar")));
+		else if (JB_ROOT_IS_ARRAY(jb))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call jsonb_object_keys on an array")));
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

This will detoast 'jb' into the expression context, since
PG_GETARG_JSONB() is called before the MemoryContextSwitchTo. But that's
ok since the percall code only deals with ->result, right?

- /* make these in a sufficiently long-lived memory context */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);

wh remove that comment?

+#define JENTRY_ISCALAR (0x10000000 | 0x40000000)

Isn't there an S missing here?

--- a/contrib/hstore/hstore_compat.c
+++ b/contrib/hstore/hstore_compat.c
+/*
+ * New Old version (new not-nested version of hstore, v2 version)
+ * V2 and v3 (nested) are upward binary compatible. But
+ * framework was fully changed. Keep here old definitions (v2)
+ */

That's an, err, interesting sentence. I think referring to old new
version and stuff is less than helpful. I realize lots of that is
baggage from existing code, but yet another version doesn't make it
easier.

I lost my stomach (or maybe it was the glass of red) somewhere in the
middle, but I think this needs a lot of work. Especially the io code
doesn't seem ready to me. I'd consider ripping out the send/recv code
for 9.4, that seems the biggest can of worms. It will still be usable
without.

There's just about no comments in large and relevant parts of the
code. There's not much documentation about the binary layout of a
definitely not trivial type with convoluted interdependencies with
hstore...

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#49)
Re: jsonb and nested hstore

On 02/01/2014 05:20 PM, Andres Freund wrote:

[Long review]

Most of these comments actually refer to Teodor and Oleg's code.

I will attend to the parts that apply to my code.

Thanks for the review.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#50)
Re: jsonb and nested hstore

Hi,

On 2014-02-01 18:13:42 -0500, Andrew Dunstan wrote:

[Long review]

Most of these comments actually refer to Teodor and Oleg's code.

I will attend to the parts that apply to my code.

Well, somebody will need to address them nonetheless :/

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#51)
Re: jsonb and nested hstore

On 02/01/2014 06:15 PM, Andres Freund wrote:

Hi,

On 2014-02-01 18:13:42 -0500, Andrew Dunstan wrote:

[Long review]

Most of these comments actually refer to Teodor and Oleg's code.

I will attend to the parts that apply to my code.

Well, somebody will need to address them nonetheless :/

Yes, of course, I didn't suggest otherwise.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#53Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#49)
Re: jsonb and nested hstore

On Sat, Feb 1, 2014 at 4:20 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-01-30 14:07:42 -0500, Andrew Dunstan wrote:

+  <para id="functions-json-table">
+   <xref linkend="functions-json-creation-table"> shows the functions that are
+   available for creating <type>json</type> values.
+   (see <xref linkend="datatype-json">)
</para>
-  <table id="functions-json-table">
-    <title>JSON Support Functions</title>
+  <indexterm>
+   <primary>array_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>row_to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>to_json</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_array</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_build_object</primary>
+  </indexterm>
+  <indexterm>
+   <primary>json_object</primary>
+  </indexterm>

Hm, why are you collecting the indexterms at the top in the contrast to
the previous way of collecting them at the point of documentation?

diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
tsvector.o tsvector_op.o tsvector_parser.o \
txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-     rangetypes_typanalyze.o rangetypes_selfuncs.o
+     rangetypes_typanalyze.o rangetypes_selfuncs.o \
+     jsonb.o jsonb_support.o

Odd, most OBJS lines are kept in alphabetical order, but that doesn't
seem to be the case here.

+/*
+ * for jsonb we always want the de-escaped value - that's what's in token
+ */
+

strange newline.

+static void
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+     JsonbInState *_state = (JsonbInState *) state;
+     JsonbValue      v;
+
+     v.size = sizeof(JEntry);
+
+     switch (tokentype)
+     {
+

...

+             default:                                /* nothing else should be here in fact */
+                     break;

Shouldn't this at least Assert(false) or something?

+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+     uint32          hentry = c & JENTRY_TYPEMASK;
+
+     if (hentry == JENTRY_ISNULL)
+     {
+             v->type = jbvNull;
+             v->size = sizeof(JEntry);
+     }
+     else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+     {
+             recvJsonb(buf, v, level + 1, (uint32) c);
+     }
+     else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+     {
+             v->type = jbvBool;
+             v->size = sizeof(JEntry);
+             v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+     }
+     else if (hentry == JENTRY_ISNUMERIC)
+     {
+             v->type = jbvNumeric;
+             v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+                                                                        Int32GetDatum(0), Int32GetDatum(-1)));
+
+             v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);

What's the *2 here?

+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+     uint32          hentry;
+     uint32          i;

This function and recvJsonbValue call each other recursively, afaics
without any limit, shouldn't they check for the stack depth?

+     hentry = header & JENTRY_TYPEMASK;
+
+     v->size = 3 * sizeof(JEntry);

*3?

+     if (hentry == JENTRY_ISOBJECT)
+     {
+             v->type = jbvHash;
+             v->hash.npairs = header & JB_COUNT_MASK;
+             if (v->hash.npairs > 0)
+             {
+                     v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+

Hm, if I understand correctly, we're just allocating JB_COUNT_MASK
(which is 0x0FFFFFFF) * sizeof(*v->hash.pairs) bytes here, without any
crosschecks about the actual length of the data? So with a few bytes the
server can be coaxed to allocate a gigabyte of data?
Since this immediately calls another input routine, this can be done in
a nested fashion, quickly OOMing the server.

I think this and several other places really need a bit more input
sanity checking.

+                     for (i = 0; i < v->hash.npairs; i++)
+                     {
+                             recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+                             if (v->hash.pairs[i].key.type != jbvString)
+                                     elog(ERROR, "jsonb's key could be only a string");

Shouldn't that be an ereport(ERRCODE_DATATYPE_MISMATCH)? Similar in a
few other places.

+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+     bool            first = true;
+     JsonbIterator *it;
+     int                     type;
+     JsonbValue      v;
+     int                     level = 0;
+
+     if (out == NULL)
+             out = makeStringInfo();

Such a behaviour certainly deserves a documentary comment. Generally
some more functions could use that.

+     while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+     {
+reout:
+             switch (type)
+             {

...

+                             {
+                                     Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+                                     goto reout;

Hrmpf.

+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{

...

+}

Hm, shouldn't that be in jsonfuncs.c?

diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index a19b222..f1eacc6 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/json.h"
+#include "utils/jsonb.h"
#include "utils/jsonapi.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -51,6 +52,7 @@ static inline Datum get_path_all(PG_FUNCTION_ARGS, bool as_text);
static inline text *get_worker(text *json, char *field, int elem_index,
char **tpath, int *ipath, int npath,
bool normalize_results);
+static inline Datum get_jsonb_path_all(PG_FUNCTION_ARGS, bool as_text);

I don't see the point of using PG_FUNCTION_ARGS if you're manually
calling it like
+ return get_jsonb_path_all(fcinfo, false);

That just makes it harder if someday PG_FUNCTION_ARGS grows a second
argument or something.

+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+     FuncCallContext *funcctx;
+     OkeysState *state;
+     int                     i;
+
+     if (SRF_IS_FIRSTCALL())
+     {
+             MemoryContext oldcontext;
+             Jsonb      *jb = PG_GETARG_JSONB(0);
+             bool            skipNested = false;
+             JsonbIterator *it;
+             JsonbValue      v;
+             int                     r = 0;
+
+             if (JB_ROOT_IS_SCALAR(jb))
+                     ereport(ERROR,
+                                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                      errmsg("cannot call jsonb_object_keys on a scalar")));
+             else if (JB_ROOT_IS_ARRAY(jb))
+                     ereport(ERROR,
+                                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                      errmsg("cannot call jsonb_object_keys on an array")));
+
+             funcctx = SRF_FIRSTCALL_INIT();
+             oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

This will detoast 'jb' into the expression context, since
PG_GETARG_JSONB() is called before the MemoryContextSwitchTo. But that's
ok since the percall code only deals with ->result, right?

- /* make these in a sufficiently long-lived memory context */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);

wh remove that comment?

+#define JENTRY_ISCALAR (0x10000000 | 0x40000000)

Isn't there an S missing here?

--- a/contrib/hstore/hstore_compat.c
+++ b/contrib/hstore/hstore_compat.c
+/*
+ * New Old version (new not-nested version of hstore, v2 version)
+ * V2 and v3 (nested) are upward binary compatible. But
+ * framework was fully changed. Keep here old definitions (v2)
+ */

That's an, err, interesting sentence. I think referring to old new
version and stuff is less than helpful. I realize lots of that is
baggage from existing code, but yet another version doesn't make it
easier.

I lost my stomach (or maybe it was the glass of red) somewhere in the
middle, but I think this needs a lot of work. Especially the io code
doesn't seem ready to me. I'd consider ripping out the send/recv code
for 9.4, that seems the biggest can of worms. It will still be usable
without.

Not having type send/recv functions is somewhat dangerous; it can
cause problems for libraries that run everything through the binary
wire format. I'd give jsonb a pass on that, being a new type, but
would be concerned if hstore had that ability revoked.

offhand note: hstore_send seems pretty simply written and clean; it's
a simple nonrecursive iterator...

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#54Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#53)
Re: jsonb and nested hstore

On 2014-02-03 09:22:52 -0600, Merlin Moncure wrote:

I lost my stomach (or maybe it was the glass of red) somewhere in the
middle, but I think this needs a lot of work. Especially the io code
doesn't seem ready to me. I'd consider ripping out the send/recv code
for 9.4, that seems the biggest can of worms. It will still be usable
without.

Not having type send/recv functions is somewhat dangerous; it can
cause problems for libraries that run everything through the binary
wire format. I'd give jsonb a pass on that, being a new type, but
would be concerned if hstore had that ability revoked.

Yea, removing it for hstore would be a compat problem...

offhand note: hstore_send seems pretty simply written and clean; it's
a simple nonrecursive iterator...

But a send function is pretty pointless without the corresponding recv
function... And imo recv simply is to dangerous as it's currently
written.
I am not saying that it cannot be made work, just that it's still nearly
as ugly as when I pointed out several of the dangers some weeks back.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#55Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/03/2014 07:27 AM, Andres Freund wrote:

On 2014-02-03 09:22:52 -0600, Merlin Moncure wrote:

I lost my stomach (or maybe it was the glass of red) somewhere in the
middle, but I think this needs a lot of work. Especially the io code
doesn't seem ready to me. I'd consider ripping out the send/recv code
for 9.4, that seems the biggest can of worms. It will still be usable
without.

Not having type send/recv functions is somewhat dangerous; it can
cause problems for libraries that run everything through the binary
wire format. I'd give jsonb a pass on that, being a new type, but
would be concerned if hstore had that ability revoked.

Yea, removing it for hstore would be a compat problem...

offhand note: hstore_send seems pretty simply written and clean; it's
a simple nonrecursive iterator...

But a send function is pretty pointless without the corresponding recv
function... And imo recv simply is to dangerous as it's currently
written.
I am not saying that it cannot be made work, just that it's still nearly
as ugly as when I pointed out several of the dangers some weeks back.

Oleg, Teodor, any comments on the above?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#56Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Merlin Moncure (#53)
Re: jsonb and nested hstore

On 02/03/2014 05:22 PM, Merlin Moncure wrote:

I lost my stomach (or maybe it was the glass of red) somewhere in the
middle, but I think this needs a lot of work. Especially the io code
doesn't seem ready to me. I'd consider ripping out the send/recv code
for 9.4, that seems the biggest can of worms. It will still be usable
without.

Not having type send/recv functions is somewhat dangerous; it can
cause problems for libraries that run everything through the binary
wire format. I'd give jsonb a pass on that, being a new type, but
would be concerned if hstore had that ability revoked.

send/recv functions are also needed for binary-format COPY. IMHO jsonb
must have send/recv functions. All other built-in types have them,
except for types like 'smgr', 'aclitem' and 'any*' that no-one should be
using as column types.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#57Oleg Bartunov
obartunov@gmail.com
In reply to: Josh Berkus (#55)
Re: jsonb and nested hstore

Andrew provided us more information and we'll work on recv. What
people think about testing this stuff ? btw, we don't have any
regression test on this.

Oleg

On Wed, Feb 5, 2014 at 2:03 AM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/03/2014 07:27 AM, Andres Freund wrote:

On 2014-02-03 09:22:52 -0600, Merlin Moncure wrote:

I lost my stomach (or maybe it was the glass of red) somewhere in the
middle, but I think this needs a lot of work. Especially the io code
doesn't seem ready to me. I'd consider ripping out the send/recv code
for 9.4, that seems the biggest can of worms. It will still be usable
without.

Not having type send/recv functions is somewhat dangerous; it can
cause problems for libraries that run everything through the binary
wire format. I'd give jsonb a pass on that, being a new type, but
would be concerned if hstore had that ability revoked.

Yea, removing it for hstore would be a compat problem...

offhand note: hstore_send seems pretty simply written and clean; it's
a simple nonrecursive iterator...

But a send function is pretty pointless without the corresponding recv
function... And imo recv simply is to dangerous as it's currently
written.
I am not saying that it cannot be made work, just that it's still nearly
as ugly as when I pointed out several of the dangers some weeks back.

Oleg, Teodor, any comments on the above?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58Teodor Sigaev
teodor@sigaev.ru
In reply to: Andres Freund (#49)
Re: jsonb and nested hstore
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+		v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);

What's the *2 here?

Reservation for aligment. It's allowed to be v->size greater than it's actually
needed. Fixed.

This function and recvJsonbValue call each other recursively, afaics
without any limit, shouldn't they check for the stack depth?

added a check_stack_depth()

*3?

Jentry + header + reservation for aligment

+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+

if (v->hash.npairs > (buf->len - buf->cursor) / (2 * sizeof(uint32)))
ereport(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE)
2 * sizeof(uint32) - minimal size of object element (key plus its value)

Shouldn't that be an ereport(ERRCODE_DATATYPE_MISMATCH)? Similar in a
few other places.

fixed

+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)

Such a behaviour certainly deserves a documentary comment. Generally
some more functions could use that.

add comment

+	while ((type = JsonbIteratorGet(&it, &v, false)) != 0)
+reout:
+					goto reout;

Hrmpf.

:) commented

+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{

...

+}

Hm, shouldn't that be in jsonfuncs.c?

No idea, i don't have an objection

send/recv for hstore is fixed too. Should I make new version of patch? Right now
it's placed on github. May be Andrew wants to change something?

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#59Merlin Moncure
mmoncure@gmail.com
In reply to: Oleg Bartunov (#57)
Re: jsonb and nested hstore

On Wed, Feb 5, 2014 at 12:44 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

send/recv functions are also needed for binary-format COPY. IMHO jsonb must
have send/recv functions. All other built-in types have them, except for
types like 'smgr', 'aclitem' and 'any*' that no-one should be using as
column types.

Yes -- completely agree. I also consider the hstore functionality (in
particular, searching and access operators) to be essential
functionality.

I'm actually surprised we have an alternate binary wire format for
jsonb at all; json is explicitly text and I'm not sure what the use
case of sending the internal structure is. Meaning, maybe jsonb
send/recv should be a thin wrapper to sending the json string. The
hstore send/recv I think properly covers the case where client side
binary wire format actors would want to manage performance critical
cases that want to avoid parsing.

On Wed, Feb 5, 2014 at 1:21 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

Andrew provided us more information and we'll work on recv. What
people think about testing this stuff ? btw, we don't have any
regression test on this.

I'm intensely interested in this work; I consider it to be transformative.

I've *lightly* tested the jsonb/hstore functionality and so far
everything is working.

I still have concerns about the API. Aside from the stuff I mentioned
upthread I find the API split between jsonb and hstore to be a little
odd; a lot of useful bits (for example, the @> operator) come via the
hstore type only. So these types are joined at the hip for real work
which makes the diverging incomplete behaviors in functions like
populate_record() disconcerting. Another point I'm struggling with is
what jsonb brings to the table that isn't covered either hstore or
json; working through a couple of cases I find myself not using the
jsonb functionality except as a 'hstore json formatter' which the json
type covers. I'm probably being obtuse, but we have to be cautious
before plonking a couple of dozen extra functions in the public
schema.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#60Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#59)
Re: jsonb and nested hstore

On 02/05/2014 10:48 AM, Merlin Moncure wrote:

On Wed, Feb 5, 2014 at 12:44 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

send/recv functions are also needed for binary-format COPY. IMHO jsonb must
have send/recv functions. All other built-in types have them, except for
types like 'smgr', 'aclitem' and 'any*' that no-one should be using as
column types.

Yes -- completely agree. I also consider the hstore functionality (in
particular, searching and access operators) to be essential
functionality.

I'm actually surprised we have an alternate binary wire format for
jsonb at all; json is explicitly text and I'm not sure what the use
case of sending the internal structure is. Meaning, maybe jsonb
send/recv should be a thin wrapper to sending the json string. The
hstore send/recv I think properly covers the case where client side
binary wire format actors would want to manage performance critical
cases that want to avoid parsing.

The whole reason we have jsonb is to avoid reparsing where possible. In
fact, I'd rather have the send and recv functions in the jsonb code and
have hstore's functions call them, so we don't duplicate code.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#61Andrew Dunstan
andrew@dunslane.net
In reply to: Teodor Sigaev (#58)
Re: jsonb and nested hstore

On 02/05/2014 10:36 AM, Teodor Sigaev wrote:

+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{

...

+}

Hm, shouldn't that be in jsonfuncs.c?

No idea, i don't have an objection

No it shouldn't. The json equivalent function is in json.c, and needs to
be because it uses the parser internals that aren't exposed outside that
code.

send/recv for hstore is fixed too. Should I make new version of patch?
Right now it's placed on github. May be Andrew wants to change something?

I'll take a look, but I think we need to unify this so we use one set of
send/recv code for the two types if possible, as I just said to Merlin.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#62Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#60)
Re: jsonb and nested hstore

On Wed, Feb 5, 2014 at 10:22 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

I'm actually surprised we have an alternate binary wire format for
jsonb at all; json is explicitly text and I'm not sure what the use
case of sending the internal structure is. Meaning, maybe jsonb
send/recv should be a thin wrapper to sending the json string. The
hstore send/recv I think properly covers the case where client side
binary wire format actors would want to manage performance critical
cases that want to avoid parsing.

The whole reason we have jsonb is to avoid reparsing where possible

Sure; but on the server side. The wire format is for handling client
concerns. For example, the case you're arguing for would be for libpq
client to extract as jsonb as binary, manipulate it on a binary level,
then send it back as binary. I find this case to be something of a
stretch.

That being said, for binary dump/restore perhaps there's a performance
case to be made.

In fact, I'd rather have the send and recv functions in the jsonb code and have
hstore's functions call them, so we don't duplicate code.

yeah. Agree that there needs to be two sets of routines, not three.
I think a case could be made for the jsonb type could take either
json's or hstore's depending on if the above. FWIW, either way is
fine.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#63Tom Lane
tgl@sss.pgh.pa.us
In reply to: Merlin Moncure (#62)
Re: jsonb and nested hstore

Merlin Moncure <mmoncure@gmail.com> writes:

On Wed, Feb 5, 2014 at 10:22 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

The whole reason we have jsonb is to avoid reparsing where possible

Sure; but on the server side. The wire format is for handling client
concerns. For example, the case you're arguing for would be for libpq
client to extract as jsonb as binary, manipulate it on a binary level,
then send it back as binary. I find this case to be something of a
stretch.

I'm with Merlin in thinking that the case for exposing a binary format
to clients is pretty weak, or at least a convincing use-case has not
been shown. Given the concerns upthread about security hazards in the
patch's existing recv code, and the fact that it's already February,
switching to "binary is the same as text" may well be the most prudent
path here.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#64Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#63)
Re: jsonb and nested hstore

On 02/05/2014 11:40 AM, Tom Lane wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

On Wed, Feb 5, 2014 at 10:22 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

The whole reason we have jsonb is to avoid reparsing where possible

Sure; but on the server side. The wire format is for handling client
concerns. For example, the case you're arguing for would be for libpq
client to extract as jsonb as binary, manipulate it on a binary level,
then send it back as binary. I find this case to be something of a
stretch.

I'm with Merlin in thinking that the case for exposing a binary format
to clients is pretty weak, or at least a convincing use-case has not
been shown. Given the concerns upthread about security hazards in the
patch's existing recv code, and the fact that it's already February,
switching to "binary is the same as text" may well be the most prudent
path here.

If we do that we're going to have to live with that forever, aren't we?
I don't see why there should be a convincing case for binary format for
nested hstore but not for jsonb.

If it were only for arbitrary libpq clietns I wouldn't bother so much.
To me the main case for binary format is that some people use COPY
BINARY for efficiency reasons, and I heard tell recently of someone
working on that as an option for pg_dump, which seems to me worth
considering.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#65Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#64)
Re: jsonb and nested hstore

Andrew Dunstan <andrew@dunslane.net> writes:

On 02/05/2014 11:40 AM, Tom Lane wrote:

switching to "binary is the same as text" may well be the most prudent
path here.

If we do that we're going to have to live with that forever, aren't we?

Yeah, but the other side of that coin is that we'll have to live forever
with whatever binary format we pick, too. If it turns out to be badly
designed, that could be much worse than eating some parsing costs during
dump/restore.

If we had infinite time/manpower, this wouldn't really be an issue.
We don't, though, and so I suggest that this may be one of the better
things to toss overboard.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#66Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#65)
Re: jsonb and nested hstore

On 02/05/2014 12:48 PM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 02/05/2014 11:40 AM, Tom Lane wrote:

switching to "binary is the same as text" may well be the most prudent
path here.

If we do that we're going to have to live with that forever, aren't we?

Yeah, but the other side of that coin is that we'll have to live forever
with whatever binary format we pick, too. If it turns out to be badly
designed, that could be much worse than eating some parsing costs during
dump/restore.

If we had infinite time/manpower, this wouldn't really be an issue.
We don't, though, and so I suggest that this may be one of the better
things to toss overboard.

The main reason I'm prepared to consider this is the JSON parser seems
to be fairly efficient (See Oleg's recent stats) and in fact we'd more
or less be parsing the binary format on input anyway, so there's no
proof that a binary format is going to be hugely faster (or possibly
even that it will be faster at all).

If anyone else has opinions on this sing out pretty darn soon (like the
next 24 hours or so, before I begin.)

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#67Merlin Moncure
mmoncure@gmail.com
In reply to: Tom Lane (#65)
Re: jsonb and nested hstore

On Wed, Feb 5, 2014 at 11:48 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

If we had infinite time/manpower, this wouldn't really be an issue.
We don't, though, and so I suggest that this may be one of the better
things to toss overboard.

The hstore send/recv functions have basically the same
(copy/pasted/name adjusted) implementation. Since hstore will
presumably remain (as the current hstore is) 'deep binary' and all of
Andres's gripes apply to the hstore as well, this change buys us
precisely zap from a time perspective; it comes down to which is
intrinsically the better choice.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#68Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/05/2014 07:48 AM, Merlin Moncure wrote:

Another point I'm struggling with is
what jsonb brings to the table that isn't covered either hstore or
json; working through a couple of cases I find myself not using the
jsonb functionality except as a 'hstore json formatter' which the json
type covers. I'm probably being obtuse, but we have to be cautious
before plonking a couple of dozen extra functions in the public
schema.

There's three reasons why it's worthwhile:

1) user-friendliness: telling users they need to do "::JSON" and
"::HSTORE2" all the time is sufficiently annoying -- and prone to
causing errors -- to be a blocker to adoption by a certain, very
numerous, class of user.

2) performance: to the extent that we can operate entirely in JSONB and
not transform back and forth to JSON and HSTORE, function calls (and
index lookups) will be much faster. And given the competition, speed is
important.

3) growth: 9.4's JSONB functions are a prerequisite to developing richer
JSON querying capabilities in 9.5 and later, which will go beyond "JSON
formatting for HSTORE".

Frankly, if it were entirely up to me HSTORE2 would be part of core and
its only interface would be JSONB. But it's not. So this is a compromise.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#69Andrew Dunstan
andrew@dunslane.net
In reply to: Josh Berkus (#68)
Re: jsonb and nested hstore

On 02/05/2014 02:03 PM, Josh Berkus wrote:

Frankly, if it were entirely up to me HSTORE2 would be part of core and
its only interface would be JSONB. But it's not. So this is a compromise.

You could only do that by inventing a new type. But hstore2 isn't a new
type, it's meant to be the existing hstore type with new capabilities.

Incidentally, some work is being done by one of my colleagues on an
extension of gin/gist operators for indexing jsonb similarly to hstore2.
Now that will possibly be something we can bring into 9.4, although
we'll have to check how we go about pg_upgrade for that case.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#70Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#68)
Re: jsonb and nested hstore

On Wed, Feb 5, 2014 at 1:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/05/2014 07:48 AM, Merlin Moncure wrote:

Another point I'm struggling with is
what jsonb brings to the table that isn't covered either hstore or
json; working through a couple of cases I find myself not using the
jsonb functionality except as a 'hstore json formatter' which the json
type covers. I'm probably being obtuse, but we have to be cautious
before plonking a couple of dozen extra functions in the public
schema.

There's three reasons why it's worthwhile:

1) user-friendliness: telling users they need to do "::JSON" and
"::HSTORE2" all the time is sufficiently annoying -- and prone to
causing errors -- to be a blocker to adoption by a certain, very
numerous, class of user.

That's a legitimate point of concern. But in and of itself I'm sure
sure it warrants exposing a separate API.

2) performance: to the extent that we can operate entirely in JSONB and
not transform back and forth to JSON and HSTORE, function calls (and
index lookups) will be much faster. And given the competition, speed is
important.

Not following this. I do not see how the presence of jsonb helps at
all. Client to server communication will be text->binary (and vice
versa) and handling within the server itself will be in binary. This
is the crux of my point.

3) growth: 9.4's JSONB functions are a prerequisite to developing richer
JSON querying capabilities in 9.5 and later, which will go beyond "JSON
formatting for HSTORE".

I kind of get this point. But in lieu of a practical use case today,
what's the rush to implement? I fully anticipate I'm out on left
field on this one (I have a cot and mini fridge there). The question
on the table is: what use cases (performance included) does jsonb
solve that is not solve can't be solved without it? With the possible
limited exception of andrew's yet to be delivered enhanced
deserialization routines, I can't think of any. If presented with
reasonable evidence I'll shut my yap, pronto.

Frankly, if it were entirely up to me HSTORE2 would be part of core and
its only interface would be JSONB. But it's not. So this is a compromise.

I don't. To be pedantic: hstore is in core, but packaged as an
extension. That's a very important distinction.

In fact, I'll go further and say it seem wise for all SQL standard
type work to happen in extensions. As long as it's an in core contrib
extension, I see no stigma to that whatsoever. It's not clear at all
to me why the json type was put to the public schema and now we're
about to double down with jsonb. Having things extension packaged
greatly eases concerns about future API changes because if problems
emerge it's not impossible to imagine compatibility extensions to
appear to bridge the gap if certain critical functions change. That's
exactly the sort of thing that we may want to happen here, I think.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#71Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#70)
Re: jsonb and nested hstore

On 02/05/2014 03:15 PM, Merlin Moncure wrote:

On Wed, Feb 5, 2014 at 1:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/05/2014 07:48 AM, Merlin Moncure wrote:

Another point I'm struggling with is
what jsonb brings to the table that isn't covered either hstore or
json; working through a couple of cases I find myself not using the
jsonb functionality except as a 'hstore json formatter' which the json
type covers. I'm probably being obtuse, but we have to be cautious
before plonking a couple of dozen extra functions in the public
schema.

There's three reasons why it's worthwhile:

1) user-friendliness: telling users they need to do "::JSON" and
"::HSTORE2" all the time is sufficiently annoying -- and prone to
causing errors -- to be a blocker to adoption by a certain, very
numerous, class of user.

That's a legitimate point of concern. But in and of itself I'm sure
sure it warrants exposing a separate API.

2) performance: to the extent that we can operate entirely in JSONB and
not transform back and forth to JSON and HSTORE, function calls (and
index lookups) will be much faster. And given the competition, speed is
important.

Not following this. I do not see how the presence of jsonb helps at
all. Client to server communication will be text->binary (and vice
versa) and handling within the server itself will be in binary. This
is the crux of my point.

3) growth: 9.4's JSONB functions are a prerequisite to developing richer
JSON querying capabilities in 9.5 and later, which will go beyond "JSON
formatting for HSTORE".

I kind of get this point. But in lieu of a practical use case today,
what's the rush to implement? I fully anticipate I'm out on left
field on this one (I have a cot and mini fridge there). The question
on the table is: what use cases (performance included) does jsonb
solve that is not solve can't be solved without it? With the possible
limited exception of andrew's yet to be delivered enhanced
deserialization routines, I can't think of any. If presented with
reasonable evidence I'll shut my yap, pronto.

Frankly, if it were entirely up to me HSTORE2 would be part of core and
its only interface would be JSONB. But it's not. So this is a compromise.

I don't. To be pedantic: hstore is in core, but packaged as an
extension. That's a very important distinction.

In fact, I'll go further and say it seem wise for all SQL standard
type work to happen in extensions. As long as it's an in core contrib
extension, I see no stigma to that whatsoever. It's not clear at all
to me why the json type was put to the public schema and now we're
about to double down with jsonb. Having things extension packaged
greatly eases concerns about future API changes because if problems
emerge it's not impossible to imagine compatibility extensions to
appear to bridge the gap if certain critical functions change. That's
exactly the sort of thing that we may want to happen here, I think.

The time for this discussion was months ago. I would not have spent many
many hours of my time if I thought it was going to be thrown away. I
find this attitude puzzling, to say the least. You were a major part of
the discussion when we said "OK, we'll leave json as it is (text based)
and add jsonb." That's exactly what we're doing.

And no, hstore is NOT in core. In core for a type means to me it's
builtin, with a fixed Oid.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#72Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#71)
Re: jsonb and nested hstore

On Wed, Feb 5, 2014 at 2:37 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

The time for this discussion was months ago. I would not have spent many
many hours of my time if I thought it was going to be thrown away. I find
this attitude puzzling, to say the least. You were a major part of the
discussion when we said "OK, we'll leave json as it is (text based) and add
jsonb." That's exactly what we're doing.

certainly. I'll shut my yap; I understand your puzzlement. At the
time though, I had assumed the API was going to incorporate more of
the hstore feature set than it did.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#73Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#72)
Re: jsonb and nested hstore

On 02/05/2014 03:45 PM, Merlin Moncure wrote:

On Wed, Feb 5, 2014 at 2:37 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

The time for this discussion was months ago. I would not have spent many
many hours of my time if I thought it was going to be thrown away. I find
this attitude puzzling, to say the least. You were a major part of the
discussion when we said "OK, we'll leave json as it is (text based) and add
jsonb." That's exactly what we're doing.

certainly. I'll shut my yap; I understand your puzzlement. At the
time though, I had assumed the API was going to incorporate more of
the hstore feature set than it did.

And we will. Specifically the indexing ops I mentioned upthread. We've
got done as much as could be done this cycle. That's how Postgres
development works.

One of the major complaints about json in 9.3 is that almost all the
functions and operators involve reparsing the json. The equivalent
operations for jsonb do not, and should accordingly be significantly
faster. That's what I have been spending my time on. I don't think
that's an inconsiderable advance.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#74Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

Merlin,

Not following this. I do not see how the presence of jsonb helps at
all. Client to server communication will be text->binary (and vice
versa) and handling within the server itself will be in binary. This
is the crux of my point.

Except that handling it on the server, in binary, would require using
the HSTORE syntax. Otherwise you're converting from text JSON and back
whenever you want to nest functions.

I kind of get this point. But in lieu of a practical use case today,
what's the rush to implement? I fully anticipate I'm out on left
field on this one (I have a cot and mini fridge there). The question
on the table is: what use cases (performance included) does jsonb
solve that is not solve can't be solved without it?

Indexed element extraction. JSON path queries. JSON manipulation.

If JSONB is in 9.4, then these are things we can build as extensions and
have available long before September 2015 -- in fact, we've already
started on a couple. If JSONB isn't in core as a data type, then we
have to wait for the 9.5 dev cycle to do anything.

In fact, I'll go further and say it seem wise for all SQL standard
type work to happen in extensions. As long as it's an in core contrib
extension, I see no stigma to that whatsoever. It's not clear at all
to me why the json type was put to the public schema and now we're
about to double down with jsonb.

I'll agree that having hstore in contrib and json in core has been a
significant source of issues.

On 02/05/2014 12:45 PM, Merlin Moncure wrote:> certainly. I'll shut my
yap; I understand your puzzlement. At the

time though, I had assumed the API was going to incorporate more of
the hstore feature set than it did.

That was the original goal. However, Oleg and Teodor's late delivery of
Hstore2 limited what Andrew could do for JSONB before CF4 started.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#75Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#74)
Re: jsonb and nested hstore

On Wed, Feb 5, 2014 at 3:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

That was the original goal. However, Oleg and Teodor's late delivery of
Hstore2 limited what Andrew could do for JSONB before CF4 started.

yeah. anyways, I'm good on this point.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#76Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#75)
Re: jsonb and nested hstore

On 02/05/2014 04:06 PM, Merlin Moncure wrote:

On Wed, Feb 5, 2014 at 3:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

That was the original goal. However, Oleg and Teodor's late delivery of
Hstore2 limited what Andrew could do for JSONB before CF4 started.

I also had issues. But this is the sort of thing that happens. We get
done as much as we can.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#77Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#66)
Re: jsonb and nested hstore

On 02/05/2014 01:10 PM, Andrew Dunstan wrote:

On 02/05/2014 12:48 PM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 02/05/2014 11:40 AM, Tom Lane wrote:

switching to "binary is the same as text" may well be the most prudent
path here.

If we do that we're going to have to live with that forever, aren't we?

Yeah, but the other side of that coin is that we'll have to live forever
with whatever binary format we pick, too. If it turns out to be badly
designed, that could be much worse than eating some parsing costs during
dump/restore.

If we had infinite time/manpower, this wouldn't really be an issue.
We don't, though, and so I suggest that this may be one of the better
things to toss overboard.

The main reason I'm prepared to consider this is the JSON parser seems
to be fairly efficient (See Oleg's recent stats) and in fact we'd more
or less be parsing the binary format on input anyway, so there's no
proof that a binary format is going to be hugely faster (or possibly
even that it will be faster at all).

If anyone else has opinions on this sing out pretty darn soon (like
the next 24 hours or so, before I begin.)

I got a slightly earlier start ;-) For people wanting to play along,
here's what this change looks like:
<https://github.com/feodor/postgres/commit/3fe899b3d7e8f806b14878da4a4e2331b0eb58e8&gt;

I have a bit more cleanup to do and then I'll try to make new patches.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#78Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#49)
adt Makefile, was Re: jsonb and nested hstore

On 02/01/2014 05:20 PM, Andres Freund wrote:

diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
tsvector.o tsvector_op.o tsvector_parser.o \
txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o

Odd, most OBJS lines are kept in alphabetical order, but that doesn't
seem to be the case here.

This whole list is a mess, and we don't even have all the range_types
files following each other.

Worth cleaning up?

I'm actually wondering if it might be worth having some subgroups of
object files and then combining them into $OBJS.

Or it could just be left more or less as is - it's hardly a breakthrough
advance.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#79Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#78)
Re: adt Makefile, was Re: jsonb and nested hstore

Andrew Dunstan <andrew@dunslane.net> writes:

On 02/01/2014 05:20 PM, Andres Freund wrote:

Odd, most OBJS lines are kept in alphabetical order, but that doesn't
seem to be the case here.

This whole list is a mess, and we don't even have all the range_types
files following each other.

Worth cleaning up?

+1. It's just neatnik-ism, but isn't compulsive neatnik-ism pretty
much a job requirement for programmers? It's hard enough dealing
with necessary complexities without having to wonder if some seemingly
arbitrary choice has hidden meanings.

I'm actually wondering if it might be worth having some subgroups of
object files and then combining them into $OBJS.

Nah, let's just alphabetize them and be done. The Makefile has no
reason to care about subgroups of those files.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#80Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andrew Dunstan (#78)
Re: adt Makefile, was Re: jsonb and nested hstore

Andrew Dunstan wrote:

This whole list is a mess, and we don't even have all the
range_types files following each other.

Worth cleaning up?

I'm actually wondering if it might be worth having some subgroups of
object files and then combining them into $OBJS.

Doesn't the MSVC build stuff parse OBJS definitions?

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#81David E. Wheeler
david@justatheory.com
In reply to: Andrew Dunstan (#77)
Re: jsonb and nested hstore

On Feb 5, 2014, at 3:59 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

I got a slightly earlier start ;-) For people wanting to play along, here's what this change looks like: <https://github.com/feodor/postgres/commit/3fe899b3d7e8f806b14878da4a4e2331b0eb58e8&gt;

Man I love seeing all that read. :-)

D

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#82Andrew Dunstan
andrew@dunslane.net
In reply to: Alvaro Herrera (#80)
Re: adt Makefile, was Re: jsonb and nested hstore

On 02/06/2014 11:38 AM, Alvaro Herrera wrote:

Andrew Dunstan wrote:

This whole list is a mess, and we don't even have all the
range_types files following each other.

Worth cleaning up?

I'm actually wondering if it might be worth having some subgroups of
object files and then combining them into $OBJS.

Doesn't the MSVC build stuff parse OBJS definitions?

Good point. At least in some cases it does.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#83Andrew Dunstan
andrew@dunslane.net
In reply to: Teodor Sigaev (#58)
2 attachment(s)
Re: jsonb and nested hstore

On 02/05/2014 10:36 AM, Teodor Sigaev wrote:

Should I make new version of patch? Right now it's placed on github.
May be Andrew wants to change something?

Attached are updated patches.

Apart from the things Teodor has fixed, this includes

* switching to using text representation in jsonb send/recv
* implementation of jsonb_array_elements_text that we need now we have
json_array_elements_text
* some code fixes requested in code reviews, plus some other tidying
and refactoring.

cheers

andrew

Attachments:

jsonb-10.patch.gzapplication/x-gzip; name=jsonb-10.patch.gzDownload
�K�Rjsonb-10.patch�][s�8�~�V��ND�%_�5��d=��rlew��)EB��!)��L���n�Q$E]����*G"	4���
�����U*c�gjM������xj�t�W����x��9
K�Olx���g�j���j�^g�z���>�T*����r>��~b�F����2~4������wQs���!���\q�w�H����m
/j���&�������}��!W
��fO��z����^��x1
�'YI��oX#�2|���-�2�T��,��j7��y���f��t�@b���gOS�v|j�{�{����K����d���>~������q�oH�^�3�7�`d*(���a��oBGT������uQ�d}(���$���^XR��������a[|�e!�5����Zp%(�BH�\�hy����r�jY4���*D���"��!&�0��4�bC�f����m�3B�����z����R��A���a�^H����#H��D��k�)M|�yU�������Q�v�5w��_��yZ�������k�W5�~Ye�~�Mb9UM�Fv�M�}�A�o
�|�O�����	���L�U�W����&\{0�1<Q}������j�83<@9|5t!+�[%i��T�CN]n�>T�f�c���Pu�	�J�l��v��_�� �'��r�R����L����J/'�#/$�rQa��5��hfi(3����:4�k�qP�'���b=p�HXN�]U*�P$����*��?��}}�:������
jom�TM�0��V��T��=�u�C��3���L�yX�4�0���T6U}0X��F�f@�Eu�- �CK�O�&����	���1�p���s\[���I\�8�����p�q9��+��'�)(���&�`��Mu���T@g�>p��<�O|s�Ll �=�*8G�>rw��������5b�:�0���g���?�=��eUZ^�q�c
c�4���sK�t���~����Rh����sa�54%"Gji���
|h�Lf
���G� X��SU
deX�1������8"0�{���h�mW'@	j|����O�	B�M6���+�E0�\SaJ���Cv�@��`4�� 'j���|�Oa�=u��a6�)
���E�${pV��p�u�6�����T*e���@	L@10"���p$���X�����rJ
����a�-�F�/�y�q\���P�������8w�>sLA�+]e=F8��oT|.�m|
��\�:��`����
�
��:������QGm
��=���!�3/E�
�Y����B���gB~�;L�������h�>�|�t�	����?�[�GP�s��(<dFc�2�d���|5��`����J����_4�6�9uT�/�.�:X��4?s1B|�N'9��>��W���+��S�G�J����f�M��40u�h:�s:X5���G�/���c�>����qQo9��zt64���,a#lN�\���><���PR)����z��rMSoD;C.������	�}����W�wQ��45peh|��:INN�Kzf0��.5�A4�����jut�8?9k��Q��:zH�s��9S-���[��A��fe
�5L�5�O{�!B������s�T�d�u%�{�H���H����0lhka�4��)n=��L�o"F"�t���IW��g���(b2�
r"V}���g"w�.�A#�%�����@�,�0�s�}��C4��3�i
�+~�]��2d��n9����^�����/
N/B�������X�l�i��:D�
���@9�Q��`>�mI�6tNA����k�8�NV*��SJ6��J�#�I�H�B4.#q
b���A��R`�Z$���?HS6��/b,�>(��\��,��P]W]|{ ��g1��94\{�#��jc��pf�����;���Bg=��9�Ip����,�2��H
�mzoJ� �'\�/�s�r8=S���7
%���C[_$(��J&+,��utw=c��t��`^�p�Z���(����b0�m���h������&Uz)q
�:oKI5�9�
3���T���S��1�g��H5��RF�����.��!��)��Wy4O�F��i(���s�����[c�^8��A�W�������b���_Ww7W�n�Y	�\�o������o�e�TN8�b�A��� �!1&l�
A�=���k������!��'��\����w?v��l��
�������y�:�wv/0&���l`�$��re����PXSa�VIam�u��=�����,Q���-����_�`���.�M�����d��3��%\W���6VsQ�;�0����-K8�������)o'�HY�l�Hi(GC�=R�[C���NEQ�o%h��^1�T	�������'�{�T~�����B���+��$!6�y�]Dd�GYV���
�����q�%�1k6�l���7��a#���?�
iy�P���Q�U�xB���-��p}���S����q�/gBO\��,���`�{�H�%��������� ��>����i
kU;��^0
�$��R�����6|���~\�����X�����]**T�QB�����%�.����+-$�S����( j`�i���r���e%b3(�A���������!��K���\.P�� �c����*0��b�L���.3�9�W =�~���o��7�E�����.��AQ`Y^����C��`Y��Qn-?%-��M���L<EHI���Q����5��RF~* ���4�0���������m
;r�+�<������88{"�Dg2�#5iehywbj?t�M�>���X��$)��6�{%'MH������T�����/��~{�G;��������A��LM�N���%�?E�p�zKIOK�B�����d��t���k����E�q�6��P�>�S��� �;��L����E�s���p��K�	�$��p��0��us���'�D�X�U������7P�`�Bm �V�R8� {�����VKn��H��8���:��j���������s�a�k��m�Q-�����w����Kl�x�,
k�|�XX���y��Sa���7��'����d-�!U�5�pOl�����%�Op�z����!�y���B��4�8����X�Z���:��!�Fi{�������g��SH���y��U��S������QI������|p�I�a3������+G�oj;C�M[*f�W�g/c�z�Iq����
���.DNi��z�Q�}q7��������o^�����Szu~��F'�W�0��y}��p~�I3}[�����K�E���+d�73����v������
`~V(�'�6q��z��0���$�v� 6R���#����Pe����g�Q��������~�����9�����f-p"�f�89�{����N�m��c�^���@
KZ�/
���\d���z3RM����fgs4o��p+
iC����s�0�8�Y��B����f�V6�|b{x���MA����Mb-�-������|dX`��d������X��4��#�G#��_�M�$e�%`�A
(m��*`@Q�1�M��skf�Uv�;D�t�/<l�<��8�e�Xr|�L
�Op�w�R�<m1��9/�3 ��� 	q���WO�:[RK�JiXz�L����4��������V[i�����r�w7���f&r�l%0�
�pv6�a�Az�,�f�������4V=���3�1
[����=����f��������t~Y��
]������&��,H��;�k��?��f�xn��:��S����6YKZ"�C�?�S��$���i
�����xZ5���S��zS��m:C�7���y�b{	�������
����f�F5�V�����5+{6���ld�*�X���]�.x�Hp�;�h��B�%�||���L�w(�F���S���9���pX(�0���(v�g�%�l�����Z�v=���������c?�{|+)�~*)�{��$q\���d���
Hk��P$mr�(V=�LQTn�3E;@s�3EE��v�(��C%�L�_�]a�yT��s��J�}U���~T)���Q�����J�uiG�RIlb�������V�4�-rb����'�~���Kq���������Ra�_:��?a�������E��r��8yi�{0�/%�7�@�T~�q��	 KD2�zQwh�.t,H� ��:��''	|5Wx@+����b$E�#N$G���������4��'|����H)�Wp��5s�M�����9b����
��%���{�+5�/�$�
�@B}(��h�U+����%�����d�6�,x����%�T���5M���F-��A��>Wm�[�{�~@��q�`[U�4����H����IU�������N!6��28UrA�vE5�$��K&�O{��\U���3�C8j�cH?F<|?ir���g�������o����
U
j�4�WM{\�@m:x4���z������o���|��j�����V{��z��w�*�*|�8WZ��B{���W�.�������W�]����u��w{���e.��]M]��0�����>�3Z����u���n�Yr��}������C�%��o������]v���=�0|��2x<z��r�W��fWJ������W�����K���-���`�q	=����o�^M���/����U��5��u�N��������e�^
�U�K)���l(��L��e�����
S5�js��\�!^���
�Z�������*�����$?|�g���?��A�t{�oP���E�x�[Tld�������>���+�s�b�-��$0���v<�o����4Cw�o;�
�s|��k�hT��|����x
�S

���|�{Vev`��Cp{�j������=))�����8�7�a\C�$���.mC��]6>@�DF�f��Ou<����X�&���6Ll��{��l�����Z&���`.i��ON��>�k���d�@�er���mD���[���5o��
#N��x�\�W�=->�X#|<�`T@T�������0��'��5��\}����a��>]�e����m��+��A����i��jB}A�Wg/k��F���y���������28z
�1��(�I	�7�� �������0Kvs���We�'$b�	���78��5=L������,���������6��oVD;����#w,:n�8���{�,�u��]��].���/�z�7��k�LZ�"Q*�-�����=z��,th����K
C�����O����$��h$2�������@���9: �r	V���_�0k���Rl\
����u`�H
X��o���f8�����^0�'g�4�'�u�i��~�0|'���+
X���K�N���<G=I��������H��gr6n�x<�8��F�/��������f��S��AY�yI2fT���"V��d/q�8dd�Y|CC����P��?���%��o���C�A��k���L������D��>��k�1x�i�7��+�OdNE�H+�"J	o�m�6P4�|4�c��]��[>�g�(@�������>������it�w���	0t��{@�����fq�������A�.��
��u����sF!������^]��V����>R�������.�Z�n(]��R
V�m��k/����_�)����������M�G�����/��r��������,\�v��`����J��h),������F�����
2?�+>Bj��~�*}>Rb�c0#cKt�8��0&z��
JM�7I�!�!�������j��O��%Gt��NJ��������Mc��^s~�\���.���=$�?��������������R��
WI����_k�CfHW�]�-X��{�d]�a�a��Q����:�A�����a�V�����/W���w7����uq9�q���w�����.��x�����yw����|�g��#z�V����������*��6��P�k�����(��En��;�����L�2�\����b�6��;h��#����R�K�;�W���	�F����B�*�d���:�W��G�MWh������{��4�dQ�o��������������-�Z�������P!����@����}���Ovc����!�s��U�FF����o�!u��(�t��
#o��3�>���J�Du�"A�a�"��i�c-jj�}^nh���MD������-��F�6t�
�U���z��%i=g��vt"�����f����������|.���K���c�d���rY��Wn��;��tT� �;��'�^��
�\+��2�Nj�����{���mrb'l�PMK�����S4�J/(���o�di���x�S�TSN�*�U�r)���������u�zz�����,*�
��@(.D�7�Y��z���3P�����1���j���/Os7H����x�2���_�C��Ce�13�� �5�N���(�	��.fX�6��1��I��_V�D}�Z�f�=�{�1����#�	�(O�m{��������<E{�#���$e���_,�-5(LB�X��������rU����Z��~:>a��*��y�s7s����_���5���4��6�B�n;�'��i�5�=Q�`dR0f���Lm�������m�?��|w���������� ;�O�d�h��v��ks�X�6o�����1%y-^�6�X��L�,���������A��c��qx�����obo����p��J|����E�J����6����L�*$y
 U��F��UW�?r6i�O��'*�4��������l�Z"�QS�m@��(}�"�^��/�$����g<g������Jvxvht67�x�R�kU�@��@��I��\,��H���L�:P*3�#��T
��b����������R]�m����H��}��]�a��l��������y��BUcS������M_���o���-6H !J��xI$��0�# ��ci�Sy5Y�T��7]m�V��*3uoC;�:��8t�4�~��dqt����.���V��M�T��H�����u9�e�I��E�����rL)���E���- a	�dY���E����V�����n�^1�P�����c{�Y�!����g?�l�= ��oQ��N�dvk�Kq]K�{r~'E�}��9�����D�FCu�E�`vO9��X�]���e�bVk[����d�#3���T�X��3�gD�L�%j���/3�J�n�kpK�}��k6v��E�9`g�'R���Q����D��D��e�3�[��T;f�"����Il�%���-e1�������-T�
Ek��W��I9���8�6~(t���8PTJ���w��r��?�a�Vd���x^S�&��t�E���8�6[��e�y/*��"����j�7����2���`�e>�
��N����b���p�Y[���=�"/����}D{�f��GM~��;���N�(���h�|:a�7���}�M���o���"�0����
'E���&|����r!�����������bhL��]t���i,��
�noO,[3%t���"��
�RB��o�xp
�`J������aKc��F�e*)�obQ��*���L�e%k��+x�����D@�����%b�	������P�n�����z�|��
�J6,��C�JR#pj��<�7A�Y���8=�0L��#d��T<i5@�Jx�3I���l��o<�U���s@Z�`���j��RI1����{�V��_�;c����um��1p���}|�O�9)��C�<�������i�6e�i}��SC���3�|��]�)�������F
�q�w�T��k�(��#1��ls����������TS�x
�5�z�q*��nZ~1��/�V:-�Ixn���7w�[�x��Yh�\&j`'�,����7��~��,�Y�||X�7��ai�qH�;�Y�6o��F[�����%�kYzR��Zk�_����"p�l����e�`oI�d���H�)��`��K��6�@C<@f���<�.������7g�,�z^F�K��!q�!2>���.*�mMI��KM�jc�R��)�L(u��K�S�	�k�	F���6�M��F��������<@�4(x��B������)�K�������i�M	*b=�=%��D��c :�_D��p�<h�5�;��G��4�����?�d��]��l������<�kAk�rfm$���9?R���T�0o�{���
�������"#���}��V_0;�,&J[]��P��NzV="�XM<x ~�^�>����@�D�u{J.�$)���)���R:��J3CF��aM$+@K*:]���`�������Cd��|�M�C%�#�tLtHc�]�g�A�sY�h6��a���3$e<���6�l��L;�z���p2��b��4������%m&���3J�a�b�b|�x�!:�w�7�Kk2������,����R��P��n�w�w�u(�z���{�,t}�����o�>�v��'_���C0�����{h�U�m��<����Ci�����������f���r��6��"M?�]�y��A]?�^I��s�����4���k�5���^�0�m5����+R���Y�$�Wc�Os�I�Ix��G��>5V[L�2j�s��2b��^����������l��oO�KT��Z{�gD����E;F8�V������
~��rzt�R2&�����T�����2I,bX)4r�>k~73fw3sn7�3����tQ���n��Q�������$Z,�i`g��dO�j���d�[O��wd()M��U(���`�jxa��o���9W�P7D��(
m����kg�q���K���G�D4H��=�����K]@�r��C�
���Tf��1O�
��
mFk
�z�f;�a �#���������KHD��dl��5�go:�H���*�'�E�������P��G��t�����43tVf3��F;��D��U��Z�7�
z��#���9��;��UN-�x(*$wQ4xk�Y�����l�V�c�E��b����G��%~����sF@��� ?Tl���r��N1���
�A����7���zTw~�S��z��c���t�����PE���T�p�p%Cb��"�^��S�)�4��HV��Fd�S=��������<&J|����b�<����K��D�B��h����t������������*p�R@���}4�������������V->_?�~;��^�����h3`� ���o���,�g�<�O����>;�F�5����WQ��$�X����8Z�d�q;�Fci]��o<=��T�4�)��;o�T�s���'�e�. �����r6I�Vlzq4%8l�BX*_�!)�X����D�����7�%+��r��a�G�hDVU�1f��K��;�-���� t�+��
7�&
V������*l-[���>�(}G4�!�5e�X<���x�G�=!�$-)��]��s���E�=e1j�:�;&#�F=�>DE�V�M1�~�X$G�/n��]_o$��]��]�����&jDy��V��ij?�uJ<����L�x��E���e�mw���	Wk$���cQ�z=kc}�V@�K�G��"��:<�T�������rqU=TE�|�3L]F�"��j��z�}�^X����I)`]��`u3��/�.T�%�_2a���-��}�D�����<G�����\��x�l�������<�*i����?k�����k�v4�s�wy��hkRD��h�mO�U���H^x�W�%�1{j���VN^��)�>E��$>M�K��Z�h���0��^�����d8��u����j7M�y���)��81yn������^2N�K�mb���@��
�u��N��V�9E���,�(+%��M��o���F�}����izd��Yr�MCN����@%���x-�C������r�0B�i�����K�F��m��R�6�07��	�|;m�6�uX��'w��q;�FN8Z�O���<�$�s���]�������}���T���]�G�wL��.����%]��O����F�M�$l�'������$��#�/�#M5���B���Lug-�t����/���p/&[�X����
�<�0o�9��|��b�X���CRlm,�^l�����������%OR�������Xl�T���^������c)���k���O����P����d(l��E���&��5��p5� G��� =���x>x��EY���v�@�+2��7Uf�0�������^==���/�W����ZR+�{@/]$96�Q#������'djL�?�hr�:>E>T���U:x�g�~�V>GQ�xe������(
Z|0�����W����X���1cU*������G'��5cV�9��fF�\�:����� �Z���ZZ�D� ���>�]%
��R�W�T�-�q"&Fx�K�jju<�Z�g�_�"J5���Q�
��%������W��Q4��z���[$`Pr��V'�^�=~��Y��!�.g���nH}@~��v|r����O'%������I#5��^�t�w"��+S���)�Gd@������Y�yHo���{��)�����d8z9����{�C�"���w�n�i��E_�..��%\SQZ��VBX�y�#�K���Y��m��� �sE�h����6�Rkh��S�Y��iN5��,
��l�g5�aJ�f��(�YK�/�yX[�g�\����ds�'I&c	�z"v����h�du�����_�e�7	"�(��Ic|.N��L��p�`Y��7����	m�d^7��M����e�l�����#q�q��f�I|����t?���rx-�2?�2V�Ll�zs��� ux'jjW��-5
7�U��Kk�dlm�?{��p��.�)��p�@��L�\��S������K�\;�k�x'��,����2U�����yl�|a=����%���s�����--���I�,���\����8O��3��?oO	��E�Q��MBs�(���l�+�HhkA20H��W��a�&Bd�~��pEw�Q��+�}���&�mG����;��C�w2�D�:�sQ�My�������n�e����e4������cHB|4�}�j���U�'=��0M?J�g���b+5hf�V�b��*
�����2�F����,�i8����	���C���_��J@���K�a����&���9�9���!8R-�epQX��K�4��Xl������Zp�/�A���P��d��1
)U�P�
�`�+��f����*�%�#�^��*�����F�G�'������-�\�	1��^]���b-���9���9G�l�;�B�r������Y�u���j�&p	��`	����Iu���i7�_�HxVpcr�,�/��)l���Q�����0�2�Em�%]��{)6���4t��X {S�1}�V;�-�7;����{8��K��4
7T�N�Nz��$vX�?�	�����nG����&�f�`��	4�9�$���"
�� c%;��h������}	�)�i��0�h2�f4v�me������fzC�O��|8��=_m`O�^7 �������;�(�H(%������K�C�O�!����=���]�����I����/�R*���x�����\�����g�@T�X��e���d >�D�y��`H��J��T��t��C���Rv���{x���c���x���,4y�p��CT�y�7���������!���� [2����[l$���<(w_���)O���&�]��3��B�e���N(��E�R�N}W�7|�G.�V8]��I���=�I���[(��w�n��O����D�RzcZ��J���x!M�����X�����<Se5#���Z�/����pkE�.�s�6��F�uo�&�3]�u���A�5�t[LQ|���f{��>Q��!9� ]��-����I2�N�	��7qGk�N��T��NGIy LH��T�/�'�%�M�m�Q��<���9c
1t/m!������s����uDy��
.F�8��]��ctU��:h8��������b*
f@��8�N��F��(Q��/�C�	Hz���=����������4�\K�,�4t8]��;`J>��C[��G*[A��%���,��(�!d����=�L�lnn�h�Z��u��a������[x�u�
��p2e{n�����g������F����]p��q[i7�k������hB��*�U�]�B�,�ad�V-�F�%�.�)���	�6%� ���s (��*��Z�R��:���|����-)��������.!54{	sa�A��:k	����k,f�d�G��Y��X��1�v�I�}%�x!�(�-ePD���B���W�Y�O5e����%|.� V�@}�JQ�OF�$s�5/B
���_X�UWz���2�&���^�-�H?}�_�1�������� =�-8s�g"�a���X��d�l���7�2#*�L0����(�~)�9����@8�����rg��������f�+����S[m������I��G'go�	����oO������35�����23MBv�X�����Q��
����������PE����>U&�Y�d�	��r�n����Y4�(��_����?p����h�������2���3'�0�����4���j��d�g�nP���'�VN�������,���}N$P��eyd�
��X��U�
�@
����w�6�<�����W���A���w���S���=��I��hU��U�v�3��p����I-���g�$q�b�Pp6^����/��a���!�5�{�+D���nX<�Y*�'�-��H���
��n���Z���9��:��;MB���!e��������I�JM�����C�K�P��	��>��qPR���r�,��t�e)�i�TY���@����X��K�=
ti� a���T��Sn��5Kxvb}�8�B��}�[�x���y�Lt�LZkP�B.���a�uM�oI����y�5�'i�KI�F�
�T}['�|}��7�N����>�
=����xaL3+��T�1���TC:N�8��-�;���9�8$09��:���>?�E�A�E��fA�6d�
�@�nG�����T�\M||��4AH�/�
>���x�$�����'yITs5.6��vb$:���P���!Qr���/��
�T�)�$3S�n�C�����0�Y\F^����h�P}�G�3j�K��r���Iguni������X<F�*������1+(�&��p3��8K�4@��!f�`�%&���Sw����Q� .H��;V�FA�j�+"�m�&L�b �	����Lv~�#%�P����[6V:M=[��(z���'���1��(`lK�T�.@����2�{^L��t��p�	0]�!�x�~������{,����c�o�:���>�������@Sa?R���<Ue�gY���f
YI^��5}����=��j�QA��K��l��������D�+�Qr����8���w�X�1��x�(dR���:G��u��ad�NAe����-o��enx�Q�=5�$��tb�3�����R��7N0*/���k�#/��w!��V*�r�m9����h�-Z�W����Jvp�����W�S�
h��
gf�@:��b����M�%9��c�>����:|]���O������i�s��
���NE�����M�w�$����T7�9W���h�V+�o]�)�1�1�y���@����1������fn����{�k�y�(N8���4��k/�M��rj��q'���|+i�X��g���s%k�����9�kB����%(����CN�s���	o�\��i%���
i@��!�4oZ��u}��4��[*��/�w�b;m�j&L)X�2!qr�����^����W�uK,j�rP�$~������c�Up-���pV"�B����>���7K�2���e�����YY�
�����&���9����rx��QH��.i��1��0����X9�����`�0�Y����w�i������}��qb9���H8�8�N�
;r04f�'[SD(Z�8�}��w����q[|��g�nr`a�d�q��99"�*��aS	@��h8B~����+)�`��9�)0P/�@����o
��1d�t.}�%87��bL�����~)������}u�#�y�������n�<���C�{n��(7��Gs$&����������=5.���hss�~q�������Z-OO3���r����W��w����,���Wq�w���������*�Q/����s�^G��_������}Q���6�0-a��P�E��%�9c�ky�,��.g�bLtk�����[��J�!@���3�z��Di�A[k(�v�����#L"��]������z����Q�}�����4�Q{r�^��a-���)�."��&�����\t;=��hI�-�S��-��J��� CY�X�G����k����1�qp����>���(
d��}PZ�(8��_����Rqt�`�m�D�$�����t8X(]����������5>nQ�� W�1�H`�Z-��j����8���FI�?�/���>��l��w�g5���||�.xB�J<��Z,�� ��+�Dow�
4o��z���d�	���v�)F���Le�D,�������P��Rq+vzC��������L��EmC��;����E�E��%����>c�Cc�c�Y��bF0_��q�}8%Q��i���3��l2T��p������O�����(�W^�otMN��R`�3�e=��em���u�Fkik&�H�9������d�������1g�������k��7�,�~;�{�^����-XI�F����!��)��w����[5`����^E������l���Z��:���B�D9~M�2��Mi����t������<�fgy�%4�n��~l�E4�PVU�D���!���2�����_����k����%�[�j�	0�:���D���]�bq��G1��gDy����0���^D��x"F�a{���K0]L�d��������W��[�as?��Dk5<K���5UL�Gd�D�~�i-p�A�=�0��9o����^3������.�AN/��#�������F��0�~$��I�12L�3�f�V����`P4������0����
��o���a,�=�&Cj����RZ�@��+���v�@���ob�������*�vH���$��<�@��_m*�N��'4:���6��C'��z�Z��-r�y�������r������d�B7���0����`d����>yV������\p����D\��?�I�<���>k�}�������_��u�h��@��8#�@�h<�0����G����|���E������gGo���c���R��������`���O/�0V�=H2�}�1�]V4�U$�\4@��g%��Ys(���)���%�J��5������(����/}���+-;kOX5o�SF
|�0|�,J!����:�F�|���%��g6��O
���'#-����H)��.�,-S[PZ�P�B|�^7�6�����D�j)����j����X���������X�������E/o�`�����[���2X����C��A�>����!�]��'��FC��<�� Y�[]�s)��m�,6��{���[�	�Gg���V�&s���E�CY�8�g��9~V+6���i�$WG�F���ur��3�#��9	��\8���K�a����`:Bn7�.���S�/�$%��B����������!p���>����rY3|���H��,�$@�e�i�[il����:H����m`�Xvq]dT��I2
aI��7(E���(#�4	y�)�\�(p������?=����,�
�1��6m�aU���%V\q[T���f��]���_n����`x�m�B���W�i @�u �Q`!h!�'��q��%w����F8��k�I%S�6L�M��{�%������`�zV|Y�U�����)&$ �+��!f����`���5@>k���\����}�R�t�M��7�E�a� �
d�A����e�,_H?=������T���e����p
�=��_���6��D�>�_�G�h;��&r�K��|����5{H�z��48v�5��T��}��W��-���C����(�;�#H�mJ"�B]�!�����4��{�A���O6�K���s�CFZ��~����rE�fQ<��Y|�j��O	��GZQ��JR=
��"� Y��A�Q��I�5O������T��+>[�I�R�(�~�5�KR�T��������l=�O�v�����Pw��<-��_��M���n�"���A��W�@:@�K�
�U��a��J�R�*j��m�Q��*��$�$��t��X�srgv�]�Fv�[oT�
;��G�����
2;R���R�'g[�R�>8��kKZ�����r=�y9��K���6���W�^����n�;E*,*���������#o7H��2p��s�����[S����%/� ���=���d!��-!1M�^	����G�"��;$29����L�u&�A��:?�5��{H�B;T���	�p�1�]5a�x����qz�t��:�"Dj��i���wQ��-y�;�"HFct�����'�>w�U��"��
:��W"�g�|sDF�G6�W[\D���I?-��>r�=t�'�?E�?\�1�v�MV���8	C�
 `�W}��n,y���s��;���)��
&v0^M�$��^�`�������K-��V%N{|z���3#��u��������;����a��+q����K���k1'������
{�_m�-����Y��|��0�5�B9����w�N��rS?�{^�lZ�Eq��2{|�����_�� ��T�QbI�7�_������.��
�sq9�������������w�� �]����`���nA?n����5��$����C�_����7�zN��g��#c��f�!S�����[��A5>c%|��>�P���$��G\<���>XmJ^.h��n�:_�n�u���c�>�<&c�z�2������`�I(G%�����6���������`}�ATZ��	�W�3��f��!5��ek[��N�=\���4/��{�a|&o��2�����l�;	q���Wv�P1���� S���zH����D���#gd��A����+���G��OoT�D����@�yz����X���}���{E_&g��o��F��<�s��@���0T�zo]{��j-�����	�J��T�]��dR�Ix��C��Y1�F�2Q�Kk������|�F"�����5lK�}tADK���K�:��z��$7��-H���7����p�������S��p
��9���������\��>�����s�j%��C��*���=���Q'�~I7��%���q��j~�����,"�x���[����z�gQ#����UfOa@.+��+))Sm������b���5L$���OE��6�[��;2@���m�����o�X(2�O��4Bq;V�q��doT��a���^h����n��K��y*�Y�!G^9�6�$n	��@)N�r&N��WW2�������R	�>��B���)�	[<��D��2�9@�"ac��':k��L�8�f�c�gR�rPF�6'�M�\;���+t#�`���\j�������o3�����I�*�$&Ld�N��C�
Z�r�c|�
�U�av���(j��_F����>aT��7R�5���(��O�����S�]${�9{��}]������Tt�,�����v�C����A�����/��|# v�~RPf4�[nOJt_�
OL����>y���z��6j����d���
�����.��s�q�i�!������j���I��BV��`}"��N�>��[]�(�n�HZ��,��mV�
���|��)%m}4y�?K�5�<lM��+�!U=��|#���y|��k�E��A��&�r�� I�E)D�-��4��6�qL��kuX�?���QZ�tvc�Z����/��S��[;��l�Pj]�Ls�G�+V���	�]	�D����H�+��{�tI��f'���K^�C:'���"��"})��Td:���J�r��:�a#~WL�`{��h}�n�TL���`��<MD�]���B�`H��H����PX#C��PB'��n���E�I�>���~��U^�Q7���)�"��@��$�U��#��O��gyP�D9%
B��A��D=�G�Kf���3�t��%�B���U������*(��Do�E�n]E�������`$&8�����+�z���BoA^��z��	��"�j�%6�,#�"L�)nt@9Y�g[z�����l�J��=�����0�*�X�K=}������������{y�zu|��@f�":������2emS���2\�d��t��a�	k%�!t9�c�����2�$;"<�>����?�J��P|y=�[.b�C Y|OE���;Ga�s�K�YTb�vL�zd����%�T�r�Vs���Z.J���Nj�����
Y�	�t��i�pCF��'t�`��}�s`����F�E��e*.���������T����9�7G���;�t<��u��~��@���o�����dg����<��X3�Ppy�h�3��/�������C'��/�C����5����,t0�G�XE^u�U�i����^�j�������t?TYC�w����,���Yjs5���a�2QQ\���~,�<�N�����L%-be������K�J%�9��e��$5�	�m�tJ���ph��-�UN��`��`�����}�RNB�j�:�gK�XUR������o�����*!��2�6����y4�J^
�W-�Z�u �������)��U�TWI�r��|+J�`���������:���_�x����u*���7o^�=;z���Mk�O/���������MT*Y���F�<	�f&��L�p����+Cp!�H;X\%�,K%����Q�*L@%�YW�KQhdL��B��n������=�uw��{��
3a� �������9��T=�;�" �����wKqC����&�p~��=�1�ev5��.�p�	�lKX
���T_��@t������otN�2����#���59y��P�*.�x���� �,3C��eM\��9��Z���-�
U�\#`�k��w`�#"(B�(��gk����y9�w�8p���z�����w����]
J�a�Z�����p��������RX����n��}/<��0_��4�KB�;�|��hl�|�vy���9���}���`k"��jNe�h��1�7p[1�.��,���EHW��+�O���np�Fs�n�5���jc����4~�%
��G��<��c4��/�#+�2��p:��Bb�"�"TLR8�S
@?i��)w���8EP�(s=6����=
�=�!�7=�<j_���U?����T�
:'3gc0f�Nf�N��l:�������aRHI�)�[v/�/S�^w����o�CF���D�M���V�3EBJ3!��y���G���s���
q(�1)h��x<�RBR?3�
Lv�[���L����&H�������<A Ug<���s��[y�_��j
�t��`�.3m�q%��1e_f����Bv��U�ou���{+���A�����Sl X"���B�k�n�����P�_�00��Sb���o��7�e�(���s�)={�:���9�����:��s�S�9��5j�
�����=�e�*i�bL�@�e-�����4��Z�U�W�����=��\��,�gE���������S��R,���0������W���j��Q��sR#�8Qg�H)m���n��t�K�t������<z5�q�)��2�9M��11�q
��t���%_�$�!)�{���M�c��
���0I������fhJ#��B�F�7f�M@��oB�8�W���M�����i1��E�K�H��!Hw���SL�G���5�3K��N3
ew�8��{|c�7
R����h��L�x4`���YT���]|�1��#�����*}S���JUb�L(a�u�3��^w��{w��5��jcg9��W2��\@���<��%
\ix��#����'gGo��_K�*
�2�XXTb�r~x�I�L�1�/��
���W�^�N��t���� f]�����I,���&%5k6�e��;��������k�z�b���B:�xp��o��=�����,����Q���X���K��j�=X��T�o�lX)dHW�VH�2�%o4q|9@c��x8�Q���%����~/�������O�+���fsI\����4���ut�R�^�
��s����>�"'���d�2,S4b�6+'�2{�*d�??=������f�k�d�-��afL��}��'�rg�/��m�Bh���|��3�M�7�E:�p���[�$�`n�>a&I�����@8���t�S4O��Z����	��D~���a����>'���Y�F����a���	T7���d�y�L3����8_<LE�Lx^��=N��#����($s�[���J�HTS��oo<�L�����,2 ��������X�I���9����(��(��K��T�R���(���g�y�P�tcW�I��V��(���X�8&��l+"���GD�4�2�G<�3z�+�b{;������n-�Y���� �����jE�R��;���g�I$I!���W$a���Fg�V��]��3\t���*|���hl��&�A�b�'��c�?�A*h�����}�D;��!�	�c�9gj�nm��d.�#,���9�3J�l�,{�M,�8��)�rY4���&O������[�����
�����+�SbQe/��h~H�@�H3�c��	�#�*(P�=��d�-��������MZ�	�`��'z����sN��R�Ec}~��"�%��|&P������q������#����K)`Q��i#�kw:�aZM���6>���9A�t�c+�n�J��ZeycydIu%a�v��qcJ�Sz�����S��G��}2��z�����n��������K9}�W��@���3�[m��"n�U	���&�����	�������}6������tq�C�g��~�&6'-���4{�D^r�
k<����#I�����LJJ��8�w��J���v���*�d�_5?�d�������Kx2J��L�W �����x@��R<)��D$2�l�R�Y�[6�S�@��3 ��n3;����T����?$��Y/*9�z!Y�GE3_�U���W�sj������[C�wI
0�d������}46����f�ce��d��� =��&��^<A�-I�0�6mX�Z����	HUV�I�Eb8�����FLm[�;*QU�rg�����]��uI�
��6Z:�$H����~�Ez�"��VO�z��bka�%�DE$O���U��E$E'�Fv	������;��*��&B��A(n4�J!�&_�����
t�������
����z�|�pA�6`2C�uJ�q���gH�Tq3��yu������e.��+��F��8��]�W`�%��y4�;)�F���Vfj��������=-K�SzC��7]]�]y��!�B����-�Rf������3�60�E��s�c���.)����>�
��L�����b��cQ':�������GWKz�-��.�a.L��h�e���C��:f�H��n0��H�<�~pF��QK��&
����2��^�Q��B`���%A�<����?D�+���L�R�jS���b�D�94tu�x�exi�&��,
4�s����UQ�]�n��,��-�#���w&21�veC���w	
�!���D����������u�PG�[v��Z��}m)7k��&o"�� ��&5���*��G"�5�T����=�h�h��4����Zc1����ZKu�s=�Yb������BCq�T�)(�a���,�m�����Q�����c��<�a�fj�lr�2��l�%�M�p�y�;[�����%y�b���_3}&���4h��#t+�.7�$-��qE�H)(��z@-�����h<�0~�4�����6��g�����4���t�a���h&^��V�V����#M%��g�y�U�������������z�Nu�et�,ni�����_}v��^g�G=�J��V|K�)2<�wjfn�u���@��������e�����Q��8�y�-,��"�����CJ�k�C�6����2�l��,�)����C
��K�
j�����BZ$�p(/|�?V��[��L����G�Z�F=��Q��i�1m���&�^�|��j%i>�D��d����������������F.�q�����F�	2��59�j�:���w	�!
�iA=R�z8�/8��N��� %��s��jS���R��K^�����"/�[[�JY�%���=G���N�H��GV�in�I�����{��|a.z����.���q�ao��O/������^>��i���+q��~�sXm�lo_����Q}k�`gO4������Z�6���J�2���v�������{��$;�u�������� /Q�)����K-�W���L����k��C2\��C�z-4���~�)�H�����X�*�&��6��,����g����<+6��Z5SF��n���P����w��F�k��V2W�*'�d3��ED���b�`w�#�zR�s��NN������9�D���<������a)�N��-��hB��������[|=�=9�'E�=���?+]&@@���d�#k�|a.<�J`��F�FXK�!N�a]r[�C�XCc�V�a�X��uB��,�#`�<�0��!���-"�,�1��\:Lvs�d���+���3��P���ML��=�8�l�q%���A����-fF��*s�e.{�	�_B���YD��K��5������z��������2��,C��du��_����� 5����J'q���.l�j�]���
,q��(gQPV���S�B����:+�����{��?	��g��g�F7B��7	$M�Lc����pE4������W�_3�7���	�"�o/��������RE������������\75�������hj�j��8B�Ci���������O����%�[rB2����3	���cO�C�IX�84�k5s"V��`���B7��y�$�fo��"I��w���`ub3����2��������r�f\�G��k��CrVv37�a8Z:���]����7��������w��E�n��x�EZ�A�f�W�`�.����N����gl�������]��?|���{{�B���E�����g:��=���(n��&���'��L$T���mR��I�@���9-B~�J�\�%Pm�fr�)�1Br��H�yF���u��f���d�����*�}��n��"r�1TB�Y-����4E���ji�yq��1A*�Y�*����p���og�)��m���B��fT~���)��fb�F�q,�[��Pj4�_�T\m�i��Lc�Y�LZk��zUx����,�����*�����'N�)w�(f�� ����A�M���J���Zm���f�H�EN�,ds�^�1�����&������T��U]h������C���?��}������m�L���is]H�qf;�b�}~{�������.f��+���YT�6��K�L�)�Y-r�y���	X-}]l���\��QsL��}��?g�%�o�Q�RU��a�}�|�����rg�[m)6{n����o���nU��?�M��-������@���u�`���W�0H��f/�v�dsZ�`��8_�4������F�0/���;[��Yv�J���1��&�2d[��X����j8I1�d��r��N���Y����E���5~�<O��f������=�?88�u�&[�<^�e���[����4�)���m�q[���x]���s��@�K}��v(�3_����qt����m�mm�6{��Jf�F�*�x�&<4��0������/�E�m������
Y9�r��)f�z����z����[���0g����=c�4�6�>h��j����U��&�'$��?]D]������Sf��f+c:�b���Y�J��9&�*4@
I�%�N�}B�qY�t�{+�*uV
���8�P����#��.K����h�����Y?�4��`��ln��w;��4��L�b�i0�7�Y886���x@����z��)��2]�O^R��=���|�+���$,��W%y��H��W��t���eV��N����-���\.[�]�Y)\����E&Y����L���
�L���|;GnN]Ct�����������3�G�b*�%|E��=W�H�s�'�����3O��)2���V�I�?D
��,��D��vwo�������2�i��I�r^��F�R��q`!=�3�<!��3r�d�O�b����Z�C�f�II���XI�4Ww�)m�:suf����Mr���y��1�|��r,Rn��7��Z
�{��(��p�6��<�����A�4K x�_R&5u�ln�����>�����Cd��$�\��z�.*�j�r���Y[�S�Q:2�|r�"����6%O�X#$"R�:V/��/Rl��o�����c��V��E��`���7�0f<�G������?<o�����?��O��t�[;y}v�);��(�[��p���Hp��;�w���|����W:`e���t�?����������������C,�#E��yU��{�4�������QCod�%z�C��+�Y8��C����]yC��L�h��'�6���TI4X��0�=�/8�v��2��OZ�����&��(�8j��3 �9�XlKG�����p
��Y�g��?�==�~���`�&1�pQ��gf����K��K��<9�T�)S��n-��hE�����?[r�B������2�go�O~���I��)��oQ4�����M>3�����{u���9��P������P3��hTh�.#��F{<�����O_�
N�bgo��7L?�M/^��*��~@7�(�Y��GA�(+���)RCL<(L�CT�61��g`GC��p�*�`7���xW2=�;"��4�'m�
���Q�\;��S�Hi�I�:@�j�y}�������?�'Q3�aQ��Jn-h��#e�N��(FL��Zj"B�|�
pD�v:�
��h����Sp��HuS��Z���}��������n�O�[P3�<G�O4(7�"-���&���l*��i
S]��q
�\��K�����I����b��b�p�D�d��,0l������N^�s����������e%��_D]���p��k�e���G')�~��T����;L��e����E�5�C��H�00JE��������	J�'$�z��o#�����b��W��QWj="v����m����KC
�8NA������?�9����`��NS�������B�H�������|1���W�; f(�\#sqh �������X&'��U�G�X��N���(�H��dC��e�).�,��F��
�[�_�;9k�(8��)e�-���������u����cN��_�=�/��7��k����R%�6�&���������*vb�5�`��f�N���>t0����r��

K�^�@*L_7�x�����k�a�&�g�XJ���~x������S�����>��C���L�����)<�7���|{.�!���G�&�+�D*`�
����-W�w�[�oHWS?1����)�����t!�epX)�c�T�����:L�n���m�;H�q+Q<�]�(����%V-a�&*��s|3E�&PUMX�IH=�
�!sI�U�5* ��������N���#}E�I�YQCLp4\�(��:`�����I�v�����cm�����H��Uu
2?(�0�(����g����3:��=8�G�G�)6�clG����h��-0���[j�6���1��R����XOt&H��p�Ub08�0��{���&�@I&��6eN,-M*2f,9���A�oG��8�j7��;���^6��zD����=;����e��z}��}��~��[�<K4����������s����v�7>|mu��*\� ���!�6y�l��W*
�����s]���9|hZ�3A"�C����*�a��y��nt�)n��l��,
c�u��SF��,������������H;O3�
1����Fm���<��`C��������&=��3g��n7����t�Z��I�sa����@��E�c�G>�K�.��p)��n��qK��q�(P_������� ��KT��V�-���F>��W����6�L���1�7$�
|�@�P����4�r�K�%nU�A�Qy��5��������Q<N2�^|�c�%X�~���\c:�xb����3L*,R�8�2�ZE�<�_)!�;�"`����C3�nY���E#���XqC(p���3D�gS�F �@L���Y}o��U��&$��������r����J��C?D ��m:_�5BKf��}�@\S��Iq�|,1��A���70T���Y�
f��>��
r��NsN�m7oY4�OC��W3�<C��a�B�r����Po��B���EK�M{e[Nl�u��>�s���:|8�.�~<Ty'�����)��Pt���nnuSEg�a�����(���D��)Hx���z��~xH�����������L������J�!���$����?����?�&��p#��`��O���	��t�B��#g���J�gP>�����������#�8�Nal����g������:%T��"|��`~�_�`\P(�.8��}�^�77m��;�Faa����� �$x(ta�2nGy Cc�8X���Z���F�����C��hO�s`=T��}��3}4tS�������h��r�s����/�_~�gN���KP��QT�$��,_da����G;	R�F2o��{�#��2�yD��T�#�c�y��w�2M�g���~4��4�i�bI�l�f�~3�]E�� �����0�x���Eg���W������4��M�~�m9�K��@x
=qK������|��Gt��3�
3D/�`�6�����H�D�B���xh�AK{ ��3aTW��8�05r�W�n���o?����?<��d����t4����
~cN�x>���t�L�gg}v�	�LI����H
.A��R�z���p�D�if\mw����3Cb����V�yX{�1]�7��m��.�B������k��v�w���v����E�2�-qr�i6�pp2
P�/��
�}3M�%��#�O������!�n��3����	,����>a on���7s�$Gw#6v�y�
����@q��|j��;�K��\*�E2�����\t_�J�
�ZYi�5�u���Y��p�����������>
B,J���,F���|5��>���MP.�����t�����FU4����
Iyv2n��l���n���y�d�TN_�Rd���������8��qo�,��)dy�Y������;j�NO��Zg���h�Y��e���3Mg��8q����
}��H��(,���6'k�r�_�
z�
�v_���.��������L����s;l��v���Q~���5@30��A���k�~���AP"4�s`��
��c�a�T}H�)���q����l���bw��Ts�K50��)�'��u~�w�R�}<�t+���0�;�T���rSu��/<�������7�E<�9lV��W��������j���k��K���u�p�ZG�j���sN�7�����&ZU��M�F03S<7!''v�	MO��2��a�������^QF�;2�P�9)@�"v�����%[��/�9���C���n��ju��
�y��;Q��Dx+2�L�]��-�	0�b����
6�>��`o�@4B�hDc����~��M�x��D�M���gk3���nd|�;����a
Fqv�/w����c�u�]���;���gK
��C�I7�������=�N�Xa�����IY���U�9-D3�c���2��W�-B�������{ZJ����tI��=j����\����#���h�^�|]�|�"��=Y�*q��x�2N���������6r]��B?8)�Y��p����g���CbCs�
�c�����V?������%Cq�:�0�����gG����q����#�No�H/JzI�7�R8��~��
8������g��������8����U���z�-���1�ii��/���� ]�����#�@~[-��<��n��Y���@�/E��Q��A��w�7����!6v�����h�R{"�?��U~|���5����>z{d���PSyd�A2E��@aH�����fX�����v��%�w�R��z���*$��VkO�yY��/��^��-���)���<Trs?����=kLJ/n�eM��u���\q��]�Da��i:���j���K'd����G��f�A�$�����U�� �C������T_�`��p�b������5�Dgk�L~pP�3�����G����|���oW9[u\
)sL��-6$���!z����cv��aK~�$��&5vT��r��	���m[ }ud�����~��������d�@��.d��I'E�@��[��%�[`���GS��%�������v�����b��Y�P(G�����
��P0�_��4��]�� ]���.@�N}Q�.��Q�Z�R��I6��Iz-��J�t��3����N�����������"�C��3��]���_O�sw�6����'M|������n��w���88����U���I��L�c8�3�"~-���ZZ�����@��h��|�U
����]�0V|���������2�ALq��)I�.h���m�g��UN�����1,�3�2�����@?���c�j�C�AU��|^�A'�9����j���`�D0&V�e��=3���pN�s��L��3����2����*���|�F��<�I�D���E�����F^��W��b��p2����9F��|���Z�vNb#��zB��{M!?8F���,�{�!4g��I��$����uo��+�y.��Fv�A��(����9�����o���,y�Q-���V@�����|H����h�����/��two��������:�,�����l���7<����s�;=��_a��u���K�����2'�l�����@�"���'vu��>�tMn{������*�9��*���cd5d�kD>G��=��N�~�������!X�T��f��L�-&��l&�������"��hU#_���f����sN�"{���rf�~��������U:�#����9�F]��f�	q�W���#&��(���G��!8�yh`@���*�����q�_�?��6��Rm�������NE�e�2��!��9([Z��^���7��f
�����������zy��Z��/��@�J�ym���L$#��	�����*��������j%�7��j�c��>A������Xk���=09��uV���������/g
K]�9����|n���bb�����I�Ia0c��lFv������4�z����F�p�y��U����S���*��46�*�8���b�3$���l� Z����^A���>��B"�VJ�,F����C��m�&�K��	"M4���������x���N��G3�������3���(
�����6���h���p E���c]Q���?� f�3��1��5��O�V�)?dVT)�<�15$�r�
�,���t��do�
������#�1K�W�� vu��x:/�YJ����,,P��A7Q0"�����������nQ����6J�t^/�<0��X���5�t���G!���y/6�!�#ul��5�c�W��+3���b��]p�s������N��;N��X��41��S`�-]]8L�^Ll.��^6%ZY6��#�jh�	0�?|X
�#����v���������=`�w�Z���qk"��[��;�����f��3�)��Y���"���Ft�����S?y����=��5�).�	Obt�?�;��
��t�B���>���v8����?2 �-a�A�<J�<Wr���Y�����n���E��g����	���b�����6]���Z�5����a1��j�x�w�3
P������9�P��/(g�L����x0��,~�Yg��o��^N�~��`��|�g�2���j��"�A/|�%j��k�M?T�����X�
�)O��e!��U�r���E��C��]�VSX������N��r���.��ak~C+7��#i5�e$�r�r��/����6N��*+�*+�*+�*+�*+�*+�*+����*+������o(�������Z
S��st����!Q��W�������h�< �c�LPCJ��p������E��[e]Ye]Ye]����������������������r������U����VYWVYWVYWVYWVYWVYW���U��U��U��U��U��U��U��U��U��U��U��U���*��*��*��*��*�J
\WYWVYWVYWVYWVYWVYWVYW��*��*��*��rF������������������������������������������������������������������*�Jp^9�00���\e]Ye]�����������������������w M+��=�#������+w�����������>#7����5��5��5��A��5�ms�,��F��]�]��*��*��W`�C��$W9t��:j������s]L�R�$
�D9;{;���������N�SC��<����I�<:��}Q����
�NK{���!�O��9��T���#���e�{j�&���M/��OaZ�&���^��>H��E4�0��gq9������
�(R���a��$�������u&�n7���#>]���&��~��vq���������d��=��a;����������"Ku��~\�6������4B�W>^�p��� �R�?����'`�����rC����"r'��om�ww67���v�i��	~#)��/����lTwE�X��WcM��E=v�F=dI�|?W/����~{�i����~��7������V.4Q�@��\���V��"_j.�y+'Wswo��\w��5+��B����	j��Jw���P
����!
��	d�M�����$3�����H�����t��01;9����"��,��(�Y��	:�X�Ecb�bz��9b2�%����������t���NA[��{�x��Y�Ag���SH�E�u.�r����d��p���cX����'|��]����[V@����[f����[R���D�[R0��v���m���^�����N����~C��o��`P�;��l�^9�rY���k�~�_-=�W��;*���eZ~P����t1��30�=E��@��'5��7���MB��t���&3��}D�YjP����Yz��{	��(/����`,_1�����,/
��b�,3h����,1����#��S,��
��5"^|��������_1$�=�C��
�7����������������������V�ws��������=��o�~�{��������WsD�\�;��
�%_T\����n[����w�����=\�Y�u��%����K6���m���
nested-hstore-10.patch.gzapplication/x-gzip; name=nested-hstore-10.patch.gzDownload
���Rnested-hstore-10.patch�\�s7����+P��J20f�>9v��W�8e'U�J�$�)���d��k��C����������_��xph��������3;N�}s��LGc�����N�������@��&��1���*�(UB4���6��f��U�����[i��"6��`�=���O��I<�^^]���o�����;���r����X=�v�����7>�5��Z��"3���{�J�����\���yk�7C�Y�y�����?>ra�]rN*�y����_^�G���|tus��O��W��;������F����wF���;���b�O~���w����������~x4�l�Y�w&o���&��;�5\��}����C���\�!�����_�BM\�R�3i�_>y����x��;d�'S������4��&�������OE�>v��tl:���;��Z���d9a�~p>�_=����O�����_|�_�����.�o��*a(2%k1C���s�8�����Y���vp�<����������������>�����M�v�t��M��!P*���=��}\m���7�^�O�=}E�����#r�j����-x�/u8�~�������%i�m�+@o�)�:]SC����Y���r��P[�`��th���3e�0�9��lx=`\Y�QE��Ec!h��m��]�A������G����ODy��q�	�������=k���f�����&q��`���AMY����B�QeD�(�Mf���O�Vh�_^
��N'd�����?����w��3r5|�.��r��'����
�iGc��W#H�are/���w��>���Lz���C
����z8�a�2
�lx1�C����"b>�WW~h��<���t
����^���T�Q��
X)F.����x��!����HqJ~%p�g��+4�NO�_����
����:&��������B����\U��:T7a�h��X����C�s�+�������/��:��i���Lo@�����z
>���~HiM[�^��<�����S����,i)���J"W�
V�U:�"�D�2-3�^�R�,Ixg�"�(Q�l��%���d%r�]YI�4JXnP��[�P"7w4G	js%�$�390
�*m(���)s&�x^�s>Ih���Q�r�y��� �0R2��\VH�'�L��������"n�)�\��V���&	A���QU�:�-h2�2�p��.�J"3�sj�^aU��D|�TT0��i����<IX�y��^a(X/��+d@����He��4R����Pgg���C��56��&	����
	�����S�Y�O�2'�J�+WfT�^	�S�a�p�d�f���1�$��G	e2+8�g���,7S��$QPn���������Up6���%I"������*�e��e`�����.s�DJv���$��+���>8�(O���a�,�^��2�!>e0�����Nd�yAs[j��JC�a�KEK]��g��K�������k9��d���D��R��4�#�z�UV1��A:���+N�H�P��Rz��eZ��_�i8��XX!?�4���a&��D�
c�)<�?X,�?h��C|�t�;���e�
�` ��J��p�$����S����XJs��k2�?�vAK��f��t���X��?P?,�����P�������B�N+
1L��
�r�7�h	�q��ja%��u����R�����r�4�g<��e��-��?d�
#9��T�B���J9A75��2���CI^Bd�q�(�����\&�C�&�C|�7q3�q�fK�VV'~�������22���0�����y�����<��M�E��%���A�4�-��5[�q�N9�3�/�JK�?�����B*C(1�TS�=G�rGJ[*�D;�[���@)�JO�q�%��<$~�\q�K�[P%R(Q*��qS^�|�G�N0�#>E�2�F	�t��D���6�#��tP�P�����!"^`�!i�]�G�����2���p������e��c��<��Zy��d�H
#J�gJ$~8�8b�TC��8g�fB�O0* &���������8gL��B	�J���k3Y�z
L���a0y	�k�MB\<��$,�y^�^c�9���Ls��&%!��a���aQ�7n�����Kk2���H�F|�tJ��a�)����q^{���&2h��*�#>�TZg%�G�`�$,�<�h�74&?��s�y��GS�H�������k�q�
��h��&@�w��%��������:��A��q������.����T�������E�?
f<�	J������G�Xar���q���#>!P�r�M)���c�Y,�F&~A�H��� ����fBp�.D�G*h��)���OE�I�/+�ze6�C�\)��+��%JU�\����.�C{��h�������j�B����	����D{�7&�g�nC��A	�!����k������K�0+�:��F;
E����������*���\�r����r�)��q�NAp)��k�)?`�>e���`�5�&5�Y#������PD~�q?.YW)�6.S.&��U��������I�_q�������?ON��zz?n�����t�*^A��;w~���p���;���G�q�%v/�hLNbc���-rm`�}�h���f��4*ZZU����a9z=��=��c�-���-��m[��
��E�~�"�m�����_��0nl%������kZ%�D&����qz����T�?L�xX�N���7)���A�e���	X�r�6��������>��C����_U[a���?\y����z����R�8c�����U�d��s���-��1��zR�\���	d���c��}w�|pt|�^%t�A���G��9ad<z� ��� � ��o��	<�^�@kR	BG��LcsY#���F��[?]�/��/������%����e�����	X��&I�8:����-b@��c��_��o@S`����7P-�
�����$J3��P����ln\TG���SI��F����?j�#��#nI|�_�I,[���#c�<�7m0��`rd�l��$�����hl�[4y��-����Zjt�;���m���!i���x?��k;\_�>���`;_�����9��
����[I{����-^���o��-�U�k����]���(�_�F�q��F-��)���VoqF���x3��tq2@N�x�?��UE�|��~�������/�����Y��������3�g=7V,��xz���y��4;~��~����Q��E��\�x�.x<���'�>t��0����|{}X��_pwv�,8��>�/�8��]..�I|����N��R�J j����#p����K��/<Y��WJy�\���p�Y<:EX,w+�����i���hU�����sS�bpR Z�
V3z�#FQ�I��C���T�,K-��s�a��3K����m����Kq,V�m�����/2���/v*]�k�u���o�^���no��5e`��{H��4�]����~l��.~�N�]��'f�{�qi*����OQ|��_v��[^g�������F�=���t�V����J�x�eAk5A����1y�$����8�`{�EB��A8^)���c�j��]�V��m<�����a�Lo�aKD����;��;����H����	x|<}�@�K��v�n�����a�v+l����B-@QY�b;k|����l���T���o�w�����vw�uK�[r~�J�TV�J��z���G�#�%nm�W����'���'����f��.,�{���a�ogMvmgE��j����~���Z��������q6�'�����^��/�Qg�u���?k��w*V3���q���ri�mEy��daw������d�������n2�9�ywJ�xtIN������s�p
��:`pv�^���8=%�Nt��l�����������kBP�K��f�sW?%�d����������x��v�5���(�H1��'S�`DO�i���d8���g�8�z0��b�Y�A�T��k�����*w9�3]�K$n�A6��K?��q�
S��b��'~<���������l'��<[�R�F�s���:0����3Z�d�M�y��_�%�h]	J0��[��0�8�Wi&E+_{�lM������]|r���\`^��Wv�K0{ya���OB� PE���6�6}���@fU|	2[��������kyL��<�����R���60@v�I$������X�������'??�]L����ex��GW�z��w��K��=2.�P������W�o���|���a.+����=���'^��q�y�?�s����{�C��V��,G���������d�����!o6RW��L`q��������s2_��E����IOw�����}��t���QF�*���"��Uj������|E���X�u G�3.��E!�*��=������A����?�f��P������<jU x`��x���ZY�������vW�0Y��5���0�CT���5�af<\��� ��]M ��Mk��������O�����s�Z�f��<�yz��oV��'+o���TQ��_�����G*2����pe����.����#M���j���)���#�}&d�c�9M!����[�.����M������1;�<��^���{�hZMJ�%@`V7�j�Wt~�1����j
���������OEh�Y��h4�����r���G$R� �6]���Q[C�dz2�d~����hr�,��~	Z�����J\�=�to���,]f��`4��Y)�3f�����s V�r)	��p�����������I�3L��I`i�$pr�n������lap<���"��������mm�������xN���r%��$���$���7��i�6xbl�mHX�����Hj�[}�%�����R�T*�J�R����Mm��6����l/�
x�Zp���{�P�hi�Zj��Q3U�������>,����_`�����x{]�����8�U��kW-yA�����ID����{�u��~����=�B��Aq��Cpx_|Y[��/G��s#}�)�����m�o���k����q�S�M��
���EmV�b����Q=�+y�M���I�Q�Gf�)0������_�5eJ�b,��1.b�g��d��]6
^�Ps�D�V����(2��������w���PQ���xFh�X���n�C��������gGbzJ�5����u�&v�_���p�<Gz����5���i
�RYXx��GV��8�*5+2��o?��M'���{��KL������q-0�[�}C:�u����a�m�����y#RH^�/�����|���>���;�_}�H� �<�{[���R��v�h��g|]:a���;jb�1�&�����
�O1[��y�5�

��X+���c3��|)�^`��T��8�9N�ug�������Y���7z_z�4����X�����"�gm%�i��b�M2-���8���h�!�[�u���Y����������u����U��������o�5>��������L�
����eY���*����eB�N�C���j$+�Ilh
`�'�y�O�w4��3�5H��9@��\LAR9��-TRd��;���.`{��Z����as��o	����D.L6f6���d^��q�I<wD������"�(�$R5.U�r�E�YZ ��th����I �#��|��
������!KQ�$�( 
���b�$�J�O�h���+�@�ts��B������R��-)�fi���ss���zN
��r�b��CF/CT��d��H�Y����k��!�B��'H��N�Z�Y]�"��p+#4��}�3�Kn�"�)�7������wj
�E,��{$�7Mn�zO���o�	��C@s��)�H0�ob�~Q�'>�x�<���*]��;��X}���T�B��QA�������?�
�xz(�����b[�v����������������7e��
�bB6����<�v���V��)O�U��6�>a����	�xv���hO=�����a�$Q��'�'��4�]�x���r�j��<�;8�/�^E5������C�LA�$�J{�7������$�dw�������|��v	���]l����_�6.c2%`&	km�Z�q���zU�5~���v����xho���=��|��0su�:3	V�������2����$�	`�����L�RN�}�8�Ol����������
����	��Y�jZ*��\���z�����|C����(����-�-o�[� }o)@g�jA�2����@�������hf�b']&g+�b�)u���6�	�I
TLJ\:����)��U�����m���FT$���Y!wYHC�]�o�Qx|�4|c�%S�E���D:��Jl>c���C��v%����e'U��/����1�q����^�Xn�)������+}���F�axj���v��|�0��G�O�J����1m�O�d��-��P�#��^�b|�����Y��3�;e���D#������0!N��I��/gN��]������3au����82�O+�z!C7K��)>y�P0�� ��h\��~�����
���_|5L2�r�#~7,$��k�qo6�K4x�}�.^������e��4������\W�U���&�ev5=�1�g0�1��j� ��=�8AKF4��kv��]�E�����8e1Q��E$���u�]����()a��)&3�X����=
+����C�������
O���wU���]u������(���^�`B�����$�X�]����G�Y8D'��t��\+@I��J���4��A��z��A�?�P�+����K�P���6��P*�h#N��0�l'��7��	���L+�[Q3*'�f�������$�2��m{�e�m���n*Q
��X�����Y���p.��}��&G���Q'����@g���'��8L�x�pOf��;��t���ts�	���?���,O��-P�y�\�~�e������R��l�[�Q{C�=�5+(�����1����-;{8g����./����^]3���l���.[~��n��VCaMnc������Q��V6�3���������
:�FH�
�\]8}�~�Y���������*3��U��Fy�����<��z?'���,��� 7g�w.�l�I9�������f����b����b�/,��_[�5��k~)�\���AsA.Z�5�%����3�L�8�
f���K����/,I�����M����K���Lf^��j����nfN����l�3���-h&��(�YiF�������J���������=�07��C�G
n�~6H������`�3P7u��A�D�B4l���-����rSB�����I���C�Y�m�PaV�6s�0+/4�f ��
�R{��m�l������_����L��|Q�nzb+E�nC(g���~kX?eK[�9�n�j%d��b�������5m��vy���D��%��[K���#eo-K��Z����,A{k������G��������
I[G3 o�,d�$�Z�>m����M��y��2�3Kh/s����27@Kh/�l�n/{7������@��Hy g��<�3�C�F|.�6�,A$e[g���/-��:�h�K��lC�l�?#�����ra.A���1�0� �f�M�	k�|����9�����b�g���D�'K�	o��G�	/|{��7�K�����\2y��33��%���<��m4K��1\���F�x3e�f���w��8��{�}����O"��c-
&��5#Z��RgSnw�-MG�^���yH�	�d�+E�)�5vR�1}}��:34v�����<����Xp��b�*�_�{�
4N�
4:_
+���c�O�]����e@�r����whR�4�}_'o�)�\c�bD��
��l��8�[0����*��E��@�fq���Y��%1�\)���mx�nh��Ces�����/]���K��*b�Lf_�'9�>�g�*�b)d����O����T�o�'Y��!������_�����gw�����/��nZ���]��E��r�\{�d�����=�
)��|{j}CZ4����	�����\��2Ge� I��A��_
[�u����|j}-��K�/F�oW�������7�|C|���E�lK]>����kQ�?����<@����z���)��/�4����s������HX�]�i�jJ���<qqt]o��,��[��������]v�G)��wn��N�������^=?�O�f���G.i��m-����I0�F�p��6������7fr�"��d�y���r����+�N���x����$)�s��L!�&�6��i�k�����9\�i�E�J����M�M�t_������?����l^�)�p4=^E��h��1�����:�	.���YHZ�����|#�3�����.�����bV	�����VEdujV���X�s�Z���Y�����*��w�LY�t��q�3�7}c��zR�'_�?9�		�����A�����Fa	O��_��D�6�+�����h��C_,����~i�U��Ey���I:����9�@�kE^2G���M�4�sV���F���u���>�7��Z7^��E��V=|��UU���?<���(��b��P�	��wp����'~W"���I��?���+��{GO�_@��
�}����'.���q��]qxt��������G��X�Y��X�V�?��+F=��/���o�hhub�!��F�k�\�6q���)�iz�qdm�xh�c�xv��I���S�4\�����r�����3fX�d��lY���s�Ql�6��\�q�f�����D~����L��)I~�t+z�y�����*��G���=�v6MS�bIR�c��5�YZ��g�������m����8�/�.n���Y��knz��������a����
.e�~�~��e�r�T6=i�T6����v������pZN��r�~����J��N�*�` 7����I�v��IB��
�1�e�0	�����,{�[�'6�1*�dE_H����F�H�e2�\����Pi��l-G�^�z�����#��j����-|�����j�����j������81��Qh�#���Lg�j����
}�����}�����}�����}��+�3�����3�]����]��q�}b��s�y�~�~����3�gN��3
������G�Vr��;���B&�3�B�2{��p�O:_�{���{���k!m�� J������;�9������3A����[�k������t����g��J�;��8�1|V���v:������]@.��������-:��X<I~���>t�cC���[������o���{��{]z4<����A�����]p.��W�g��R�ulMu	�*�M1��@��Y�9��-�3!�@.�����^�PBk�����1���S�����5�0y�|��-:����I����{s�Z�v�{{��qG����UA��bo�d]O�b�S�u�V��x�'Uk[��_]�}>�q��j�m$*9:i������?A�}\�:�J��I0�^�'��T����"�.Dl %i�mo�C������'Nwv�
�J������k�]C���Z������Yr<��1(r_R���1��B)]��%j'�]G������5dx Q�	�����b4��U�n��f\,��,����m�K6�%��M��u���V��U�}��{�"��<���	����A�v
�V� g�.�����W�6��b����QpuJ��P���I4j��b�E���!��o�Z�[\���HR�K~J���$�;�e�'-/��U�3��jo���o_�x�Y��2.��X�������iUv�
��jE2CL&U��vWT?�/�,�r��Fa �9�,J��z�)�+B��/C��h4=>���c�w���,����q��t^\$u��u�����O&~'���2��t]:Y��������_�`�>"d�����F���6i�� MM�����8�{� ���I^�������_�$)���_T��'��i����W���@���}AO�:��s_���3���)_�H�t�f������E;d��J��d�3���[���{=�l������
�����u����j�����8�/�2>�^��Q7���{[[+�a7�,6�o}}��q�s��
�I���w�.��h4
��������!-�������"Cl�7!]`�47� J�h���*�>���`2�y��*N��r�����$0���K�q�D�Tn'�R���W����o�g_�������d:B������/�&�^��`�C�P�,AS9�
e� ��������`����b�Jg����Z���@��*3{��$���m�x����	��0��X �ql����B�AW�v����Yp�X������8�t�`��E��T\�� ����A���9V���������3�����L��X���])�l�E���2d[#W��D�Q��'��p�%I���D\c�2��)���f�Z��k,P�5)�K�q��;U�����"@f�*C�;��#M�)���lI��W�4�$I�)I
��9��na�8�Y�$k�6sDJ3]�d�G6��K�f�Ti�K��&G�`��lr��-^ZA{c#>������ZmO��s�(�w�^�$J�9/�Dk����)x��]��{cIp[�"�[h��O|~���:�}.��A���y�F~����V��6s�!u�	
�Mlv.���rf7w�@\D�c�d�'x)���
1���d���Z����7up]��r&f�,O�Lp3Q3��d�'E'�L0[�03��6W�N=W�g�+n`����+�Mo�s%
�s%��s%�D�|�[�\I��3W��2�c<����_E�:��O
I�.�!�,��E���D=TO��c��n�_8T�{�U��_L��U$���`��	��rA��� 
/��I������_��0��d4 8�41������R[���3�g���X��i���i��1x��2&���������3@,]��Gr�
|��.<�t=��5-�i5������=h2�[��_���|�
�_������3$�_��%�k4���+"i�������'�D�N~�z����axSO�:�����
j��HI�M3
�_Y'������IU����`E���S�)��n:����[^���������w����������C�Jy�C-�����5o���v7q��F[�F�-�z����Q�p��t�N=o��Qn�<3pr"�=;	�)�;{�W��b�P������2��p��8���A��m����&�������t���7�8b�������Z7���ho��~������I���o!�����{NQK� ��ZjB@0��[���IO���g��`���z��U�{6y��#�K&u/r,����-���Md�<�h��{]>�
�����������,+�r��0�nP(S��1�f�Y[��1^���N�Tv�[X8���{n1j<&5���~d�����}�E���b�#� �[!rd]\��f����6������W�B���{��/ry���q}as�>]_��&��a�24���~(�=v&��T�C�.&��+���{b�O������Cq�������G{b���^��z��xt�F�
o���������
o^?�:G��*��Z��-�P�I���x���?E�d?�y��a��iC~��T��z�f�9xy�9`������o^��~z�������z����VW/���������3��c��_�=���������{O�^�qXm�Qy����������$��9���g���G�^=�{��`��_i��3{�J_�Sz,���,>C����c�kv�UOaq6:�����k���������������t������k��$����Y=.��rC4�b&�:�}�	����x8���l�]����/.���<� ��^���������rF����c�� ~Z���{���R����-��uv���f	��z�`�r&���������7�D"�ee-�[�n�>{zx$�):r�
�J�_�G?g#��A?>��i�����W?�w?��n�*���u|�A�R�=��m��`�O��j��|���?��i���s
(F�r����_�yc��}~.l�� ���z.�e����ia����9!��lS�P�S�M��$�Nl���<,�=	k�9���%"�:a0��h�g��|f �h���-�v�8���I�~������"W����h�28���#
��TK��Q�������3�t�q��,�(-��o����r����5�����8����n;jT4�b8�Jl��0�!���`�I�`,�L�������A��t�8��-���(b�e�7����-{�g^�E�2��^������T��a��qg��!���8�A�N���XIT�Q�e5���'�7�������~(�]�LA���E0�r�B����_I�!(��0�Xq��r�L����5�g/��&Gc��������Q������(����!�������r�� �Z��d�=���k�S���*����`7��l0�0���-���
9�t��TX��lq�xE�
����Ww3�{��^[��Z��d(�%7Hg��m��o�-�!�r�`n��S�V^��~����7/�d����#jafyW��
d��,���"�4X�j�bfJ�����L�s�����f��Oeu���m�.�R��'m�TxRT<�U�n�.P]�������^����6�n�Eo�u���^W�6��"�Ur���0�������/�a����4�\+Z���I�-���ixU�_��p�\d%C��6�%KX�p���J3�KM�kf.l�-�.�z���d�n
����H�[n��`�Mv��j�^>s�d�����Rb�_��|������`� ��o_�9��;_���7G�K��������E������������m�������[7��4�Yd�&a4<����$�p�N����zWg��a]'��e#� o��=i:�����f�}p�B�p��H�.n*����}N����~��>����������v1k�'��V��L�:2��6�q��34�8Zn����L�+O�������4(e�����R��0�,D�}v��^$1	W�Y�1�E�=�=?1�E�v�^tg����6m��Z.$
�B�./���e��&��6Q���uz:�T���6C���Y=�l(x�Hn��P�z���B���3�{v����S�W`�+3m;T�H���Bi�I��B�]������+j	�����[�c��=��"��I��;�����#�6��\5�o!�����r��|�B�N��w���8Xm�c%�S���`)�xEZ���Hn!!zh~��n�R����i�xr�������*��,��aBH���>��e;`[�S���V���c��0��]���]mfc�[�K�A=���C����^�$Q!����[�3�K6�W���Z�1�A�s:���s\��}?O������(�f���A��-g��m�`�6Cw?
��s��}
�}-����E��k_S�
�>���b�����cC��f�r<_�i �:��<N��}�.O����,�P�Ex$���;1��@�������O�� P�UR)�����N9��q���a���X��Yl.��i��X��%6�������=����=����=G���������c�ulgZ����kko�=���|�x�h'�5y�(���c�Q��7����w������{�,p��%tw�t��Y��gw���u�
	:=�1����?g4&>`�����������{�>l.��Q��w�*6�A�:
��S�Wv�agt1�O���!@�Z����W�"�O���u����CxC�~!�:!�b^9��V���/a0����~�{�h|����s@��x0u��zg
M�;K�`��=1{���.���������x������-eOS�q�>^P�]dOg���/��������L�q5�O���*8�=�Q�Bs�z�Y��lU������-|��I��P*�H>��z%9���b�Y&39^�2�mNy'�,f�A~�9BM�rY�J"����N��2������&Fe1�df���\E�p�\���!T\�'A�|��5������6�����:���B���%��w>��A������/�����4���v}�K�]�ps�7�w]��~�Y��=�pZB�b���U�>��c-W��C�����pmm����,���=�'��U�f3^���|�7�\��
��l`-y����bi���L �������T��t�a.�Y@6c@�x��d������k��wc���L�N.�{q�J����� �!�����2��*�Sf$��}a���7�������@7�u����a));���el)[J����.,?O�X�#���|����9Z���Lw5�-d�����f��
H���r#���`�n��{X.8k".�������_ $k��C��/��P��Ds�W�"���b��C���o�M�.-�_�����:]��%�������o���;��!�6���~��<�L^J	&$�;/5����������<4K�����
�8K����.�t96�]�+��_A��&�_A��
��W����.�g]���n����������/Q]Nv��������5q��~�}�|��E�'^n��BC����bC�������IQ���0�@��P���2������01���(���3?���PBC(=5�j��8��p���/<>'
�L�T�r�@����j�7���p��B����!�<�i9�=,x�i ���	�q<�!P6<��c����x�����D�U���E��dT�P(�v�QE�p���*2�NcG�?��G����G��*�$����!��-�����Ud���N�����t!��8���#|�l��Jf2;~�X2���;%�1��@��t���n��FCO�J�������
d�����
4OD�T��������
D����X��Y8Jh�1���4��T�J���d��L:����F�qI����K����4e;F&4���
�T�w���;�)�{v��r}��Jo��M%��1Oc��g���3�5t��\Z���r���WL�����.�fL���5k�����E61��	I;��xq��0�����R[q�\6������+V�J�+��M
�r�%���3~28[I?N�6��Kg+��p�)��V��������=?�����l%���dp�����sGp����(8[i�{�P�6���r��^�����vp6�N"8�z������l���FR;q��������=���9�;t����������l���\�����0�i�`P���hpH3��X��b��]��5�m�L�6�'��lK���2�w�:����{�9���;���R�4u���u0����l�"���l�:8[�����pgk$��5
gc�hgK_i��l%�X�Vz�ug��2gKy'�,vp6&��-v�[�Oq��MJg[�uvgp�_��g+y������ngK�(fg�����,(�sfp���Z$8�����������-������C��l<�
���-G�Rp6[��IogK�N
��,���Q:#8��H9��"���-��M[Jp�T�e���)�-�v��l��g
��
�lp�T@3gK�a6��D����b0ul
�Fk[�[L������hmK���Rbw�+u��R2���5�o]X~���%��������hm�������!#h+w��A����m���������X��� 2���Od�OW�!NW�)�Sr���[�9;+����3�k�ba}��b:�����a�w�	����+~K�$�2���Y��;����	/��OF�)���?Y�Y�NU3K�s��H�S��;�7A�CX���QjK�^��|��{w���z���<��@E����9-d��e0�e���q,�bK�����#�����A�m��?����h"�$/��4���F]�����I�Q�C(��6P��bsuCv^�������O���ud-&�`�dN�~:���,� g������^o}}������n��e������=�-�>����z���g��{�?�S��{����?�.�����O'�gU����?��4�7+��;�$�wbE�Y[bM���^=��>�4��WgA�#�N0�^s��������%�8������g�k�P����'$*qX�<
��@j\�G����
>�?�q��f��?d#x?
���,�P%�~���D�D�����J�z�Y������T���J�Fv�B+Z�������c����2:��:�(��V����_���l�����5t��������zL�����#�BO�H	���]�t���0��
�C=�M�)�kg�q]�j��N��c=���v�F=^��S���j��|V�]�%=b�^��������!��k��E�m�����b�e
��;~��1�j��Q�]3�mX$����H��q�S��w���.�E�����7���"��\E�������f)��(���Wf)������{fw��������D��Y���#��|zp��W����=}�����ps�d2{��R��D��h���{�"5h����#,�����
J,���w����B�S�-��)�`?�f���!���%W�����
{qT��$l%m"��I�(��]T?q=FN�����Sgb�d��q����`��'vQ-�*\P��Ki&K��r�n��
@�?���������M�u��������V��������~��I�NP5������8�N @5����yt�O����'g�=M����6
4
���t6hG[�a!�Pa��1�����U="�C�<������T��XG�>}�f/^���R�9mr���K�
"5���1P�x�RA'��q��ZwD~���h\�����D�� ��>@U)��^�� f��(��N��,�P��*P��-��>t(��za�;J�q�/���!a�u�PN�)h�������@�^8j�
������0�HF����|B����������8�e�^S��AB���I�����h��#`:�^S��7�b���zQ�N�
�z���|�����{������7��FF�J%��\A`F���xA���,��]P�3����Wo^��
����gs������rX*����QT��$���>��AK�q���x!mTs��Yj�p���G�r�!��U����#�WX<�0Yc��������h����z<��Q�|n�������^��T�j<�L��>{���)�}���� W`|D�3���)%+����F��T�:iW�����O��[z-�1��8]`Fuq7?t�����*qB�?�
4L�RF�5.�f|:iV�f�h�?����[����gk>�_@+�'�k��������
��-]
��Z���@A
��"y��CT$�(81��ua��[w�����������b����8��4��+c����m���}l���N9��^	q@������	��5\�V�5�'5;��|������O�?��S����8x�1����x��#�	�$Z���%�;��s�3����1���J
q��A����p*��0}�� ZC���e0��
�h%Uk�M����OF��q�P�s'#�<�
U�Z�]��-w�<C@�S1�^�������������O��������d����}�$�h|E������|��'j������_�S�&����h�	��d����!g*�b:N
���9H�1�f8��j:��]�X�=���$�(O3k[#s#�Ot�1UR>�5�O�QcP��`�
������;���)5�	D4�����O�g�^����K�z�������y��/���E�l��]Wbd�8�;��Z���_�Vo�\��mT��Ec'^l;�Q�7�]6�@
U�	�(v���V��m�'	)Y�^���J��ZM
5������^b�P�������Y���d��q���1���Yx�@W*�����!1�:���V5���e{���C�����7OQ��7O�����g?<�J?��	sB�������]=Q����I��;����O*'UM�x�+���^�/�i��+$%�G!n
�����!�C?}�<e �����3>�]�������[�f�|�����
�C�Q���<�"�8+l��9�q�q��M�T�^�)��CSUu$AU.��?3U���i9��u6�����b|\E\t����q�	:����] gcn����=�3�I��:�m�D��&����(��n��&��wb��m'<4�G�+��m8���4�v\��n[%�A�AI�I���nz��|�=��@�;���am`T�b};m�a�r!u4e(Q#���
z9���wFT���?�1���N�E4����a�N�-V�S\��Y�('��Ab�$��X���Y|�����A{T�;\x`� ��Y��e�^����P|`�a������f0�����e9��~��H�l8���(�XT�U��e�-|�����|x3T�A�+�.=�9�����)�f�)9��|����T���	����E5��'pE�"�f��Os����z�
��H��b���sc�
R��.f�R�N�a������
?�������7>��T�z���R����D�*"@`��D�U/�U&:�J��
��$��=%��tv_���n��Ax8�m������]����Fr���x4G<.�'}�TQ)k�az
�
��,�6"�k�������d��V*R�4�H5���.�uAi��N!���m�\���&A�'������\���i�'�"�TM�p��{�H�X������@*���2_�gf�!����'$/�aO\�+X�l�
b���%7-���q�_����?�m�;=.��.���$>���L5����J�%9��#|Dw�W����~�'�����?U�%�a�1���������a��	����b��CW���R�Rl�:9Y��cR)�/
�0Z�}\�Ci�S��?~�x�����B<�h��
�=��!��ggA��?����&_��I��K�n�S��i�:��� ��9A�A���t�Xk��c� !�o��R�fx��?1��Z����!
�m�a��l�e�F��Y���u���������u���t�thl��3�}���W<�*����(H�T�!�x=���D�u�����K�EQ�F
$�qx�9;E��~|�V0��{����������C��k~�.��������;g��	r�_��5�G#�k��W��1��B���V�=6�����@'/��lL�����H�{�M4%"���������=%������O�y�����'�t�^n#��j�Qm/�3�����
�>����'�J��6����a+r��?�O��1��iC�O�P�]E�o�t{�\���x*}����4RhP/W�u�r,���G#�������in�������f��� X_�������~�������{^kC4��-��xD��|���93���w7�<���:n�b)�a�`��0[�*�YJ�Q�M�A�s�O{(�&W�����f�"1���7 ���
b_�,O�����T
���Aq�I2��O���Tm�e>]��,����h!��1�����8}�'��;�+�6_��M�9U>h�g1�s�N�=�t=�%]��E�7��������'�<:�~��a_!6RdR4�Y(C�.���	������G���:���rq��v�.��6z[-�1�}�*�Q�h�r�F��j�������� t��}|���7�A\��������L@���k�[0$M�D��ar���2�����x+���R��v#�M����h�D](z����)(��(
i�#����X��wX(�(���=�J���t����NCz�p]���vQ�q�����P�;�l��Pl���r8�
~T>��!}��w��>�	�I�n{R$�PraW:G�5���,O�'�X.�l��-�	#�B7�/�����m�#B��Qm�[��Oc���#�����,�l���1�Z�R��(���4��g�Fi������h��R(� �iA���n���R��w��m��"J-�V�M��;�BY>?4�4���F�\[��B"^h����J����� 2�(O���P��=��7����En�^:�e�2E�zyJ�G]TJ���Z��G�����^�t�~�k�������H�����!�������"k��.�|�����#i�Ug��D5$CzEE�c���#P���>M�t{��\q�=��3=?�}������'85�6h�U�L{tP���K\�IR��;�PRN���/q����;��:�������B�"�@	�J���R�x-`/t���}��.�<����k�R�p����X�&�ec{��?�%��z��@)^�zH�U����y������x
��-�b-��q{1RpG���&ub[
�
G���7���>h�>��

�f�-6��p�b���?�/�H&n����p��J�0{`J%j��D����
�Z�������=���p�lo����L�g��wj����`r���q%�Z5�Q�o���BkQc������@E�9�6=��$&�v��������.�TQ����8�q�'Z�"5lK:����9������F}&A���93)&q3��(b
:�3�@�V]�����`�����x�� 8)b��Y .���v��?-y�W���{�w�$^�M��<
�7xyy0:�3>����6qu���:�0��w���j1=�Gmqk�<����
�����J����;i�a'��<�9�RA[h3f���4�*Ch���a����?�:�g��#��C���,�%|���m�*�=h�8:�?hL��N��P(J9����S&X��)X?NF�|��<Xc�dE�����l��je
�@���t�j��]���U�5���#Z���q'�
���&��N&��a�����n���;���}��'�|��
U:�2`�V�������@?J'�g���T�70���#r(�*T�Q������C�����z�����OBv��~s8���/9U�;8xu�����(��s�Z2UV��t5��T��
����@��d������y�Z7j�~EaCk-�����"��;c�xr�s�dx�����B(u�������Dqw�u���`#� T�w�!��LG���6����Uu�\���Dh��a������V~2H[�I�M����1�h�z���Z=�%�����4�&�EI^#��Ev=��h4�u���=���[B{$�p������'@U������rS�L�0y���c�u����7�Py�����BQ�-v���!&2�B����|>��:�n����������������Q���:H�6�L2�yz%|3`�2�sBY�7j�Dy�m���M�aA��@*������I�h��|���r�5(������H�h#+�����w����XS��h�h�\�������7D}�ym�����F��y�6����k�2^��_���(��Er��"u��Y������<��H
��X��4�:N�.$�����2���c3������DP��I�}�/mtk�w�\SR���E�RM�����~.���q�[9I�Cn�j@������g��`Z���F{���zxO
H2��c4���5�0�tvwL6�����ns��
Yo�Q���d�����S(s�q��
�a{��VI?-��
Th�c��� 
��X;�����l��1
���4��vm3)�5/�=�O� ��{��Z�]�;��w��l�����0a�^E���"��d:O���+��������p�:�8�!���b�	l��*��,������pK(j7�q�CT�q����Sp�.�d��bj5"}=�t��=�?�%�?�eA��\����=��%|m4����T�$>�����&���\��?��-��s��a}�F�������n��O%/9gp#s;�7���+J�{�����?=�{}�S���'����3� ��R<�Pz�V\�p�BAUo����;%<>�{���f���6�u��r�v��.���X�!~H�Rx����/���<��M���Q�`(Q[������ZT��(�nX��>������Z[����H��t�c����j����"����gm���7`��Ye�V��	s��z�R��OX-������k����%0�%��!s�*�`��ic�Mb���
���"� ����#���:b�:�=�����N��?��3����$)���d��:n������X_��H^W�C�gd���]g`�&]v��b'�z�>�������ZE��$(�X���!V�H$��c����g�������T*������?+<������5=��^�tVr	�'j�k�E�n���Vo�� �''R�<�4~k>�%�����z���������b�"[q7i���T�a������vp�g�jg5��}B`	��LQ��?]�C�#.��8�o�$���x,~d������E$���$��#�;cWb�K���B���8��"��_��k$�b��������A�kNFI���%a7id�MJ��l=�W���,�-u���p���R[�������?���2�9-�������y�M[z���aM�
C������a��%�Y������1��w���8 2�_�!^ f$R���������v@����$������/��s���\����u�N"iuGY��^�C�������g?���9������yw�&�I�b�@g�x~
j�D@ov��s6g�������k�~c��z���~�q����V1��0���,�����A��\��+��ca\'�g�c��[,�x���v2�"�����DDf.��!#�c�y��a/GS�?��tycg�&K��Q5��T
'�_uU�^���{JF9S���
R~>�?�q�b�A�ql^���a�5�I����?� a�l�b����w�,�����[0S!�l5��'I[���S�d
y��Z#R�du(�i�0�
����A�`�������k�J1��RF���I�)fgZ���Gb����l������j/��4��F��J.S~��7�e#��x�Z��g����k�]wGL������������=��3�p�)��.����L)�7��|������>5w��z*Cy#��xD)��L��)�H�l6jx���#:S#��f�t��$W<��l[�
�~-����"n��:��%���[����C��5*���@������Mq����2�'��6�Z������:x��N�
'�+i2�6�d���<�@}�`��kr��Eq?�
CE�p���tL��3�Ju�����b���\&Vz
��H4��@���1A�\����w�`�*�7�tuq���J\r���v�w\kI�u�\���2�H%���o�`o>�[[w�K��|���q���=�j?��:_><��|lnN	�i�e������f�F<d�UOei�{���g�eb:��q�^��������.�	"&�|-�P�+�?���x�)"
L�����
����6�{�&y�6)5h�2��v�r;�&3���)!/����G�q��z�0��e�%��7u�&�������M�^��Jn�/����Pt��T��.(
`�r��]���ok
Y�Kv�-�d�W�n�)Hj y^����1�Te�3������`�O����R��~]�|��k@}�����������u������m��c)e���� ��V���'>^�W]|���s������
B���|4��q,��Lp����y���8a&����{@�������bT����xp�U=�5������J�E�A��q���g�KT��	c���1fD�,zJ'��k��J�HSW��"�Y*�,�1�M����4+���k��-h�c`�x������4L�!����{q��W�w;�����s��v�$��[V��Z%���3�h���_�V��)�
;3�k�n�3
�
1(��-o��yH<!-�HQ�I���e�!�^7��Ql.W��,
��c����!r���O����g��?����_��2~�1��3��W�H�W��=�L*������Q�v���?�������l.e��0���j�/�����|0���F��`hS)�a������f�������	���,6�H0�Sv5��$��M#�L�I8f����D�yr:�v�P�����\��+�TD�v��0�
�����-SPz�����B��<��g���#�>M�����FR����h���Q��d��L�R��P-M�]��M�F%w��"��E@������T^Y�G�%[�2�@m�JCv�^���!�U`���1����|	g�9Y�����������������g���l����<�"g��'�H@y��MX
|��2w����9*��nB����S�S����qt�f(
uU�JT���S�[a���_�{��`���X���!���Aft\_����7;��p�\�R�,7�&�
oC4Z�&Eq�g�E�a(C#�j���O��6�QD#��������=��&Y������������&�V������_�������0�c�A��dt1��2�!-�����ut)��__��������_P��L)�"GY�������<^�2��HxuuE��%���c�d�������$7ZG�����4�c�R��Lv`����2��+a�A��a��CI�72�w/x�n�~������|���,�i�w)�=Y�ui3����!A:'B@F����`2�xS����k�>����)
7F��<u��z��#��P8��4��Z��������Z�P!�L�
DT�YrQ�b�O����=�h�����#�F�<���Qsn��\�.V'��Q�T)����Y��F���_��x���������=<�$�J��)�R��?��1u`=���l�D�����z��p���1=�"@�{����1Pz�3�;���-P��a#t���zq<��lM�5N���ep������Z�:�gF+'��nWU\��t
^�<������h��w��5uJ��������OA0dg<tOF�b�:�k^K�Q����B���
��c����1G���lWb�`H����[z�b�Vb�3���������z��_��|���5]a�����R�e���1�5 ��?
��H�q�9���D,�b��'4Qq-Tn�_\Z��K��v����6b�&�C��@=�o[>���:���9p��0��c���!�������g:i9�V-�N�0V�_qk�J6�X�C�J2Uj_D#*���r���-�1T��|z���~c���~��!�N��
<�D���m�>�I@�g��������nu���`��)�V�u��G�FL�6E�3|�)
gH����(���
����K1+�����4�4nC�D%�Y�Y��yB����<�u��6[u�����G�~6�����/&����/)��[4�E8��������t��1��A�K�S�k��,��q�;�b�Sd(��������SJ	���h+Usch�Y7?>�����j�r��"^ZP�&���}�TH�#	�A#�^���w���^KL>3���-3HJF�zNq�L\t%�{��r�:�>���"�����K��!JgD��Z���k�������8�L�I��u�uh����!����*6�1�O�g5B��Sv����/����A�ks����n��^9K5EJ��y��Y��o�]��R�Y0T�:�\���S���l�T�l���;w����������O�qQS�3�#�p}@�����)j6h1���]u�i-T�%M��ka���e�_�X������R�j�Y��h��x�N�"^VY��6� ������T_u���HV��F[%�X����*^���RC��%�Vb����_�E`���|?/s�
-�����Q�,N����������)E]�F��� ��3��4*E�Bo�+�h~� eM�z����s�����wd+��������$|;+�����_L�&?�W�y�6���?�t�z��7KU,�P�2����}����^JRT�ij;R�QM�t�4x�HBi[��+�iJ�a���Q���Y���\[]?S�1���4����������������&��������?�����^��/���w�� 4m����.���#/�����7fu
�<�����i<&��sq�����	U%��8@�1��'�n�KFo7�:�5e��(����u���J��X&JC7���Uu�.w�I-����jYpZ�.)�����Z���6�(h�>�$+�E*�W+��9�It�����?�p�����X�W�n�������c�Y�rU.a\�F�H�q�*$�xH��V�W�f)�z������R��_1�oWE��b��jj]������]�&e����Q ��:'�{�%������.IB'�
�L�@�!���������������r������k�FI%+���U�v�8u���VbWLUK�CY���'B���	�R�p\�e�&����I>5k)��K2�gSw��~���j���� ��w��|�F#T.\2����b�Q�y�A�����1��K�Fo�e����Ea���!v�TWb��)���D��Y-����������"#��F����"�;�"���Q��y|�n����[�{�����z��1O�������Z���k�U���
��(��3z|�2t�e����_������g���q7��Oa�����������v�q����C�>��/���?r|�Xk���C��Q0�������O�G�n����.g������ ��w��
����@���;X�I���lc�'�����	�X�y�w������mS@������������s�Z�����/�a�b��dt~��dOL9��^a��3�����oUV`i�N���4�6C��n�������l�A��M���:h��xmt�|g,�����~��MP�5��k����2���U� ��������h��B��/N �H�` ��]�K �W�����C��"���_���G�f����S�)Y�����~ �4pw�|�\u�*!Tdv��<�J���J��Q2�;��p>B��=2��F)/�["���*������i��������?���S:b�����g���/�_�M�s~������<
�/55���]�9=�����8pd�9�Y"�����N1r��=�L'Vd��*���j5�y��$eK_'`oB��F����vx[S���J�VW��M��1��-�b���<?�n�7�w6pif��'
��YE����0X������>>K�abj�Vg�N`�d�+rp���d���0�F�����d���wvoM� �s����c��jC�"���g(&��� �"6���Ut;>+��(�Cx�R	���X.����r�m6�-V�x4��X�F�<���SO���.m��D���a^�)�6U�����1g[6m����M���V2��[��|j���[�:��CR�dw���'c9���<a��a8�4�e�!kU��r�K��qA_���#�'��_@�����}��{t��4~c�S�����C��`Y1������=���#Si�����-��L�D>�Q�b
SR%B$9����aT?XEj�3T$u�V��7�1��q�2�iu�>�X����u�������S���,���)=Rj����m�0�(��&�������Z��'H�Uy���1�J:%��Zo
�c�g[q������\��#�z��g�`�y0�/P���f����{��*v�]���q�U0_���2���td'�:�%�����o6��+�K�Tqi�
OR��"A�t+1��Bb����5��!��1w�����^�����MZ`��[��v�	���n��
G��wu��O���`�s>6��Up��0z�`�E��mi'����=x�;=�t�+��{�?�\+��-T��ZzlR���<��|U�W�A�������T
�tu;���08$c��hg��_�.Dj_i��S�������
E�a�%B"���M0x6���&N/��?�At�����0-N���kG�����CM��V+�K�\j���5��i�%GC�%7(�kH<-��+���l�t)���)�P�4�$,��xJ�.N��wN�a0��?������o�Q����^-��W���k|q
���	�)���O�����j~[
$�� ��F�c}���x��N���H��i���\E���Gt%����y�.s;O�B��\���%c��t�-�6eNDF4���	� ��;��di������m)�@��lU��!j5���GxF�'W:�9K�H>�?�xK_n��b�'��Lpx�����6q[����������h2�,T�?o����i*z��g���t7��@u��"�0�6P0�)�x���:���E���5�~�d�G&��Fj����(�(���]��y���]�yw��1�����}2u����5�O���I�.,��>&������pZ���tqx����8�sf��d���8k��g/�U,����b��>LA���{8�#d��-D���7���)��<���~�����-|u0M���y�q�z��t���������l�s
i�.��X��?6:��
�T�oc�(�7����@2;��Rb�$���v�K������lt<����--�$�����k�<�����`L�c���G������/������Br$�|���]9
pf�w���na�������.�r���kJ�[b]�#x��)������B���_��f�8��8��(CodC��Bp2J>�5RS�D�uCc�	�2�p�Xc��6iQ�������������/���0����������c�r!Q�0��rED��9����X�����j��bZ�ZI���P-����W��>���KM�D!�I�Wy��S�#q+�\���E8+�J�_WT�@P����$=4�Jr�K�c�����/�[ct�WD��"���c�#�	5�)���8�I�m2����G���Gy2�QDY+r�V(�.oD����1�V�	u��L�������(_������l�TJz5&;U�$v�6B���B�$��I�R��vQ]��#~1m��HQ�["��Z{:���|���q[��w\�z:^ra�d>6�g*��SRXL��v����&�;If��<?;8?�x����i��?�O�����klw�p�q�\��t_��9���<�Ii��z�Hl�� ��h�-�����A��BX�Z�����w�)�.�fR��Z��8*N���<)G�<���h�p�\�-s�i�
cO�����R�H]&�]*Q���3�	��v�������X��	��`���4�����gn����	��S�H;(��t�a'Uq��vH�nM^i������ ���.k	�&R�^H��yK�ZW��\���2.�
�O]kS�dJ�T�]R\�{:^�W�B�A��c�1BJ�a��	���w����}��J_�8^3#nQ}�����)��C��
{rQ����b��3�����m�������R��\_/�I��6��}=V�����g,�iz�IM���!�3�/�SL�O<��c��QM"��n='�=G)�G��6Wn��$��v2��q^`�M�A�1�QkG���D,|���,t9�w�/_aR�W��{^W��L����A�-��a�5"���%dRZ�k���7G��~<>x���=������0��i��Z�!m�!���{�|��Lb��2e�I�����+���3N�a�0�H�F�'���|j���T%�t��]e�u��u�������v	��4B/�ur�����s�qZf;������L��P���,���8���bO�R0�_��H7������d5��H:X�u���m]�(h^'�0.�;)�/54w�F`)�}�^��|Mm
�,I���8����R�������D���L�7&t�vNf[�$�.�fc���t1J~GG*��-�|F��S�|���W3pL�,$��1iu���0$��8���>#2MLn�E

�*
�/'5mQa�yoI��K�Q$wG�i+�*s�N$M-�hX��w6�b;�);gg�4�m�`F��L�,�:j����L\�~
+d�2-}�)�I�����4��W��ey���F�J�m���%��U��MaCW�0��pXQF8���S����$3�l��Q��J��M�Q�E`e5����ic����-�!�8KiM&��j4���)�l%��e0I6�,9&r���Hl���o��6n���M��b��a5������Ec�C&�5��Aj������L~���9��r����Z���a)3;�����pi<��v�u0B�&EP�{h4�3�!��������=�X�����!����~�x���Vrc1�7�jGB��0i�3^:W�<�h�N.��U�Lo�@���1���kq�kqdk�p~�53#
��y�c��:���'���X/�������5�������aj/t���a�k�:��%��hccbu���O���	Q�eo�c�h�(gD+M�Gr��$F.>����@|���]�t,�)�mK;.���oRC��!�}P��Fz��-�z��8�8�/���b���tBt�T!�)��o~8|v�����
+�jl��)>���g\xqv&��4T[�k��+%s�hsn^���9����<��d���\��y�5�TDv����bq�2;:�f�%7��0[�6Ii����	���4Y��N����'p��L��xt:o�N�#���WT�&����'��V
y�\��>'V��-~m�ka�$p�c�L�L!����k�����QW����������w�Do���Q���{U������Y�����|��,����1����,e	��]=\%g^8-�n���h���4�6�E<M_�}��������LA�g��������o�0�$$��*W�����n������
Y%e�J�����B~�d|������2]<�2:.�&y���I���i�j��~K�n+.1d�����9!��_w���|���_�������K�i<(r.�����d �],^�$W����W^���v��z�������u*�9�����r��'������X�tE\�)g~w��������x�f�v��NX���F�����p7�,��tw��p��]1Vc�e��+Q��Y;��3J��io,�x����e��2���<;b����c���b�,O��S#���H�<%�_Q�.i�Yi�ZA�{ner�,�1�u�1�u�1�u�1�uc~:��R8�@!	���8������$����Gw�>xHW��my����o��!47����gL�x=�+�p��g�J�N���3��5�d�������F
�/
D���Z��x�-��}���*CF�Wg�'}�Jx���R���;>�p��H�?u8�~)s�7�[a[�n�e���>6�������qpx�:��,r��/��eS�������o�Kz�5�K����L=>aQy����lkzMvTX��,Yh��/`��9]n�2*d�u�����08hW�O�D��Kri��e��s��i$^��Q��e��.dy";.S����8��� ���	Z��mnyE�^����X��k��}������
a���V�Tm��>���+�Fm�e�1���F��]���H$�D[�=����W�D!��5�1���wPl<"��C3c�]��q�;�J��F[�H��:;�������[p�z|���w7U�tu����(��q�C��U{t����������X����"W\at)���%e��=P7�3��@W�s�m��.&@�QMnCMT�X���"��]Y,F��<�_��E�F'z2�1e�A�Q����M��*�o~���z��n��������8q�fs��`H�.�e���`���J�.�9�������;����|��@����{��q���������?>����Z0��U�lV^�����1*�~r>������ ���.|x��s���7||@�����k�W�������������|h\�K����
�{��Ev���fj���N��sQ��wf�F���5b���6�
���8���~����F�Lf'3n/�Z�`��4������sP��n�*<a�E(��Au����&���l���6���;��d-~��L�f�Hd��(�D��'~�tf����W�c���=T��cFJ<���H�I��YZ��D�L����S���
^����{Bg�����=h�!R�
�v'�1E���p���e�&�M�;/A<���
�F������JX��U���z��A�n��A���Fa�{������jI�-M��-ME�-CE������F�7�q��5w��I����b�p�sN�I�#�.k\LG~7&����*<�������#����?}��;@G�����,���*w����)�#�W	]W��y4��OiE#�(��2{3F�}��f~���*�
����ip:�\����__?{z�����_������R!�T��zG���~�U�+���[J��i0��9 3801P.��`���f�����-�G���w>Ok)0b���QW�^���sx|��<����������E#����m�@�&�T�p�����SebV��H#���8����;x���/{G?�Rq8T$;-�.B������(A�)�^`�����h����8��*�AY�X=����oHeF�l)jz"j:�?s�S�7sM���2NLesR�����RS�������1Y�B����f���t���BE'�S�/xA���56")�g�o+��'�����4��R��R=�:G�Z)�D�i@*b���/^��eH���o��sp���F2�T��������c�V��f�H�@o���N�i_�p������+����'����?9����`����c��fOm>��e�,td������b�q�p�p���6��w��,
�����q�����y�������12�?=C(��
�gA��*�b&y������l@P&)�`��?���Vl�/?�@��v���G�EzHtG�~���uztG��W��u��
�~�p���G�R	�R�+�
w_� f0}�OF��S����GO������6�M�Fk�����%- s5�Fr_�����_��\2�wk���1��&�)G��&��
��U#�p�
,���-&����.IO���]��E?��]��o�!�������M�}��T&V�8�L	�//8=����J.tM��L�a�(�W��)H���?{���?`�S�}�R
�NS~u��ph�S�0)(�O~�z�7
K����@�M�[��M:�om��,=O*�
�kLfv�`�����9���#l^��Y�`�p��3�M0EZFJ�����g���������W"Yx�kjs��cK�p�H����9�������fb�+/4�k��0���wd����k�Px
��AFUr��	�aV��@y��4S2��0��������>q�>��$
�0��6�^�aY�8Ny�J��~������*uD���Xf�0�1��yT�� 0��E�����B���s����F!��V�`2��#v�b\��	xvX��j��

@.��(����N�����`���(w�L��U)��� Z:���C�/�
���P���[V����q+��_�|������4���c�����d��1���������_�tL�S3��.y����D�q?��>�n|������x�8m�H(��a_�%R�E��jG\�{d�;�U3J�+���JZ����?��c�R5;VD��:�v�,Nl�8����z�m�-��DZ3ho~�����������J��A�f��t��&E=�T�=�2BY�L	.V�����������?��H{����;xe�6�cL�=������^F���<�?<|����r��+�k}�k���S���*�
�IcU�������AY�MyU���@�1���n���r����F1�;�o��}���T���YSpL�������F)�V7V�A}9�bXIzoGVQaY_M�FC��~��?����jvK���v:��L����O�f���1,��|
������^��8���,�<��D�+`B���4ML�#�q���m@Um����=�b0�����6U��uX:�h�u� -�d6�"C&:��a����7�3�$�+����S�J9Ow4�����.���|�$����LR��*���@B5�b(�&$O��X^�F^X-�H��b+m1��o_4F(����%�v,����[k���uO��nD�g��_��d�YW����Oc�$����Ib��|:QrQy7\�W�-<@=��:e�;��Z}u
��D�^��>�\��<��L{��J��h���Es\�|�nU��n4?�]�~�&S3��]��k:[�.�����vQ
pH���2���Hb�,����1A7%��{"�����x��X���e�x���������iUp��G�b�6����s��6[��I�|d���.��iF��H����������MW��A�(�`G{:�:�vG�g�C3o��2������:!7���v�VS��@������y�a��ub�$���$T0e�\el0AE��JR�9�@�:(8�=�[l�2J�,{���K��4�=��h�s$�{p�H�������g*6�I��&d
���S�4��V)M���F�z&��3c���pyRG�����Z<9=q7���|f�{����%2�Zf�>���n=�1��%1x���`4}�aMk�Z�$R�|��9�Oz6	�(.v,�yd�P	��r�W�u���Q 
`���T�
��O�4�_�QG^/�4���;�����
�u>=��xF9��O^���X(��r8���q �0�8#k�6#SAb
��
G��S�����5�g>�K�g���z�����A���+1�HD��#����?\qp<y�#}@,XGq�~������?!<��	���>�:$���3���(��:
�CA���@S	G�bxq2�����������������2*aevI��9KP�Y[i�m�n��?�9��B�Ur�9���YX�SV����S���U�AJ�����'uW��P�K�)0�����`��-�4k����B����;���Cn���V�X�g�[�0@��ygp�
D�*| ��.}tF��~3���<9�;�xn��{���C|�c�%�@`8��S�
����WT\{��v���� q@Uw�b���*�>�������0���wv�����	�$+)i�����#qo���L��_����?�J��*�j���.	�U�v�������iC�2o�!���:�V:����h�����b����N����N��6�4�|��{���'oO^�=1�]N Z����*�H�(����u|iQ���N�7�	�_P�����_r���&E�1_�L���������U����-��so����Q�5.^��bu;����HE�
�����ik#�M�`3��E���j��&���U������iE-*SJ�
&:�|��lD�����(�;�i/%���4���B����������3��
��3�rL%�����������68c�D�(��*�p��K�!.����X��������y���e����d=��h��6%���t��WNG����F���P�k�e� .�������I������<�X����/����E�f5EHd�)��K1�����)��v�> �L,���8�La����j �R�T�eH>���4��6V��DET���X:�SEs�@�x�RoN���FG���i��$y�_n�r��5��%���/�����a���tDy�A>;��y����{\�v�l��Ns�-��&?����na�P��Y�
�%���T	���G�l-E��?��������������*e�������5b�:�S<�`�1y+N[���3X�a;?���Y�N�b��SN�p��Z�J�'k�1���n��EdO������4#�B���x6�S
��g%'H��p��)���JN�9�����iL#��qr��lhN���d#V�BU�o|���$���Wc"����x��0��mJ�.�����Y��f0
� J����>����y�I�H���Y�6���{c�B���
|�xr5
|x�6�dj:pf�$5�{�\ �T�U�e=��1�l:�����K����`2a�c��M_;�qX{�&���5�n������;�_������������,�W#R�2�P�6�:��i��?��<:��:�==���=&�g'��]`�����e�U\��(<�G�����"���q���b��d3�
)cO����Xa������Z�w�w;{����_�+�a�����Zi�E'�N�j:����Go�L�������"�������q�����3�AOH@�����������
����CK*���� ��")8�����co�*����&sJ��i[b�v�Yv����u#���@��7�D�u����~��~���9&#]j��?����bW����n�V�*�C���#`3���S��R7�������K�L��on�D�?Y�7�M�/������ �r�����19pC�����.c]�+�'�#1�
�;�=����b���H��nO��
K�9���X����>�����Vg�}���]�l�%���R�N#�i'���`4�G�_��x��q��qSF��:����X���7���o�����W����� ��a`�aZ�_@��"�����3������.�����x��E���_�����ASc������b5�������~�;�i�;����t����:]0>��U�O�hZ3j����"��Z��_����������4�+�,{�E��.%���.�W�x��1�R,j����x?��H�������Q��HN�������d��+����������:�V3r�����cr�B6��4 Y��o��\�<�Y�}W�Ss^�[h�V�k>��a�?�g���&�[��+i��0�	tj47Z������?x��W�m.�;�
�b������2���8�c�p����h��s����������'�W��D��c������C�����)�UY�V�����f�o3O�T��J
]��B���1�m���\k$=4A�;p:
���p��{W!;���+��3�+��G?��R|8�Sv��zQ�I��D��������	u�(2��� ���o������c����D[Bg��
������8���:�K���n4X��C�=u1Y1E����
L�jz3���a����G�*e�D�j\���e���u}���4O�d+������V]^��������������v'��d��]�k���]������%i��[����8�J����������)�!���L0�u<>����"�2��q���e-i�hF��3�4����g�d�����m�K�ki�(�u���2��bTZ���-K�h�k2@,�N��O�`�
/Y�;���dRL�V��0-"dS"-�-�gy�l9f��PZR���g~�,h��==<:|���
��QD�W'���~W�]q�����!�uJw��z5�C�%����#�RTa|�3�|Q�#������Q�^U����I�)��#�h������K��[?�R>B�v��I^��f�a%��tZ��[Y�WQQV�g�T�s����FM
�h�),���������^��I���t^���7Y24�(���8�@>x.�O����4��D���(W(>�o�2�~������d��kO	�5�Sv����Wu�����FE���R(6*G#��������>��pjk� ���`91�e���#T������~����P1�>�8�.`�n����Y�����B��y�ie_��V��2���z��]&�B�;�E1��Ue�wg�����E)\,�
���,5^��vuF�G�\4D}V�z�b5x���o,@}5B%k�>w����#�-�tJ�l'���g
���%r���Q��J@H��}���������~'8V�yd�\5$��D��~a:��~�F�-�%L��a�8liT��>?�f��C2�^/���Exf�P�%���&������v�b>cj���8-���K}�d �E�}S�Lu[>����a�������P����Z&EtD�b����X�K������!=�)-?�f�nT�T��S%gF��P�����������!��8bN��i!m��qU3���1�!8���PF���A '#�TN#��M�"wB������3�z�tP�yxe�i���}�rs29���n��cE�+���K/��pb������5�0�?��������lnB�<��D��4C�����,>��=�������KX�����c��PR
CN�+9�F�Y	3NV����7���R����~y�0�9T�&��B_�r����F%0��Z|��!��cF�v�h�j�#��T��UK�S�m��E��
wc��T�kN=����h�����a/6�*R�������m\�O����Y;�\�kI8�'���N"��)��Be��!�;���pB�p���Q�r�o�D���V�v�x���d�n�=�(]R��#���?pj�7/��=w��P`xY;�t���<�Q��Nf���8�t�������F�1�{(pp8���C�iu����3ga|(~��IJ^����{��������=�<!��Ho��+�6?]i������^<�j����O��O`�Uy��$VY�Wi�W��XJ���������
�6�_�z�|���g��������7��_�::���Oy-���u������;�O��O� ��W��k�l4��5���&#�JC����w��_�����Tp69��������K�	�#������P����=9�	���7��^O4��w��L�'w��w���x�#N2^��������tO6���O�v�Ekc����J��������e�G���]�uwK4���Cr��������/���[��+���h�J)�����R��9��K�0udk���<A���	h������X��T���0���1i�Fnv�X'������n+���53zY_����(g~�r4�Dy���)���W�:��S�c��#�/���E�i�`�#���;�M��Nxt��`��S%Uez@M�k�n$:x�(�+s"�y���7	�I0�������6�+FCv�����������J�1G�� ��As|O/�� ���6�����&�I�Vi�����#����(�@I����>	�����1W/:S���W����J��������h�=�!s���l����H�7��s����������o[f��)F$��2��!7w�<�x��:%{}��ju� �i"�v<L�����,����"�G�GS�Z[�XU���7C�M�6bC�ACmC�-���]��>Ap`B���E
0kx�����-Y�$��	����+u��[��
���j��,z����!Z�`�c������*�z��x�
� ��jf �"���H�M�����1��UF�=�A�������hV�{�&h�|���*
j�-�*��}��D9�/�m�!{�A��?L.?�������Sv!C��2J�q4��F���W ���O�Nr������s�.�s�����L������KV�ckb����ZMaS��y�%���h�Q�b���
��B�Q���G=[b\i��L�A%�k��`��aS=��;�m��Z0�i5�M4K�Lq�@d�EW@�s6a�(kM("i��-GI��v����0������?0�My�!�p5��<��4�M�����':� ����Q�z&���F���� ����u�#*Z3�X�� bh?�X[��S�<�J$�s���:&����HR#Nh�����=�b�S�w�L`M�����8����KU���3-��`�9VMa��v���,��Ak� ����r�����e��!vD��nY^��P!e�k�o��ES�e�y�"s�&=l[	!m��u�r��-�!V-m�g&���-6��������L�����Y�T�Y�����;v��C:lt�s�}���W�TE����hL����"���8�q�U��|H�b�q���"v�Dw���m���
��������*�m�4���@T�����b���t����TO��32�L*�H:��e84��_���}���#6#Pa���<kd1E)v��p��pj}�������"���`Z(�zb�9z��Y���\��g��g��#�E�0kEP�	d��������W����u�B��	��k���-1l��lf1�_w���<���:��`��9;�����5����������f��)T�L�K�t����*���tE
��Smm�q�#O`"I��X�~b����|�n�Ig�9J)���Vta�G�f_�������x^�<�tW��E~�wv,
���1��vD�p�gc��kbL���5��������e�H7�J� ��SL�a�"�80��g��FG{9N� *xh�&xt�1�b��Q��*���6�s<�8~4X����������
��������b�R�Q��$c�_��c-�L&#>{�|�g�������>:���J���5��'�;�z|�SX��I<
�uSip���Qt3?��+���Nx�"{����*z*�`��s'v��V/���:'R9:����8�r�H������nc
CNH$����p��x���t`�	��z�!��R`'����^	����c���^��>14����}�p�FC�{�Z��?zi�:�%�����>�KT�D�����q�x���{� Hy�N��z�/�b��-R���}
=�7�6g�@*$��x�.s6�!�t+��&����Q�a��7-!�#V���qfr�$_����L���>o��]��iu����/O�%N�i�V�v��#C��
I�J�m9�����8��H�J��IJ
���/��4��mgl����� ��6�s6����C���
�zz��
��Vi�[I��Q�i1K���ue�]���]��U\�]�nE�JF����l���z�E���sB��CP`wn+;$�����wC����a�8_j�8_Z��w���EXv����f�����S973����9�����@eY��c����^��fF�����B3���X~���1���=A�ci��>��+6_&�Pv����#(rB`�p8����1Ia�C�����'���������n$�hW+u����U�4=�� ����#�������=
��{M���s�^tbx�KV�aR^��+��D<��mo�������|6k&;r����CI��#s��j�~�).��0i��d�2/qq�������d,W��+�e"q��]���}����o����rY�\��*����?��e����^���Z��E�J
I��y���0�����v��wv������2\Ux�fsU�Z.c�J���g�Y4��(� �bw����7�����yh���)��
.z���w�����(�~tj+����S��r�1J������Y���'�~���H�=d�(���2i�,N�/j�L�>UvT���P;_���4Y��
�Cw��21:�%�f�E��!�Lq��B�r8�K������#�����ev�����a�y�Z*���J�22X�����At
����kc������>��zf��l1���[���XU��_���i�k9a�z[}�C�;k��X���g(��R�����r���8����?�~�J�D*t)Qj��tp����C��;�o�<�D���V^f���������!e,r�p���'S�\+P���������?�E��W����o%1��k�����Y�w1y:�C����E:;n`��r;��),":Zy��c/�$J8�������pUz�'���=M��e?���#�Jl��2��_x��k��{�-o��N�����B��u�s	\ec��I[��������	pm��)��[�O��O����JF����iB�N��t���@�L�Z=N���g(���V���/�5��~�[ZvR�H�^[��M��k��{^�Arm�R�"G�����Z�+��.��a�0Z��3��2C��b�`�eO����]��k��%N�|�m=�V�����b�A0u�L��.�hV�!������>����
���Gd<3r�E/��j����d���F���T����X�<g\'���0,* o�`��ip>M���w��3�����8\��n�J\0mx��P����9B�K����Cg���c�\�#�s�Pa/9�Bj�����e@���~��H�
�Ld�~�B)�3VH��������t���w���':OPT���|��w=3z~��G���Dm���S0���O1(-F2SD�l<1u�9���e�����BT��#������7��%5�i.���,�`:!);[������e{T>(\����"��.��+�RM�r=�G!�P�\�E1���L5��mF!�-LyiA���q���G�R�P�2C�;�O����_���~�]�.������z1�3�y��d��n�������!��R�d���G�������KhO�H����>�$���Y#c@i���I/��B��D�>Q����	{��^J� N'���1E`0�I��l3��J��J:��%���EZ��zE����Yx��GR(](��G�IR��A�8;]��V�c�0%2-��j(��!��6���RVh!kJ'�!���y��bS\��.���Y�����+!��K����T6�9��(;I��dRIAq����wZ��,z����4�Rv�����!L��)����+��Z3K����0b2�6�Wb��x
�Y��{�5Dek�8�}
#�zAg
�;&����	"S�@NF�AT������j�N�T�1��^��abW[J�`4f\Q)Xg���K��B��S��pZ��
M,PV���|kLF)w����
TL�	����+r9��U.�E:�,+`T�,���������p�u����G�����cz�R���v�������=v��9��veJg��~�C���?C��'����k�������1id�L�v�>����+���c�fe����krf��"�j���y`������TD��0�5�����}���v��'�-"�7��������/z#X�p�������8�i�B}e���am�R^���X��
���D7�"�n7:�X��X�6Y�
�vl:5�t���}[(�<#���X�������s�)����Yqh�d��WV���:��2��n�T���D;}�6��m��z��u_qW5�q����\�,��}�e�_����f���������-�w��6}-������{?�5i'w~������cM���v��D�.//E�� ����Qi-k8�Oj�$����)�B���v��$�dT��WhU�b8!�		`��Z��1��dA���piTq^� �G�{���N���0iKL�%&m�����M��b��
p���Q#���mc;�B*��QvTr9!@}l+��V����AO��E�=��m��X���j�X��fR�I��'����Di�r!Dt;TG0�.���k	Sddu�
]���!�h��m|S�M���f�%�3/�Z{�O�;��X���f���xcr�6�~�E�F��Y�Zb��5m��!��_5v���A�������6��6�o�Hqn[�b��M
�j����\� �E	��?�����1�g1��c#��m�(���}�MB�1�E���b������LU��F�_��-�d�?�b�N�Z��t\�Y������K,}���faF,B0��k�1��$J}�������$X�$J�5��|����Y�L�?�]��K��a y����=���kkhab#���U!
(*� �ET�g�B�-�Z8�1G��`��+�<�b�$���O�,~�E���@us�������$u`@���V@
9�eqx�y�)��?���H��+��e��V�Q�Tiq[���s7���N�}��,�����)llP�R��B/�3
����T�M�`w&^lG:����2���6���AAL��='�D����qj�����X�����E��b�}t
ak|��o��p~��+��"�
���x���iq��5v��r����d���3O��@qG�A��%������Q8\ �Z�Qtp�e�1�W����0��We����0G�&��Q�����F�CE^6�f5^������|��� ���|OH8������|0�.I��~�tRl����"��a�N-Xd��j+�D�!��2[����}lV�t���� �s(HiQG]�g��!��
���������\y9�x6(#	���"\RVK7���i2YO��Fi.�C}���eg����1���p��B�������K4G�m��p���**���{U=���RT*F�|�Sd�F�#cs/$H���a|i���q�4�H�C"����I���<�Y����b ��4	Ogk�)6�&������6�����f�n�fC��pc��$�aH/�8�x�I������f��!�m�s�<i8/�/
�/)��B�H���g%���xR��E��{<��N,��'M�:w{��^��������w/���LG����-����(�'�@P���aY�v�=-��o����,
���u`��^����][�YnT�w~wz��a��th��]�'!v�vPnd�f�l�%���u,�b��������.�����e�4"�+�l�^Z���������ug���K*g4��]q9U,Z����5����h_����E�	C&�]�XI�����?�C�p��0����Z�sYC$D#����5��!�o�������>��o,�xA`q�,�{�R��C�O�9�[�[u=W{2��"�(VsL�s���P~��gs�I���?�a�W:Dey�������������?�����2X��w�0P;�GD:K��(����C���p�����4TU���<kX���`u���b��zm��aP��^�kfN�DT�U�HT{UOo��l�iE�9�������u�]�HCZf(�7��0o��1P�����$�L�v����,t���6r�\�����]N@h�&I�6��h�B������(������4�/vv%��@�V"�N9��<J�<
;��u�����i[�U���II#�?�]�}�/����H�N���O�v�	4�R���BR�C�^�)�
���-���=f�h����4�no�gy�����g�-&��f=2�zm��~��nA���d����x�CbB��x�m�!��[%}������a����4����)�t�LX���N��s��,C�:D�����:>�jGuWl3r!o���&�.�S���H����y��Ny��H��I�%����
�����^��yW|
�M�l��|7]�w���-�A�����|j���k�]5��e�5�*�V/�v��[�dhKwg5l5�F�s4�����.Am�l�(�-�+��b�fmR����QDn.�y?�1��$��=���VV_���&%��abr��4gej�
��t9���sZ�gu�_�{=�����`��.+(Z��4��V��G�iT��j�2,�ww4���<c��n�����)��,���������J��yZ2�r��t]��8�������3�\3�������~���t��6'�0��1�x����{~^�h�V�a��"w`��%�KY�c��.��8o��\am{I���������l^+en�����\3���<[�I��5�-�l��V��FQ��1q�x��sZ��/l�����	0�������#�B\���(����s�3�p���u�W�q�k���4Tw���1����"���z)O�w�1{���?�)�n��y��|������/�j���TbW3�"\b���ND�H�K�0�.���kE�1Q)�(���`Q]���^��@���X�k;�4��"�����n�KF,���,���#Wi����.�;�[��N��VA���hZ"�E�E=�����S2n��	D-NU����?����8	<�J� Q(+�����t����_�����/~x�����)�R�0}F��i!wb��B�&3��9	����*����{%��%iq�k�*��v���l�Q�RgDR"372e���7SDgKO�B
�$mi���.����Px�ir
B��������m)���$�('Gi��N����vuc	���^4 ���EB2����*������p~vm�0���N���c>�������M��U����r����TT��ntO�����_��+���t���E�V�g���O�M���-��Uor.+z���
^���\���^����u[nxVu�T���3����J_b��Uz}�q��|���0h/���vL���[s�?Hv��A3{���S�����v�s\�4Q�.�e9�������7p=12��y�0��n�d�S�q���I�0�?Y������,Y�]���{_�x6�n���oZ��(��%+��>�j>���&�9i2�/2��4s��@%B����"3F.�O��(��A��5q�@��E
�����A�C3}L�N�o�K4N�$���?<��ug�c�]CH<�Q�a�������j���
4�����B�tS�d��6f��2FB�	�a�rg�?�Gt����/u�@te�msv|�k���2�����5+[�������;��B�A�����^ j:��a�%+�������^<v64���:��Ce�p�M���A��O�����Z�c���|��g}j��;��M�� ��
��/�wY,s^V\�8��Cre�:��]��)��gj�V�����
�D���+:��v����1������j��D�n�����Ad[x�X�Y�A��d��D��Tg�N��{��������U,��������n���u�c ����a�T���7Oc�w����])������S��&W�Sj+�\�Y�x�b���Yg��Jf����O���Y1J��Zt��Y@���	��fY��XZ�|�$��	og92)7��$Rs�#v�+�C������#���Y�s���B4���Lq�j:]��).XM�V3���4����p�d�j5�j����}�
d)����R���xK�*����]2�\��2���/X��/�_Gq��8W������y��$�U`�j���V3L�9^��LLa�OYb��v�\�������JD�nz��o��z������=�R�r����S�#��g/���a�X_f\����?�D�Z20�2�GL$+Z���q�<���,/�/{�(L���3��5��T��Bt�lU6M�����)�f)h-<U������[s������U��}4������,5�!�P�}q�*���}u�E��~��k�_tT �/�����p��,��E�S��y�9��<3�_0N�Gh�,p�Z�5��oz�k��F[P�\�?_���w���T������[Nq�����q�K=@5rr���R�3U(���6�_�c�����r]*���;�\��cz����G�sJ=y�fA�tfq��m�x�8^�s"a>��~M%�-��K?�4��~
D�y<��t���;���_������9�:bfQ��t ���	9(vJ��O��D�0�'��m��3�Ct��<�����@
��`J�&�et�j/T���A�%���'h��0,yDK�r�����9nU�������w<X�Z�m�p�nKyin ��bv���K<Q9���\����}1�Rs�x���p��u�Ly����o��li�V�V�����Im��u���k��Z���e ���
�����Q{��$0��:ljd�^���!R`��:�_�/X��3X+�I�����0$��6)&q����h/�>���B��w8����W;���/�a+��8CE��
����I7qSV%>��q���/��g��G��}R��V%u�L��-��o(e2��D��no0�������eN�S���%"s�Y�����(N=�7������=�\$:�(T����;�9\��|������(j��������a���w%��y���8���Z�sk\� IKa�mkD���#fA��G����v�'#u��m��(v���e���������)��%���tC�[�!3��o�y�}�������]okC���������r��Ul��]}��u��D�?�	�|i�U7����	�fE���"^C�
/BZm�[�r]�l��t����u��p�*#�-�>M��irz�D��':�)h(����F��z�n5���k4T"D��O`�{@�MJ����$�j�T���B���������p-y�����kI�� =�"1����uL4�In\B�3j����j����?P�m��^�xn��K�|Tk�����7����-n�2n��n#�M��A����?���)(B�������[��	��8����M{Y�)�%�R���.a��H��xO�������0��G��8��m����B�fE�
�(�E��nFO��G������tO��F�7�U>ges;�������@�%��H��D>]kP���We5���|KO�bWpY��O��|���c�G�8���p���
@3�_kJ�����(%����-Z�M���H�Q�w`���E�V�31�By��p&�*��-�����S���oe�Q/]
u�w7�vi��oyw��m&������'�bZl?���������B'.xcM��I���A���o]4�O"wPZ�	��!,{��^N; �k�'s=
���:�����t,��fJ��ao��-��:}�2E�/�9`�`���������'x������dt\j����t��`�[�yY�����o���p����@�[�D8����$��Kr8�lyx�/�0�j3$j
z���p�x:���7B�G_�sS����J���$���@O��k�j��7�r:ev��=�p�,��$0���]&������QMb��=�Y�?�.�S�cx,��p��X���~�����d�J�IGoA�q7e@���\�0i^Q>��b���2-��E�6���^��]��|���G�@Q����
���x<�N���C��������/�XD�;t������~����-:*:
�L[~}��e�����l��!t�@�{�_^�:�?�s$���<5E�'(Y�{�|`XI��p>�6�?�0�����;���,W�O���[u���&wV1���������;�T�|B�N!%�Bfq��q"�s�$�j�����~�~+o��k�<)�2�k�YE�n�Fu��X��k]�P�\��������Q6{����������&�x�'
���������o'��%��(?e��/����q-��a��^Qn����}���������l�����z.nj�6����%?���^��eL2��U$t�������Hl�M?�K��k�V��K�/�I^��DV�X�1U	�SkO]k�B�n���,�K�����V-��eZ��n�g���z<>r��z<*r��:�*�U�J�VXX[Q:��RIg�k����h+�q�����nkQ4_�#��.=�?�))��=��i}-v����x0^���{��������
5����H��F�c����-��d��hd�W���������+4E��|m��/��/aq:&+��b=.b�
����\0j?�r�l%���\������8��X����o����a���~�������R�/^����a"rq�t��C��3�l�_��}z���#v%�~$�������H]5v����N��o[��JTV�?uZ;��.�	��l����}��C���Y�w��9;����1)�������M�/�m��RT��d�Yg�5�|3LEv^����ku76��5
	���|6���pe3�uN���Z�-g�j��s��O�$��}�.{�R&��i|�7.@��ez�ok�e�)�\�b'����.`�cS�[�x�
�1!���a;vDbP�L�����E�RDx����y�+}2��T��>���`�l.���A���2+z�;��B���M|�%�,S����/O���!QJ���1=2�Ml]��]�nC����	.5\��%,K����W�6��i}��f�n���EB4�-c{.���3��C�+���������K�-�,���N���b��;�(�X���j1Q�\��bU]@%;�����^�_M��"�GCc+r�\]�,ja��a�A�m��wE�����
c��������Y�o����c�AU�i�-�t�u;����$�M�F�xI3m9��nk�B�����6+��S���I���
-��!��Knc�J��g����V�������0NryrP
������T�~K��������B-���t|�Yv��v�[/\�U����."��-).�RmIyv��uVJ~�Y�
���K�q��V��dEY	�RFz��?bm���i�������U����t�	�����h�����WE����R�;@IC
�J���.����.�����9`�����|����R�M"G]���M>��4����3m��QIw����f�}�)����!������Y$5>-�&�Q-����QlY`���D!���B�A�as����i�@�[-���_���/��;��~iUW�y5�&��h��Q��sS(�
0�EQ�R���PN1b�0���D1S�<�Sd��M���)_��I��w��2���6���.E�|6e%mp����0Rc���8�H�wN�A��K��'���hqatAk��a���.T�N����M��GK���?&�T�,V�/�#8>p�7����+r������rDVQ��������H�jA5�]������0�1w�w���:��l�;�]�"�)FIpQL�>�:���*n�^����5�^f�WR ��%��Q�A����Y-c�� �������d�i�P�b[~M���Q�hkTe�r&���.R�~x�����	@u�lDL�c�S���$:�����������iFD�Z�8�#m�QBC����5_�"G<��t�J�(�w?"�=z������$�L�K���g������[���*��1)��D���,�7�i�QN�"�}3���(��"���� O]�z�C}�S��#Z	�11�fQ�N���D�X��%�Z�pjI���?��SI[�XjE������S[���[��6�`��\g*�"�-xI>+��hu���E��[T~�:�-
H���5����f��J��u1�4m�����mk��}��h��o��Z��#i�<uA����Yi`�g�:d�	%}j�3�����Sy*(7i'V�Kx|I�e���C�#�aR9�:n�l��&�@�\��^������A�O�����1�[�*T7�Ol�Pg�fg���,]R����������+����,Q���Tm�"G��q���}:j8�����&��yn�{����5�����\�������%����W����0��;�.���P
��!�/�H��$op�n?G��y	�d���19���~'�`�����%�:
��%�b�%3}}��y��`������$��;�n�E�.�"�c;1�t���D��H)�^���H�Ah�%�ns�0�_���r�M���.��qIP��.�3�r���S���l����"L����%��d�0QY��yd�����s��c��X?V!���^4�K+I1]�qb�k��G�����imE~���J�8(v�������2���1;�+�FK����_3�K�(�"B��|�G�l)�7���H�c��k(zJ�
O��'BE�y3���t
�""3C�����K�)+�*y�.N#Hwa	��J���pyc���d/������F��S��T�q��J,�i��f�T���[��L����3A�$���!����tT��d�:�CC3�Z�q^z�c$7?�G
���
e�NC43E��k�%��n�pZ{�3f��������z�n��3-`�xg��$�L1�>F����x���cb{�����a7����I���{JK�y0����Jn%<��c������d$d<h����,������8�n76����h�<���m�@�(�����)�%J9���`�^%��Y�Q�&SR\�����D��(Xb4�9;�~���<�F@���}cd
�+|�~IH��e��4�������g�
�������x���������Uy��dQ�R�j�KqJS��'��D��V]����#���5N��R6�� !��:+1�F�pJ�m�m�����m�g��z[����*�6KH�xO1���Ysu����v�z��1QDmb��x�v�R�������Yc������_��"�Y��m@�X��C���vI�b8��'0�q1s@�T�q�\3^0�(��L�g�z	�j�`"C�� �[t6���E�U���������r{V4"��c����c��a��fF|���J�c�����:�T�j�d����P����3F�j�(7�X�K��D��w��N�;��X�:�b��h�G��46:�����lE�=��5��"���QN�Ru�-P��
�>����J��ciEG_�cl�(�e��^�I�~���"�����u_4676z�6�0f�u�����J/;��W���0���^$P��I�=VG���9)������XS��"�c�b:b�.v`���-tT�d�`�`.�	H_P����O^m���s0l�fI������O[T���3��@3:]�u?q�``,��f���Rt�d��u���$���T{`�;;��
R�|��t�	����/�!9��YH�C�3,:z���J�C]<�z?�|�a*�W��=�B?B���R�/>h�+�g�)��c����Z7?/�r�t����J$7S��|�FX�,+�C���\<�Aj�(���eE!���1b
�����nf��lb��jYq�o��}Q�h���Vb}iX����: 
}���%�'��;~]����x�U��l5�	���miC,��p�b�f	P���d���LNF������s��0b��uu>�eh`t��������xM=eb]���Ml�����t���h6Oa�wpS=�����w�
��?\����+CX${�Hp\bZ���V��9�,6�o}}����}���l6��npyYs��h��w��������v�qg������X����(>���gX��������+����g����z6�N]�>��lo�6��.��N���~!~�N@x�WoGc����dt1��2I!-��w�?n]��ifL�-�WO�a|9B�FD��d�X��NZ>�K�.&cX�.T��h�_�{����7?��)Z�����n��@��a�9@cD��.D�4!�_d���N��@�q�����������m]��,�2z�;��c8�7L��v�����S�8c���*X Q!�[������pt���T
�R����z�#����W���7��� �T�5�>8�O�_C`�I��\�20^��81�����E������?��|�'���x��yGf�8���F�N��D��I�{�����~��h�9������d���}��{�����w7���v���w�w�w�w�w��=z�V�[�����������[�����{�B�����(^4k`�8B��K���a��~���e2��nx�g�o;���En����{/��?�|m��!��R�}�Pv#:�F�\�P��v���G�VG*���������z�����l
��_���;0�����������@+~7�g4�����%���[/�U��@������v��d3R8�
�'����U9�Y}���<�;r�������wwe,�B�����Ke�:��,j�����1g#
�qy�h���H�[�>8	�������#�X]I1��=��K��GQ���$��*�E�Z���a�S1��\`/6e����r�����������n&$�C!7�����:ZEs�����9��puElU�����A9�M�f���B�`����c�R�����b���n8�]�{��1O�j>�/(f��
�/1)��`L�����?
j�$+{�����C��������
X�@�L���^��^����~~z��E�\�T���<z�?v�������[!(��E�<�*n�4OD�7P����J�V�P@X��j����M�j�����XZ���n0���Z���I�5
�+�(�x�����4]�����M�����j�y�w����?Zi$�F��O#�_QS��������M�u��k�^7������N�����Z]`��,>�����>l�'�!�5����_H�v�H�|�����3���w���6W�h�kvw���t��S�}t�fO�
�U�g�������B�lY��O����dx8�&N����
�6E�/�H�A�:��9���1���	+Y4���o+;�����	�h%�Lv�
V���';A7u�
�3��������!�����a��c4�X2��V�����$�����v������E�0"�GP��M�MH�)�t��W��:�S�`mx���`���\�����n0��1��b��l��)[^������u&���Xo|^W��gd����b��������iUdb.D~�����������9WG�2��� )�DM�@5R&jre7��l�1�q
���b,K���^������y�Y)1�V��\��$�s���L������?�.��4���t*�Iv*q���9V�/�l�a�|�y!QP�6�*
1����&��{����&��Y��7.���w���Q�F|��:���8�s=@)�X{X�,^���%!9�1HClm���N9��h9}I�m�P���~����#v�z����-�iti�PC���oq����E������O]��Z�r}���2L�����m����.,�a'x��zu���6��i� ��-�8�_��k���~/��m=��������6;�(��l���>��������x+�}�^/!�O�EW}g�zrR^������>z���W�2����{y�"�]_V�����WP/��4A
�d����������P�Z����
���:���w3�����>2��e]�&�sX��)?jQ�\��������diE@����CJ�]�M�W�:��N��*�����@��Q�������6�3����^�����&��0�,��G�����M�xOvj��
;�������l�f�����q}�l����J^���D=��)}u|q2�wV#\�+�.�A�����v��Z���2png�Yi���`�����#�/Y�;���~cA`L�i`<x5zAj�i���q�3������
�f��5�M���]'?���1e}����C�i�ID�B)o.P��p��
,��N�����b[&��lo�PmnX�����s-A��!nw�����n-��aT�����J-VW��}u���Vq�^&��9�H�p���6N{��
���:;��S)aE��l��Z^K2�{Bf��w��AZ��AS�����@�A�n�����;��~x�q���U"*$CrP]i$h�(#�����=���!�
�>�v�o�b�����������Yj>{6k=O�`�5Rg~�|�h���Vtt��������*x�����E�VP�jon��e�n�=|��=q
_���4hl��A��5ukP,�%�/��o�j�CJ����F���=�;��"�e1��s��v.[i���{v$V���k�e��������dvIO|��t��}����/_� �I\��O�}C#r�m�~
�m�D�
o����>�����������-T��'��~��;h��v�d����_|������b[��;�7��������M�!���"���~��������Y:��Dy!������M���B�B*�YO='��q��N�^\���_]��j�3jb�N���;�;����,�,���,j5�T��������^�Mf	�vv��"r�r�0�� �1�m0Z4��m�)�����Vf���\SK����%�W]�j����p�C����}4�����x�
;�y!}�� ����j]����xzZ�b0��j-o�@`��5��]���L�g{�|���{��Q��Z���y\�>>x���)�����p��H��<z���G��+2K�D����Z�\}$�1�sX#V�;���3���~�Z�������p}�{�=�������^.H�-�5d�Wv8�xp�����B��X�DS��B���r�G�T��������B��_��
,R(�H����J1DK��l����H�S����	�P�zz�_m�x�
�eR(X_�Q���Z����	U�T�V��*��:���K�m�r��T����Q����"`<A�C��� B�,@����[9�����K%��E�����R�p��j��Am���N8�w]�~�51m���a�M��MX$�48�ip��W�G����S3��@�t�@�X���6����$��E���1-�5�B��)�j6N�4��u$h4�[��(�40<���t�,kFq��=o���Q0R�������V��zf��f�r���G����h����NV��N=M������Y��7V���J�o'�k@��BZ��������T!��$
�������������N�
%�6I����g��t���$��~�{JwU*t3G}~=g}o�0���fS��5f��v����������E �n"6',��N��@X�����Vl�N�=�f�{�B�l�-hX�%����uyka�6��0H��A��9��/�}���*Sx�L�v���2�7��k�.����1usaL�\S7����1u�S7�0u�S7�0u�S7�0u����yoa��\��h.nj477�������[��_��h���R��YjH����~7��8�;q���=n<6�JrVa�L�N�����\���'�|b��S>�}�)�,�P�,F��(m�6�o7��(�����`�#����������:��x���m�Z��m���U��o���[���V)��U��o���[e'���3�V�)p���Uz��o8X�D�Y���Y'���Lx� -���T�'����,Ja�(}�����'���'%��'%t�'%T�'%4�'%�8%�"����'���,LI�0���T�'�4�'e�'e��'e��'e��'e��=2��'���������'B�%�������L-��3S�����3����^sVD��b����Y+��*��S���x}w���ww�D}/N�f��8��lo���P�s\y�H�2�����F�3��bF�d��O�N������m�����,�E�r�]8d��\��>r�����2��)����~#�}3������N�Xl�,���LW�x�\e�r*�Q�H���N7��F����c���_(���B�,Zw��p��,`��qY�F�l/v�i�o.�b{�0�O��o"�"Y�X���1�Q��l&M�����~�0D��7�_7s�'����9������`�!�\�`�V�r�5�l��Y������$��CI������h�7���YtA�O<��Km���P�XrT9���9uF��?5�������S�b8��E[��lf�e���\�Vn����up���6N�+Kl���IJ�������{wYMPmx��~��y^W>��D�[��%%H0��!Aj���^�����>j�x����[}��!��W}$�5]��W
������gt{xtA��7� Q�\k��������?
W��!��������1a��M~�e�M�>�j����1�5���=��A���p���xOHyf�<���%0�����wc9`�����KkIr
l���w^i�:��-[4S��NK����N���K�{�$��%�-"zf%��L����["M�NWxj�f�j3�3&�~�h�gV���� ���A��� �5�Y!.��2s���C������$�9���,��K�vI�-�����|��;}���7]��n�:?�"�g	���H/
x����K���4��l��5n�"-Gn\�N�@���V�(�'Q�Uf��'��X�I�e4"������W�[f���ttFJ��<���	d;}������8(g�
�
���xr�b�|IJ�d}<	������Y�(�EZ�p�o����zO�uO)��o1�d�<���k����>\k0�������!����HM	;�w�������c����x�������h|�����(�#��XR#��h���%c�e7Iqz�;���1��]v�hB��Xz+_h��&��X�����g�W�A��A��^��6#:1R����3��@��u�8�'[[�^�n�8�p#�z�n��������;�>��	o�_r�����<�vV��z��1g|�U?�n�U�V?�X�o5����t����������xg��@��]�Z)����p���z����wY07Z��Jos*7r�7���g�J�j=��L��3���y�|+��r#c�Y����F�6rF��7���1ldb#k�����F�Hd`���f�H43G��=���h��D3o$�#���f�H4�G��7��
�xf
��OL�(�w�^���K/
����(\2v�
�z�
{���L�s����+�Q�l+I���X��2���k�e;������S��OR�3��k,�E�.����l<�;�/�pr�R
�������7j)��#�+>b)E]#�.�1w1�����XZa���N�XJ�������j��{B���zl?e�1��[GU�������GY�v��%f��d�}_�$���]��Z�8SA�����
/��I��Zp��0pj��U&V�����g�}E�>Il�u��Q"���zk+�|�H�(�,�j���D2:�����7�oDz�
GYM{�K�}9�P�<�W�c,yMMS�c5O������9)V}�uLG�@������^�u{������l8m�5����5[�3T�
F�L�Q�B-��b���Q���Zd���.�	��~�>o�2�yja���-o�u����O$~�����H)X|�[d#���Zq����Y�O(�S��wp��o����;�o%���b��tF�G��d���e)��:w�	�wze����$��4�N��w�6���������9�S+��0�5xw����u�1
��59
&����[<��X��C�T^���@��p��Y �
�����]�J#�M>��a�G=��!��C�f���OB��?=��/0�)�<�@C���?v_����$8�����c&���''�����WO������<fASH4;�P�`���w1�ut���k����s�	='�O��8��W�����b�H����*��D��?���'�c�y�����^L�+��u!�\q7d~_���B$��@�$�Z]gh�[�*�'>��a�D���=,l'���8
�aH���z������w��]~E�&G��
������:�QrY��?@��M��P����(b(:���
S�4���er�L�(�"������;��S~��
���P7z!��t5=8o�����w���>���\�a����C�g�3|���#`��"�N��'!	.�>��+��w:Ao��;��w�N�t��`��9Y����h2M�6�#�_��A��(����O�����c�3
��w�'�DT�v�k�Z�/�J�/�:���(��O�������!t��	������$i�L��9�}�T�Av�Y
����(��	L��>���C��1��0��K�b�u�+a ��<~B�S{0�@�|M����"��d�8f�M��;��m���,�v�(���b	_��+�8����W�����8���x��[C]������Ag��]!>�O�n�=\�0!��5I���E�i��	�@{Z!}�c�=�N�G82 �G�1
����;���k�I������zX1��n���t�P}�$���B�7.Q�;�������+JhW$��W8��sX;x��%�A�7��~B+\���Nr]��a�-K&d^x]1E=�a�f�L�/���	��U`A�%p�!q-	�t���N��`x Iwttm~�a2jq4"Hr�@��{A��\��!`I����z"���3���'h�����;�h#�E����w��b���rF=�]%�2;�#��	u��s���n'a�@�!~^Pc�������A�?@a��dL<7(��W���*���D^��q�!����{��W�q�N�s���S��;��aJ2��W�g}��z2:yW����S�O�������P��GY!����������?�%�3����fl�
?[np**�I����H
1������D��N��J�����9I��9���'?^��n�c�����kF	�p���z���|o�[��G��N[,�w!��{sh��"��FH��4�C���o�0s�e�=@u`#�_���A�����\���,�BWZ>�� �������"3�K^�I)���1������	7d+�Z���yR��i�+����p4�!��@\�4a��d�>��-~u
���f�����i��
�����T��r6s�f5�+�b���8���r�Rv4d��'��T�6���l���o�C��p�
�����`xs&d�	��p�����2�`
�0�P�����d��@~�I�Q������J4�'S��Z���6 �p���k2o�?�{�]�BXC-��j\"q#3c4$=�u�����V;���Sz-�E�`2�@���'+�m�Vx:�M���\~�m�i��D������
b����M�RCpJ)6�RC�XQgE���a-{~���"��"��r1��>c]��p �Ag��$^��&�� 0�@�
A�����Xt�����
a2c�SX��'`��.�q�C0��V�����:�r�U�d�PJ�J�F-!Ed��	��mCi��"����+�z���@�j���Mg������M����ue ����c	�����<2�]�������'�70b�3R���Z��2�5	>^�'r�g��~��b+�q�w�1.���!��� �,�;���t�����`�;����T}o����~6=Tw
F?|�R���w���{�����d 5p�K!�����-��k�N
_���k;�>��&_#!���z���@��^���>�Wo����4����?�si �y0�\��l��x=\��������c �uKo����lQ+�m�hJ��]�����{�@���nV��?m=_�X�qQvv�k?��o�Z��?�����W����~��\����O�w<$;��p��0�}�w�9��p�(������;�����������O����%�g��l{m9\�w����g��.�=9���d���|/�?�.�\������o���6�{��zx���'�AL�cEe5l�
m}u�����'������_���A�+�$���V��4������A�1� Xa�K��|��������{����2��no��\�#�@3W�����'��X��k��;���1����nJ��%�;���	�l`��J����n�p��j.�D���>=��+P${��/�����T�l����)��A���T^��L�X����Q�N'�]��>�M�V�!U�����52�,���+�1�d����0�������@�#�����?�/��~�<`����N��V�������Y�ww��x<}R��c�^5���9����D�A��+?����?�cg	izIy{��x�%v��q�ko@���?H+�c'��UV����|��6/*�O�v�����-�����w���Pb�=�m�iUO������gt
�/W��"�����-�t���E�a�YttQ ��x�x��)��c!>�-N�F
I���{'������r�(�H-,����R����|)����)eQU��"/s���L��L�!}	:��I]��g%9q4�q����qb�K�zk)�~R���
�E���3��U��-����pPg�>)����,���zF���Y)��������N	P�L{	��>��o�/r�S�@X����kX�����v�����r��)��k9tv,����ca������9Zw�f�������C���~�M�Y����(��[A�Si�V�TC������[��Y��}i�S�
���gI\~k�u�4����9���Y�L�xk���g�W�#F|�/Ff��-�~�V�t���:���B������O0$_q�#$��^#���QLG�K���iMR<\��o����7�O���v����n�E��������^�s����\����*.�����~�=�<>O+e7�Y@<Q��M3����h��h���b����K_>���
P���]�:��g���?>�d��{m������66]�Y����S���&D�����'G����$�`������#�m��~���U���h��e�97�6�i���B@I����"��q��}�C�!ERR65�F������k�~�ye9�V[,�&T����t���*�rg0{p^�Eo3%#KLS2�6�z6un[�����y�����b�J�u�xF���3��J/sk��|�J�9R�������p��+�~�2���/
#���_
O�:_9��<w
������uX�#���J��z���;�%�_��{���[���w
D�������b���ts���?��O�"xl���#-u��-���k���d/T��<��XM���[��#�����(5���u��1�erq���H,t��m��%�����v<�� �:n�<BvE�u5�z\@�[�v�r��C�����+��{��]E��_��<��b1����X���9�\������gZ��U
]�����1R?�o�
�/�%���$��������nN9Rk�&X�"�r�T�kGy�2�V_��Q������@��������n��ZW�h[����Z�yUu2I�>�A ���H���������S����~��}��q
w���z��=�m�_��G�p2���Hz��� �������K�����y n,�-a<�[��9����!��&B��l�r�~_�|�x�!����p��r6�$1�5c��@@�Ys�#���s��x%q�A�f���)D���@Q��Ld�9��F%E������SC��W�e1C����r�^��l�R :�{�-K`�<,
�g�1���B����J��Jq^�l�
���Gg_`:}�]+���	X=���#��v�K ��3Sz���<~����w�mX���C���2�;?���B�Mq>�����<�S$��nz�~.[!Hi��l�$q�-�$��!Q

��X� ��K���u��1�:o���$W���}�J9���A���R6u�kQA�4�.��i��
c�c5���l�%����Nsh�mN��ns8<(�]���jw-?3g,��i�F����v%<b�k��yH�>�x��p�I���\����r�4U
�	��r)�r��q���t�(TR'HQ�^�>�K�"��hj4����2R�U$�R�C/��]�2��f����!,�����r��J8RP���o[�]P��^�u*��k��QU��[t��F��Z�f�f�����C+��_T;�1S���`!sU��e����r#����z���+����_2�[Z�Z2��R8�{9�`�����o��V���'�R�U�U���kT��� �B����
���
7���>�}�6gG�xo�/�g
��eR�@P`
���PA� t�QA���-{
���N�����]-n(��2iC\��B��t�`�jV-��E>���(6��%J���A>��V����|s�RAYl%��?�V�:�����H�
~�V�!+xd���6< ��"�L_�6�f�@����CCe7b��v���A;c88��)]:�G����#y�mu����K�8��4�_!�8�	�Ec����p�����p�aptGj"�b|��T/�r�f|��
������i(,����������!��B?��M@��
����6�%���J-g�\
����@�E��jN�oUy4(.U�4US�o�@m�,���3A%<�o�2k�v��XK��u�<�H
\M��4N���(��3��m��qU2�x�W�>
�����I�	}|K<�"��D���58[Db����H2�q>I*Y:���`5b-��*�a9�7����O+Ka%C@���f/o��v�
�]C��}��>i���J]�u������n�u��}p��������}�t9P��� p=W��dC�@���S��#����{b�x[�PsW��b���[V��Or����|��
����U����Jg� ]��X�-	���:���#������U��� n
�\G�X�K�cfrJo��`KF��\?JT�g�I?E~���8[^;	x{�����)�Ph�6��6t1�����*�j~vG��
~�m�0���g���>.����z���v����rU-HR���>�]I�����ag�X����I�����=�#�\n�V���;Fi�KDC�����')�����'jH�2*%G{BHn*��$k��-]b�����5�����)d�@����*~jo��kp��[g��H	����U�9`V�&RG�>=��	�S���8#H��~�*oJ�J�5*�V�T�6U�x:>����<��+O2�w$�O��	��k��`>w���EaXs+s������W8�um�+J�[�U��������f.I��,��z� S/&��Z��P�/�^����S�
\�k��w ��x��Cn��(�NP9}a�L!/Wt;D@�6�3����$T�?Lw����n�;�4�z��i�t��]��$������#�qG����6�7:'����������}6����j� H��d��bKw������z��[�k+F��R�kM3�X]rzP���\�+��63P@�^�3��Xp		�6�QTk�IR�T�W�R���Y
����n\��YzT}�/���Z�����.z��c��� �^]^��/^�|�oq=�|����?9L���/^�"~y�����>x�W���|��KPK!n�$E1�� W�9S�;0����@�G3&GA�G���A���*s6��S�:�%�P��:^�ks'O�t�"8
�~rDD
P%�������J���������R�pN���A��{��H~��9#q]�{�����g�������E���Zc����P,��I��u"E�Q�D;-ws��6�m:�{������b�F\qF��
tg�q�|���!�V�g"E@'�4������R��p����%C�_�O�t�]`�L�y���\C�dj1�����g�B�.��"��A#����
����t^�����_-�b���nC	.���@��7j �J��;����WS�����VsTS���Z��P�YA�.&R�,*�O5�����_��H�bc
m�L��6�j����i	}���q�`T�K_�@�y�^��*��#��O�O�Y���.S�J�S����cS_�j~2T�<��>��h���Y�I�\�zwHy,Q�L�e4����dB��+�V�jUH��2Hr��\�����C��P2!�g�����`MZ�5�����������������]��(��������+�$��G��x#dS�������x`M>@�,oA�B�t�e51X�4�6<`�[�2�����{� 	�DJ.Ut����mM�n���F�j�7�oFXY�ZNf�������1��Z@TQHn�A��������WT$���3��9(MXn�V<	Mc"g���@
f"`�R���SM������w�������Q��I�X�Sw�[��N��j��A��{������-?��AN��>�
}�Z&�!�j+g>�����
����'Z�HI�8��?������8��*��V����d�i�x�s�+g����������U�<�������GKy���x]"o�����)q8����e�r`�~�~�[�+�M�7?�z��������As\��A(�g�bP&y�� ����������7����G�������18H���|���������B�B/W�%��,��7����{yr�����Z(�Z�������Z�-��iY|t=�H��
�s��>��ar�k���p<gi��I������`Q&�q�E��d���T\��3�K\6�L�Z�U�GV�2S`����8�^��N�X�T��CG)�m�s�(i����aDl��D���}���"��fCP9*P��<���<�%M�H���+rUC�4�����;�Xm[������6_�|}������o���y���a6$�`6���o�'(!/���>'�>���?v+����%������S|�2�LGUQv���C��M
yc��y<cFm�)PG���!�m+8<�����.#�s+^:=4�:�+�\��F����V��4k�1��TOB���S'r�Oqm6���M
���w����~ ��|��Ry&���>F3��o���$�LY�Shp���U�^�Xl.��G5X"����|@�l�b�^]���� t�V��%�Bj��'���h��8J�8��
ag7��CNA>x{$~��F,�������rCng2�T���X�7Q�XB��V7��	;�YJ�c��Z���X0��@W�B�zWn����'�����:r��B�����ss�v����c`�1}�9�sB���P���}�0�*1"A�mA�@���$/q�I����!Et��po�(OF��/��2�G�b�%��`k�j
#84Michael Paquier
michael.paquier@gmail.com
In reply to: Andrew Dunstan (#78)
Re: adt Makefile, was Re: jsonb and nested hstore

On Fri, Feb 7, 2014 at 1:18 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 02/01/2014 05:20 PM, Andres Freund wrote:

diff --git a/src/backend/utils/adt/Makefile
b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o
array_typanalyze.o \
tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
tsvector.o tsvector_op.o tsvector_parser.o \
txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-      rangetypes_typanalyze.o rangetypes_selfuncs.o
+      rangetypes_typanalyze.o rangetypes_selfuncs.o \
+      jsonb.o jsonb_support.o

Odd, most OBJS lines are kept in alphabetical order, but that doesn't
seem to be the case here.

This whole list is a mess, and we don't even have all the range_types files
following each other.

Worth cleaning up?

+1. Yes please.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#85Erik Rijkers
er@xs4all.nl
In reply to: Andrew Dunstan (#83)
2 attachment(s)
Re: jsonb and nested hstore

On Fri, February 7, 2014 00:47, Andrew Dunstan wrote:

Attached are updated patches.

jsonb-10.patch.gz
nested-hstore-10.patch.gz

Small changes to json documentation, mostly of typo caliber.

Thanks,

Erik Rijkers

Attachments:

datatype.sgml.difftext/x-patch; name=datatype.sgml.diffDownload
--- doc/src/sgml/datatype.sgml.orig	2014-02-09 14:27:55.264512678 +0100
+++ doc/src/sgml/datatype.sgml	2014-02-09 14:36:47.758675826 +0100
@@ -4254,8 +4254,8 @@
     There are two JSON data types: <type>json</type> and <type>jsonb</type>.
     Both accept identical sets of values as input. The difference is primarily
     a matter of efficiency. The <type>json</type> data type stores an exact
-    copy of the the input text, and the processing functions have to reparse
-    it to precess it, while the <type>jsonb</type> is stored in a decomposed
+    copy of the input text, and the processing functions have to reparse
+    it to process it, while the <type>jsonb</type> is stored in a decomposed
     form that makes it slightly less efficient to input but very much faster
     to process, since it never needs reparsing.
    </para>
@@ -4275,7 +4275,7 @@
 
    <para>
     In general, most applications will find it advantageous to store JSON data
-    as <type>jsonb</type>, unless they have quite specialised needs.
+    as <type>jsonb</type>, unless they have quite specialized needs.
    </para>
 
    <para>
func.sgml.difftext/x-patch; name=func.sgml.diffDownload
--- doc/src/sgml/func.sgml.orig	2014-02-09 14:28:12.888989051 +0100
+++ doc/src/sgml/func.sgml	2014-02-09 15:15:44.378580545 +0100
@@ -10157,13 +10157,8 @@
        <entry>
          Builds a heterogeneously-typed json array out of a variadic argument list.
        </entry>
-       <entry><literal>SELECT json_build_array(1,2,'3',4,5);</literal></entry>
-       <entry>
-<programlisting>
- json_build_array
--------------------
- [1, 2, "3", 4, 5]
- </programlisting>
+       <entry><literal>json_build_array(1,2,'3',4,5);</literal></entry>
+       <entry><literal> [1, 2, "3", 4, 5]</literal>
        </entry>
       </row>
       <row>
@@ -10177,12 +10172,7 @@
          constructed out of alternating name/value arguments.
        </entry>
        <entry><literal>SELECT json_build_object('foo',1,'bar',2);</literal></entry>
-       <entry>
-<programlisting>
-   json_build_object
-------------------------
- {"foo" : 1, "bar" : 2}
- </programlisting>
+       <entry><literal>{"foo" : 1, "bar" : 2}</literal>
        </entry>
       </row>
       <row>
@@ -10197,7 +10187,7 @@
          such that each inner array has exactly two elements, which
          are taken as a name/value pair.
        </entry>
-       <entry><literal>select * from json_object('{a, 1, b, "def", c, 3.5}')  or <literal>select json_object('{{a, 1},{b, "def"},{c, 3.5}}')</literal></literal></entry>
+       <entry><literal>SELECT * FROM json_object('{a, 1, b, "def", c, 3.5}')  or <literal>select json_object('{{a, 1},{b, "def"},{c, 3.5}}')</literal></literal></entry>
        <entry>
 <programlisting>
               json_object
@@ -10215,13 +10205,8 @@
          The two-argument form of JSON object takes keys and values pairwise from two separate
          arrays. In all other respects it is identical to the one-argument form.
        </entry>
-       <entry><literal>select json_object('{a, b}', '{1,2}');</literal></entry>
-       <entry>
-<programlisting>
-      json_object
-------------------------
- {"a" : "1", "b" : "2"}
- </programlisting>
+       <entry><literal>json_object('{a, b}', '{1,2}');</literal></entry>
+       <entry><literal>{"a" : "1", "b" : "2"}</literal>
        </entry>
       </row>
      </tbody>
@@ -10419,12 +10404,12 @@
        <entry><type>anyelement</type></entry>
        <entry>
          Expands the object in <replaceable>from_json</replaceable> to a row whose columns match
-         the record type defined by base. Conversion will be best
+         the record type defined by <parameter>base</parameter>. Conversion will be best
          effort; columns in base with no corresponding key in <replaceable>from_json</replaceable>
          will be left null. When processing <type>json</type>, if a column is 
          specified more than once, the last value is used.
        </entry>
-       <entry><literal>select * from json_populate_record(null::x, '{"a":1,"b":2}')</literal></entry>
+       <entry><literal>SELECT * FROM json_populate_record(null::x, '{"a":1,"b":2}')</literal></entry>
        <entry>
 <programlisting>
  a | b
@@ -10440,13 +10425,13 @@
        <entry><type>SETOF anyelement</type></entry>
        <entry>
          Expands the outermost set of objects in <replaceable>from_json</replaceable> to a set
-         whose columns match the record type defined by base.
-         Conversion will be best effort; columns in base with no
+         whose columns match the record type defined by <parameter>base</parameter>.
+         Conversion will be best effort; columns in <parameter>base</parameter> with no
          corresponding key in <replaceable>from_json</replaceable> will be left null.
          When processing <type>json</type>, if a column is specified more 
          than once, the last value is used.
        </entry>
-       <entry><literal>select * from json_populate_recordset(null::x, '[{"a":1,"b":2},{"a":3,"b":4}]')</literal></entry>
+       <entry><literal>SELECT * FROM json_populate_recordset(null::x, '[{"a":1,"b":2},{"a":3,"b":4}]')</literal></entry>
        <entry>
 <programlisting>
  a | b
@@ -10521,7 +10506,7 @@
          If nested_as_text is true, the function coerces nested complex elements to text.
          Also, see notes below on columns and types.
        </entry>
-       <entry><literal>select * from json_to_record('{"a":1,"b":[1,2,3],"c":"bar"}',true) as x(a int, b text, d text) </literal></entry>
+       <entry><literal>SELECT * FROM json_to_record('{"a":1,"b":[1,2,3],"c":"bar"}',true) as x(a int, b text, d text)</literal></entry>
        <entry>
 <programlisting>
  a |    b    | d 
@@ -10539,9 +10524,9 @@
          Returns an arbitrary set of records from a JSON object.  As with 
          json_to_record, the structure of the record must be explicitly defined when making the
          call.  However, with json_to_recordset the input JSON must be an array containing 
-         objects.  nested_as_text works as with json_to_record.
+         objects.  <parameter>nested_as_text</parameter> works as with json_to_record.
        </entry>
-       <entry><literal>select * from json_to_recordset('[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]',true) as x(a int, b text);</literal></entry>
+       <entry><literal>select * from json_to_recordset('[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]',true) as x(a int, b text)</literal></entry>
        <entry>
 <programlisting>
  a |  b
#86Craig Ringer
craig@2ndquadrant.com
In reply to: Tom Lane (#65)
Re: jsonb and nested hstore

On 02/06/2014 01:48 AM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 02/05/2014 11:40 AM, Tom Lane wrote:

switching to "binary is the same as text" may well be the most prudent
path here.

If we do that we're going to have to live with that forever, aren't we?

Yeah, but the other side of that coin is that we'll have to live forever
with whatever binary format we pick, too. If it turns out to be badly
designed, that could be much worse than eating some parsing costs during
dump/restore.

If we had infinite time/manpower, this wouldn't really be an issue.
We don't, though, and so I suggest that this may be one of the better
things to toss overboard.

Can't we just reject attempts to transfer these via binary copy,
allowing only a text format? So rather than sending text when the binary
is requested, we just require clients to use text for this type.

That way it's possible to add the desired binary format later, without
rushed decisions.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#87Hannu Krosing
hannu@krosing.net
In reply to: Tom Lane (#65)
Re: jsonb and nested hstore

On 02/05/2014 06:48 PM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 02/05/2014 11:40 AM, Tom Lane wrote:

switching to "binary is the same as text" may well be the most prudent
path here.

If we do that we're going to have to live with that forever, aren't we?

Yeah, but the other side of that coin is that we'll have to live forever
with whatever binary format we pick, too. If it turns out to be badly
designed, that could be much worse than eating some parsing costs during
dump/restore.

The fastest and lowest parsing cost format for "JSON" is tnetstrings
http://tnetstrings.org/ why not use it as the binary wire format ?

It would be as binary as it gets and still be generally parse-able by
lots of different platforms, at leas by all of these we care about.

Cheers
Hannu

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#88Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#83)
Re: jsonb and nested hstore

Hi,

On 2014-02-06 18:47:31 -0500, Andrew Dunstan wrote:

* switching to using text representation in jsonb send/recv

+/*
+ * jsonb type recv function
+ *
+ * the type is sent as text in binary mode, so this is almost the same
+ * as the input function.
+ */
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   *result = cstring_to_text_with_len(buf->data, buf->len);
+
+	return deserialize_json_text(result);
+}
+/*
+ * jsonb type send function
+ *
+ * Just send jsonb as a string of text
+ */
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	pq_begintypsend(&buf);
+	pq_sendtext(&buf, out, strlen(out));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#89Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Andres Freund (#88)
Re: jsonb and nested hstore

On 02/10/2014 11:05 AM, Andres Freund wrote:

Hi,

On 2014-02-06 18:47:31 -0500, Andrew Dunstan wrote:

* switching to using text representation in jsonb send/recv
+/*
+ * jsonb type recv function
+ *
+ * the type is sent as text in binary mode, so this is almost the same
+ * as the input function.
+ */
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   *result = cstring_to_text_with_len(buf->data, buf->len);
+
+	return deserialize_json_text(result);
+}
+/*
+ * jsonb type send function
+ *
+ * Just send jsonb as a string of text
+ */
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	pq_begintypsend(&buf);
+	pq_sendtext(&buf, out, strlen(out));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

+10

Especially as this is one type where we may want add type-specific
compression options at some point

Cheers

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic O�

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#90Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#88)
Re: jsonb and nested hstore

On 02/10/2014 05:05 AM, Andres Freund wrote:

Hi,

On 2014-02-06 18:47:31 -0500, Andrew Dunstan wrote:

* switching to using text representation in jsonb send/recv
+/*
+ * jsonb type recv function
+ *
+ * the type is sent as text in binary mode, so this is almost the same
+ * as the input function.
+ */
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   *result = cstring_to_text_with_len(buf->data, buf->len);
+
+	return deserialize_json_text(result);
+}
+/*
+ * jsonb type send function
+ *
+ * Just send jsonb as a string of text
+ */
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	pq_begintypsend(&buf);
+	pq_sendtext(&buf, out, strlen(out));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems
like more future-proofing provision than we really need.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#91Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#90)
Re: jsonb and nested hstore

On 2014-02-10 07:27:59 -0500, Andrew Dunstan wrote:

On 02/10/2014 05:05 AM, Andres Freund wrote:

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems like
more future-proofing provision than we really need.

Hm. Isn't that just about the same? I was thinking of the c type int8,
not the 64bit type. It seems cleaner to do a pg_sendint(..., 1, 1) than
to do it manually inside the string.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#92Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#91)
Re: jsonb and nested hstore

On 02/10/2014 07:39 AM, Andres Freund wrote:

On 2014-02-10 07:27:59 -0500, Andrew Dunstan wrote:

On 02/10/2014 05:05 AM, Andres Freund wrote:

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems like
more future-proofing provision than we really need.

Hm. Isn't that just about the same? I was thinking of the c type int8,
not the 64bit type. It seems cleaner to do a pg_sendint(..., 1, 1) than
to do it manually inside the string.

OK, works for me. I'm tied up for a couple of days, will do this when
I'm back on deck.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#93Tom Lane
tgl@sss.pgh.pa.us
In reply to: Craig Ringer (#86)
Re: jsonb and nested hstore

Craig Ringer <craig@2ndquadrant.com> writes:

On 02/06/2014 01:48 AM, Tom Lane wrote:

switching to "binary is the same as text" may well be the most prudent
path here.

Can't we just reject attempts to transfer these via binary copy,
allowing only a text format? So rather than sending text when the binary
is requested, we just require clients to use text for this type.

That used to be the case, back when we didn't have send/recv functions for
all built-in types; and client-code authors complained bitterly about it.
It's pretty much unworkable if the text/binary choice is being made by
a code level that doesn't have complete understanding of the queries it's
transmitting. Consider "SELECT * FROM ..."; how are you going to know
which columns to request in binary and which in text? Even if you're
willing to do trial and error (ie, it's okay to cause transaction
rollbacks), the backend isn't very helpful about telling you exactly
which column(s) would need to be requested as text.

I think the downthread solution of prepending a type-specific format ID
byte is a better answer for giving us flexibility down the road.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#94Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#91)
Re: jsonb and nested hstore

On Mon, Feb 10, 2014 at 6:39 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 07:27:59 -0500, Andrew Dunstan wrote:

On 02/10/2014 05:05 AM, Andres Freund wrote:

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems like
more future-proofing provision than we really need.

Hm. Isn't that just about the same? I was thinking of the c type int8,
not the 64bit type. It seems cleaner to do a pg_sendint(..., 1, 1) than
to do it manually inside the string.

-1. Currently no other wire format types send version and it's not
clear why this one is special. We've changed the wire format versions
before and it's upon the client to deal with those changes. The
server version *is* the version basically. If a broader solution
exists I think it should be addressed broadly. Versioning one type
only IMNSHO is a complete hack.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#95Tom Lane
tgl@sss.pgh.pa.us
In reply to: Merlin Moncure (#94)
Re: jsonb and nested hstore

Merlin Moncure <mmoncure@gmail.com> writes:

On Mon, Feb 10, 2014 at 6:39 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 07:27:59 -0500, Andrew Dunstan wrote:

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems like
more future-proofing provision than we really need.

-1. Currently no other wire format types send version and it's not
clear why this one is special. We've changed the wire format versions
before and it's upon the client to deal with those changes.

Really? How would you expect to do that, exactly? In particular,
how would you propose that a binary pg_dump file be reloadable if
we redefine the binary format down the road without having made
provision like this?

Versioning one type only IMNSHO is a complete hack.

I don't feel a need for versioning int, or float8, or most other types;
and that includes the ones for which we've previously defined binary
format as equivalent to text (enums). In this case we know that we're not
totally satisfied with the binary format we're defining today, so I think
a type-specific escape hatch is a reasonable solution.

Moreover, I don't especially buy tying it to server version, even if we
had an information pathway that would provide that reliably in all
contexts. Granting the presumption that more than one data type would
want such versioning, it's still possible that different data types would
have different ideas about what they needed to do and where the cutover
points were.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#96Merlin Moncure
mmoncure@gmail.com
In reply to: Tom Lane (#95)
Re: jsonb and nested hstore

On Mon, Feb 10, 2014 at 12:15 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

On Mon, Feb 10, 2014 at 6:39 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 07:27:59 -0500, Andrew Dunstan wrote:

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems like
more future-proofing provision than we really need.

-1. Currently no other wire format types send version and it's not
clear why this one is special. We've changed the wire format versions
before and it's upon the client to deal with those changes.

Really? How would you expect to do that, exactly? In particular,
how would you propose that a binary pg_dump file be reloadable if
we redefine the binary format down the road without having made
provision like this?

Versioning one type only IMNSHO is a complete hack.

I don't feel a need for versioning int, or float8, or most other types;
and that includes the ones for which we've previously defined binary
format as equivalent to text (enums). In this case we know that we're not
totally satisfied with the binary format we're defining today, so I think
a type-specific escape hatch is a reasonable solution.

Moreover, I don't especially buy tying it to server version, even if we
had an information pathway that would provide that reliably in all
contexts.

Why not? Furthermore what are we doing now? If we need a binary
format contract that needs to be separated from this discussion.

I've written (along with Andrew C) the only serious attempt to deal
with client side binary format handling (http://libpqtypes.esilo.com/)
and in all interesting cases it depends on the server version to
define binary parsing behaviors. I agree WRT float8, etc but other
types have changed in a couple of cases and it's always been with the
version. I find it highly unlikely that any compatibility behaviors
are going to be defined *for each and every returned datum* now, or
ever...so even if it's a few bytes lost, why do it? Intra-version
compatibility issues should they ever have to be handled would be more
likely handled at connection- or query- time.

Point being, if an escape hatch is needed, I'm near 100% certain this
is not the right place to do it. Binary wire format compatibility is
a complex topic and proposed solution ISTM is not at all fleshed out.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#97Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#94)
Re: jsonb and nested hstore

On 2014-02-10 11:59:53 -0600, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 6:39 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 07:27:59 -0500, Andrew Dunstan wrote:

On 02/10/2014 05:05 AM, Andres Freund wrote:

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems like
more future-proofing provision than we really need.

Hm. Isn't that just about the same? I was thinking of the c type int8,
not the 64bit type. It seems cleaner to do a pg_sendint(..., 1, 1) than
to do it manually inside the string.

-1. Currently no other wire format types send version and it's not
clear why this one is special. We've changed the wire format versions
before and it's upon the client to deal with those changes. The
server version *is* the version basically. If a broader solution
exists I think it should be addressed broadly. Versioning one type
only IMNSHO is a complete hack.

I don't find that very convincing. The entire reason jsonb exists is
because the parsing overhead of text json is significant, so it stands
to reason that soon somebody will try to work on a better wire protocol,
even if the current code cannot be made ready for 9.4. And I don't think
past instability of binary type's formats is a good reason for
*needlessly* breaking stuff like binary COPYs.
And it's not like one prefixed byte has any real-world relevant cost.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#98Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#97)
Re: jsonb and nested hstore

On Mon, Feb 10, 2014 at 5:02 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 11:59:53 -0600, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 6:39 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 07:27:59 -0500, Andrew Dunstan wrote:

On 02/10/2014 05:05 AM, Andres Freund wrote:

I'd suggest making the format discernible from possible different future
formats, to allow introducing a proper binary at some later time. Maybe
just send a int8 first, containing the format.

Teodor privately suggested something similar. I was thinking of just
sending a version byte, which for now would be '\x01'. An int8 seems like
more future-proofing provision than we really need.

Hm. Isn't that just about the same? I was thinking of the c type int8,
not the 64bit type. It seems cleaner to do a pg_sendint(..., 1, 1) than
to do it manually inside the string.

-1. Currently no other wire format types send version and it's not
clear why this one is special. We've changed the wire format versions
before and it's upon the client to deal with those changes. The
server version *is* the version basically. If a broader solution
exists I think it should be addressed broadly. Versioning one type
only IMNSHO is a complete hack.

I don't find that very convincing. The entire reason jsonb exists is
because the parsing overhead of text json is significant, so it stands
to reason that soon somebody will try to work on a better wire protocol,
even if the current code cannot be made ready for 9.4. And I don't think
past instability of binary type's formats is a good reason for
*needlessly* breaking stuff like binary COPYs.
And it's not like one prefixed byte has any real-world relevant cost.

The point is, why does this one type get a version id? Imagine a
hypothetical program that sent/received the binary format for jsonb.
All you have to to is manage the version flag appropriately, right?

Wrong. You still need to have code that checks the server version and
see if it's supported (particularly for sending) and as there is *no
protocol negotiation of the formats at present it's all going to boil
down to if version = X do Y*. How does the server know which
'versions' are ok to send? It doesn't. Follow along with me here:
Suppose we don't introduce a version flag today and change the format
to some more exotic structure for 9.5. How has the version flag made
things easier for the client? It hasn't. The client goes "if version
= X do Y".

I guess you could argue that having a version flag could, say, allow
libpq clients to gracefully error out if, say, a old non-exotic-format
speaking libpq happens to connect to a newer sever -- assuming the
client actually bothered to check the flag. That's zero help to the
client though -- regardless the compatibility isn't established and
that's zero help to other binary formats that we have=, and probably
will continue to-, change. What about them? Are we now, at the
upteenth hour of the final commit fest, suddenly deciding that binary
wire formats going to be compatible across versions?

The kinda low effort way to deal with binary format compatibility is
to simply document the existing formats and document format changes in
some convenient place. The 'real' long term path to doing it IMO is
to abstract out a shared/client server type library with some protocol
negotiation features. Then, at connection time, the client/server
agree on what's the optimal way to send things -- perhaps the client
can signal things like 'want compression for long datums'.

The only case for a version flag at the data point level is if the
server is sending version X at this tuple and version Y at that tuple.
I don't think that's a makable case. Some might say, "what about a
compression bit based on compressibility/length?" and to that I'd
answer: why is that handling specific to the json type...are
text/bytea/arrays not worth that feature too?

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#99Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#98)
Re: jsonb and nested hstore

On 2014-02-10 17:35:12 -0600, Merlin Moncure wrote:

Wrong. You still need to have code that checks the server version and
see if it's supported (particularly for sending) and as there is *no
protocol negotiation of the formats at present it's all going to boil
down to if version = X do Y*. How does the server know which
'versions' are ok to send? It doesn't. Follow along with me here:
Suppose we don't introduce a version flag today and change the format
to some more exotic structure for 9.5. How has the version flag made
things easier for the client? It hasn't. The client goes "if version
= X do Y".

think of binary COPY outputting data in 9.4 and then trying to import
that data into 9.5. That's the interesting case here.

What about them? Are we now, at the
upteenth hour of the final commit fest, suddenly deciding that binary
wire formats going to be compatible across versions?

It has been a concern before.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#100Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#99)
Re: jsonb and nested hstore

On Mon, Feb 10, 2014 at 5:38 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 17:35:12 -0600, Merlin Moncure wrote:

Wrong. You still need to have code that checks the server version and
see if it's supported (particularly for sending) and as there is *no
protocol negotiation of the formats at present it's all going to boil
down to if version = X do Y*. How does the server know which
'versions' are ok to send? It doesn't. Follow along with me here:
Suppose we don't introduce a version flag today and change the format
to some more exotic structure for 9.5. How has the version flag made
things easier for the client? It hasn't. The client goes "if version
= X do Y".

think of binary COPY outputting data in 9.4 and then trying to import
that data into 9.5. That's the interesting case here.

right, json could be made work, but any other format change introduced
to any other already existing type will break. That's not a real
solution unless we decree henceforth that no formats will change from
here on in, in which case I withdraw my objection.

I think COPY binary has exactly the same set of considerations as the
client side. If you want to operate cleanly between versions (which
has never been promised in the past), you have to encode in a header
the kinds of things the server would need to parse it properly.
Starting with, but not necessarily limited to, the encoding server's
version.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#101Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#100)
Re: jsonb and nested hstore

On 2014-02-10 17:48:32 -0600, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 5:38 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 17:35:12 -0600, Merlin Moncure wrote:

Wrong. You still need to have code that checks the server version and
see if it's supported (particularly for sending) and as there is *no
protocol negotiation of the formats at present it's all going to boil
down to if version = X do Y*. How does the server know which
'versions' are ok to send? It doesn't. Follow along with me here:
Suppose we don't introduce a version flag today and change the format
to some more exotic structure for 9.5. How has the version flag made
things easier for the client? It hasn't. The client goes "if version
= X do Y".

think of binary COPY outputting data in 9.4 and then trying to import
that data into 9.5. That's the interesting case here.

right, json could be made work, but any other format change introduced
to any other already existing type will break. That's not a real
solution unless we decree henceforth that no formats will change from
here on in, in which case I withdraw my objection.

Sure, it's not a full solution. But it's better than nothing, and it's
likely that we'll see breakage soonish. I don't think there's been much
recent mucking around with incompatible binary formats?

I think COPY binary has exactly the same set of considerations as the
client side. If you want to operate cleanly between versions (which
has never been promised in the past), you have to encode in a header
the kinds of things the server would need to parse it properly.
Starting with, but not necessarily limited to, the encoding server's
version.

It works in enough cases atm that it's worthwile trying to keep it
working. Sure, it could be better, but it's what we have right now. Atm
it's e.g. the only realistic way to copy larger amounts of bytea between
servers without copying the entire cluster.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#102Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#101)
Re: jsonb and nested hstore

On Mon, Feb 10, 2014 at 5:52 PM, Andres Freund <andres@2ndquadrant.com> wrote:

It works in enough cases atm that it's worthwile trying to keep it
working. Sure, it could be better, but it's what we have right now. Atm
it's e.g. the only realistic way to copy larger amounts of bytea between
servers without copying the entire cluster.

That's the thing -- it might work today, but what about tomorrow?
We'd be sending the wrong signals. People start building processes
around all of this and now we've painted ourselves into a box. Better
in my mind to simply educate users that this practice is dangerous and
unsupported, as we used to do. I guess until now. It seems completely
odd to me that we're attaching a case to the jsonb type, in the wrong
way -- something that we've never attached to any other type before.
For example, why didn't we attach a version code to the json type send
function? Wasn't the whole point of this is that jsonb send/recv be
more spiritually closer to json? If we want to introduce alternative
type formats in the 9.5 cycle, why can't we attach version based
encoding handling to *that* problem?

The more angles I look at this from the more it looks messy and rushed.

Notwithstanding all the above, I figure here enough smart people
disagree (once again, heh) to call it consensus.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#103Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#102)
Re: jsonb and nested hstore

On 2014-02-10 18:16:15 -0600, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 5:52 PM, Andres Freund <andres@2ndquadrant.com> wrote:

It works in enough cases atm that it's worthwile trying to keep it
working. Sure, it could be better, but it's what we have right now. Atm
it's e.g. the only realistic way to copy larger amounts of bytea between
servers without copying the entire cluster.

That's the thing -- it might work today, but what about tomorrow?
We'd be sending the wrong signals. People start building processes
around all of this and now we've painted ourselves into a box.

That ship has sailed.

Better in my mind to simply educate users that this practice is dangerous and
unsupported, as we used to do.

But we don't have any alternatives for such scenarios, so that just
amounts to "screw you". If there are good reason for just breaking
binary protocol compatibility, I can live with that, but that's really
not the case here. The additional amount of code is *miniscule*, even
after adding a real binary protocol format since all the code has to be
there for the plain send/recv functions anyway.

The amount of interesting and acceptable binary protocol changes has
gotten lower in step with the acceptance of on-disk compatibility
changes, which isn't particularly surprising.

I guess until now. It seems completely
odd to me that we're attaching a case to the jsonb type, in the wrong
way -- something that we've never attached to any other type before.
For example, why didn't we attach a version code to the json type send
function? Wasn't the whole point of this is that jsonb send/recv be
more spiritually closer to json? If we want to introduce alternative
type formats in the 9.5 cycle, why can't we attach version based
encoding handling to *that* problem?

That doesn't make any sense to me. jsonb is a separate type because it
behaves differently than json. So I don't see how that plays a role
here.

And if we add a new format version in 9.5 we need to make it discernible
from the 9.4 format. Without space for a format indicator we'd have to
resort to ugly tricks like defining the high bit in the first byte set
indicates the new version. I don't see the improvement here.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#104Tom Lane
tgl@sss.pgh.pa.us
In reply to: Merlin Moncure (#100)
Re: jsonb and nested hstore

Merlin Moncure <mmoncure@gmail.com> writes:

right, json could be made work, but any other format change introduced
to any other already existing type will break. That's not a real
solution unless we decree henceforth that no formats will change from
here on in, in which case I withdraw my objection.

Well, I don't recall that we've made a practice of changing binary formats
a lot. Doing so would break existing dumps, which is something we
strenuously avoid.

Even granting that sometime in the future we invent infrastructure to do
the kind of protocol negotiation you're talking about, one byte per JSON
value seems like a cheap and worthwhile cross-check that both ends came
to the same conclusion about what to send.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#105Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#103)
Re: jsonb and nested hstore

On Mon, Feb 10, 2014 at 6:24 PM, Andres Freund <andres@2ndquadrant.com> wrote:

And if we add a new format version in 9.5 we need to make it discernible
from the 9.4 format. Without space for a format indicator we'd have to
resort to ugly tricks like defining the high bit in the first byte set
indicates the new version. I don't see the improvement here.

Point being: a 9.5 binary format reading server could look for a magic
token in the beginning of the file which would indicate the presence
of a header. The server could then make intelligent decisions about
reading data inside the file which would be follow exactly the same
kinds of decisions binary format consuming client code would make.
Perhaps it would be a simple check on version, or something more
complex that would involve a negotiation. The 'format' indicator,
should version not be precise enough, needs to be in the header, not
passed with every instance of the data type, and certainly not for one
type in the absence of others.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#106Tom Lane
tgl@sss.pgh.pa.us
In reply to: Merlin Moncure (#105)
Re: jsonb and nested hstore

Merlin Moncure <mmoncure@gmail.com> writes:

On Mon, Feb 10, 2014 at 6:24 PM, Andres Freund <andres@2ndquadrant.com> wrote:

And if we add a new format version in 9.5 we need to make it discernible
from the 9.4 format. Without space for a format indicator we'd have to
resort to ugly tricks like defining the high bit in the first byte set
indicates the new version. I don't see the improvement here.

Point being: a 9.5 binary format reading server could look for a magic
token in the beginning of the file which would indicate the presence
of a header. The server could then make intelligent decisions about
reading data inside the file which would be follow exactly the same
kinds of decisions binary format consuming client code would make.
Perhaps it would be a simple check on version, or something more
complex that would involve a negotiation. The 'format' indicator,
should version not be precise enough, needs to be in the header, not
passed with every instance of the data type, and certainly not for one
type in the absence of others.

Basically, you want to move the goalposts to somewhere that's not only
out of reach today, but probably a few counties away from the stadium.
I don't see this happening at all frankly, because nobody has been
interested enough to work on something like it up to now. And I
definitely don't see it as appropriate to block improvement of jsonb
until this happens.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#107Merlin Moncure
mmoncure@gmail.com
In reply to: Tom Lane (#106)
Re: jsonb and nested hstore

On Mon, Feb 10, 2014 at 6:39 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

On Mon, Feb 10, 2014 at 6:24 PM, Andres Freund <andres@2ndquadrant.com> wrote:

And if we add a new format version in 9.5 we need to make it discernible
from the 9.4 format. Without space for a format indicator we'd have to
resort to ugly tricks like defining the high bit in the first byte set
indicates the new version. I don't see the improvement here.

Point being: a 9.5 binary format reading server could look for a magic
token in the beginning of the file which would indicate the presence
of a header. The server could then make intelligent decisions about
reading data inside the file which would be follow exactly the same
kinds of decisions binary format consuming client code would make.
Perhaps it would be a simple check on version, or something more
complex that would involve a negotiation. The 'format' indicator,
should version not be precise enough, needs to be in the header, not
passed with every instance of the data type, and certainly not for one
type in the absence of others.

Basically, you want to move the goalposts to somewhere that's not only
out of reach today, but probably a few counties away from the stadium.
I don't see this happening at all frankly, because nobody has been
interested enough to work on something like it up to now. And I
definitely don't see it as appropriate to block improvement of jsonb
until this happens.

That's completely unfair. I'm arguing *not* to attach version
dependency expectations to the jsonb type, at all, not the other way
around. If you want to do that, fine, but do it *later* as in, 9.5,
or beyond. I just gave an example of how binary format changes could
be worked in later.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#108Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#107)
Re: jsonb and nested hstore

On 2014-02-10 19:01:48 -0600, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 6:39 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

On Mon, Feb 10, 2014 at 6:24 PM, Andres Freund <andres@2ndquadrant.com> wrote:

And if we add a new format version in 9.5 we need to make it discernible
from the 9.4 format. Without space for a format indicator we'd have to
resort to ugly tricks like defining the high bit in the first byte set
indicates the new version. I don't see the improvement here.

Point being: a 9.5 binary format reading server could look for a magic
token in the beginning of the file which would indicate the presence
of a header. The server could then make intelligent decisions about
reading data inside the file which would be follow exactly the same
kinds of decisions binary format consuming client code would make.
Perhaps it would be a simple check on version, or something more
complex that would involve a negotiation. The 'format' indicator,
should version not be precise enough, needs to be in the header, not
passed with every instance of the data type, and certainly not for one
type in the absence of others.

Basically, you want to move the goalposts to somewhere that's not only
out of reach today, but probably a few counties away from the stadium.
I don't see this happening at all frankly, because nobody has been
interested enough to work on something like it up to now. And I
definitely don't see it as appropriate to block improvement of jsonb
until this happens.

That's completely unfair. I'm arguing *not* to attach version
dependency expectations to the jsonb type, at all, not the other way
around. If you want to do that, fine, but do it *later* as in, 9.5,
or beyond. I just gave an example of how binary format changes could
be worked in later.

Comeon. Your way requires building HEAPS of new and generic
infrastructure in 9.5 and would only work for binary copy. The proposed
way requires about two lines of code. Without the generic infrastructure
we'd end up relying on some intracacies like the meaning of high bit in
the first byte or such.

Anyway, that's it on this subthread from me,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#109Tom Dunstan
pgsql@tomd.cc
In reply to: Hannu Krosing (#87)
Re: jsonb and nested hstore

On 10 February 2014 20:11, Hannu Krosing <hannu@krosing.net> wrote:

The fastest and lowest parsing cost format for "JSON" is tnetstrings
http://tnetstrings.org/ why not use it as the binary wire format ?

It would be as binary as it gets and still be generally parse-able by
lots of different platforms, at leas by all of these we care about.

If we do go down the binary encoding path in a future release, can I
please suggest *not* using something like tnetstrings, which suffers
the same problem that a few binary transport formats suffer,
particularly when they're developed by people whose native language
doesn't distinguish between byte arrays and strings - all strings are
considered byte arrays and it's up to an application to decide on
character encoding and which things are data vs strings in the
application.

This makes writing a parser in a language which does treat byte arrays
and strings differently very difficult, see e.g. the java tnetstrings
API [1]https://github.com/asinger/tnetstringsj which is forced into treating strings as byte arrays until the
programmer then asks it to parse the thing again, but please treat
everything as a string this time. The msgpack people after much
wrangling have ended up issuing a new version of the protocol which
avoids this issue and which they are strongly encouraging users to
switch to, see [2]https://github.com/msgpack/msgpack/issues/128 for the gory details.

While we may not ever store types in our jsonb format other than the
standard json data types (I can foresee people wanting to do it,
though), I would strongly recommend picking a format which at least is
clear that a value is a string (text, whatever), and preferably makes
it clear what the character encoding is. Or maybe it should just
follow whatever the client encoding is at the time - as long as that
is completely unambiguous to a client.

Cheers

Tom

[1]: https://github.com/asinger/tnetstringsj
[2]: https://github.com/msgpack/msgpack/issues/128

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#110Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#108)
Re: jsonb and nested hstore

On Monday, February 10, 2014, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-10 19:01:48 -0600, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 6:39 PM, Tom Lane <tgl@sss.pgh.pa.us<javascript:;>>

wrote:

Merlin Moncure <mmoncure@gmail.com <javascript:;>> writes:

On Mon, Feb 10, 2014 at 6:24 PM, Andres Freund <

andres@2ndquadrant.com <javascript:;>> wrote:

And if we add a new format version in 9.5 we need to make it

discernible

from the 9.4 format. Without space for a format indicator we'd have

to

resort to ugly tricks like defining the high bit in the first byte

set

indicates the new version. I don't see the improvement here.

Point being: a 9.5 binary format reading server could look for a magic
token in the beginning of the file which would indicate the presence
of a header. The server could then make intelligent decisions about
reading data inside the file which would be follow exactly the same
kinds of decisions binary format consuming client code would make.
Perhaps it would be a simple check on version, or something more
complex that would involve a negotiation. The 'format' indicator,
should version not be precise enough, needs to be in the header, not
passed with every instance of the data type, and certainly not for one
type in the absence of others.

Basically, you want to move the goalposts to somewhere that's not only
out of reach today, but probably a few counties away from the stadium.
I don't see this happening at all frankly, because nobody has been
interested enough to work on something like it up to now. And I
definitely don't see it as appropriate to block improvement of jsonb
until this happens.

That's completely unfair. I'm arguing *not* to attach version
dependency expectations to the jsonb type, at all, not the other way
around. If you want to do that, fine, but do it *later* as in, 9.5,
or beyond. I just gave an example of how binary format changes could
be worked in later.

Comeon. Your way requires building HEAPS of new and generic
infrastructure in 9.5 and would only work for binary copy. The proposed
way requires about two lines of code. Without the generic infrastructure
we'd end up relying on some intracacies like the meaning of high bit in
the first byte or such.

Anyway, that's it on this subthread from me

Fair enough. I'll concede the point.

merlin

#111Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#83)
Re: jsonb and nested hstore

Hi,

Is it just me or is jsonapi.h not very well documented?

On 2014-02-06 18:47:31 -0500, Andrew Dunstan wrote:

+/*
+ * for jsonb we always want the de-escaped value - that's what's in token
+ */
+static void
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+
+		case JSON_TOKEN_STRING:
+			v.type = jbvString;
+			v.string.len = token ? checkStringLen(strlen(token)) : 0;
+			v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+			v.size += v.string.len;
+			break;
+		case JSON_TOKEN_NUMBER:
+			v.type = jbvNumeric;
+			v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+
+			v.size += VARSIZE_ANY(v.numeric) +sizeof(JEntry) /* alignment */ ;

missing space.

Why does + sizeof(JEntry) change anything about alignment? If it was
aligned before, adding a statically sized value doesn't give any new
guarantees about alignment?

+/*
+ * jsonb type recv function
+ *
+ * the type is sent as text in binary mode, so this is almost the same
+ * as the input function.
+ */
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   *result = cstring_to_text_with_len(buf->data, buf->len);
+
+	return deserialize_json_text(result);
+}

This is a bit absurd, we're receiving a string in a StringInfo buffer,
just to copy it into text, and then in makeJsonLexContext() access the
raw chars again.

+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	switch (v->type)
+	{
+		case jbvNull:
+			appendBinaryStringInfo(out, "null", 4);
+			break;
+		case jbvString:
+			escape_json(out, pnstrdup(v->string.val, v->string.len));
+			break;
+		case jbvBool:
+			if (v->boolean)
+				appendBinaryStringInfo(out, "true", 4);
+			else
+				appendBinaryStringInfo(out, "false", 5);
+			break;
+		case jbvNumeric:
+			appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+			break;
+		default:
+			elog(ERROR, "unknown jsonb scalar type");
+	}
+}

Hm, will the jbvNumeric always result in correct correct quoting?
datum_to_json() does extra hangups for that case, any reason we don't
need that here?

+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{

...

+	while (redo_switch || ((type = JsonbIteratorGet(&it, &v, false)) != 0))
+	{
+		redo_switch = false;

Not sure if I see the advantage over the goto here. A comment explaining
what the reason for the goto is wouldhave sufficed.

+			case WJB_KEY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				putEscapedValue(out, &v);
+				appendBinaryStringInfo(out, ": ", 2);

putEscapedValue doesn't gurantee only strings are output, but
datum_to_json does extra hangups for that case.

+				type = JsonbIteratorGet(&it, &v, false);
+				if (type == WJB_VALUE)
+				{
+					first = false;
+					putEscapedValue(out, &v);
+				}
+				else
+				{
+					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+					/*
+					 * We need to rerun current switch() due to put
+					 * in current place object which we just got
+					 * from iterator.
+					 */

"due to put"?

+/*
+ * jsonb type send function
+ *
+ * Just send jsonb as a string of text
+ */
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	pq_begintypsend(&buf);
+	pq_sendtext(&buf, out, strlen(out));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}

Why aren't you using using the stringbuf passing JsonbToCString
convention here to avoid the strlen()?

+/*
+ * Compare two jbvString JsonbValue values, third argument
+ * 'arg', if it's not null, should be a pointer to bool
+ * value which will be set to true if strings are equal and
+ * untouched otherwise.
+ */
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue *va = a;
+	const JsonbValue *vb = b;
+	int			res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool *) arg = true;

Should be NULL, not 0.

+/*
+ * qsort helper to compare JsonbPair values, third argument
+ * arg will be trasferred as is to subsequent

*transferred.

+/*
+ * some constant order of JsonbValue
+ */
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{

Called recursively, needs to check for stack depth.

+JsonbValue *
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key)
+{

Functions like this *REALLY* need documentation for their
parameters. And of their actual purpose.

What's actually the uncompressed bit here? Isn't it actually the
contrary? This is navigating the compressed, non-tree form, no?

+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK));
+		int			i;
+		for (i = (lowbound) ? *lowbound : 0; i < (header & JB_COUNT_MASK); i++)
+		{
+			JEntry	   *e = array + i;
+			else if (JBE_ISSTRING(*e) && key->type == jbvString)
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e),
+						   key->string.len) == 0)
+				{

So, here we have our own undocumented! indexing system. Grand.

+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+		uint32		stopLow = lowbound ? *lowbound : 0,
+					stopHigh = (header & JB_COUNT_MASK),
+					stopMiddle;

I don't understand what the point of the lowbound logic could be here?
If a key hasn't been found, it hasn't been found? Maybe the idea is to
use it when testing containedness or somesuch? Wouldn't iterating over
the keyspace be a better idea for that case?

+ if (key->type != jbvString)
+ return NULL;

That's not allowed, right?

+/*
+ * Get i-th value of array or hash. if i < 0 then it counts from
+ * the end of array/hash. Note: returns pointer to statically
+ * allocated JsonbValue.
+ */
+JsonbValue *
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;

Really? And why on earth would static allocation be a good idea? Specify
it on the caller's stack if need be. Or even return by value, today's
calling convention will just allocate that on the caller's stack without
copying.
Accessing static data isn't even faster.

+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+		r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2 * sizeof(JEntry);
+	}

This bit of code exists pretty similarly in several places, maybe consolitate?

+/****************************************************************************
+ *					  Walk on tree representation of jsonb					*
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level)
+{
+	int			i;

check stack limit.

+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *						   Iteration over binary jsonb						*
+ ****************************************************************************/

This needs docs.

+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{

Why invent completely independent naming conventions to the previous
functions here?

+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry * e, bool skipNested)
+{

Imaginatively undescriptive name. But if it were slightly more more
abstracted away from JsonbIterator it could be the answer to my prayers
above about removing redundant code.

+static JsonbIterator *
+up(JsonbIterator *it)
+{

Not a good name.

+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int			res;

recursive, stack depth check.

+	switch ((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:

I don't know, but I don't see the point in avoid if (), else if()
... constructs if it requires such dirty tricks.

+/****************************************************************************
+ *		  Transformation from tree to binary representation of jsonb		*
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	   *begin;
+	char	   *ptr;
+
+	struct
+	{
+		uint32		i;
+		uint32	   *header;
+		JEntry	   *array;
+		char	   *begin;
+	}		   *levelstate, *lptr, *pptr;
+
+	uint32		maxlevel;
+
+}	CompressState;
+
+#define curLevelState	state->lptr
+#define prevLevelState	state->pptr

brrr.

I stopped looking at code at this point.

diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index e1d8aae..50ddf50 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c

there's lots of whitespace/tab damage in this file. Check git log/diff
--check or such.

This is still a mess, sorry:
* Large and important part continue to be undocumented. Especially in
jsonb_support.c
* Lots of naming inconsistencies.
* There's no documentation about what compressed/uncompressed jsonbs
are. The former is the ondisk representation, the latter the in-memory
tree representation.
* There's no non-code documentation about the on-disk format.

Unfortunately I can't see how this patch could get ready in time for
this CF. There's *lots* of work to be done. The code as is isn't going
to be maintainable. Much of it obvious by simply scanning through the
code, without even looking for higher level issues. And much of it has
previously been pointed out, without getting real attention.

That's not to speak of the nested hstore patch, which I didn't even
start to look at. That's twice this patches size.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#112Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Dunstan (#109)
Re: jsonb and nested hstore

On 02/10/2014 08:50 PM, Tom Dunstan wrote:

On 10 February 2014 20:11, Hannu Krosing <hannu@krosing.net> wrote:

The fastest and lowest parsing cost format for "JSON" is tnetstrings
http://tnetstrings.org/ why not use it as the binary wire format ?

It would be as binary as it gets and still be generally parse-able by
lots of different platforms, at leas by all of these we care about.

If we do go down the binary encoding path in a future release, can I
please suggest *not* using something like tnetstrings, which suffers
the same problem that a few binary transport formats suffer,
particularly when they're developed by people whose native language
doesn't distinguish between byte arrays and strings - all strings are
considered byte arrays and it's up to an application to decide on
character encoding and which things are data vs strings in the
application.

This makes writing a parser in a language which does treat byte arrays
and strings differently very difficult, see e.g. the java tnetstrings
API [1] which is forced into treating strings as byte arrays until the
programmer then asks it to parse the thing again, but please treat
everything as a string this time. The msgpack people after much
wrangling have ended up issuing a new version of the protocol which
avoids this issue and which they are strongly encouraging users to
switch to, see [2] for the gory details.

While we may not ever store types in our jsonb format other than the
standard json data types (I can foresee people wanting to do it,
though), I would strongly recommend picking a format which at least is
clear that a value is a string (text, whatever), and preferably makes
it clear what the character encoding is. Or maybe it should just
follow whatever the client encoding is at the time - as long as that
is completely unambiguous to a client.

Its treatment of numbers is also broken from my POV (numbers are not
just integers or floats), so no, we're not going to use tnetstrings.
Plus, the whole idea of us moving to text for send/recv was to save
code, not to have to write new code, so to suggest using it now is to
ignore the discussion that went on before.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#113Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#111)
Re: jsonb and nested hstore

On 02/10/2014 09:11 PM, Andres Freund wrote:

diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index e1d8aae..50ddf50 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c

there's lots of whitespace/tab damage in this file. Check git log/diff
--check or such.

I don't know exactly what you're looking at. Here's what I get:

[andrew@emma pg_jsonb]$ git diff --check master
contrib/hstore/hstore--1.3.sql:465: trailing whitespace.
+ WITHOUT FUNCTION AS IMPLICIT;
contrib/hstore/hstore--1.3.sql:468: trailing whitespace.
+ WITHOUT FUNCTION AS IMPLICIT;
[andrew@emma pg_jsonb]$

I'll have a look at some of your other complaints when I get back home
in a two or three of days, weather permitting.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#114Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#113)
Re: jsonb and nested hstore

On 2014-02-10 22:15:21 -0500, Andrew Dunstan wrote:

On 02/10/2014 09:11 PM, Andres Freund wrote:

diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index e1d8aae..50ddf50 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c

there's lots of whitespace/tab damage in this file. Check git log/diff
--check or such.

I don't know exactly what you're looking at. Here's what I get:

Sorry, forget that bit.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#115Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Merlin Moncure (#102)
Re: jsonb and nested hstore

On 02/11/2014 01:16 AM, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 5:52 PM, Andres Freund <andres@2ndquadrant.com> wrote:

It works in enough cases atm that it's worthwile trying to keep it
working. Sure, it could be better, but it's what we have right now. Atm
it's e.g. the only realistic way to copy larger amounts of bytea between
servers without copying the entire cluster.

That's the thing -- it might work today, but what about tomorrow?
We'd be sending the wrong signals. People start building processes
around all of this and now we've painted ourselves into a box. Better
in my mind to simply educate users that this practice is dangerous and
unsupported, as we used to do. I guess until now. It seems completely
odd to me that we're attaching a case to the jsonb type, in the wrong
way -- something that we've never attached to any other type before.
For example, why didn't we attach a version code to the json type send
function?

JSON is supposed to be a *standard* way of encoding data in
strings. If the ever changes, it will not be JSON type anymore.

Cheers

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic O�

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#116Merlin Moncure
mmoncure@gmail.com
In reply to: Hannu Krosing (#115)
Re: jsonb and nested hstore

On Tue, Feb 11, 2014 at 3:35 AM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

On 02/11/2014 01:16 AM, Merlin Moncure wrote:

On Mon, Feb 10, 2014 at 5:52 PM, Andres Freund <andres@2ndquadrant.com> wrote:

It works in enough cases atm that it's worthwile trying to keep it
working. Sure, it could be better, but it's what we have right now. Atm
it's e.g. the only realistic way to copy larger amounts of bytea between
servers without copying the entire cluster.

That's the thing -- it might work today, but what about tomorrow?
We'd be sending the wrong signals. People start building processes
around all of this and now we've painted ourselves into a box. Better
in my mind to simply educate users that this practice is dangerous and
unsupported, as we used to do. I guess until now. It seems completely
odd to me that we're attaching a case to the jsonb type, in the wrong
way -- something that we've never attached to any other type before.
For example, why didn't we attach a version code to the json type send
function?

JSON is supposed to be a *standard* way of encoding data in
strings. If the ever changes, it will not be JSON type anymore.

My point was that as we reserved the right to change jsonb binary
format we'd probably want to reserve the right to change json's as
well. This was in support of the theme of 'why is jsonb a special
case?'. However, I think it's pretty much settled that the any
potential concerns I raised in terms of providing a version flag are
outweighed by it's potential usefulness.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#117Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#35)
Re: jsonb and nested hstore

On Thu, Jan 30, 2014 at 11:07 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

Updated patches for both pieces. Included is some tidying done by Teodor,
and fixes for remaining whitespace issues. This now passes "git diff --check
master" cleanly for me.

So one thing that isn't clear from these patches is how jsonb will
have the benefit of the new hstore functions and operators. The cast
is not implicit. I believe that Teodor made the cast implicit on
Github on February 8th (which has not been formally submitted), but
that has problems of its own.

Does anyone have any ideas about how best to enable jsonb to take
advantage of the new functions and operators?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#118Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

Teodor, Oleg:

Some bitrot on the nested-hstore patch on current HEAD, possibly due to
the recent update release?

josh@radegast:~/git/pg94$ patch -p1 -i nested-hstore-10.patch
patching file contrib/hstore/.gitignore
patching file contrib/hstore/Makefile
patching file contrib/hstore/crc32.c
patching file contrib/hstore/crc32.h
patching file contrib/hstore/expected/hstore.out
patching file contrib/hstore/expected/nested.out
patching file contrib/hstore/expected/types.out
patching file contrib/hstore/hstore--1.2--1.3.sql
patching file contrib/hstore/hstore--1.2.sql
patching file contrib/hstore/hstore--1.3.sql
patching file contrib/hstore/hstore.control
patching file contrib/hstore/hstore.h
Hunk #2 FAILED at 13.
Hunk #3 succeeded at 201 (offset 9 lines).
1 out of 3 hunks FAILED -- saving rejects to file
contrib/hstore/hstore.h.rej
patching file contrib/hstore/hstore_compat.c
patching file contrib/hstore/hstore_gin.c
patching file contrib/hstore/hstore_gist.c
patching file contrib/hstore/hstore_gram.y
patching file contrib/hstore/hstore_io.c
Hunk #1 FAILED at 2.
Hunk #2 succeeded at 23 (offset 1 line).
Hunk #3 succeeded at 53 (offset 1 line).
Hunk #4 FAILED at 63.
Hunk #5 succeeded at 297 (offset 13 lines).
Hunk #6 succeeded at 309 (offset 13 lines).
Hunk #7 succeeded at 348 (offset 13 lines).
Hunk #8 succeeded at 359 (offset 13 lines).
Hunk #9 succeeded at 394 with fuzz 2 (offset 20 lines).
Hunk #10 succeeded at 406 (offset 20 lines).
Hunk #11 succeeded at 462 (offset 20 lines).
Hunk #12 FAILED at 508.
Hunk #13 succeeded at 551 (offset 21 lines).
Hunk #14 succeeded at 561 (offset 21 lines).
Hunk #15 succeeded at 651 (offset 21 lines).
Hunk #16 succeeded at 696 (offset 21 lines).
Hunk #17 succeeded at 703 (offset 21 lines).
Hunk #18 succeeded at 767 (offset 21 lines).
Hunk #19 succeeded at 776 (offset 21 lines).
Hunk #20 succeeded at 791 (offset 21 lines).
Hunk #21 succeeded at 807 (offset 21 lines).
Hunk #22 succeeded at 820 (offset 21 lines).
Hunk #23 succeeded at 856 (offset 21 lines).
Hunk #24 FAILED at 1307.
Hunk #25 FAILED at 1433.
5 out of 25 hunks FAILED -- saving rejects to file
contrib/hstore/hstore_io.c.rej
patching file contrib/hstore/hstore_op.c
Hunk #1 FAILED at 25.
Hunk #2 succeeded at 202 (offset 14 lines).
Hunk #3 succeeded at 247 (offset 14 lines).
Hunk #4 FAILED at 253.
Hunk #5 succeeded at 756 (offset 15 lines).
Hunk #6 succeeded at 799 (offset 15 lines).
Hunk #7 succeeded at 885 (offset 15 lines).
Hunk #8 succeeded at 1416 (offset 15 lines).
Hunk #9 succeeded at 1605 (offset 15 lines).
Hunk #10 succeeded at 1720 (offset 15 lines).
2 out of 10 hunks FAILED -- saving rejects to file
contrib/hstore/hstore_op.c.rej

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#119Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
1 attachment(s)
Re: jsonb and nested hstore

All,

Here's a draft cleanup on the JSON section of the Datatype docs. Since
there's been a bunch of incremental patches on this, I just did a diff
against HEAD.

I looked over json-functions a bit, but am not clear on what needs to
change there; the docs are pretty similar to other sections of
Functions, and if they're complex it's because of the sheer number of
JSON-related functions.

Anyway, this version of datatypes introduces a comparison table, which I
think should make things a bit clearer for users.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

Attachments:

datatype.sgml.jsonb-jmb1.difftext/x-patch; name=datatype.sgml.jsonb-jmb1.diffDownload
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 00ccbe1..4baefb6 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -13,7 +13,7 @@
   </indexterm>
 
   <para>
-   <productname>PostgreSQL</productname> has a rich set of native data
+   <productname>PostgreSQL</productname> has a rich set of native data<
    types available to users.  Users can add new types to
    <productname>PostgreSQL</productname> using the <xref
    linkend="sql-createtype"> command.
@@ -139,7 +139,13 @@
       <row>
        <entry><type>json</type></entry>
        <entry></entry>
-       <entry>JSON data</entry>
+       <entry>JSON data, varlena format</entry>
+      </row>
+
+      <row>
+       <entry><type>jsonb</type></entry>
+       <entry></entry>
+       <entry>JSON data, binary structured format</entry>
       </row>
 
       <row>
@@ -3156,7 +3162,7 @@ SELECT person.name, holidays.num_weeks FROM person, holidays
      coordinates, as floating-point numbers.
     </para>
 
-    <para>
+    <para>f
      Points are output using the first syntax.
     </para>
    </sect2>
@@ -4233,27 +4239,101 @@ SET xmloption TO { DOCUMENT | CONTENT };
   </sect1>
 
   <sect1 id="datatype-json">
-   <title><acronym>JSON</> Type</title>
+   <title><acronym>JSON</> Types</title>
 
    <indexterm zone="datatype-json">
     <primary>JSON</primary>
    </indexterm>
 
+   <indexterm zone="datatype-json">
+    <primary>JSONB</primary>
+   </indexterm>
+
    <para>
-    The <type>json</type> data type can be used to store JSON (JavaScript
-    Object Notation) data, as specified in <ulink
-    url="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</ulink>.  Such
-    data can also be stored as <type>text</type>, but the
-    <type>json</type> data type has the advantage of checking that each
-    stored value is a valid JSON value.  There are also related support
+    JSON data types are for storing JSON (JavaScript Object Notation)
+    data, as specified in <ulink url="http://www.ietf.org/rfc/rfc4627.txt"
+    >RFC 4627</ulink>. Such data can also be stored as <type>text</type>,
+    but the JSON data types have the advantage of checking that each
+    stored value is a valid JSON value. There are also related support
     functions available; see <xref linkend="functions-json">.
    </para>
 
    <para>
+    There are two JSON data types: <type>json</type> and <type>jsonb</type>.
+    Both accept identical sets of values as input. The difference is primarily
+    a matter of storage. The <type>json</type> data type stores an exact
+    copy of the input text, while the <type>jsonb</type> is stored in a decomposed
+    binary format which limits reparsing and supports future index and operator features.
+   </para>
+
+   <table id="datatype-json-table">
+     <title>JSON and JSONB Comparison</title>
+     <tgroup cols="3">
+     <thead>
+      <row>
+       <entry>Feature</entry>
+       <entry>JSON</entry>
+       <entry>JSONB</entry>
+      </row>
+     </thead>
+
+     <tbody>
+
+      <row>
+       <entry>Storage Format</entry>
+       <entry>Varlena (text)</entry>
+       <entry>Binary structured, decomposed</entry>
+      </row>
+
+      <row>
+       <entry>Parsed On</entry>
+       <entry>Every use</entry>
+       <entry>Input only</entry>
+      </row>
+
+      <row>
+       <entry>Whitespace</entry>
+       <entry>Preserved</entry>
+       <entry>Normalized</entry>
+      </row>
+
+      <row>
+       <entry>Duplicate keys</entry>
+       <entry>Preserved</entry>
+       <entry>Removed (keeps last key)</entry>
+      </row>
+
+      <row>
+       <entry>Key ordering</entry>
+       <entry>Preserved</entry>
+       <entry>Normalized</entry>
+      </row>
+
+      <row>
+       <entry>Indexing</entry>
+       <entry>Function indexes only</entry>
+       <entry>Function and GIN indexes (with Hstore2 Extension)</entry>
+      </row>
+
+     </tbody>
+    </tgroup>
+   </table>
+
+   <para>
+    In general, most applications will find it advantageous to store JSON data
+    as <type>jsonb</type>, as jsonb is more efficient for most purposes and will
+    support future advanced json index, operator and search features. The
+    <type>json</type> will primarily be useful for applications which need to
+    preserve exact formatting of the input JSON, or users with existing
+    <type>json</type> columns which they do not want to convert to
+    <type>jsonb</type>.
+   </para>
+
+   <para>
     <productname>PostgreSQL</productname> allows only one server encoding
-    per database.  It is therefore not possible for JSON to conform rigidly
-    to the specification unless the server encoding is UTF-8.  Attempts to
-    directly include characters which cannot be represented in the server
+    per database.  It is therefore not possible for the JSON types to conform
+    rigidly to the specification unless the server encoding is UTF-8. Attempts
+    to directly include characters which cannot be represented in the server
     encoding will fail; conversely, characters which can be represented in
     the server encoding but not in UTF-8 will be allowed.
     <literal>\uXXXX</literal> escapes are allowed regardless of the server
#120Tomas Vondra
tv@fuzzy.cz
In reply to: Andrew Dunstan (#83)
Re: jsonb and nested hstore

On 7.2.2014 00:47, Andrew Dunstan wrote:

On 02/05/2014 10:36 AM, Teodor Sigaev wrote:

Should I make new version of patch? Right now it's placed on github.
May be Andrew wants to change something?

Attached are updated patches.

Apart from the things Teodor has fixed, this includes

* switching to using text representation in jsonb send/recv
* implementation of jsonb_array_elements_text that we need now we have
json_array_elements_text
* some code fixes requested in code reviews, plus some other tidying
and refactoring.

cheers

Hi,

I'm slightly uncertain if this is the current version of the patches, or
whether I should look at
https://github.com/feodor/postgres/tree/jsonb_and_hstore which contains
slightly modified code.

Anyway, the only thing I noticed in the v10 version so far is slight
difference in naming - while we have json_to_hstore/hstore_to_json, we
have jsonb2hstore/hstore2jsonb. I propose to change this to
jsonb_to_hstore/hstore_to_jsonb.

May not be needed if the implicit casts go through.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#121Oleg Bartunov
obartunov@gmail.com
In reply to: Tomas Vondra (#120)
Re: jsonb and nested hstore

Yes, the repository you mentioned is the last version of our
development. It contains various fixes of issues by Andres, but we are
waiting Andrew, who is working on jsonb stuff.

On Mon, Feb 24, 2014 at 5:34 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

On 7.2.2014 00:47, Andrew Dunstan wrote:

On 02/05/2014 10:36 AM, Teodor Sigaev wrote:

Should I make new version of patch? Right now it's placed on github.
May be Andrew wants to change something?

Attached are updated patches.

Apart from the things Teodor has fixed, this includes

* switching to using text representation in jsonb send/recv
* implementation of jsonb_array_elements_text that we need now we have
json_array_elements_text
* some code fixes requested in code reviews, plus some other tidying
and refactoring.

cheers

Hi,

I'm slightly uncertain if this is the current version of the patches, or
whether I should look at
https://github.com/feodor/postgres/tree/jsonb_and_hstore which contains
slightly modified code.

Anyway, the only thing I noticed in the v10 version so far is slight
difference in naming - while we have json_to_hstore/hstore_to_json, we
have jsonb2hstore/hstore2jsonb. I propose to change this to
jsonb_to_hstore/hstore_to_jsonb.

May not be needed if the implicit casts go through.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#122Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#119)
Re: jsonb and nested hstore

On Mon, Feb 24, 2014 at 12:20 AM, Josh Berkus <josh@agliodbs.com> wrote:

All,

Here's a draft cleanup on the JSON section of the Datatype docs. Since
there's been a bunch of incremental patches on this, I just did a diff
against HEAD.

I looked over json-functions a bit, but am not clear on what needs to
change there; the docs are pretty similar to other sections of
Functions, and if they're complex it's because of the sheer number of
JSON-related functions.

Anyway, this version of datatypes introduces a comparison table, which I
think should make things a bit clearer for users.

I still find the phrasing "as jsonb is more efficient for most
purposes" to be a bit off Basically, the text json type is faster for
serialization/deserialization pattern (not just document preservation)
and jsonb is preferred when storing json and doing repeated
subdocument accesses.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#123Merlin Moncure
mmoncure@gmail.com
In reply to: Merlin Moncure (#122)
Re: jsonb and nested hstore

On Mon, Feb 24, 2014 at 8:46 AM, Merlin Moncure <mmoncure@gmail.com> wrote:

I still find the phrasing "as jsonb is more efficient for most
purposes" to be a bit off Basically, the text json type is faster for
serialization/deserialization pattern (not just document preservation)
and jsonb is preferred when storing json and doing repeated
subdocument accesses.

Hm, I'm going to withdraw that. I had done some testing of simple
deserialization (cast to text and the like) and noted that jsonb was
as much as 5x slower. However, I just did some checking on
json[b]_populate_recordset though and it's pretty much a wash.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#124Merlin Moncure
mmoncure@gmail.com
In reply to: Merlin Moncure (#123)
Re: jsonb and nested hstore

On Mon, Feb 24, 2014 at 9:08 AM, Merlin Moncure <mmoncure@gmail.com> wrote:

On Mon, Feb 24, 2014 at 8:46 AM, Merlin Moncure <mmoncure@gmail.com> wrote:

I still find the phrasing "as jsonb is more efficient for most
purposes" to be a bit off Basically, the text json type is faster for
serialization/deserialization pattern (not just document preservation)
and jsonb is preferred when storing json and doing repeated
subdocument accesses.

Hm, I'm going to withdraw that. I had done some testing of simple
deserialization (cast to text and the like) and noted that jsonb was
as much as 5x slower. However, I just did some checking on
json[b]_populate_recordset though and it's pretty much a wash.

[sorry for noise on this].

Here's the use case coverage as I see it today:

CASE: json jsonb hstore
Static document: yes poor poor
Precise document: yes no no
Serialization: yes no no****
Deserialization: poor*** yes* no****
Repeated Access: poor yes yes
Manipulation: no no** yes
GIST/GIN searching: no no** yes

notes:
* jsonb gets 'yes' for deserialization assuming andrew's 'two level'
deserialization fix goes in (otherwise 'poor').
** jsonb can't do this today, but presumably will be able to soon
*** 'poor' unless json type also gets the deserialization fix, then 'yes'.
**** hstore can deserialize hstore format, but will rely on json/jsonb
for deserializing json

'Static document' represents edge cases where the json is opaque to
the database but performance -- for example large map polygons.
'Precise document' represents cases where whitespace or key order is important.

Peter asked upthread how to access the various features. Well, today,
it basically means a bit of nimble casting to different structures
depending on which particular features are important to you, which
IMNSHO is not bad at all as long as we understand that most people who
rely on jsonb will also need hstore for its searching and operators.
Down the line when hstore and jsonb are more flushed out it's going to
come down to an API style choice.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#125Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/24/2014 07:08 AM, Merlin Moncure wrote:

On Mon, Feb 24, 2014 at 8:46 AM, Merlin Moncure <mmoncure@gmail.com> wrote:

I still find the phrasing "as jsonb is more efficient for most
purposes" to be a bit off Basically, the text json type is faster for
serialization/deserialization pattern (not just document preservation)
and jsonb is preferred when storing json and doing repeated
subdocument accesses.

Hm, I'm going to withdraw that. I had done some testing of simple
deserialization (cast to text and the like) and noted that jsonb was
as much as 5x slower. However, I just did some checking on
json[b]_populate_recordset though and it's pretty much a wash.

Aside from that, I want our docs to make a strong endorsement of using
jsonb over json for most users. jsonb will continue to be developed and
improved in the future; it is very unlikely that json will. Maybe
that's what I should say rather than anything about efficiency.

In other words: having an ambiguous, complex evaluation of json vs.
jsonb does NOT benefit most users. The result will be some users
choosing json and then pitching fit when they want jsonb in 9.5 and have
to rewrite all their tables.

Mind you, we'll need to fix the slow deserialization, though.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#126Andrew Dunstan
andrew@dunslane.net
In reply to: Merlin Moncure (#124)
Re: jsonb and nested hstore

On 02/24/2014 11:06 AM, Merlin Moncure wrote:

On Mon, Feb 24, 2014 at 9:08 AM, Merlin Moncure <mmoncure@gmail.com> wrote:

On Mon, Feb 24, 2014 at 8:46 AM, Merlin Moncure <mmoncure@gmail.com> wrote:

I still find the phrasing "as jsonb is more efficient for most
purposes" to be a bit off Basically, the text json type is faster for
serialization/deserialization pattern (not just document preservation)
and jsonb is preferred when storing json and doing repeated
subdocument accesses.

Hm, I'm going to withdraw that. I had done some testing of simple
deserialization (cast to text and the like) and noted that jsonb was
as much as 5x slower. However, I just did some checking on
json[b]_populate_recordset though and it's pretty much a wash.

[sorry for noise on this].

Here's the use case coverage as I see it today:

CASE: json jsonb hstore
Static document: yes poor poor
Precise document: yes no no
Serialization: yes no no****
Deserialization: poor*** yes* no****
Repeated Access: poor yes yes
Manipulation: no no** yes
GIST/GIN searching: no no** yes

notes:
* jsonb gets 'yes' for deserialization assuming andrew's 'two level'
deserialization fix goes in (otherwise 'poor').
** jsonb can't do this today, but presumably will be able to soon
*** 'poor' unless json type also gets the deserialization fix, then 'yes'.
**** hstore can deserialize hstore format, but will rely on json/jsonb
for deserializing json

'Static document' represents edge cases where the json is opaque to
the database but performance -- for example large map polygons.
'Precise document' represents cases where whitespace or key order is important.

Peter asked upthread how to access the various features. Well, today,
it basically means a bit of nimble casting to different structures
depending on which particular features are important to you, which
IMNSHO is not bad at all as long as we understand that most people who
rely on jsonb will also need hstore for its searching and operators.
Down the line when hstore and jsonb are more flushed out it's going to
come down to an API style choice.

Frankly, a lot of the above doesn't make much sense to me. WTF is
"Manipulation'?

Unless I see much more actual info on the tests being conducted it's
just about impossible to comment. The performance assessment at this
stage is simply anecdotal as far as I'm concerned.

populate_record() is likely to be a *very* poor point of comparison
anyway, I would expect the performance numbers to be dominated by the
input function calls for the object's component types, and that's going
to be the same in both cases. If you want to prove something here you'll
need to supply profiling numbers showing where it spends its time in
each case.

Having had my schedule very seriously disrupted by the storm in the US
South East a week or so ago, I am finally getting back to being able to
devote some time to jsonb. I hope to have new patches available today or
tomorrow at the latest.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#127Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#126)
Re: jsonb and nested hstore

On 02/24/2014 02:15 PM, Andrew Dunstan wrote:

Having had my schedule very seriously disrupted by the storm in the US
South East a week or so ago, I am finally getting back to being able
to devote some time to jsonb. I hope to have new patches available
today or tomorrow at the latest.

Update to this: A recent commit caused an unfortunate merge conflict in
the hstore code that I have asked Teodor to resolve. I can't post new
clean patches until that's been done.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#128Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#126)
Re: jsonb and nested hstore

On Mon, Feb 24, 2014 at 1:15 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 02/24/2014 11:06 AM, Merlin Moncure wrote:

On Mon, Feb 24, 2014 at 9:08 AM, Merlin Moncure <mmoncure@gmail.com>
wrote:

On Mon, Feb 24, 2014 at 8:46 AM, Merlin Moncure <mmoncure@gmail.com>
wrote:

I still find the phrasing "as jsonb is more efficient for most
purposes" to be a bit off Basically, the text json type is faster for
serialization/deserialization pattern (not just document preservation)
and jsonb is preferred when storing json and doing repeated
subdocument accesses.

Hm, I'm going to withdraw that. I had done some testing of simple
deserialization (cast to text and the like) and noted that jsonb was
as much as 5x slower. However, I just did some checking on
json[b]_populate_recordset though and it's pretty much a wash.

[sorry for noise on this].

Here's the use case coverage as I see it today:

CASE: json jsonb hstore
Static document: yes poor poor
Precise document: yes no no
Serialization: yes no no****
Deserialization: poor*** yes* no****
Repeated Access: poor yes yes
Manipulation: no no** yes
GIST/GIN searching: no no** yes

notes:
* jsonb gets 'yes' for deserialization assuming andrew's 'two level'
deserialization fix goes in (otherwise 'poor').
** jsonb can't do this today, but presumably will be able to soon
*** 'poor' unless json type also gets the deserialization fix, then 'yes'.
**** hstore can deserialize hstore format, but will rely on json/jsonb
for deserializing json

'Static document' represents edge cases where the json is opaque to
the database but performance -- for example large map polygons.
'Precise document' represents cases where whitespace or key order is
important.

Peter asked upthread how to access the various features. Well, today,
it basically means a bit of nimble casting to different structures
depending on which particular features are important to you, which
IMNSHO is not bad at all as long as we understand that most people who
rely on jsonb will also need hstore for its searching and operators.
Down the line when hstore and jsonb are more flushed out it's going to
come down to an API style choice.

Frankly, a lot of the above doesn't make much sense to me. WTF is
"Manipulation'?

Unless I see much more actual info on the tests being conducted it's just
about impossible to comment. The performance assessment at this stage is
simply anecdotal as far as I'm concerned.

Er, I wasn't making performance assessments (except in cases where it
was obvious like poor support for arbitrary access with json) , but
API coverage of use cases. "Manipulation" I thought obvious: the
ability to manipulate the document (say, change some value to
something else): the nosql pattern. through the API. Neither json or
jsonb can do that at present...only hstore can. jsonb cant't; it only
covers some of what json type currently covers (but some of the thing
it does cover is much faster).

On Mon, Feb 24, 2014 at 11:31 AM, Josh Berkus <josh@agliodbs.com> wrote:

Hm, I'm going to withdraw that. I had done some testing of simple
deserialization (cast to text and the like) and noted that jsonb was
as much as 5x slower. However, I just did some checking on
json[b]_populate_recordset though and it's pretty much a wash.

Aside from that, I want our docs to make a strong endorsement of using
jsonb over json for most users. jsonb will continue to be developed and
improved in the future; it is very unlikely that json will. Maybe
that's what I should say rather than anything about efficiency.

I would hope that endorsement doesn't extend to misinforming users.
Moreover, json type is handling all serialization at present and will
continue to do so for some years. In fact, in this release we got a
bunch of new very necessary enhancements (json_build) to
serialization! You're trying to deprecate and enhance the type at the
same time!

The disconnect here is that your statements would be correct if the
only usage for the json type would be for storing data in json.
However, people (including myself) are doing lots of wonderful things
storing data in the traditional way and moving into and out of json in
queries and that, besides working better in the json type, is only
possible in json. That might change in the future by figuring out a
way to cover json serialization cases through jsonb but that's not how
things work today, end of story.

Look, I definitely feel the frustration and weariness here in terms of
my critiquing the proposed API along with the other arguments I've
made. Please understand that nobody wants this to go out the door
more than me if the objective is to lock in the API 'as is' then let's
be polite to our users and try to document various use cases and
what's good at what.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#129Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#125)
Re: jsonb and nested hstore

On Mon, Feb 24, 2014 at 12:31 PM, Josh Berkus <josh@agliodbs.com> wrote:

Aside from that, I want our docs to make a strong endorsement of using
jsonb over json for most users. jsonb will continue to be developed and
improved in the future; it is very unlikely that json will. Maybe
that's what I should say rather than anything about efficiency.

In other words: having an ambiguous, complex evaluation of json vs.
jsonb does NOT benefit most users. The result will be some users
choosing json and then pitching fit when they want jsonb in 9.5 and have
to rewrite all their tables.

Mind you, we'll need to fix the slow deserialization, though.

I think you've got your head stuck deeply in the sand. The json data
type works exactly like the xml data type has always worked. There
have been occasional noises about making an xmlb data type, but
nobody's minded enough to do anything about it, or at least not in
this forum. So if the json data type has no future and is crap, then
the same presumably holds of the xml data type. But I don't think
anyone here believes that, unless they just hate xml on general
principle, which I can certainly understand.

You really *can't* fix the fact that jsonb takes longer to
(deserialize than json. I mean, it's possible the code can be
optimized. But since json is stored in the exact format in which it
is to be emitted, the output function is basically just memcpy().
You're never going to get that kind of speed out of code that actually
has to do something, and I suspect you're going to find that it's hard
to come close.

In short, I think you're viewing everything about jsonb with
rose-colored glasses on, and that your enthusiasm is mostly wishful
thinking. Will there be good things about jsonb? Of course. Will
lots of people want to use it for those reasons? Very likely. Will
it be better than json in all ways and for all purposes? No, and
implying the contrary is just plain wrong.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#130Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/25/2014 08:13 AM, Robert Haas wrote:

I think you've got your head stuck deeply in the sand. The json data
type works exactly like the xml data type has always worked. There
have been occasional noises about making an xmlb data type, but
nobody's minded enough to do anything about it, or at least not in
this forum. So if the json data type has no future and is crap, then
the same presumably holds of the xml data type. But I don't think
anyone here believes that, unless they just hate xml on general
principle, which I can certainly understand.

Well, if we had an XMLB, I would in fact be making the same argument.
I'll point out the only reason we're keeping the original json instead
of forcing an upgrade to jsonb, per earlier discussions, is
backwards-compatibility. If we had never had a json-text, and Merlin
was proposing adding one now alongside jsonb, I'd be arguing against
doing so.

In short, I think you're viewing everything about jsonb with
rose-colored glasses on, and that your enthusiasm is mostly wishful
thinking. Will there be good things about jsonb? Of course. Will
lots of people want to use it for those reasons? Very likely. Will
it be better than json in all ways and for all purposes? No, and
implying the contrary is just plain wrong.

It hurts our adoption substantially to confuse developers. We need to
recommend one type over the other, hence "Use jsonb unless you need X".
Merlin is pushing the type of multivariable comparison where *I*
wouldn't be able to make sense of which one I should pick, let alone
some web developer who's just trying to get a site built. That sort of
thing *really* doesn't help our users.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#131Bruce Momjian
bruce@momjian.us
In reply to: Josh Berkus (#130)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 09:38:50AM -0800, Josh Berkus wrote:

In short, I think you're viewing everything about jsonb with
rose-colored glasses on, and that your enthusiasm is mostly wishful
thinking. Will there be good things about jsonb? Of course. Will
lots of people want to use it for those reasons? Very likely. Will
it be better than json in all ways and for all purposes? No, and
implying the contrary is just plain wrong.

It hurts our adoption substantially to confuse developers. We need to
recommend one type over the other, hence "Use jsonb unless you need X".
Merlin is pushing the type of multivariable comparison where *I*
wouldn't be able to make sense of which one I should pick, let alone
some web developer who's just trying to get a site built. That sort of
thing *really* doesn't help our users.

I agree it would be nice to have something simple, like "Use JSON if you
wish to just store/retrieve entire JSON structures, and JSONB if you
wish to do any kind of lookup or manipulation of JSON values on the
server".

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#132Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#130)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 12:38 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 08:13 AM, Robert Haas wrote:

I think you've got your head stuck deeply in the sand. The json data
type works exactly like the xml data type has always worked. There
have been occasional noises about making an xmlb data type, but
nobody's minded enough to do anything about it, or at least not in
this forum. So if the json data type has no future and is crap, then
the same presumably holds of the xml data type. But I don't think
anyone here believes that, unless they just hate xml on general
principle, which I can certainly understand.

Well, if we had an XMLB, I would in fact be making the same argument.
I'll point out the only reason we're keeping the original json instead
of forcing an upgrade to jsonb, per earlier discussions, is
backwards-compatibility. If we had never had a json-text, and Merlin
was proposing adding one now alongside jsonb, I'd be arguing against
doing so.

You can argue that all you like. But the same argument was made and
rejected at the time we (I) added the original json type. So I don't
believe that you can claim that your argument is backed by any sort of
consensus, because AFAICS it isn't.

In short, I think you're viewing everything about jsonb with
rose-colored glasses on, and that your enthusiasm is mostly wishful
thinking. Will there be good things about jsonb? Of course. Will
lots of people want to use it for those reasons? Very likely. Will
it be better than json in all ways and for all purposes? No, and
implying the contrary is just plain wrong.

It hurts our adoption substantially to confuse developers. We need to
recommend one type over the other, hence "Use jsonb unless you need X".
Merlin is pushing the type of multivariable comparison where *I*
wouldn't be able to make sense of which one I should pick, let alone
some web developer who's just trying to get a site built. That sort of
thing *really* doesn't help our users.

I don't have any objection to editing what Merlin wrote to be clear
and concise; I don't think he meant for it to be considered for
inclusion in the documentation in exactly that form anyway. I do have
an objection to including your unjustified partisanship in our
documentation as fact.

The reality is that if you have a bunch of JSON documents indexed by
some ID number and expect to usually retrieve the whole document, you
probably don't want jsonb. You probably want one integer column and
one json column, because it's gonna be faster that way. And if you
expect to usually retrieve only part of the document, then you are
probably better off using separate columns for the separate parts of
the document, because I bet that extracting a portion of a large
document is still going to require de-TOASTing the whole thing, or at
least all the data preceding the last byte offset of interest, which
is full of lose. The situation where jsonb is going to win is where
either (1) you or your client are so stuck in the document database
model that you can't fathom the idea that using a real database schema
might improve performance or (2) you have so many different things
(pseudocolumns, as it were) that you might want to extract from any
given JSON blob that it's impractical to use real columns for all of
those. I agree those are both real use cases. I do not agree that
they are the only or most common use cases. And I definitely don't
agree that our documentation should push people towards stuffing
everything in a JSON blob instead of using real column definitions.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#133Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/25/2014 10:31 AM, Robert Haas wrote:

And I definitely don't
agree that our documentation should push people towards stuffing
everything in a JSON blob instead of using real column definitions.

????

Where did you get this out of my doc patch?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#134Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#133)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 1:45 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 10:31 AM, Robert Haas wrote:

And I definitely don't
agree that our documentation should push people towards stuffing
everything in a JSON blob instead of using real column definitions.

????

Where did you get this out of my doc patch?

Way to quote what I said out of context.

But to make a long story short, I get that from the fact that you want
to railroad everyone into using jsonb.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#135Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/25/2014 10:50 AM, Robert Haas wrote:

On Tue, Feb 25, 2014 at 1:45 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 10:31 AM, Robert Haas wrote:

And I definitely don't
agree that our documentation should push people towards stuffing
everything in a JSON blob instead of using real column definitions.

????

Where did you get this out of my doc patch?

Way to quote what I said out of context.

Way to put words in my mouth.

But to make a long story short, I get that from the fact that you want
to railroad everyone into using jsonb.

That's called a "straw man argument", Robert.

Me: We should recommend that people use jsonb unless they have a
specific reason for using json.

Merlin: We should present them side-by-side with a complex comparison.

Robert: Josh wants to junk all relational data and use only jsonb!

I mean, really, WTF?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#136Josh Berkus
josh@agliodbs.com
In reply to: Josh Berkus (#118)
Re: jsonb and nested hstore

On 02/25/2014 09:45 AM, Bruce Momjian wrote:

It hurts our adoption substantially to confuse developers. We need to
recommend one type over the other, hence "Use jsonb unless you need X".
Merlin is pushing the type of multivariable comparison where *I*
wouldn't be able to make sense of which one I should pick, let alone
some web developer who's just trying to get a site built. That sort of
thing *really* doesn't help our users.

I agree it would be nice to have something simple, like "Use JSON if you
wish to just store/retrieve entire JSON structures, and JSONB if you
wish to do any kind of lookup or manipulation of JSON values on the
server".

(to clarify below: "json" refers to the current varlena datatype; JSON
refers to JSON serialized data).

I don't think that's decisive enough, which is why I wrote the doc the
way I did. The problem is that most users would prefer that we tell
them which one to use, which is why I want to structure the doc as "Use
jsonb unless you need one of these things", or more specifically:

In general, most applications will find it advantageous to store
JSON data
as <type>jsonb</type>, as jsonb is more efficient when using JSON
manipulation functions, and will
support future advanced json index, operator and search features. The
<type>json</type> will primarily be useful for applications which
need to
preserve exact formatting of the input JSON, or users with existing
<type>json</type> columns which they do not want to convert to
<type>jsonb</type>.

Part of my reason for wanting to recommend jsonb over json is in the
context of the third storage option for JSON, namely TEXT. The only
things which distinguish json from TEXT for JSON storage are validation
and a set of json manipulation functions. jsonb works with the
manipulation functions better/faster, causing the old json type to start
looking like more of a DOMAIN over TEXT than a real type comparatively.
In other words, if you ask the question "Why would I want to use json
instead of either jsonb or TEXT", the answer becomes quite narrow.

Possibly I should expand the little chart and add a column for TEXT?
It's a viable option for storing JSON data, especially if you store a
lot of broken JSON or fragments.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#137Adrian Klaver
adrian.klaver@aklaver.com
In reply to: Josh Berkus (#135)
Re: jsonb and nested hstore

On 02/25/2014 10:54 AM, Josh Berkus wrote:

On 02/25/2014 10:50 AM, Robert Haas wrote:

On Tue, Feb 25, 2014 at 1:45 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 10:31 AM, Robert Haas wrote:

And I definitely don't
agree that our documentation should push people towards stuffing
everything in a JSON blob instead of using real column definitions.

????

Where did you get this out of my doc patch?

Way to quote what I said out of context.

Way to put words in my mouth.

But to make a long story short, I get that from the fact that you want
to railroad everyone into using jsonb.

That's called a "straw man argument", Robert.

Me: We should recommend that people use jsonb unless they have a
specific reason for using json.

Merlin: We should present them side-by-side with a complex comparison.

From the cheap seats.

To me the whole hstore/json/jsonb family is a WIP and any enlightenment
in the form of comparisons would be greatly appreciated by me and other
end users I would suspect.

Robert: Josh wants to junk all relational data and use only jsonb!

I mean, really, WTF?

Seems to be a hot topic all the way around. I am neck deep in learning
Web development and am coming to grips with the role of JSON in that world.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#138Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#135)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 1:54 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 10:50 AM, Robert Haas wrote:

On Tue, Feb 25, 2014 at 1:45 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 10:31 AM, Robert Haas wrote:

And I definitely don't
agree that our documentation should push people towards stuffing
everything in a JSON blob instead of using real column definitions.

????

Where did you get this out of my doc patch?

Way to quote what I said out of context.

Way to put words in my mouth.

But to make a long story short, I get that from the fact that you want
to railroad everyone into using jsonb.

That's called a "straw man argument", Robert.

Me: We should recommend that people use jsonb unless they have a
specific reason for using json.

Merlin: We should present them side-by-side with a complex comparison.

Robert: Josh wants to junk all relational data and use only jsonb!

I mean, really, WTF?

OK, since what I said seems to have become distorted somewhere along
the line, allow me to rephrase:

I don't agree that jsonb should be preferred in all but a handful of
situations. Nor do I agree that partisanship belongs in our
documentation. Therefore, -1 for your proposal to recommend that, and
+1 for Merlin's proposal to present a comparison which fairly
illustrates the situations in which each will outperform the other.

Thanks,

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#139Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#127)
2 attachment(s)
Re: jsonb and nested hstore

On 02/24/2014 04:02 PM, Andrew Dunstan wrote:

On 02/24/2014 02:15 PM, Andrew Dunstan wrote:

Having had my schedule very seriously disrupted by the storm in the
US South East a week or so ago, I am finally getting back to being
able to devote some time to jsonb. I hope to have new patches
available today or tomorrow at the latest.

Update to this: A recent commit caused an unfortunate merge conflict
in the hstore code that I have asked Teodor to resolve. I can't post
new clean patches until that's been done.

OK, here we go, with bitrot fixed (thanks Teodor), Teodor's latest
changes, and versioning for jsonb binary input/output.

This reflects what is currently on the jsonb_and_hstore branch of
<https://github.com/feodor/postgres.git&gt;

cheers

andrew

Attachments:

nested-hstore-11.patch.gzapplication/x-gzip; name=nested-hstore-11.patch.gzDownload
jsonb-11.patch.gzapplication/x-gzip; name=jsonb-11.patch.gzDownload
�z�Sjsonb-11.patch�][s�8�~�V��ND]-��5��d=��rlew��)/��5EjH��&��~�IQ$E]����*G"	4���
������jC;dz���F���p�4,=�������wm��OL7�'�&�����i�X��<�tvk�Z!��j�ZL���X��9��X?Zm��O�L�;k���|�E�tYUW�
����:�o���Y���5����9����0�Jc7�����.,y�F���,^��iV�s����v��3��<��l��HN�Gqu����>fU�r������U�=�o�P��e������_�nz�Ov�������w�+7���]0�zSQ#SCiV�wk�8�C:�����F$���9�A9�$=$���b,�U!�G����-��}{��:k�+A��@��2D�D�&�V�T������B�w�Y79���_�����l�a�����9#t���?�w�o�CA�k���n<`F�����`�M{`	�eg���2��7��0�j4��i��������?0��s�>��Oa����%���U?�3v71�b9���Cv�M�}A�o3&!���Z��{��n=�n��f�s��v��D���G��pf�r�j[BVt�N���������C�L�c��hF�KLTz���������"������!}�jr���zy�?�R����Y�:&��2
������_���J<�|����`E�rR��Ra�"��VU�V�������������~]P{���0]����j����a�2��(s�OB�8�`�\��#���=�������m�PpV_jH������nJ���x��p����`8��
��}��A�K\�8.����pk�s,W������(������J<�	j�6#�����{x:3�`��������!�z���1����/(���
���
dO��uiy%"��<<�)TL����)��$�����q����6�u7��J�hJD��� Q75�0���
 ����dA�d�G���v{��]qD`6����!�|�%�=�Y��vxL�=�QA�]�.�	��S
�"`�#r`n��9Q[�JB�e?�������(��%�����!*�u�����kWH����[h3%T��b`D2$'o�}��#�;`����S�i(����w�P����������P�����D�83k2v`�f�t�u]�T?p����4��)$Vr9\���r$�h06��>��<�6x�jSd��I�r7���B���%��f���>��ar@����]��y��]���'�w������nU|A�M����`����@�=R*�	���������:*
?�~�pz0�vH��Q��d�|{h[`�k����X�%:�����{_;��/����!�T:L6X��4�h�=�S �����#{0��UC��a<��)i8V�c4��V��]��������Ng	Qs��2�F��y����Iy������|�h�z#�1����� ���?�_��YC]3��reh|��INN�KVn0��.3��� z08l�O��f�i��q��:~H�s���h����[�����vm�5L�
�Oo2V!B��6��s5�T9g�=�4J(��4'�8��H����0lhka���G�>z�#���D�D��]��iW��g+�{1Y
�69������3��um��A�z@��` [����?$�!��e����F������?2��}����@�� v\����@������$1�)���7�jAZ<����t�Y��(9�L�g��=��F�)(�;s����ZM�{J��lX	yP �$�^��y$.BA���2���lP��P��'i����D��������Y2�����Y?����dc>�Q@���R��6V��������_m:�)���aN�S)~<���D��#-l4�����d����u>���QG;%�z������Y�SW2�Xc��D����!��0���o�x��������������y����s��WM��\�t��j����2���T����S��>�g��P�J���'�M��@�;#h{��h��V�rp��N��%2HBG�����I�V�_���w���TJ���]�^_���dr�P���3K�g|���1Sy���+r�Y
	X�1a+<��4}[�l�'��n��:rw�����z���������_���������>��X"Mt�Z[����4��X��������=��S����������q�X���`��Js����q�ae2a���^���L��k�(������V*�t�� f*d�K5l�)�����i-m���=��6��"�L)(}�@����*�6~m6<��c�����E��B�-%X�E%	��$E�""�=���\�e��0��,1`����!��#��LGy�������.�9��$���(.�D
	Z����4	���k�q�/g�@\%�����`��H�%���%�}���!���`�L�����`p�H������o�6|���~R�����D�����]:*T�UA������.����+-$��T��y�
0W��4edA9�
��1�� Bnj\`�����9��E�(�r��1��J��x�O�CA{�Z��6��il�����MyY�e#��	�`PEX�W2#(�P2#X�an�[+N���?K�2"!�LRR&�|Tv�w�lM5��Q�
���WM)�"�G��#��}�MaC.6���'�x����������HMZ�>Z���������?��T�F,m�V�T�7����&D�t[�F[ ���9��K����l���$0m�94#"KPDwrS�������O�4���2�������"V����ny8�]��4��;�F�*WO�h��R���-&N��WJv�,�9�tQ�L�%��k�A�X$ad���na��v��c�*�H��~\�*U��jq�J�����i�H.N�N�������~��_G=]L��R�X1��r�VL�b����8j�X����^�=m�E(^"��Z&�0U-��b^��T��|"���O���i�K�Y�U�CbHw�"�S����)o	��1�~p��!�y���B��4������X�R����2��!�Ei}�������g��S��N��<<�U��S�����JPI������|p���a�3��������)of;���-�3��bI���	j��d8��6�l���7f"�4�e���c����o^O�y=n���#��28��:=�*���+Z����EA8���d���UhE����2�}���2���m�]b�4rNZ+a���P�N����Ns	`�+�I�)l�A��I�GW	����g�Op�(�g
q���-;;��B��[�D����
6�<�;���[N)B�������4��_4<�����41�G�f�;��!����h^�c-JiC�����r�r�lrZ���X&n3r��lz�x����@�Fzh�'Z[�i#����v�j��G3.�9��/���A� ��_GM�$e�%��A
(=��*`@Q�	���i��;q�:��
�M:�6J�JR��x,9�q&����q�w�R�<m3���:/1�s T�� 
q���WO�:[�+�ZZ���j���@O�NKk���N�����V�q>���}Z3�
�6��%����R�
��(� =	y�t5
u�3L�R���cJ����Mi��K��M��v3�Z����M}l:���N�������*��H%�;��.�)�]�\k���Q����VYK�#�A�?�S����o�iRfon�p2�G��)������;t��o��
�D��j���cJ�z/���
F5�S����_���=
+E��
6rH��C���X/��"��|�m��U���A�������)l���SJ�t0�W�b�e�\��������-�Y����~z�����x�'s�o-��O-c{o^�$���6lJ�i�����L�E�*g������:S�4W8ST�Yg��O=�����`���G�~<0�=����Q����G�bZU��,;�TZ��wT)���'���i�_��|e���2'�~�*�b�G���9���vN,m�K.��a)�e��cV*Z����3_$H�G9����K�<��.���S��K��<'�,���E}�}��J� ��:�E '	|5Wt@+��n�b,E�#N${���������4��'|����H)�Wp��5��M�1���9b����
��%���{�+5�/s%��@B}(��h�uWm_�i_�c�om�'2C�:� �M|� f�zM�?���Q����"t�V#��V���)�)XW0��Emr0����o2�B�ud�b0/S��h�N�B�+�.���}r������M����``Q>a�~GM�b�'�G�'M6�������\}�
z�2;C7��f��C����`�F�G�O�z�;�O�L1���c�H��z����������"$��W�(��I�T;`U��{y{u��b�[v{�����{����w���-|�r�f�.�f�Er�wW�/>�1Z����^�>�����J#�xq�����+�������zo?^�����P���;�����k<xT�����:�R�����L�ToXZ�/������&���%�0W'��
�
���;�WIK
H�=;:9>��O��e�[���AbQ�2
���[�1���p�}��{�t��{�\�D��>8��7`�A^�?8���.�����N�-���9���Y�u2�O#������
��:��os��
O�w=�|vt�~.\�������{���{I~���}����zcu�����uH4j;����p�E��DC��B����W��Uf;�g>��#����?\���0.���� ��mS��+R��l��a�x=$���#>��P�w<�;`��^{0�q�=p�K����?e�Mb0s!'�W����GG�z�2�f��`RY PkY����qa-?��.z��;���S#k�g��5vG����
�G5����x�s��MB����x���>��+f|X��O�`�>to����
����x)��5�;P����A�����`).�!1��FQl<b��k[]�TgA/�S�]�p ) �?$wU|��f�n.v^��L��D,>�������JY��,��@#������_`�!�(|��?�fA�����r����.�}i��@�7o��_vo�u��������{w��z
c�K[$J��e����Go�q��T���Ky9����&q���?B�A
����F"�������$�\�������<���+}fmkqX��Ki��qZ����+Q��R��hF��?����=:9��=:m
MS���C_9Q0�

V$�[��$�,]�x�z���=Q���OH��'r6��p����
�/���lt�h�|9�Z�(K1/	@&��1hb)�|A���]Fvh�%w6d��}���^����A�W=(��]����oS��b����;?������l�^b��
����\�����R��=�

(���)j{��Vw�-��g�#���P!�/��Q~���"fc&}c��x��}��Y�C�p�s� o�t��%~<�u��Ql3�]�s��k���~:hr������r-����5�&h��9�7��V/�.�,_������Ts�}g����wh�M�S7��'p�){��g>���7_����Q��lh,������'��G�xc�6?��>"j7��U]�|��v�`F���<���b^Lx��;���'�G�
�E$�*�U�50�������~P��$oc��[#�M�wlc�{c����REGp5=�(1�!9�y������'�U�vl��~ P�J��
�l�2�@�*���z-^�&�b0�w����~����
��?��
�F��p���������������xu#\��P�_��vo5���s���o^v�]�:�.z������{�`D/�����T����[`�����p-�T^� %2��-\}���!>�yT����v�Bz� ����=��6>�%5�Ek�/Hn'YY
�!��3�
����M�COh�,^S��X����������i������Sh�=1p�;���I��&N����9�<�h��``hp���~��5�'��"�%��i0N��������R�T�*��0�	��+}�%!�	��x������0��O%�U�/Q��H0yX����0M��F��Z�����l�f�*���~>FS;�Q!���i�����^�rIZ�������?�9���}_���a�{$�K:���r���p�s2���i�,E��
7j��/�;�$��N�����/u8��c���Z�g`��^==~��)�=T���h��Nd��s��c*�[%Yh�*��������z����
���=;������/%�qYT�)�_��w�x(f
��qr�OA�RcT6��t$6��G/�r7H����x��7��[�C����b��"�R��]
��.na��V��Z�6��X���_F��}�[�b�=�k�1��\�#^�-Q7r�������P���H>��I���r�j�����lK����4��{,=�@����������S����M�������������O�+���H���{�����Hv�Y���D1,n��I��	��#5)N��S]-����^�;Ni�"i���R�� ;�O�m��(�r�s�����m�2ro
)�����xx	�����t���t�������c��
3���b��9��������	��Ep~h%=\��~7�f%d]�a�6����L��'9�*��Xr�UW�?r4i�O��'*�4��������n
:VS�m@9�(}�E�����^hJ�e#a�x���kya�1������_a�qJ���UqC��&1���	^�D^D2��@��h��bv5J(�m��+(*]+�#s��x�,�=T0F����	]C�K�����r�QC����&a`��!D	hJ�(=�7�j+�/@?V�J��:i��X{�����D��Mfl�+�����'ecj��>9=?~+���������e8�^������#-��� 8i���&H[�����yB#	t��;&j�����\�5������VW�8�0[8���U�U��������!�
��HRu������|��s!���-�l���~�i�0��&�i2�SqJ{=�����7���}U��ik���d�2�N���i�a��?���0
���A'�������IH�e�k�Dx]�c�E�_����&��w$�������bS��M�'�=�e
a�,j<~d�
R��Mxj���U)��8��7c��j����.[DAl����CD�[T�/&@^2%�L�9	���	��@n�2ezl��>{���b����RZ��6a����
0�c�!��j�S1����H�U��M��|���T�YE��������K�re���*����-�g)T]�7((�D�e�����-;�=�f�2�6���R0�lpV-R���N*L�Xc�.��@m)����x�;
m��
o(L�X�w`��dSN����%�7���y� 
7���������#tL"�7p���xVS7]O.�������L�|*���@���AEU�r���C�&���VF�s�r���9B��B��	8�.>c)@s���TP�g���h���t,G�,HS�������)v$'>���]7l|��|PARwU�����E|C�3Y�p{�0��yC�����u��������bh�C�"D��=kck<.�B��7�&���7%����������Z�|��Ch���u�gv�-�Pb���I�Y�@�i�2VW9.�$�`Y��XT�Q	<�=�$.�H��/Cw-�,��'0�2hA��C���`C�-��zW��x��%�!J%��Y5�=�7AMG�������]�g�r�qi�A�Jx��E3Y����G�-�8�Eoq���h������q"�J%���q�qXi�
:c����umI�8���>
��Oc�_f���y.��Z�cR���(��E�N�l�g�|�l�jSV�I����I����h1��������AK�s�q8��z;���WY�,�SJS���zq9��tM����q94��gCO�sS?(���� ��t�B�2I+Fvb�r��Fek;4�K�fP�������(L
K��b!68��m�3
�,{]��K�����������������en27;[�6�K��j��\I3�#`sY�2������h�4��&�0����3qrv����/�_�e�sI;��H<���Uu6IE��XSR�:�%��������<�@w���q)z��=>a�;�(�+h7@��s)����!u�{��������#x�>@[�2IIp�UL�'��U"-�83T,���������"�3�s'��
I���G���D��O]��yl;�����x�cx�v!�`�;�D�A������$����L���z���Cx���N��������]	]�k��x���9P��}(���L�a���"��}1�z����x�@�`�8{v�����A���=%�Jn�o@n3)R��b�T0J�^�6n1���]M��;������\����jE�mq�WU;TI=E�Cf�%|����4�lr
���xV����m���B��r�h��A&et�1F-�(�^/6����%��e
��Dz*��;S���3�����HC4���~���a�G�����ot�`����o����m4w�����v��s���������\J�wG��>��=��q���Zy���B�Ci��!�H�3������H�NXXX�P'��\HH��R�4� �|�9�u��y%y@��)����N������.�<0
H=�������o�H�{P�Q�d�'^�)�}�U�L���=�Nu�2��'v�5���FzN�����������O�������e�����f�o<g�+���[U��Z�S�<T��c�������AY\�7t�z-��=t�JR�J��5�Y���1���c����U�o�ikd|�N�r�gg�Q��$Z,�Abg��d�j�C����w������JJ�A�w�e�+��^6��di|�����3�gC[���;�`�(�(B�1���A�0dO����{�����. �����y��UL;���1�
��Ck�a<\3��0��X}�,6t7����P���9M���!R"PP�������Y$�������z��Igxn=|(�:0Oge�pPi��KL��y��VAOXzds��d}�b��������S%@r5G����gu�������[�vkS5���b_��Z����<����D���bK���,W�����"���9����]}"Hu���:E�'_$uBX5|���YUd)A�\e�~
O3��[d��@�"�}
2%�&��j����x ������C��u��a����U"P���g����/����Y��'�����-�@L�6_<���*�N)�x_`��c)�a��5}��t,��Q�����o�U^+�5�m���n���!B�[#C�B��X�����5[���c)�xq�GLHr�E�w3��UD6�E8���G�v����OY�XzC�����.U�@y-#[p����\��J*�b���)�ac��R�*�Hy���`���p����KV��4��C@G8"�,��G�������A�r8]���c8���d����,���QPZ�I�e��L���@@�;�Y
���(����u����]���������N;m�z��������w,�
��i�"i�PT}��k,���9����@�~������U�nj�F���	j��v�^���I8����]6^�l�t��5��H��Z#��|������0��!]�/�yi��|N���RQ���Kcg���U������1���rc�E�b�tf�V�0��B��6R��/#�j	f�UW�]�FKN�d��b��)��}�D�h#�����\��a�g�xb�.�'��b3����l%�����gm8�u�{M�����~��h�6El�����0l%�����;�Ei�94v�X�4��HR����I�����>�4��1�"��}�v]���6�u����j7M��x���p��D��4���ay��p��M,�4�Q(q�A�n������j>�(���%e�d���v��f|h�X�>�i�4��n��Yr�MC������wW���2����&-�o�G$��V���8�4
*{�V�.u�s��0w�N����5B�����g'����vn��x4v?
��E���g�b�]��@lni�d�I�uF�.��Z;q��������/J��[�ju���l�\�FC�v������������\NdfB�3��V:K�s�RM�P���c�U�we��������^\`�`���/Sd,,�^l������k+30d�')c�U�l<%U�,�$ y��hclYFJ���Zx���G>���]HFA@a��,Nh���]���n�]��A���nx����pJ<<`����LF�o����P����x�����N�[����&�K�UnhZ\-��5��������h,���2��'d
&��t49n���>T��U&�{�/}o�"��t�����!G
Z|0v��"�Wz��@,mh&�X����4l����lh-6+_���d3�I�P��/^`�|�N��Y\K���(��ro��DA�A��j�J��;N�>��\b�l-@�Nb�0&�����D�RMb��z���l�<�l��#�jt$��t�w�����{����W�oO���An\�)���)o�����#?Iz;9=?zy��i� ��%}%j�����'����9�*�SR���(^�tHY"�w���������t�������#����lj��O�K�XOk�,������7���4�?$����t��GR�T��E��M��vA��
���#��K�a�O�f���1����x(�
Z07?�������AWe|�����yK��(����E����$7C��1p�f
��@�x$�����
����m�&�d��n�6>��D�M������2���W��8�Ur��!�n:�<���d�1~���	�����\5�M<&q�j�!�����Y�5��h��-2���cj��!Q'Aw��Vu��p#�YX5�K���H���������!��i
��OX�@!�2��`��c@���U]���e���q��y�dA�����"��F=��d�����V�g�j�Y*sRO6��G'��|���:��k��$\;}�����8�N� %��$4Ib�����B��Fl-H��)Q�j0=l�����Oa���5*�u��/:����+���x�GU}��N���P�|����g���=�K�f�M/-\�����?z�0�$����OK�������7���G������Wl����
U�V�ACR��B��\�"����Bv��4
�s�]5_\}H:����V��w�q������3�D��uNy�B�m|�B�e�l.�pb�C���bJlw������LG���r`���X �-��(J��X���Gm,nFBn�W�`.�;��u���B�#��&0h�8F�<f�l��
Vt��HBx>�=�����O�-:�Q��F�l�w�]h�eZ�g42��:��j��s	��`	����Iu�1�i7�_�H8V����YT_��S���3�h|#'W�|�@5q�t2���X4�����R��M�����yY)�i5����$�6���i��b��v����$��h�����'�iwd�^�=o2�kFy���� ��%imVi�D{(�!��%��6����K�Ma� ]�A���U���+����e
7���b���q)��j{���u���>oc���Q�����T�������!�'��F�V��z���_FW������^)�FK_�n�����;���{f�D�����58�O3�0��'Rq�^t��dZ�
u�!��S��m��=�hh���^�nZkZ�<@��������<�� ��������"���� [����q�8��{���\e|��	
���EF��ag�*������R�z���*����k�2�\��p�rJ����}��P?�-6_�l�����5���I��h���8�������<��jAh��qK[W�q������Y������e��"{�Yt���X��o�&�3]�����A���i����f�}�������q1��x��BNj)O��t
����
�q;��S	�:%�?#��R��d���7)[��F�F��N82�`�)������Gc7�s��k���c�[�Vp1F%i�}?BWE�����p�)�9m�.��0a�kn��D�n��%(��:�����������$�����)�BL�u�����������O$��R�C��JJ�&�@I-��mV��n��������Z�q��X�b��+�
kl��������T�����)��c�����zO�,����
������M�=6_�?�3���
����R�P5)��(��F6h�rAH6�,f�L�/Mh��$�	���`�� �����Z�T�@��w�Q>�������(;�e�)��fOa.������Z|�;/����x�d�G�����0���v�I�}%�x!�)a1eA��	a���W�Y�O5e������|&� F�H}�JQ�OF�$s�5'B
���_X�UWz���2�&���^�-�H?}�_�1������OT�������?��bl��X��d�l6.5o�eFT&���G#�UZ�R�t����@8�����rH�a05*`������������j��fH�$,�������������0���5{q��m���������<�Q�:J����ZY��To1��������g��4+�����(iP��a>�}�j�T�-e��p���8_S������X�SVN"+����)��o�qg�RP��>�d�g�nH���'�VN�C�T�pq��:'���2�<2�
���,�H�*`S �|�5��]�
��b�_��G�U�F� ���;�n��v]��qi����m���`�*l����i���������]��[��T�A(X�?/��m��`�����q���W������z�J��b�,�c?��|�|�������9��%���L���!dK:R�j���(�4��Dh/�;u�~IJ�������cy�j1��2��/�{Y�h�(UV2�H��- V+���c�]�5H�t�|�k��:���!<[�>u�Y!|
���-P<H��B&�ZkP�C.���a�wM�oI�f��y�5�'y��I��z���>�6N�-��\S������R;}8z��&C��)-�)?<Bsy�h��mP��8�`O;��n�f^g��t��$4���?�H!}|�Y���^��%���:4��%�uv�z�Rr5�qM�!y��7�S��/��n�v������fj\��v��iq(����(Y��/��xWP�*��'73u���a�6�.fl{#���$�,�[\_����5��%
Z�w���Iguvi������X<Fu*������1�+�&�P3u�8K�4 �!f0�%&���Sw�����QI 6J��;�V�FA�j�+"�
��8L�b(�����&���GJP�������[�t4�nm���Ezj���{G�EoZS���)�SiJ�}�����I��b�+^�7@�;��"m���o��C7Y<�/���ZYx,�������O�%�"�i�������(OU�,Z��[ �����D�W�����V����(K:C�������������U"�P1�GGT�������
��
/�{tC�Sa��JtE?J�9����w���X)�h��1�,f�����9k�������UP����O����`�n�M"P��~����<��f�D75�j���J��.b(����+���
�J��Y�8�V�
�����Eq}������	WND�90}e�����^�xVh�
�c�*�����T(,�(����#�G�����_m~�F4��JK���[���T:y:�����	/J�q#�|U=���h5r� x�2��������a���zM�a�~*�1�t\��1N�^���� �`b��P���N�/���c�z��f2>�U��}
�M����1��,�&��y��e���7��U@zon����Eg�zZ�����t)�T�u��[���.�����2�����C������,���3!s�a���'�^����a���P������L��Z�G��+�\X[B�TX�5y'"N���$/�9�t��a2V_���Dgi��|MSp�P��q�D��a�@T�($�h�t��A��s����_P��o0�e�}���w��&�C������##�&���'��J��:�8���P�����B�P��q����(H��@|�j�w��T3�)G8�"�*��a������h8��������t��8`�N��6��LT���	O�������D��RVbcv��ms����v f����1�rO��]Z����;�c�����!�=�U������9R�b
�hFzSYFf1
�� 77w����N]�8��jyz���T����������{��Td��L����`�����~��tz����]?�BO�*�������Am�U����_m�aX��'05q�VUK�������Z�%]��E�2�>Q��/�
�&C�6K�������B���P�����bG��@%�V^����"�U_�P�}�����(�\�`�Od���"���D<�	38)&U��I�$�������xH%~�cQ���J,�#�[D��5aD��� \f��WY��+s��������"�T^l6�S�H�+i�.A�����%��-N�WL�qi�
�����t� A�k�$u�q���6r�$�St0�$��b�~i�������g�	������H�	�+����o��J����2���*����������'X^F�����f�3���\92�G�"IkIE�\���p7
��/*����WEoC��;����E����%0�������C�K�`�(7�6#�A��(��.������ �����h6�_i4��J%E����N�r�(�^y����0
��G��z�����7���@iik&�H�9���#��U�����9g/����&�h`�f�!����t�A�.z���%[0��"���CbS����7��
`��T������3P�pQ]Z��:���+C|����N�s��4��@vK:U u.�86�"^Yj����1��-���j��J���6$�TS@�i��(]�*������X54c`	F�V��h���� Qp/�W�H��}!�#<�)�7����I��n�)N&b4v�����DSwJ&#�`p~N�We��
uT���''����,p.��T1�o��M������X!����2��im��if�@38�(��������`D�Q�V>���'���c:i���LI^��~����%��:=zu���_�j��xW^7�Lx���!�{<W�Ki� ���5��(��-��Z��i��UX���{Ipy��<��ZT�;u&����+�t���|h�"j����
���������^��~��_�B+��:��	h������g�O���#��������iC"BC�4����9��??m�}��ag7���myq7��!0{6�H;P)�1`>}���q����G/O�����I>?~+�tFt�p]���`�nC������^T-�X%H2�}C�*+�!Z$�^�4@�'�%H<�P�"�3��K����k��{-���$����/}�b�+�Z�
���j���
�Bq�(Y��K��u.�;��
�2 J���l����n�'c6���-r
����,K�$�����C�������lU�V���M���k������H;����\�������������;8X�*���/��
�{o����egT���"�aH�fH�W�dS*���$�q���\�e���M��Zxs��V�����7������e��&��I����6>~V36���ES� I�*B� }{�:=���Z�U�������/����q��0��~a0��p���i'��w�p�r��B������Se����c�} <���r#|���X��,�$@��}�����M���v� �V���Y�b��uI�QY�&��k�%a�����j����$�;0�eD�����D��	U�|e�o8��G%��i�:l��(�����*677�Df�/�����-����B�`{g���@���@6#�D0�|OZ���c������Z��u�'��J��`ZoJ
^,i�������7�;����*���%��UfH1>a^�`�x@���0�E��
`B��g��|�l��sm�w����.e��n�_����Gem� �
d�Bw����A(�|yp\ ���&WJ�S�d�E[&�m_
���v�����n�: ��5��*a��l�|�\��.�������Wl����W��N�c�o����O����������|Y|�|���I$S�K3���r���~uo���R��fqi�V|�~�H�������P�8�,���0��Z���9aN���Am�tW�� ����@��
�B��
�y�e�W���Be^���$�K%���Y��z,Y	r�.����K��@7M��������f��8Z��J�t���	��N��z�=+��\�|(����,��T���UQ��xs��Z���U!$�ArmC7ZhK�B��n��q;��7���v8���V8�J�m��#���,urz��,5�cg�{mI��6R~�]�Bm���K�	�i�����]�{����vc��I�R���Yyn��K9��v����`��?�<���4'��x�\"��������eI��k	�ir�Jx�_'<����C��
�|�T�h�:��^�C����:�Rw�����}8����0N{�i�j�8=S��y�#�|�i����-P0���I�^ JFct�����'}���Z=<�I��t2!6f�� �ysLF�G�~���(�8N2���~���}����1�v���(
��������6Y��.��1G��v��'������)<����0�u4g~������N���y����!{�:k���\lU�iO��_�9��H��o@j&A<B�E�"��N�m{�0�����~lQ	���Xw�k1�~M3��K�`���[`�5W3���P�qPkT�r��?��o;��N�M���y��y����T��g1M�w�B�����+4J,����+���Y���TaP.N':��qx���0b�8�~wD�K79Z��W��-���=�������;�qP�+��]���1�i����wdT{�9d
>x62��Z=��g������'�{��d0������M��-t�
^�����m����9#��E���L���Fr�!Xn�Q���-y�
`���xZ4 �1�1K��:�Lm|���l�5�Fc�lm���)5���c6c{'������0�&o��2�����l�;���������b���V����&�Cb��I,0��{dA���:J��r
J}TJ��Fa���
�����NnL�5k{�O�U{����������cE�t�0�c-���\�<�/���[�^s����*���wB��!3�a�*-U�Aq��n�f,V#q�(��1ueSD�b�S�r��������>.@DKD��K�:n�:���n.�[�86�m�nt���S��[�����[�tl���c��}������?��'=�z�v���Z�h&NU%�����1�d�/�����dU4THO�[�OQ�A�8�EdO0�x���?�g�5�
��]��&��S�=�2%3�vpn[-�8�^/���X�|@���I�H8���r���`F�����E����p|�[5����'aV���#z��J�7*���FMn��0�f�C$���6����d�*�9���"�{H`-�b��(�����+������1�R	�>��B���)�	[��D��e�/�s��kE�������q��2�c����c=�
��Z0*%�8�m��X)�^_�!���m�e�*6.[���3c��&���^��z�m;�3Y�k��m�q�*hV����W����J~��.���Q1��He7�����F||^16"LR�v�lhs�*���r��C+s��:Y�����v�C����A�����O�A|# v�~�SPf4�[nOJt_�
O������?y���z�A5������e���P�MCf���8�4���X^�R\�y��$J|!����`}"�|'K�t��.uZ7]$��mn�6��1���|��)%M}4y�?K���<lM��*�>U=��|�
��<>G�������`�s�Z9����������xNh���
&n��:���OG�0	-d>�1�-z�����a�)��.����w���S8�.]������g ������G"���z���u���	�d�?|3�$�������ce��H��H_� #�� d�R�Ce��g�����&y��Um4�?m7v�
fP�,0�U�&�a���IS�q0�l�$u�5��P+/�����������I�>���~��U^�Q7���)�"��`��$�U��#��O��gyPB������g�YMBM�������Z�)Z�����w��*n{vC�r�}�t��~���`�#�%�1�����g/��j����=)�D�����G��p�-rSM���H�e�R���B���y��g
�j�4�c�����j��f���/_?;;>o=?~q���y���)*�e����B����e�����9e���zC����y'�a���l������~��������"'���*��C�a��H/�h�Q�e�m<<�s�<���.fu�>������7������%�T�r�Vc���ZNJ'���Nj�����
Y�	�t��i�pCF��'t0a��}�s`����F�E��a*.����AYV��u�����H������"���'�9o������	���n^=jHf�;0��1��t���� �����9�x\�o�O>7:���#�7�,Q3\y��������V�J7��4��S^M���l�lLLA�3Q�"��=���/{,pKma��=��d��(��z�t?��<�N���sW�K��`�����K�J%�9sv��Ijb2V�6i�|�C��mL��V9EXzB��9�4�������r:P;�!X�����^�4�}��O�fT�iF�Y����0m/��aU�jX<k���a�?�?�DG(WV�U]���%r��(Y���O#�k�.��q��K�zq|Tv�:����7���?OhW@�5Z���p��	G�Qb
Y{��'*�,�t��a�F��I�?3%,�bp��\H%��W�*�R�tR�yT��
�Q��j�Vh�RS���������Exwt�����_E��
`b&��q�0y=G��G9f�F;�#��n)v��Y5o���/N��<����f{��%��N0~F�m	K����������n0�{����	R�K��Xm�_{#DMN���1C������P����%����E����.�`�;��Z���-�
U�\�c�k�����*GLP�LQR%���
��k���o�q�~�]�{W}e�����]
K
�a�Z������q��������RX����n���}/���0_��4�KB�;�|��hl�x�vy���9���}������gS�8��oG����@#�n+�x�e�{.���j�U��
������g78�b��K7����A��k��N?���E�����G��3A7!��~�p���d��{���"����R�I��S�H���A�����.���(\�@��`=�<
�e�����2�z�*�XA�d���L����S<)����4+81�g�RRxm
�������������E4�[�%�!����"�&H���_���"!��������#���9n�:
����8���5�{<�RBr73�
Lv�[���L����fH���l�[ ��s��������V���� }A�ZCQ�]'*���L[w�I��2/3���x��|����:
t������� Cou��)6-!~}d���s��W�ee��N�00��3���S���o��6qpA���M���������<���M�s�=G?��ciP�&:��,��%�LX���0}��������s���sMW�^���::�S��#+����O9J���3�k<Nm��X���0�
����W���j���Q��8�v���3N����Js�,���,:{};��O�vA�C
���xN��dL��8���t���%_�$�!)�{���M�S��
gs�a��/��a���,Fh�E��ro��� ��!^�PqN�@�y����=�C�����j����������������*EkH�,u8�;�����b]������4H-~���	�3a��p��SP�fQ��^���!&�L�A����M���
��$�3�����g��(��M������l4�����,n���
sa�����0*i�J����K�o^����U�-y��{�4cbQ������%&��S�Iz�g8Fx

^%����;����A��8��=�I$���&%5k6�e��;����g���k�z��0���?�-|�gC���} N��,-�b
5=���
^�DW3���XE���xx�e�J!C���|�U,\�B'�4�w�����B0�4(�2����^$kK�u{k��WP_����vA4�������k4������P�<��v�Y9��&��q`���y�X9�W�F�B�����O�'��y��������Nf�0�������q )w�pB��&p'�v�c���;����{c^P�c#�i��U@n���Ox������!��\�h��]j]�.�&D���A�B�i���C���b 0l�gia�����+P�x���1������i|!����(�����:�qo��oV��dNy��c�RRi��j��������B�s�l�}���X������X�I���9���c+P�_qz��dY��V?�=Q�K�O:�6���Gr���~��k��QPK�iL� �V$�+J%���ie��h�g�LWr��v8G_s�Y�Z�f�?BX3���p����J�n�J�P���q�&7 �
	?5u�"��6:�oe;	��X���<U�Ce�E�x��������A��g>HE�u�^�WI���6�n<��q��!�[a$��t��s=+����R$[}
��m�!M��5�"_.�
�1����u8�u�V�`%�@d��&r7�J��DT�K9�	Hizc,�#�r��

���l��%4r4��=c�I��!���pcE�0;t����{��F�����H|��?���%�	'���bT��"���7�n�R
X�%@�D������~F�VSep��Ogiy�@P<���H�����VY^�X�,i��$l�V0nL�yJ�:Y�zW3��X
T�>��H=g�c��tI��\y��sy���>�+Pf ������B�0��h7���t��8����h����p�>��@b
�+]�����?�� �����I���%��4������V�	t$�0�:��I�AI��������#:���9c1J �r�D�#���QF|f�%<%Y]&�+�E74|(����O��='�L��Tz������3���H4�[����,"�,"����|��JN����E��Q�������F[���95`��|j���!��$�[������A��Mq���Y�X�F)�l�P��8�I��M�oK�=��M��@��>V���Pe��[ ��1l����i+������;����u����K��O�6����7 A"r(�3�x�0��;D����R��,����K:�
I�"W9��!�>H�N���$\I!���rL��O�����q��Xx��W
�5��o��c'��}W�Y6Ca��Q���.(�&Lf��V�"n�n�3U��r^�r�j9�f�K��Jze�VJO�.�+���T�<����X���Vfj��������=-C�Sz���7]]�x]y��!�B������Rf������3�60�E��s�c���.)��/�q}����L�����b��cQ':�������GWKz�-��.�a�L��h�i���C��:f�H���7��H�<�~p���
��%M2m�e4��F������W{K�y.�?����Wh�Y�"����0���K�~=H0rh�v���2�4A���\��A�]��(�.Z��QUpK��Q���wf2�^��6		�,%����8W#��;]��-��������c5�5��Rn"��^���Xs�z.����_������(TQz�{(�J�>�i�%	���b^k
����g{��,e{��q��-�S�������UY���=�[u�dog�7����6�5S�g�.�d���p49�m��l�\�b�>T������[>|����C4���?
��L/�diQ��+*���JA���jv5t�� ����hS"t��D7�������PZH�E�f��,�g���k�n��HY�0�T��q6��>�N%u�hfe��nD�����w���7(��d9��f7�n��}{{����(}g4�[�J�����P3s��X'O�M�(r���`��|Y�s;��(��8�y�),�&tE�c<��}���^����'��et��Y6S�u[�H�� ����n���H^�P^�"����d��lQ��/��e���Q��k��1ot��f�N�|��j%y>�$��d����'�'3���=�w9Kg�\��<5*��I���7d.3krr�0��'��|=��C���z�D��_p�%��;;xAJXq��3��|'���)�����A�9E^^�6<��K�G	{�X��%���������y+E��jj��[����..D�v	D<�����A�?��;�$�/K��d�J����q�����n=���[{;{�Q��no��j�Y=�U*����F��sP��C.�t8� �%�����v�wDO\���������R{���g+.�V�w�@7zH��_u�U�������7E]�I����%����Y��:��|vtv��	��3c����Y�����{������N����>�5sF+��g�c�n�����Fb�k���:��������\����7��;x���� ��}u35L������"��Ch�5:6r�:��a��0<)�l �;.F�L��������!"�0�������h��4,�p��r�1�
Z��IZ���bi l�2b���`0pdC�ry>0��1����-#�,���\:N�r�d���-��@��l�le�&fDH�^,b6���N��y��;c���Y��\d��e�2A���h��5���{�`�f��8������~>+[�d��e�n���B��.�k��pg�*���s��A�U/\�,`����`"��se�m�?�/$?��I�#X��.���iD�Il�z$;z$vt#4;� i �0&0�Q��O���������!�;c�;�O"���$���8�\_4�*������07������6w��IS�WcG�YRJ��]g�����Uw�j�[r�^�����f�^��-�8��8�6	����c�f��"}�_�Q��r O7�=^[���H��K8"-X��F�������c����d�����p�����O!�+��I�0-���U����Y����������"�q7vH��"����
3�3w�S�fso'{�d�3�N������]c�=y�:���h���_��g:��=���(n���=�d3�PA����B��@�L��6MB~�J�\�)Pm��"$�)����#�Y��FH������:�N����@pP���B�C��5�D��-�M��4E3�jh�yi��)A*�Y�*oO-�]a�IG���OsKB
�Op�eb������c�����R�I�r�bk�sc7e����F������1�*�!<sZX�+~�U�!��G�HS�l(f3�; �u��A�M�K�J�@��Zm���F�HEM�Lds�^�1������O�����T��Y]h������1��i���n�����i�L���m��5�h�Ql���o��1�f}��b���_>�E�k��1���FBNY�j�[��c�H=L�j��b���s�.G�1���A���� C�A�%EJU1s�����,�V�-/��8^s�
7�z[x����T���6�_$�h��[�&���n�u���"_u� a�5���F�`sZ�`��8^�4�����^#�/5��;[�2�Yv�J��1��&�2d[D�X�
�Qt5��i2e{�en'�Ct�Ly����N�����k�ap�	������A���u�&[�<^�e���[�A��i4S����(��&y��8�5�mqD��W}��v(����7nQ����3\�.s�������I�*�}�����%�X����`�A���o�E�m�������Y9���)fC�{����i�$�V�"/�����x��-cS�a4�Z5�_^p�����	I��O�����h{u�hP�le����:�Q��1""������H!	�d?�a�Oh8�#c�����qg�P�I�����t�!��uZ�'�GO'�~�I)���W�3�����F����������9�U9���������m�u2����C��������$�|Zk�D%����5�NJ&&EK�]��\��6�E������72Vps�z�<e�C�[9&)7f&�U-��V��f�������T��� �*@?�D���0���gs3��9����cv�����2YJ[��z�.*�j��_��P[�sFQ�(�|M�y�����\SP��)���t������E������b�:e�88��5��-[M.����y)~��Az�	��v�8NG��������S��2oE���)i���`�
X��v_^�"���t��KE%m�]���^��?Z?�Z�Hd=���(bh����������.x��y�C���72:�C�!\�L�e�@)F����x���l:h��[���[��5ZQ!
�]�^\�J<� =�J��W{Z:��LX�tIfq�R�3S�x,��C.���Mo8����,����'o���E?��6&PGLB\E��-�����F�!E�q�E�9�	�T��c��[-������%�(�������9;{r�c�P����e
��[�\���{��L?n;��^�=y��4T;�b�jfz��%��D�{�`�#����8zyv\(X-x���}w��0���{1�Z�W����2Eqk�i��U=���L9�5����<D�uS�}�Hcj���@�����]�<*6D`gMX���f�g���*�"$I�4�$F-$:���>{ut�7����$�`j+,#J�O����k�,�	#M���U_c�v�"��}�
xD�v:)��h��d;�[|z��)]�- �R��lrt�.,��������)!���
��H��j��c=�9��1%�"�q�{�5���5sJ���:=���B��]�.V�7H�H����K����^,P���9�=W��%W���/d%���_A�=4/=�����N;/�OS�)�K^(��j��0��������$�kb��{#���(I�s�"/�R�e(e��� ����-^F8�C�:��o��{�Mu��#��n�`p7"]�)��d8��3��?�P�(P^��)���4l~���be�g�$%�9���o��#=:��fw��P�������h���>��#�EV�W�=B��������������b�1q����1��(U��mM�$�x#H�Q�R!�n����R��{���N�[���-�*g���������>���/�1K�O�������V.��k���U%��6�q�|������!���U��@kJ/��������P;)}���w	7dsE���D�����n��������xg�{�
��b)�sT.�?GV?�
�Oe���w���,���k'��}*'���&���o���8hw���w
07�{@�����U���{V�R��O����!�R?0��.DR��)E|}I���CY��-���~
�9�#n%�&�k��4�����:(GWA
|����^lrU�����TX�p2�,����_]�WP�d���t-dG1Ed�hh���`��o�e[���{���}��1�v�x,�Gp����t�)(��xa$�!F2!���U������l������Sl�����y>L��G����?o���Ac�L�1�
��T�x�s����c��%e�NH�(=C�b�	2���*�6A�����N'MR*�a$��x�cL���B�P'o��w�A�l:/1��z���{z���i+����}���s�}��mA��X����E��]�E�)m|���qZ��.�8�d�K@�����^��_�(O@cK�u��}4��C�"K�!���%�������v��nt�	��V6�dN&�1��i3G�$�Z��AU�?u������h�i&�!f�,�J��4��������������&=9�S����"������Z������
l0i��9����u��(g�|	�e�j/�r�����=n���e�
��`a7&��]���%*��������i����������	��[1���,`#��o,������4����Cj
�K���yT^E��m�*k�����H�6��8����3+����5���==UbF����8�����k�������8a�~�v�������_$0rK���ZJa����!�?��,�hbR������PGej�aB��h[��T�CN����~������"���6n�:�YL�$1����W�~R�'�JbvgoN���!@��i�2�2�{�������h�[���cG_i���.�X%|������L	�Po��B��a�%��9��-+���;��������t>���?�L��
=@�yJ�:=�m�������Y}����t�
-16�6��s�^,�R5d/(��oh�a!+���bq�Rj���#�b��?�y}��6��d
���A�>Nd�TNd��
�:0�����D�A]�D�sd��v�*/ON�E�Px;����E_������:����"����`|�^�b�G(�.8E��^�77M���$��EX��%jd��%�
F�Fw���Q��s ���Z���h��1�� �3v�S#�9����>���>��)�����6F���I����*��_���4�|x	�7���$�����d�u���D)-#�	������E��LtSo"5���Xs������5Mg���~8	�4�i�bIMl�f����
? �����l��!O������6d���=h��v��Y.r��]��z��0t���?���K�������_0��mXZ���!����WF��0N]�'2�@��3a�N��8?,5r�Wl�WB�������C�2�H�1LG�pL�����7�dw�!�C��54���qx:%%�@g#)�x7�����s���k':7X3�j�A�<
�?d
�@H�M������y�r�a�o3/v��C����^����������_��[�$C��&W�8�O'��e����@������}\���������������Xp���`�\����#ys�����/�^vt7fc����@�y�
7����O �#�4[��0��\,�����&�%���p������Y�v\:+57��r�	�?x���W�|�
�!�fsc����
���!$�_O�j�C�a��K�D�*���T���<;�>�j�����1��l���J\�,6���3�?g�2��e�9�,o8��8���Q��z�f�:O�4����5(�n�s�aZ�|�������W�{$`$r�/`lM_nsnM_n�K�YAoSw��KwA��E>�/-���I��wl��[��`Y����z�����ai1�(�����Gw�_��|���,\6f��ap#U�r�)atb>g:�;��]-F/���R��q�@'�	yy�����r�#��.<#=L�9�`@�\�c�V�w�s����o��j�o����U�x�~����Z���Z<�R��]<������p��S��%�.G�4�VU��D����L���M���xB�4�����q���Ig�g�I(��7W_�99@��cv����B%[��/ss�{���[C��=���F��u�~?�x}2�L/.$`��|'�X�KA�`-_T����k��<M�"��4���)�v�x����(���
��wGknCqd��;����Q
��^p_�Pc3�'��w�k����#�-)P�s�'YG� ~����`u�G(�
��X����k��%���h�a!��S������h02����f���R2�:���P�\��Q���br5��2�e%.�{�u�=�o�dY�Da#l��CCN(���s����^X�A��fu��������F�
�M�6��UjS@�Z���r�2�D����go��������7�����c�No�H/JzJ�7�R���~��X8���������������8���*�zu��k-�uLXYZ',������!HW�� �9�?p���Y�\�n��Y���@�/E
�Q��A��w�7����!6v���Jb��R{"�?��U^�}���Z�?�=6p�X��<��A2��H�����f\�����u��%��v������z]���j��:O�����������^3;E�6�GJv4�����gMI�������x�g�*��wb�6P�o��r��[������|s{w(��
��2�����]z�<T����/M�efkg+R�2�Ho����Z/����,?�������?��W�fe����EK�2ab�o1�H{r B����������K~�$@����$��IYdoE����-��.d��9��Nu��mu���j)R�������@��������g����S��@%z���3�������0qjL�7K�%�<]nx���9��6�A-��h��r8�-]��%�b�5��/El�ds�p������$�F�y�o;�����.+F��0�;�ow���/����z�PJ�4�	�G'S�b���K\�������_u�0�����aq��y�V��E51NK�����'h{��?��7K�������DZ�~����N����������~�������J�:�&�%���@��(����UrU�+�y1��'\��j�*^`�$0fV�/�{3���@8'�Y0.����z�u�@fSb �h���O��l$�O���tMdaz]H��6�b�}��+v�A����}���"8r�o8�h
���F�������C~�� �;PY�]Ah��=����$<�W����+e�\F����������t���������g���Rz�k��D�u(��C*��F�����u�b�v�,~��9��{���Z��6o/y�S���>����S���7��\��e=��*s�N[��NN4)�ybW�>|�SO���W��|�����A��|-�:FV K_#�0�9���$�w���� ��@0<�v��&K([L��������o�R���UM|������9X�i��3���=N�����I�V��C����.mx��@)`�	����U���uM��N��n�D� l���$�v:����O������m����t��c�"��2\�l�{�����?�W����z�Y��o5Dc�p�~���^^R����UH�i=�-���p�g��@x�o.�W���s� ����x����M������'���sc|�i���������;�+�z��>�>,,uf��N'/v��%jf|(��a}|>
'��	�,�}���yo��C+����Z�.������V���:����<?�����b�?c~�$5��l�����nx�^A���?��C�"�VJ�,G�������c�����H���"�4����T}�]<Dl��?'���7������43Bo`�'�+�
j;���;m_HQ�,��XW�l,��'��|��?Fq��������5����+����0�@�+w�P�g�G,��>f�K,�^$
����Q��
]
@2��q��x<��f)a�����DH�_�D������.�������nQ�����y:�hb�Vo��N.����G�=c��^l�}*G*l����n����U<��2�b��]p�w@�c^�z'�����? p<M�i�yK�@���0��t�f���eS��ec��XC�8H�O���tq5�0M���=P�?�3P3��"�{���
C���}�&�LXG�N
vqrl[Li-GHCL��"�iE�9��V_���K����]�Q0&��������������o���t�B����>��G^;�W����kKGXg�Y��'������A�+d���������H�b���Y4���P�q����MW#�Vj��������U�B5d��;�(�B�{���Y(����h$UQ�\<��f���������\V��&p=�i0Y�s>��5m�UIW5�C���>i��F��]���+�gZ�d������������!�CU=��{yD����n���t"~n���(���4�K�|������<�HZ�|I�\��$��^��k��$�����������������������|�����XYI�0��@I���~g��2P�2����fA�R#��
��
���4QB��1h��!�iT������,BQa�VYWVYWVYW������|-4���������������g1�7�g�u��$�U��U��U��U��U��U����e�ue�ue�ue�ue�ue�ue�ue�ue�ue�ue�ue�ue������������������U��U��U��U��U��U��U��y^e]Ye]Ye]Yd��+��+��+��+��+��+��+��+��+��+��+��+��+��+��+��+��+��+��+��+��+��L���x��c=�M��U��U���;J�*��*��*��*��*��*������J�C>���A�/��r��Fw������1o�U��U��U��o��U��o�kf�X�E�������r��r�8V9t�O��p�C������������:Waw
{�'�M��L����v�����m��f������#O3)�t�1���~u_T��=?��!��A����%P���0h~��Z���t���j��GN���|�u�?F���'@0�Q��67��):}���n8�0��gq9��vor�6����=���:��`�����^g�Vp���:������4��-hv��M/�?����o���h���h�������"Su��~\�6����9��/�1������ ��A���F�!
O�h����^���"r%l_4�a7�������V��H�:p��*h4��]Q�?�:��X���IQ���QY�����w����:��{���,�����X4s���r����\l5��Rs��9���{s���s��Y�JV��LPsgW�C���R-���ip�M ��f��'�iM���dFb��g����cv����f'���`���C��
N�H���Bx/{��s����.��T|���w������w
�:�������
:#���Bz.�s�p�s���? c�P��������w?�����--����--��2C�-5 ���-'j����-%���Dl��l�O���uB�}��f���~��y���1�X�``�������u�X[��j���B4��Q��-.���*�_D���it����)��=���=�qw�A��oJ����|�p7�1l�#��R��,=���#��CH��FyY~���c��qV�6eyQP��d�AK��d�aE�.�	��b��o��������Xf<��Gt��!�)�Whp����m�ou�>�������7����������}�ew_�|�������~7b�?:�^I�mr���j7<�|Qq���r�m�������[R�p�gi�uV�l����.�������
#140Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Josh Berkus (#136)
Re: jsonb and nested hstore

Josh Berkus escribi�:

(to clarify below: "json" refers to the current varlena datatype; JSON
refers to JSON serialized data).

FWIW the term "varlena json" is misleading. jsonb is also varlena, only
different. I think you need a different term to say that json uses the
text representation.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#141Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Josh Berkus (#135)
Re: jsonb and nested hstore

On 02/25/2014 08:54 PM, Josh Berkus wrote:

That's called a "straw man argument", Robert.
Me: We should recommend that people use jsonb unless they have a
specific reason for using json.

We could also make the opposite argument - people use json unless they
have a specific reason for using jsonb.

btw, there is one more thing about JSON which I recently learned - a lot of
JavaScript people actually expect the JSON binary form to retain field order

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.
IIRC, this behaviour is currently also met only by json and not by jsonb.

Merlin: We should present them side-by-side with a complex comparison.
Robert: Josh wants to junk all relational data and use only jsonb! I
mean, really, WTF?

Cheers

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic OÜ

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#142Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/25/2014 12:12 PM, Robert Haas wrote:

I don't agree that jsonb should be preferred in all but a handful of
situations. Nor do I agree that partisanship belongs in our
documentation. Therefore, -1 for your proposal to recommend that, and
+1 for Merlin's proposal to present a comparison which fairly
illustrates the situations in which each will outperform the other.

Awaiting doc patch from Merlin, then. It will need to be clear enough
that an ordinary user can distinguish which type they want.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#143Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#142)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 4:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 12:12 PM, Robert Haas wrote:

I don't agree that jsonb should be preferred in all but a handful of
situations. Nor do I agree that partisanship belongs in our
documentation. Therefore, -1 for your proposal to recommend that, and
+1 for Merlin's proposal to present a comparison which fairly
illustrates the situations in which each will outperform the other.

Awaiting doc patch from Merlin, then. It will need to be clear enough
that an ordinary user can distinguish which type they want.

Sure.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#144Craig Ringer
craig@2ndquadrant.com
In reply to: Merlin Moncure (#143)
Re: jsonb and nested hstore

On 02/26/2014 06:21 AM, Merlin Moncure wrote:

On Tue, Feb 25, 2014 at 4:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 12:12 PM, Robert Haas wrote:

I don't agree that jsonb should be preferred in all but a handful of
situations. Nor do I agree that partisanship belongs in our
documentation. Therefore, -1 for your proposal to recommend that, and
+1 for Merlin's proposal to present a comparison which fairly
illustrates the situations in which each will outperform the other.

Awaiting doc patch from Merlin, then. It will need to be clear enough
that an ordinary user can distinguish which type they want.

Sure.

Please also highlight that any change will require a full table rewrite
with an exclusive lock, so data type choices on larger tables may be
hard to change later.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#145Peter Geoghegan
pg@heroku.com
In reply to: Craig Ringer (#144)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 8:07 PM, Craig Ringer <craig@2ndquadrant.com> wrote:

Please also highlight that any change will require a full table rewrite
with an exclusive lock, so data type choices on larger tables may be
hard to change later.

It sure looks like they're binary-coercible to me:

+ CREATE CAST (hstore AS jsonb)
+   WITHOUT FUNCTION AS IMPLICIT;
+
+ CREATE CAST (jsonb AS hstore)
+   WITHOUT FUNCTION AS IMPLICIT;

Is this okay?
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#146Stephen Frost
sfrost@snowman.net
In reply to: Peter Geoghegan (#145)
Re: jsonb and nested hstore

* Peter Geoghegan (pg@heroku.com) wrote:

On Tue, Feb 25, 2014 at 8:07 PM, Craig Ringer <craig@2ndquadrant.com> wrote:

Please also highlight that any change will require a full table rewrite
with an exclusive lock, so data type choices on larger tables may be
hard to change later.

It sure looks like they're binary-coercible to me:

+ CREATE CAST (hstore AS jsonb)
+   WITHOUT FUNCTION AS IMPLICIT;
+
+ CREATE CAST (jsonb AS hstore)
+   WITHOUT FUNCTION AS IMPLICIT;

Is this okay?

Err, I'm not following this thread all *that* closely, but I was pretty
sure the issue was json vs. jsonb, and I'd be mighty confused as to wtf
was going on if those were binary-coercible...

Thanks,

Stephen

#147Christophe Pettus
xof@thebuild.com
In reply to: Hannu Krosing (#141)
Re: jsonb and nested hstore

On Feb 25, 2014, at 1:57 PM, Hannu Krosing <hannu@2ndQuadrant.com> wrote:

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.

I have no doubt that some code depends on it, but "all major implementations" is too strong a statement. BSON, in particular, does not have stable field order.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#148Merlin Moncure
mmoncure@gmail.com
In reply to: Craig Ringer (#144)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 10:07 PM, Craig Ringer <craig@2ndquadrant.com> wrote:

On 02/26/2014 06:21 AM, Merlin Moncure wrote:

On Tue, Feb 25, 2014 at 4:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 12:12 PM, Robert Haas wrote:

I don't agree that jsonb should be preferred in all but a handful of
situations. Nor do I agree that partisanship belongs in our
documentation. Therefore, -1 for your proposal to recommend that, and
+1 for Merlin's proposal to present a comparison which fairly
illustrates the situations in which each will outperform the other.

Awaiting doc patch from Merlin, then. It will need to be clear enough
that an ordinary user can distinguish which type they want.

Sure.

Please also highlight that any change will require a full table rewrite
with an exclusive lock, so data type choices on larger tables may be
hard to change later.

Yeah. Good idea. Also gonna make a table of what happens when you
cast from A to B (via text, json, jsonb, hstore).

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#149Merlin Moncure
mmoncure@gmail.com
In reply to: Hannu Krosing (#141)
Re: jsonb and nested hstore

On Tue, Feb 25, 2014 at 3:57 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

On 02/25/2014 08:54 PM, Josh Berkus wrote:

That's called a "straw man argument", Robert.
Me: We should recommend that people use jsonb unless they have a
specific reason for using json.

We could also make the opposite argument - people use json unless they
have a specific reason for using jsonb.

btw, there is one more thing about JSON which I recently learned - a lot of
JavaScript people actually expect the JSON binary form to retain field order

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.
IIRC, this behaviour is currently also met only by json and not by jsonb.

Yes: This was the agreement that was struck and is the main reason why
there are two json types, not one. JSON does not guarantee field
ordering as I read the spec and for the binary form ordering is not
maintained as a concession to using the hstore implementation.

You can always use the standard text json type for storage and cast
into the index for searching; what you give up there is some
performance and the ability to manipulate the json over the hstore
API. I think that will have to do for now and field ordering for
hstore/jsonb can be reserved as a research item.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#150Andrew Dunstan
andrew@dunslane.net
In reply to: Christophe Pettus (#147)
Re: jsonb and nested hstore

On 02/26/2014 02:17 AM, Christophe Pettus wrote:

On Feb 25, 2014, at 1:57 PM, Hannu Krosing <hannu@2ndQuadrant.com> wrote:

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.

I have no doubt that some code depends on it, but "all major implementations" is too strong a statement. BSON, in particular, does not have stable field order.

Not only is it "not in any specs", it's counter to the spec I have been
following <https://www.ietf.org/rfc/rfc4627.txt&gt;, which quite
categorically states that an object is an UNORDERED collection. Any
application which relies on the ordering of object fields being
preserved is broken IMNSHO, and I would not feel the least guilt about
exposing their breakage.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#151Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/26/2014 07:02 AM, Merlin Moncure wrote:

On Tue, Feb 25, 2014 at 3:57 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.
IIRC, this behaviour is currently also met only by json and not by jsonb.

Yes: This was the agreement that was struck and is the main reason why
there are two json types, not one. JSON does not guarantee field
ordering as I read the spec and for the binary form ordering is not
maintained as a concession to using the hstore implementation.

Actually, that's not true; neither Mongo/BSON nor CouchDB preserve field
ordering. So users who are familiar with JSONish data *storage* should
be aware that field ordering is not preserved.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#152Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/25/2014 08:07 PM, Craig Ringer wrote:

On 02/26/2014 06:21 AM, Merlin Moncure wrote:

On Tue, Feb 25, 2014 at 4:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/25/2014 12:12 PM, Robert Haas wrote:

I don't agree that jsonb should be preferred in all but a handful of
situations. Nor do I agree that partisanship belongs in our
documentation. Therefore, -1 for your proposal to recommend that, and
+1 for Merlin's proposal to present a comparison which fairly
illustrates the situations in which each will outperform the other.

Awaiting doc patch from Merlin, then. It will need to be clear enough
that an ordinary user can distinguish which type they want.

Sure.

Please also highlight that any change will require a full table rewrite
with an exclusive lock, so data type choices on larger tables may be
hard to change later.

Oh, point. I'll add that text if Merlin doesn't.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#153Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#151)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 11:41 AM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/26/2014 07:02 AM, Merlin Moncure wrote:

On Tue, Feb 25, 2014 at 3:57 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.
IIRC, this behaviour is currently also met only by json and not by jsonb.

Yes: This was the agreement that was struck and is the main reason why
there are two json types, not one. JSON does not guarantee field
ordering as I read the spec and for the binary form ordering is not
maintained as a concession to using the hstore implementation.

Actually, that's not true; neither Mongo/BSON nor CouchDB preserve field
ordering. So users who are familiar with JSONish data *storage* should
be aware that field ordering is not preserved.

right (although I'm not sure what wasn't true there). I think the
status quo is fine; If you have to have the document precisely
preserved for whatever reason you can do that -- you just have to be
prepared to give up some things. As noted in the other thread
serialization is more interesting but that also works fine. The
breakdown in terms of usage between json/jsonb to me is very clear
(json will handle serialization/deserializaton heavy patterns and a
few edge cases for storage). The split between json and jsonb in
hindsight made a lot of sense.

What is not going to be so clear for users (particularly without good
supporting documentation) is how things break down in terms of usage
between hstore and jsonb.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#154Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/26/2014 09:57 AM, Merlin Moncure wrote:

On Wed, Feb 26, 2014 at 11:41 AM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/26/2014 07:02 AM, Merlin Moncure wrote:

On Tue, Feb 25, 2014 at 3:57 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.
IIRC, this behaviour is currently also met only by json and not by jsonb.

Yes: This was the agreement that was struck and is the main reason why
there are two json types, not one. JSON does not guarantee field
ordering as I read the spec and for the binary form ordering is not
maintained as a concession to using the hstore implementation.

Actually, that's not true; neither Mongo/BSON nor CouchDB preserve field
ordering. So users who are familiar with JSONish data *storage* should
be aware that field ordering is not preserved.

right (although I'm not sure what wasn't true there). I think the

Sorry, I was referring to Hannu's statement that "all major
implementations preserve order", which simply isn't true.

status quo is fine; If you have to have the document precisely
preserved for whatever reason you can do that -- you just have to be
prepared to give up some things. As noted in the other thread
serialization is more interesting but that also works fine. The
breakdown in terms of usage between json/jsonb to me is very clear
(json will handle serialization/deserializaton heavy patterns and a
few edge cases for storage). The split between json and jsonb in
hindsight made a lot of sense.

What is not going to be so clear for users (particularly without good
supporting documentation) is how things break down in terms of usage
between hstore and jsonb.

Realistically? Once we get done with mapping the indexes and operators,
users who are used to Hstore1 use Hstore2, and everyone else uses jsonb.
jsonb is nothing other than a standardized syntax interface to hstore2,
and most users will choose the syntax similar to what they already know
over learning new stuff.

A real, full comparison chart would include text, json, jsonb and
hstore, I guess. Although I'm wondering if that's way too complex for
the main docs. Seems like more of a wiki item.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#155Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Josh Berkus (#151)
Re: jsonb and nested hstore

On 02/26/2014 07:41 PM, Josh Berkus wrote:

On 02/26/2014 07:02 AM, Merlin Moncure wrote:

On Tue, Feb 25, 2014 at 3:57 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.
IIRC, this behaviour is currently also met only by json and not by jsonb.

Yes: This was the agreement that was struck and is the main reason why
there are two json types, not one. JSON does not guarantee field
ordering as I read the spec and for the binary form ordering is not
maintained as a concession to using the hstore implementation.

Actually, that's not true; neither Mongo/BSON nor CouchDB preserve field
ordering.

That is strange at least for BSON, as it does not have any nearly as
sophisticated
internal format as hstore - no hash tables or anything, just a binary
serialisation.
It would take an extra effort to *not* keep the order there :)

http://bsonspec.org/#/specification

Cheers

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic OÜ

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#156Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#154)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 12:05 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/26/2014 09:57 AM, Merlin Moncure wrote:

What is not going to be so clear for users (particularly without good
supporting documentation) is how things break down in terms of usage
between hstore and jsonb.

Realistically? Once we get done with mapping the indexes and operators,
users who are used to Hstore1 use Hstore2, and everyone else uses jsonb.
jsonb is nothing other than a standardized syntax interface to hstore2,
and most users will choose the syntax similar to what they already know
over learning new stuff.

The problem is that as of today, they are not done and AFAICT will not
be for 9.4. Developers wanting to utilize the nosql pattern are going
to have to lean heavily on hstore API and that's a simple
fact...people reading about all the great new feature of postgres are
going to want to learn how to do things and it's reasonable to want to
anticipate the things they want to do and explain how to use them. I
would like to extend that case coverage to include the json type as
well as its documentation is pretty lousy for that (I should know: I
wrote most of it).

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#157Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/26/2014 11:39 AM, Merlin Moncure wrote:

On Wed, Feb 26, 2014 at 12:05 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/26/2014 09:57 AM, Merlin Moncure wrote:

What is not going to be so clear for users (particularly without good
supporting documentation) is how things break down in terms of usage
between hstore and jsonb.

Realistically? Once we get done with mapping the indexes and operators,
users who are used to Hstore1 use Hstore2, and everyone else uses jsonb.
jsonb is nothing other than a standardized syntax interface to hstore2,
and most users will choose the syntax similar to what they already know
over learning new stuff.

The problem is that as of today, they are not done and AFAICT will not
be for 9.4.

Well, we plan to push to have the indexes and operators available as an
extension by the time that 9.4 comes out.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#158Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#111)
1 attachment(s)
Re: jsonb and nested hstore

Apologies for top-post.

I have made some fixes based in these comments. A new patch for the
jsonb portion is attached.

Responses interspersed below.

Teodor, many of these comments are basically for you. Please respond.

cheers

andrew

On 02/10/2014 09:11 PM, Andres Freund wrote:

Hi,

Is it just me or is jsonapi.h not very well documented?

What about it do you think is missing? In any case, it's hardly relevant
to this patch, so I'll take that as obiter dicta.

On 2014-02-06 18:47:31 -0500, Andrew Dunstan wrote:

+/*
+ * for jsonb we always want the de-escaped value - that's what's in token
+ */
+static void
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState *_state = (JsonbInState *) state;
+	JsonbValue	v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+
+		case JSON_TOKEN_STRING:
+			v.type = jbvString;
+			v.string.len = token ? checkStringLen(strlen(token)) : 0;
+			v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+			v.size += v.string.len;
+			break;
+		case JSON_TOKEN_NUMBER:
+			v.type = jbvNumeric;
+			v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+
+			v.size += VARSIZE_ANY(v.numeric) +sizeof(JEntry) /* alignment */ ;

missing space.

Fixed.

Why does + sizeof(JEntry) change anything about alignment? If it was
aligned before, adding a statically sized value doesn't give any new
guarantees about alignment?

Teodor, please comment.

+/*
+ * jsonb type recv function
+ *
+ * the type is sent as text in binary mode, so this is almost the same
+ * as the input function.
+ */
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   *result = cstring_to_text_with_len(buf->data, buf->len);
+
+	return deserialize_json_text(result);
+}

This is a bit absurd, we're receiving a string in a StringInfo buffer,
just to copy it into text, and then in makeJsonLexContext() access the
raw chars again.

I have fixed this so that we don't construct a text object just so we
can json parse a cstring.

+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	switch (v->type)
+	{
+		case jbvNull:
+			appendBinaryStringInfo(out, "null", 4);
+			break;
+		case jbvString:
+			escape_json(out, pnstrdup(v->string.val, v->string.len));
+			break;
+		case jbvBool:
+			if (v->boolean)
+				appendBinaryStringInfo(out, "true", 4);
+			else
+				appendBinaryStringInfo(out, "false", 5);
+			break;
+		case jbvNumeric:
+			appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+			break;
+		default:
+			elog(ERROR, "unknown jsonb scalar type");
+	}
+}

Hm, will the jbvNumeric always result in correct correct quoting?
datum_to_json() does extra hangups for that case, any reason we don't
need that here?

Yes, there is a reason we don't need it here. datum_to_json is
converting SQL numerics to json, and these might be strings such as
'Nan'. But we never store something in a jsonb numeric field unless it
came in as a json numeric format, which never needs quoting. The json
parser will never parse 'NaN' as a numeric value.

+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{

...

+	while (redo_switch || ((type = JsonbIteratorGet(&it, &v, false)) != 0))
+	{
+		redo_switch = false;

Not sure if I see the advantage over the goto here. A comment explaining
what the reason for the goto is wouldhave sufficed.

I think you're being pretty damn picky here. You whined about the goto,
I removed it, now you don't like that either. Personally I think this is
cleaner.

+			case WJB_KEY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				putEscapedValue(out, &v);
+				appendBinaryStringInfo(out, ": ", 2);

putEscapedValue doesn't gurantee only strings are output, but
datum_to_json does extra hangups for that case.

But the key here will always be a string. It's enforced by the JSON
rules. I suppose we could call escape_json directly here and save a
function call, but I don't agree that there is any problem here.

+				type = JsonbIteratorGet(&it, &v, false);
+				if (type == WJB_VALUE)
+				{
+					first = false;
+					putEscapedValue(out, &v);
+				}
+				else
+				{
+					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+					/*
+					 * We need to rerun current switch() due to put
+					 * in current place object which we just got
+					 * from iterator.
+					 */

"due to put"?

I think that's due to the author not being a native English speaker.
I've tried to improve it a bit.

Teodor, please comment if you like.

+/*
+ * jsonb type send function
+ *
+ * Just send jsonb as a string of text
+ */
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	StringInfoData buf;
+	char	   *out;
+
+	out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	pq_begintypsend(&buf);
+	pq_sendtext(&buf, out, strlen(out));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}

Why aren't you using using the stringbuf passing JsonbToCString
convention here to avoid the strlen()?

Fixed.

+/*
+ * Compare two jbvString JsonbValue values, third argument
+ * 'arg', if it's not null, should be a pointer to bool
+ * value which will be set to true if strings are equal and
+ * untouched otherwise.
+ */
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue *va = a;
+	const JsonbValue *vb = b;
+	int			res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool *) arg = true;

Should be NULL, not 0.

No, the compiler doesn't like that for int values.

+/*
+ * qsort helper to compare JsonbPair values, third argument
+ * arg will be trasferred as is to subsequent

*transferred.

fixed.

+/*
+ * some constant order of JsonbValue
+ */
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{

Called recursively, needs to check for stack depth.

fixed.

Teodor, please examine and comment on all comments below this point.

Show quoted text
+JsonbValue *
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+								  uint32 *lowbound, JsonbValue *key)
+{

Functions like this *REALLY* need documentation for their
parameters. And of their actual purpose.

What's actually the uncompressed bit here? Isn't it actually the
contrary? This is navigating the compressed, non-tree form, no?

+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK));
+		int			i;
+		for (i = (lowbound) ? *lowbound : 0; i < (header & JB_COUNT_MASK); i++)
+		{
+			JEntry	   *e = array + i;
+			else if (JBE_ISSTRING(*e) && key->type == jbvString)
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e),
+						   key->string.len) == 0)
+				{

So, here we have our own undocumented! indexing system. Grand.

+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry	   *array = (JEntry *) (buffer + sizeof(header));
+		char	   *data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+		uint32		stopLow = lowbound ? *lowbound : 0,
+					stopHigh = (header & JB_COUNT_MASK),
+					stopMiddle;

I don't understand what the point of the lowbound logic could be here?
If a key hasn't been found, it hasn't been found? Maybe the idea is to
use it when testing containedness or somesuch? Wouldn't iterating over
the keyspace be a better idea for that case?

+ if (key->type != jbvString)
+ return NULL;

That's not allowed, right?

+/*
+ * Get i-th value of array or hash. if i < 0 then it counts from
+ * the end of array/hash. Note: returns pointer to statically
+ * allocated JsonbValue.
+ */
+JsonbValue *
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32		header = *(uint32 *) buffer;
+	static JsonbValue r;

Really? And why on earth would static allocation be a good idea? Specify
it on the caller's stack if need be. Or even return by value, today's
calling convention will just allocate that on the caller's stack without
copying.
Accessing static data isn't even faster.

+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+		r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2 * sizeof(JEntry);
+	}

This bit of code exists pretty similarly in several places, maybe consolitate?

+/****************************************************************************
+ *					  Walk on tree representation of jsonb					*
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level)
+{
+	int			i;

check stack limit.

+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *						   Iteration over binary jsonb						*
+ ****************************************************************************/

This needs docs.

+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{

Why invent completely independent naming conventions to the previous
functions here?

+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry * e, bool skipNested)
+{

Imaginatively undescriptive name. But if it were slightly more more
abstracted away from JsonbIterator it could be the answer to my prayers
above about removing redundant code.

+static JsonbIterator *
+up(JsonbIterator *it)
+{

Not a good name.

+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int			res;

recursive, stack depth check.

+	switch ((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:

I don't know, but I don't see the point in avoid if (), else if()
... constructs if it requires such dirty tricks.

+/****************************************************************************
+ *		  Transformation from tree to binary representation of jsonb		*
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	   *begin;
+	char	   *ptr;
+
+	struct
+	{
+		uint32		i;
+		uint32	   *header;
+		JEntry	   *array;
+		char	   *begin;
+	}		   *levelstate, *lptr, *pptr;
+
+	uint32		maxlevel;
+
+}	CompressState;
+
+#define curLevelState	state->lptr
+#define prevLevelState	state->pptr

brrr.

I stopped looking at code at this point.

diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index e1d8aae..50ddf50 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c

there's lots of whitespace/tab damage in this file. Check git log/diff
--check or such.

This is still a mess, sorry:
* Large and important part continue to be undocumented. Especially in
jsonb_support.c
* Lots of naming inconsistencies.
* There's no documentation about what compressed/uncompressed jsonbs
are. The former is the ondisk representation, the latter the in-memory
tree representation.
* There's no non-code documentation about the on-disk format.

Unfortunately I can't see how this patch could get ready in time for
this CF. There's *lots* of work to be done. The code as is isn't going
to be maintainable. Much of it obvious by simply scanning through the
code, without even looking for higher level issues. And much of it has
previously been pointed out, without getting real attention.

That's not to speak of the nested hstore patch, which I didn't even
start to look at. That's twice this patches size.

Greetings,

Andres Freund

Attachments:

jsonb-12.patch.gzapplication/x-gzip; name=jsonb-12.patch.gzDownload
#159Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#158)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 1:23 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

+       if (va->string.len == vb->string.len)
+       {
+               res = memcmp(va->string.val, vb->string.val,
va->string.len);
+               if (res == 0 && arg)
+                       *(bool *) arg = true;

Should be NULL, not 0.

No, the compiler doesn't like that for int values.

I'm confused. I just pulled from feodor/jsonb_and_hstore, and I do see
a compiler warning (because the code reads "res == NULL", unlike
above). It appears to have been that way in Git since last year. So,
maybe Andres meant that it *should* look like this?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#160Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#159)
1 attachment(s)
Re: jsonb and nested hstore

On 02/26/2014 04:59 PM, Peter Geoghegan wrote:

On Wed, Feb 26, 2014 at 1:23 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

+       if (va->string.len == vb->string.len)
+       {
+               res = memcmp(va->string.val, vb->string.val,
va->string.len);
+               if (res == 0 && arg)
+                       *(bool *) arg = true;

Should be NULL, not 0.

No, the compiler doesn't like that for int values.

I'm confused. I just pulled from feodor/jsonb_and_hstore, and I do see
a compiler warning (because the code reads "res == NULL", unlike
above). It appears to have been that way in Git since last year. So,
maybe Andres meant that it *should* look like this?

argh!

I forgot to save a file.

Here's what I get if it's NULL:

gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Wendif-labels
-Wmissing-format-attribute -Wformat-security -fno-strict-aliasing
-fwrapv -fexcess-precision=standard -g -I../../../../src/include
-D_GNU_SOURCE -I/usr/include/libxml2 -c -o jsonb_support.o
jsonb_support.c -MMD -MP -MF .deps/jsonb_support.Po
jsonb_support.c: In function ‘compareJsonbStringValue’:
jsonb_support.c:137:11: warning: comparison between pointer and
integer [enabled by default]

With 0 there is no complaint.

new patch attached, change pushed to github.

cheers

andrew

Attachments:

jsonb-13.patch.gzapplication/x-gzip; name=jsonb-13.patch.gzDownload
#161Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#158)
Re: jsonb and nested hstore

On 2014-02-26 16:23:12 -0500, Andrew Dunstan wrote:

On 02/10/2014 09:11 PM, Andres Freund wrote:

Is it just me or is jsonapi.h not very well documented?

What about it do you think is missing? In any case, it's hardly relevant to
this patch, so I'll take that as obiter dicta.

It's relevant insofer because I tried to understand it, to understand
whether this patch's usage is sensible.

O n a quick reread of the header, what I am missing is:
* what's semstate in JsonSemAction? Private data?
* what's object_start and object_field_start? Presumably object vs
keypair? Why not use element as ifor the array?
* scalar_action is called for which types of tokens?
* what's exactly the meaning of the isnull parameter for ofield_action
and aelem_action?
* How is one supposed to actually access data in the callbacks, not
obvious for all the callbacks.
* are scalar callbacks triggered for object keys, object/array values?
...

+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	switch (v->type)
+	{
+		case jbvNull:
+			appendBinaryStringInfo(out, "null", 4);
+			break;
+		case jbvString:
+			escape_json(out, pnstrdup(v->string.val, v->string.len));
+			break;
+		case jbvBool:
+			if (v->boolean)
+				appendBinaryStringInfo(out, "true", 4);
+			else
+				appendBinaryStringInfo(out, "false", 5);
+			break;
+		case jbvNumeric:
+			appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+			break;
+		default:
+			elog(ERROR, "unknown jsonb scalar type");
+	}
+}

Hm, will the jbvNumeric always result in correct correct quoting?
datum_to_json() does extra hangups for that case, any reason we don't
need that here?

Yes, there is a reason we don't need it here. datum_to_json is converting
SQL numerics to json, and these might be strings such as 'Nan'. But we never
store something in a jsonb numeric field unless it came in as a json numeric
format, which never needs quoting. The json parser will never parse 'NaN' as
a numeric value.

Ah, yuck. Makes sense. Not your fault at all, but I do dislike json's
definition of numeric values.

+char *
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{

...

+	while (redo_switch || ((type = JsonbIteratorGet(&it, &v, false)) != 0))
+	{
+		redo_switch = false;

Not sure if I see the advantage over the goto here. A comment explaining
what the reason for the goto is wouldhave sufficed.

I think you're being pretty damn picky here. You whined about the goto, I
removed it, now you don't like that either. Personally I think this is
cleaner.

Sorry, should perhaps have been a bit more precise in my disagreement
abou the goto version. I didn't dislike the goto itself, but that it was
a undocumented and unobvious change in control flow.

It's the reviewers job to be picky, I pretty damn sure don't expect you
to agree with all my points and I am perfectly fine if you disregard
several of them. I've just read through the patch quickly, so it's not
surprising if I misidentify some.

+			case WJB_KEY:
+				if (first == false)
+					appendBinaryStringInfo(out, ", ", 2);
+				first = true;
+
+				putEscapedValue(out, &v);
+				appendBinaryStringInfo(out, ": ", 2);

putEscapedValue doesn't gurantee only strings are output, but
datum_to_json does extra hangups for that case.

But the key here will always be a string. It's enforced by the JSON rules. I
suppose we could call escape_json directly here and save a function call,
but I don't agree that there is any problem here.

Ah, yes, it will already have been converted to a string during the
initial conversion, right. /* json rules guarantee this is a string */
or something?

+				type = JsonbIteratorGet(&it, &v, false);
+				if (type == WJB_VALUE)
+				{
+					first = false;
+					putEscapedValue(out, &v);
+				}
+				else
+				{
+					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+					/*
+					 * We need to rerun current switch() due to put
+					 * in current place object which we just got
+					 * from iterator.
+					 */

"due to put"?

I think that's due to the author not being a native English speaker. I've
tried to improve it a bit.

Oh, I perfectly understand that problem, believe me... I make many of
those myself, and I often don't see them in my own patches without them
being pointed out...

+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool *) arg = true;

Should be NULL, not 0.

No, the compiler doesn't like that for int values.

Yes, please disregard, I misread. I think I wanted actually to say that
the test for arg should be arg != NULL, because we don't usually do
pointer truth tests (which I personally find odd, but well).

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#162Erik Rijkers
er@xs4all.nl
In reply to: Andrew Dunstan (#160)
1 attachment(s)
Re: jsonb and nested hstore

On Wed, February 26, 2014 23:10, Andrew Dunstan wrote:

new patch attached, change pushed to github.

[jsonb-13.patch.gz]

This does not apply, see attached: src/backend/utils/adt/jsonfuncs.c.rej

Please ignore if this was not supposed to work together with the earlier nested-hstore-11.patch

github branch jsonb_and_hstore built fine; I'll use that instead.
( https://github.com/feodor/postgres.git )

thanks,

Erik Rijkers

patch -b -l -F 25 -p 1 < /home/aardvark/download/pgpatches/0094/nested_hstore/20140225/jsonb-13.patch
patching file doc/src/sgml/datatype.sgml
patching file doc/src/sgml/func.sgml
patching file src/backend/catalog/system_views.sql
Hunk #1 succeeded at 822 (offset 3 lines).
patching file src/backend/utils/adt/Makefile
patching file src/backend/utils/adt/json.c
patching file src/backend/utils/adt/jsonb.c
patching file src/backend/utils/adt/jsonb_support.c
patching file src/backend/utils/adt/jsonfuncs.c
Hunk #20 succeeded at 1419 with fuzz 2 (offset 4 lines).
Hunk #21 succeeded at 1691 (offset 4 lines).
Hunk #22 succeeded at 1843 (offset 4 lines).
Hunk #23 succeeded at 1940 (offset 4 lines).
Hunk #24 succeeded at 2013 (offset 4 lines).
Hunk #25 succeeded at 2035 (offset 4 lines).
Hunk #26 succeeded at 2054 (offset 4 lines).
Hunk #27 FAILED at 2090.
Hunk #28 succeeded at 2129 (offset 10 lines).
Hunk #29 succeeded at 2200 (offset 10 lines).
Hunk #30 succeeded at 2211 (offset 10 lines).
Hunk #31 succeeded at 2236 (offset 10 lines).
Hunk #32 succeeded at 2252 (offset 10 lines).
Hunk #33 succeeded at 2263 (offset 10 lines).
Hunk #34 succeeded at 2461 (offset 10 lines).
Hunk #35 succeeded at 2606 (offset 10 lines).
Hunk #36 succeeded at 2619 (offset 10 lines).
Hunk #37 FAILED at 2644.
Hunk #38 succeeded at 2692 (offset 14 lines).
Hunk #39 succeeded at 2702 (offset 14 lines).
Hunk #40 succeeded at 2730 (offset 14 lines).
2 out of 40 hunks FAILED -- saving rejects to file src/backend/utils/adt/jsonfuncs.c.rej
patching file src/include/catalog/pg_cast.h
patching file src/include/catalog/pg_operator.h
patching file src/include/catalog/pg_proc.h
patching file src/include/catalog/pg_type.h
patching file src/include/utils/json.h
patching file src/include/utils/jsonapi.h
patching file src/include/utils/jsonb.h
patching file src/test/regress/expected/jsonb.out
patching file src/test/regress/expected/jsonb_1.out
patching file src/test/regress/parallel_schedule
patching file src/test/regress/serial_schedule
patching file src/test/regress/sql/jsonb.sql

Attachments:

jsonfuncs.c.rejapplication/x-reject; name=jsonfuncs.c.rejDownload
--- src/backend/utils/adt/jsonfuncs.c
+++ src/backend/utils/adt/jsonfuncs.c
@@ -2090,27 +2844,26 @@
 			tupTypmod = HeapTupleHeaderGetTypMod(rec);
 		}
 
-		json = PG_GETARG_TEXT_P(1);
+		tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 	}
 	else
-	{
-		/* json_to_record case */
+	{							/* json{b}_to_record case */
 
 		use_json_as_text = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
 
 		if (PG_ARGISNULL(0))
 			PG_RETURN_NULL();
 
-		json = PG_GETARG_TEXT_P(0);
-
 		get_call_result_type(fcinfo, NULL, &tupdesc);
 	}
 
-	json_hash = get_json_object_as_hash(json, "json_populate_record",
-										use_json_as_text);
-
-	if (have_record_arg)
+	if (jtype == JSONOID)
 	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(have_record_arg ? 1 : 0);
+
+		json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
+
 		/*
 		 * if the input json is empty, we can only skip the rest if we were
 		 * passed in a non-null record, since otherwise there may be issues
@@ -2644,33 +3576,17 @@
 
 	/*
 	 * get the tupdesc from the result set info - it must be a record type
-	 * because we already checked that arg1 is a record type.
+	 * because we already checked that arg1 is a record type, or we're in a
+	 * to_record function which returns a setof record.
 	 */
 	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
 
-	state = palloc0(sizeof(PopulateRecordsetState));
-	sem = palloc0(sizeof(JsonSemAction));
-
-
-	/* make these in a sufficiently long-lived memory context */
-	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
-	state->ret_tdesc = CreateTupleDescCopy(tupdesc);
-	BlessTupleDesc(state->ret_tdesc);
-	state->tuple_store =
-		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
-							  false, work_mem);
-
-	MemoryContextSwitchTo(old_cxt);
-
 	/* if the json is null send back an empty set */
 	if (have_record_arg)
 	{
 		if (PG_ARGISNULL(1))
 			PG_RETURN_NULL();
 
-		json = PG_GETARG_TEXT_P(1);
-
 		if (PG_ARGISNULL(0))
 			rec = NULL;
 		else
#163Andrew Dunstan
andrew@dunslane.net
In reply to: Erik Rijkers (#162)
1 attachment(s)
Re: jsonb and nested hstore

On 02/26/2014 05:48 PM, Erik Rijkers wrote:

On Wed, February 26, 2014 23:10, Andrew Dunstan wrote:

new patch attached, change pushed to github.

[jsonb-13.patch.gz]

This does not apply, see attached: src/backend/utils/adt/jsonfuncs.c.rej

Please ignore if this was not supposed to work together with the earlier nested-hstore-11.patch

github branch jsonb_and_hstore built fine; I'll use that instead.
( https://github.com/feodor/postgres.git )

Ugh, my master repo was 24 hours behind.

New patch attached..

Attachments:

jsonb-14.patch.gzapplication/x-gzip; name=jsonb-14.patch.gzDownload
�P�Sjsonb-14.patch�]Ys�H�~�~E
V�	��.[V�,�u�-�DOO����"��(����<
A��aa�
YYY_fef4��@�jC+z�t�������n�z�������<��S>
�h��^o�����@����Ng�V����V����I�Z�=�@T���?��-�������"���jx%����� ������?i���?�+�s�������)
w4v}i�+^��5����m92�J�`�}�X�H�4��]�m���(�N������
_��#������������^�o�����_.>t�����C�}�MM����`O�BX��J�35�f�t���+��!����tD�:i��.��F�C�IQA?*�}yB�
�7�����e2����:��^1�FD�V���}��ZMS�rEc��Y�{+E7����_��;�/��#W���IA���Y��o�L���_�����c��t_�ciXHX�8�����g�����E����P�d0�����
��9h����rz��\��I�^?�q31n�������"����Us��j�&��@��_-j�-��n��N��a�J��r��D��CT���=������2YVt�N����9�����?�]/���T���������7-�9�3�"���;���>
9�3)%��V�������e��@�����c�����n�z��/�/A%=9@�u'�"Q9�wu���H"�UU�U����M��E�t��V���^��-W����L������f�����>��r���.L'�� ��A��)���H� ��r0�
N�mIjr�|�
W�O���#&v�F��{c�5��cG���������������p���[�f�����)(X8��CS�1tF��Dz����m`O�����Z�s����b��>��c�M@[P��p$������@+���*D��,<�)������tX���������+��b8�N �J.hJD��L'Q35�0��	�2
�/�{� X��#�����:`D@�����n���]�$@1�;9�����~�l�_g��`�����Bo2�l���@o��L��	�K����l�}}$�b1�!
�p�E�&{r�������u�:���ai�/����A	CL@10�R�;�H���D������+{�B�;n���������C	�������^��r,�������uq�@���������M!����Z�v�P: [���:�13B�|��{�C4Q�"��N����fH����&&�4�7e��u���ap@������Es��8��������7�����>��}����N�'�MvI��'���}�����4(��E���`�!�SCY�ty��2�j���!�s_���Ns�U}���A�g`�G�E�&,l@��V4�`���v`�@��=���!�� �~�J�%�
�l���29��5Z662��i,a#��Q.�Kd����j����2��V����@����/n��+���l����'����Fx-�o���������g�����d��h�2���
����^��^o������ :~5#��R��lv:Zk�g��9�x0@��]��@
SAf������
�KV��s
���T9�-�4J(��4&�8C�"�
O.�����pi�'6�`=��L�o#�E:�~����@����U����,�FH���y���r���]�w-��A�Z@�8` ������%�!�U����F������?*��m�\��@�K?v\�~��g�!��)b�)�����������"����f{w���Cx�>���(�6rNA��!��3�R#k5n���
��by��T�D/B�,������R��3�
j�j~�T M�B�
�X�mX>�U9I�#D"{�{�>�n��Of1f�4<�aM
�������6{����������Nn7��*~����DF���f
�m��*�a2��J�<���������?>�
��w�i���T��&�;�V�wC0Aa&I�G�
��3�����{L{}���<�������jZ�g� ��&K��jH*�<oFR��V^N��w�=�@����x_���~:�#��`�A���G����Z$��C�u�|*��Ab��3n'q;l��]��P5���i���������sQ!W
��;?�`z&��k�3��jq��� �����)��`[�B�(��Y�ip8A����p����!7�/��b��-�����hm���������x��#���%�D����?(�����&*{Mt4���Q,�����p�*��|g�5�~��P��t6
�Kv�P	�{�����*e�m���ob`X���Y;���P���*-�(Y�\wGki;}����+CH���DQ�o��"^�T�����O�'�z�T~���R�A���+�TRM��C��(��rU�P��_�8����5�LF}�&�$~�1I�1�=�QTH��:L�������8U�'h�WF�;P%��=�����j$�5�*Ad��4K���:@����sG"��;�t
����r����^}���3Aw������w�[�6|U���I�YQ�����)K�tT�J���E_���.����+M$��ia��i�
0W��4edA9�
��1�� B���%c��������9��<�ILx4S�>��S�P��Y��G����w4��
|@����,����^Z��V���rJe�*��sk�)�x�gaR�2�!%e�Ge'|���TSS����{��BS�zh��bdM�~��)����Tz�'zc}����:���)+�C��S�������=Ne����h�I�r��(9eBB�n�h�R���f�������8Z�����fDd��Nnjz~�V>@"9�1�3�#DFzZ�*=]���$���kL<G--�.�F��������>���cq-��,����:IzN9Md�i���&��$��.=�[���� ����%R��#���!o����?��@\��p�a��}�&��Z���;'y:���g��QK���T+�L����%���\�F5R,�t��E��--�����������G9�E���V1/Em*�V>����O���q�S�Y�
3���:�E������S�
��C8B}�X;.B���9=���i"���9M���/��Et�C6����1s���O�/L���:8����B��������U�J��4M=P��MV��a]����_5Hx3��3�����y�K���EP�U'�a�7��k�Z>�|�9�N���
c�|7�������Yl^S����`����X�*/hb��������e�VV�%5c�����WV��/g�7v����u8j-�m��B9;	�$�D;�%�YD��'������"$�A\%����7 �|��BQ�/N|��������m}�K�f�8G3�����=O�N�'���S��..��b M�-�g
7i�8:�L��uT����/k�q��,��%9���B)m�+]�f��0�f�"������j��x�u}��dOF�V#=0n5�eZHG��L9���`�yk�9�S�iV����	9�^�2�8$)�*7jx8A�:�W��mH�<����;���K\��u���h�Qz#Wj���m���3�����Sqj�Y�@�����P��P�B�4��/^<��lE��hi�~�E;�^ z�8vZZ�>v�M�u��2������#�����Mp���,��4��6�m.�t��$�^��(����aJ���9�t�MPX��f����l��.o7S�%h=��D���������j��:��,cJ��'<��/4��3�+-�����_15���e��f�����nT.+��kd�B�7�n8�V�����}��H���C�7���y�b	���]�1��
���F�F����q����_���5
K���6rH��C����Z�.<E$��������Pk���R���s�Bl��cJ�t0�W0c�6eo�\����>���
n�Y����~�Z1���6��'s�o-c�O-cyo^����M�����+i�����e�%^/�S�[iO��\bOQ|f�)���P��S��u]��nU���\u�RLo[�bj�oU�i��U)&�h�Ri]��V�L��X*����D������ev,�@U^}������c)	���X��n\*��R��\��"�T���;g�H���rB���v���L2x��B��C���YNY����^�
<hX��%@��y�3_
x4W�A+�]o�b,E��������A��|���,8��Gp��5��M�1���1b���O�(�����T�(�5:@B}(�{h�u'\�����Om�&*C6�A�x� x��-������v�E���k�F�����}? R��R��*`y�������	L�d�����R�`^�K�9�j� A;�f��)������G�j���|�\�`GM1�����I���G��ii��gO�������5���;l�S�6��[����_�<�2���z��9�����Q�`��c���W���W�(��Q����*~���__�u/����������B�����{y�a������Y8)�9)��\�=���+hZ�*���~��p#�3�����w���]��dt�mq�={��B\_�z�b���w^�����U�y��J)��j��2�
[3���m)7w�Bs���,.���z8	,�o�f���%�_P@������1�^S7�v+_�2H�k]F!��vK;U�.�^�|#^	���.�+������
Ft���	�~�[�����B>L��� ����~�}�^2����^��H��t���v�?�q��gGW����P:�E`(��;������{�3,��9�;o��q�:t��|;�����#$b[w\Q5�P?���#��^[�k���G���[>~8z�W���?d�C~���E��K���L���n�=��Hdt4�D|��a(���g��E l�{x���&�w��/~�!��7����?�lm6Z�z����G{e���Z!?��Q�p�
�n�����������������-������P��E
��o�����6��x���r�v���^��������j�_XZl�_���sp���������������l���
��M��F�
WV%�	��>��7���3p������y�h��������v�L�����[[[NH��EP��
e0��$`cP;����Wc��.8[��DQN�J$�1�1l��CQ����|s}�b�|n�\��b��W:��H��1���9�����4��W�w�����c_��0"yEG��68��� DD^�s�{��&*��$���)�c��
eb�7rtF���
tt�������ak���g),����-�I&�!���m��������kimZ�\�8���_������%���NI�e��q�t�����	�XS9D����~��_'���������;������g=x�+�o|��'r8��<?PQ�4�<�5N5Q~
�w�3s"
��,��#�;�s�#�{�mR��G\����%c��K�_���*�L��Z)��g�k@��4aS,���m�~���:7w�7p����)�C��)GX�].�=�-����>�`,������*�B�G}�����W��r�=�������Y6������@������A�`eIb��K�B���-
�������&���E�����
����{hX4��![��'�.�2��P�[[}P�;�� ����s���]]���@��f�'E���0�Q���&�M5Q3���RE/`�3 �+�����h�s@�.��\����o}<G"n*�Lw�����_[k���i�Y�3�����B��f��;cB�/�z=������m������I+�:�����e��D>�W�o�.��:������e�a�Kk$J��e����G�q�����B��r���+������F�����cA�6����R=�������o�H.�dlRk��x�g�}f-s�[��Ki��~Z��7D�����f����YP_��G���+�>����DgE���}I�������'������r(���=���
8}2/�����,)��^���,�����#��<.1 F�bZ�"�M��c{&`Sk�2C�D�5�*O��Q1v��=�W�=�8T���v%
(��L ������u=�$Y���"�Y�Bd@wy��d[��[���k�l�7_%�n4������8Ov����*
�l�Z0�T�%2222"22�-���\������ AM�ks;
����1�n�x@��Qi�]��h���o�qN+Gk��������L}7�N���������C�������>F�k���%-�J
Vpeg�n��x3���l���6���
���PHIz�2�q���K�?�0��(�7D��p�����+���?�-�[_��qo0$��O�J�v���dK�|��A]��{���c��|�Y����:���5����-e Q����R����?�dO�!�u]���/����=�u[��g#�����e4��^�,�E4���k���2��mXT�/�E��|�,f���vZ�����i��d�L����8�1�Gu�L�Z}���7_���F����3�aF1�Y����7���Z�G#4��o^��:��5�����E��p�cd�J-P�f|U*��
T5�4������*�O��fL����<_$M�u�?�P�]�����d��G�����Z����]Y"�1S���#&��aNCL��<�W���[Ur[��_���#��)'�v����yt�M>�/�S	fEnPT�"��,��<Lv������>�ni���-$��t�<�f2*�ae��Al��-|�W�e��s��o'gr��h�o�X��n�������i���F�"|����Fo76���y�Y�V������Kev'X���W�t8��S����s``g��~~�.9�3>PM��xGK��x"�^R�9[�.��@���5��xE�* -n����������?J��
Q��P��
,��O|~���5RX�'�q>EL
R�=%_�Y��x��<w�$r���W�B3���5�=D�G){F��C�I�����������R�����q<f;)I�j���K^mh
�E���L��
��9J?n�^������1Z&�����#�P�_�9@Y��-��}*���������"������O~:=c�"�F<���9yu��/Z������(1���������N���g�g
��������0�;R��t�?G��E����W������4�fp%7c�"��'t���h�p����q�)r�w��;KL�v%8�,������U��7��z����l�A��F#
��<���(���L��-��C+����:I4+)�O���U�eJV!!���T����T]-���	@)��
�8�������m�Y2�QX��@��)��E�����`hJ�����V�X]�����L5��[�A��Fc�����c�<��)H��=���K�������]�T(	pU��(��~B�HrB*��<�����&��2�JX�eQ���'f7<��"[�G�q���j��zU'u+�������g��E|��G�d*@,?x�n
u*Mk��?������ts -�Pp-���p���F��!o��W�X���P����$��4X�P(�J�D�%��f�h
��f:���T�\F��3��t������\���U���+�M:=��Y�r�}���98��I���dr�K���2��S"��������:J�JM�o��?�I�F�!Y����*y��������|`�?q[��9�&<u[�������6<�����z����C�3�w~�z�%z�`��L�<�o��>�]GW ��P�/�_N��q�Q\�lZ�"���5b�������:��Y����
2J
���T�[�r|����OYj������-�j������uN����������[rO�bH�'����Q)���YV�I�|���L�q)X�+�8���Pm7&i@�Q������EZR����P���t�M.0^eF�H��Q�<��7�=��y� 
�kD;	%=�G����������EUEe8�����%8��OJ ���s�q��X�"Gu�9t���(8��y����9F��������!����QD��(�J���G4�hI^�B�)�?m�9��@�]���������[6SU>E 9�"�-@C����m3�h�n��e�����x�a_*�����b]�5���-�I�r(�M��bx��{@�����H)��Y���Y���0�� �3�;��d*�0&�D�em*����*��'�SYK�(�R2�g�g�t���>�U�����5�������H��(�l������?^�	^����RI����n��	* ��rC���1�qj�E![��%K�"U��S}��0��������i�����0��������������o^y�a�A�:j�d�_���;��f>�4�"U4����t�\8'�>1cR��(��E�N�l��g�|�L�jcV�IS���M����h1������1�����(M9�3�Od���V�@�x;�35Y��g�>;6	�t����=�+�����D����/1� nj�R�Dtu�[��c��e���� ���I�fZ��� �0*c�����U��g4���c�b4�q���"��v�i!gI��]�q�e-E�i��Cn���8
��-�z�e<��l��|
I3��`{W��v�	���h�4��8��;���
3qz~����?J��6pw&����f�6*�Y����^�,m�K��l�Jf�j��)�p�+��.HAU���k�G����M:����dv�P))y�F�����_9�N>l��cH�}�{��������@'��>@k�#�XI��]L�'� ��b��-"=����<����q�m	��nl�|�@�#L�'�	�'���k�B{�������#uU/<��\e�r[m�`�52��-��zT���
��I�	Z;S=R��SI1�.���^����������ysT%�)
�,���XE�6��D)�z�%`5�����zq������@��I���4-�c��o�����y�*�b��hs"�%�A�_Y����wIn��V2Y����W�PItJ$�}y�(�34uoH��� ��^�AyM���*�
D[)G��Bd���A)��g
�jI���z��c�����j�1��q��c3N1�X���s�v���>��1���rT��SZ�������>�V/�oi��w�����&��s�m���.p�n���$��T�t>�Ne"��,f��P�XZ�����]�l�f��" I�	���,Xy�i�UZ��+���<G��n�x�$oH�9�u��K�M�<����i�i�P����X��)p�����yt����y\%�$\��3�T'+�-�v5���V��Bg��f��Umu�Q��`\}��`r��^���	��FxZ��(�sI��C�??�[�/}����������SI*b\)2r�>k|�3Fw;sl��#���+�����oi�m������l-�K�8��U�u�d�;O��z� P��������({_��U1����$K��5�`�n�����:��
�-$�"���
/��>C�L���>����J��>���Tx�)�}�������^��6�Z`X�lF��zH�N0�u�
�:��$i�&�^��c��T�>�-��� �%�=~��AA*�&j���_J�F�l�M)TF� ���V�s���U��*�.�Mf_�8���r�h��ce����8�+�<��`�6��m�I]���Y�,�r�������h}�hr����-�C��\��S�jDksQ<���:�������uJCNM���byaG��$���R����7�m@3���E��E��dM`M�����}�A�U���w0{�p�)�%A�����A������+��F�F��iY��Z���T���( �m�����E�a?����[��aH�rm[��c���vZ����Q#�����(A*,�I��>�D(kd|������:;�zbk��w,"%/����	IN�0|7�JZEd|7I�C������HL�O��
��-�
��"�T1w�����d]2k�sA�3���M/N����a�L��4*�)K��aD�6�aK~C�.�w`�W��;�c@G<$�/�����b`�_X��zW�`����!�7��Af����R+l
��(��>�	��#�U���O�il<���xL��y��y"����SE���zO���x8����-������)J�����h��L�I�������A��X��!�]�"�������p�t�5���NQcx5�W�A' l���Pd|����)�e[�?��}�D���(	G�#��We��a�O
h�)u��DtA�Z����B<u_ykZN���@�E�G�0`�2��BU������zaM���g	l��u�F�����d�8R���~����/#[v|����,�E2b�Q+G�������$<��#�$8Tlf�w�P���y�2��)��.~�����[���%���A�~���=W��F�@w5>�����{�E�����g�N���IV����K�4|4��pEP/���{p�M�����Ki������~<j�e8J@"�Z�x�RwiF	�d��m��-�(['=�����XQ5�m������`��]�R;�m��(������;�/�pa��u[��v��s��5��� {��M��rT1�i�����J��r�m��R�m�0��
���4��}\#���~ �vr{�n����Gk����\�aI�(.��������K���Znd��?��c������|�|Qzo�)V��6e��x����
��`������4�\v���"�"�1S�RgZ��8QC�A�;���VF�~�+y�1o�;{Kkzy��A�OUcka	�b�7��L�W��!�=I����C�7K����^�������c5)�������W�?�!��C���.A2

fQ�q��u��#���]�nS����x[���pJ<�A�f&�]>P����i��M��$����/g��������*74-������A�II�Mk�H���������I�'�SN�����6cr/��������h����O�[�����e�G#�p,���-bi_�a��T�5�ak�N�dCk��|3t��=�&�Lu���#�������m2���(%�
!��"��*Q��!{�Z�R��T�3��:WP���#�Iz��M*Q�T�� ��4q*[29��`���.I�'oxs�J�@������'�N_��An|�)���)o��V��$���]�:���d��#}%j�F����N�N�|e"���8E6D�K�)K�Y�7�W����nJ/t}�>_
>B+z����fK���B���������NO��k.J��C��JK|$uI�������hD�x���������p}�6��<����n��B��{����4%fo����&�/�����Q<q�k����C=Kn2�p�b7��ZX�����V����[������m��.����m|/N����u�&�ee�o�`�s+�y���8y��~���0hz��������a�����H����~f���,�Ze~4e�����2�BCmA<$�$�NT��.[nn0�vw)����Z��V��K����[��������g�:~����r�p.Y�5��#�S�!�l�v�*�Q���	m�|c=��#�O^�9KeN������U��n�7���yn��8y�Y|J�F/���mo��$1gffc]!Eb�X�Q�@J�*L[E0�4����t3����mhj���}t�z��>�zg�I|�S*:��#Jc���������-3�V���I����eC��1��Rs$��j<��EpEi�Q�=# �%�[�B3s�B3�Ui��T<��6���7������g!M��eW��W��=�����]l]���|��4����)�9u�AH��-��a.C,	bi�AXl������Z�W����Y,Qo������*��(jd����L3r��rs���D�t����o�����a��gV����T��$�����"�X�/��[��}��-t��{']�VA�m���L�q]�UR��.hp,������4�1����A	�
nL��E�E�<���93���:���������ug���}p�F���
doJ4���/���J�HN�u�'h����������I#��c����G�!�J������z�d���
�Q#Af3!J���.�.�2�D�
�</���9�eP_�l
���8*Z�L]���b|v>.k%��%��.V?���=�'�^� ���f{�}�"<'������V�~.�>Q�4"�:�D����2�*&u��v��J�4Z�bu�5�\������#>��X���������2����3��H����su_�QT�K��J�mC���%D�O����8���P��C��P��� �y�K���Tw���X��l���'�������b�\e|�c�����V���U
�{�[wxFa�-^��t�{����>rA����)Mj���iOB�T7�Bq��3wKw�t�G'u���[��:�V�n~�B������������<Se5��Y�8����U��"{�9t�S���7x����:W}����q�-�(�Yc_�=��R��8N������/���B�o�$�������Z7�l>�`��aR3R<)U�K�IQ��*����V�F��N82�`�)������Gc���Z��k���I�|�\�1%}�}����
�W�hK\p�+]LEn��I	��;c}mJ�D��IBM@��l��Q�k;��cw���1f����dJC�������I9���Pt�R[�I,PR�;�E��0����e�8S;����0���d]ioXc�6���^}�B�-�L��g�A�<� r�[^H�6���Z��vc�6?�3��#�
����\�P�)��(��F6h��AH6�,v�L�/Mh��$�	���|�� �����Z�T�@��w�Q>��EJ
���(;���SH
���\Tk�vC����w^�5�
3R����G��Qn,��1�v�I�}%�x!.��5�-A���^���aX�O5e?�����|!� V�I}�J���O��$s�5/z
���_X�UWz���2�&���^�-�(@�=�m�9����n�OT�������?��I�z_aAzS��~�YSj�&��L2��F�t�����A�p��_�R�����`jT�B3����������j��VL�$,����w�����w����z����5��AC��"��?,x��lu��}�g��&���b ��U�G���o��	iV6
]EQ���H�~d|M5����rGl���D\�)�{�DZi~���)+����c��e��v���T�A�7�����
�8���4��I��a��j�"��Z����\F�'v���p��E��Y�`
P�������i�T�����D�2ot|"���W���j�T����K���H�����������<���Hj
��?�%IKeB�Y�y��~���B����k��2V������y��T�O[d	���q���.��|-����iT�d��&;[�q��DP�]��@��Y�&B{F��)$y(A���q�����8()�b�9e�_:����4QjC�� F�[@�Vn�%���o�0t�|�k��E���%<;q@uZ!B
���-P�H��<B&�h�5(�!��x��L�����$ws�����������f��q�����o�>�T�l'e���^^����d��0���
�#4�w�������8�cK��k�u�9NLBc]������{�s�n1�@��YX�
��C�<X��Qg��7/�!W3�$!M��K{�O1A:�����N����$�9A.����D'�Z�|97&J��_��Blve���r3S�n��ns���a�fQp7��}�@������}h�Q��X��w����tV������o`���cT������H��bhB

7S��dNbfc,1)g���K��D>�JqQ���	��5

U#]1�m$��0Q��<'n��3��������"(o��X�t��n]���Ez~���{G��`&V��c[������6����I��b�d����e��N��j
cm���o��C�X<s/��[ZYx*�SGS��'�Z���h*�G�C�����o-�����J��"�+����|H�M�JnnH:K���������7���U"�P1y@GT�������
��-/�tCu�d�����~��7r^%������c�XiD����Qd�0��'�F���h����$����
�=�|B�e�vsC7�@�z�%��k�X�����R��7NX+�/���Pk&���*<x���f9�HBX�+$����u*���R*\{9�����^o#�ya�Y��)��e��{R�S��<�$G�{*��OT�g�/�~����_g�(-},rk|lEf�S1��}�xK��+OxyPB�[����Q�nF��/�[���ud~����{]����k"��1�������n�������z"
&��c*|Y������|$@��AoB3"�L��ZeA��������h���f�5����7�JP�3�9���{s;���.�9���Jw�[��<SE����"�u}��4�#e*�/�wGb'
P5oL)�!6
gB��������i5�a���P�������L��
Z�G����\8[B�T8�3y'��j����Qz��0�k(Zj��jV.��-8m*L�8t���0�L!�er�K��w� f�9���/(`��o�2�>}��;f��z�E����"�������z�I��R;�N�+�?3g���P$�u�>E/>J-��(��@������0�n���E���X�|�8�kS	`��p8�~����WdR:q�	���l���!��?�`��R!��L���]�sS)+��m\�]��2�
�ZW�:fR���!�Kk�?V^�^�Fs�s��q4���N�4)���'s�E����)Qe������m�v��v�Q�@�E�V�yz��U��������(����Ud��L�����}|��d'�V�������3=��8��&��/���_����A�Q�a	K��t�MZUM�m�kI�t��;�tZ0���,}�U�@5�Y����HM�����o{ps3�;�t*���
��L�c\�U	E��C_Q��I���u��Dm9�h�T��g6a'�d�����?����%z���xH%~�cQ���K,�#��D��5aE��� \f�k����,J�����J��������"��8����l$�(��W�R/�_���K���<��0�T���q%s�<@����I�T��a�m��I,��`6I���~i������g�	������H�	�+���Jh��J�����2���
����������'X^F�����f�3���\92���E���7sQ���(�m����f�^�
ir�����6I����~�8~���.a�Q����`��O�1W���$=��=6��`�F��@�J�A�V*)�g<�v���FY0{�ut�kr��<4��K!N��Nk��H���XK[3�Ej�9�/YH�J��N>�9{c#�,6�V3��0sQ�f�������e�9�df��Q$�7vI�o������VB�NL�5��u|3}~�.��~��
Z'`ud�O ��7�)s���q�nI��������H�������&�
3���+m��&��d���kSO%C�eD���Ta����kT���%���J�8�>���D��\\w�����b0�����}��=<�b�Tq3��[�t"��A�����K4u�d2B���z&��X'G���yr��Z��2N��c�p�i?�4�8Mk?�����A��4��%c�z�h�3�.�6�`zu�6���)w+�i\��1���c�i���L�_��^����%��:;~}���_��i���xW^7�L�x���!��<��+i� ���
��(��m���#=��_+��!�'����xy���hwjO>���
V��:����D�<=o����W�J���]��^��_�B+��:���i��N.��_���=/I/FNo�����iC"B�yj�(��s��_�7��ys���d���������=g���F0��x���yz����N_6���I�8y'�t�t�p3�*�Q��hM%&�9F����ZX�J4�d���0�UV4!Z$�^�4@��%��s(���9�/%�J��
���&B�do�c��>hq��n8��X5o�S�F
��8|�,J�����:���?��kY%��g6�gP�H���1����r
����,K�$��3��C����M�N��:�G@��Z��e������jr{������\������eQw�����;8X�"���/��
�{o���egT��"�aH�vH�W�dh(���d	���y�t.��
���M��Zx{��V����/oy)h���U7D��Y�4�g�9~V3���D�i�$g!~��;k����B���x�GB@h�N�m�8�kLd�8�O�������s�;��8H�]A��XIM���D��`:�X���+��\D�_�9;Q#��^ ���i�[i�&J_��5�\�;�;,�����$��,S�d4���CoP�`Y5QF�i�������Q}�&���*�	���7��G%��i��l��,�����"���6<���_Z��_[
,�����
���~e�����lF��`������c�����������'�L�X0���:�	�4~S~�#����]PX�eWT��~��3����0�|0[< T�d��"��0!���q@>k�v����{���k��O��/z�M��r6�G����2U������ �[�<8.�~z
�m�k���P2�2�-���S�m+6~e� �����?}D
f|
�	�v�T>L.�F���a�I^�+��!��Uz������_S���)�_p�T����/k����� [�9�d
5i��>8D��[?����[j?�,.����=i���3;�����xz
�������&��lI�Q��Jw=���3�p[H�C�a�2S�5O����y�T��k>[��$w�d�y?k�R�%+A�B�r�yui��������{}����[�G�w�Wi�@��y�������ip���R `�K]�U��a��J��FET�=�8�8"jE�I�<H�m�Fm��A������n�{�ze�l�3�Hn�����y;R���R�g��R�!v6����
k#�7����������������������v���[��K"�2��6�������#��I��6p^�s�����[s����%/�!���
��\��T�����&W����q��mN�;$19����LE������}t~�����!u���j4�N���!��	�t��f�:��3%����1R����o��#m��d��%�d8Bg|*1Y}v���������^A'b��a�J��oO������B�I��|�O��s�;�>:���?E�7�_�1~;�&����PP�pt�Xe���9}I7�����9������9��Mfv��t��e�K0���j�[�%��b��N{z~���oF�/|�H3	�2.*�w�o���)/�����6.��l ���Z��_�����=<����y��,�~>TbT�������N���rS?�{^�l^�E��)4e��,���_H��Xbs�z�%�n�>|��W�6+���*����D�"�;���Q���'.�����Hv�&G����������G�o����b�@X@cG2
�s���5f0�>c��^6C����L��V���(��^�	��.%7�>���&�jS�rA]|����t#�R��>��#��2bQ��� �]z#9�,7	���V��<����p<M������y�p�6>S=h4�R��_�w��������1�����em��}����@����^P����v���Wv�P1��g��MUj��!1n�G4?�O�8��TG��W�A��J���� �ZQ��!�=�������fm/��j�Y}������Q;1@��<���������X��u�5'����W|'�+2Sv1��Re$���fg����Dq4����E.�;�0)7�Z`[����D�D�)�t���a���M����c[�V�F��@9��y���_�sJ��A���52{t5�����<��'=�z�v���Z�h6^U%�����1�d�+�����dE�UHO�[�OQ�A�8�Ed�0�x�����g�5�
��]��6��S�=�2%3��p�Z-�8�A/���X�|@���I�H8���r���`G����E��������EF0�I��F(n��^�������2�Q�[��i�8D�.��-m�J��r��W���
��Ck&P��C����c��+��K���f��p�na�fl����-�w�cc�
�`�"ac���t�8fk��qH���F��BA-�X��6�r	�R�����h_����}�	�-�e����E�R��^��z�m;�3Y6h��m���*hV����W����J��.z��Q1r�He�dITD�>>�o��cLR�v�lhs�*<���r��C��R�M���c�d�<>����Jc�;J�
� �<F*@��$���h2�������1����+�|)|���W�>
�������?e���
�����.��s�q�i�!���#yJq	�����(���.n����h��,}�����Qh�t����T�����*����NSJ��h�R��k�<lM��+�!U=��|�
t�<>G�������`�s�Z9�j��������xOh��qL��k�Y���{�$�����4�	6G�_��v^t�/�v���x���u�
[>N]�z�hN��rx$�MLn�j\Y'���K��7�8Ar�_���9y���I�  �Kd�"�A�P���cC��6��w�I��I��L��A����3 - �2G3����c�k���^Q�/������x8��\U�����u��r�^^v���?��&�u����~�� ��>%���U���
~�{l��i<�,�JH�MTR,���I����`��$����h��K~���W��s�V��(����&�yGC1���q�50f88�����k�z��D�@r��Y���
)�"���;o�tYF�\�1��PY�G[z�����l[L���o`%bjWh�h�:~�����������yu�|}z�zCf�Q:�����c�
m���|8\Of����?���0I�C��r�����~��������"'���)#GF�����^r�I�U������)x��s��:�^�t�Z��z�_(X��C�N���=�[�qn�k9)5���:���u�>��{9���$�c��M�B�������}K�V�7�.�,�r��K�K���g��S}]4��u��Y�<�.���8������������	H
�N�g��?����X����pyi������g8�����}LT���Mg�����c�%:��Xju�[��������J�c������t?UY �;���L�x�S�[jS;4����e��q���X�cm����+Y;w�����,V�����Zr�3�`��;��&6c�M�N��q/����$@@����V��/Y��%��t������c�����R\�m,�h��'�:��rH�*������i�z���W!�Y��Eh�	+�����t|�reE8�U6�\"'_��5X*�45(�0Y�%���O���N�g��������w'/��i��i��y�Z�*E�����Hl|��3S��b�o��A���5���Hg��Ny#=�t����T���T"��h�(��T"��u��F��A�U�z��B�����j���-24V3aO"����Iz������v`LpcA�����F4�e�FY��+����c���j���}B��3f����~�=������:��5�g���X!��%��F��7B�����/C��
:<qAKCp���
M.B�5}tqK��V����n�7�r���\����1A2EI�<[������������w�R�]���K��w),($��keRz�R�+CO_X���j�`G�wT��F�_�/`B���!��y!�{�O�=���xu�s����E>�����V-p���������7
��V�BL��\��M$���e���y�a��s��zc�".6j��J}���4~=
��K���=�g�N4DXI�������A�8!�=��$�?�x�����T����)�"���1\�w)�v_�`=^T�ndB����e�U�������L���M+��-�?R���3L
)�6{����v}�����.�Q4j���������cy�)�����������@���_y���~�������_D]	�c
��=�e)!9�������-Pl&��jL3$��if6�-X�������c|+/l�k���@�.�`�V���@�;21�=gR�V��g^_/��/hU�H�.H��B�Pg��N	>�&�%��O��������U�	�FtzNpr�0�u��&.(O�����y�^�\�
�	��\b���:���K��Zx 7X���gA�2Qe�"���=PiY��I/J'
<8�t��������?oTpd�E��(@�Sjt����S[x��!+���2���5��^%IU����|�;�D�'RJ�i�Y����p�E��o�]`��������|H����i2��a�S��N�j���EI����wj^_��2�?��6A	)I`�R�����N�b��Y�^� ������_�����$���t&���7���t�����dL��%iDb�f�i��������Z)�	dLB�=l���&���B�7L��^#
%�*;���|���-R����p�)P��4�c���Y\������c��J89l4f�U���8��nbb�L(��M��Dw<�N���.&~k��Z��.��x&���>�5q����F�4�?z�3U��������w��%/"O�fL,*2x��7���U&[��y�cE����3�������}^�c���|�Y?�L�R����<h
P�8\��g������
H>��d��y�n>��}��C���~ n�R�,-�c
5=���
^�TW����hE����x���r!C���B"�0y����>�;��pkD!�
�l�f=~w,kK
������7*���X1����ilot7�
yd�Z��AD����;��������Q�8��F�y�X9�Y�!���������g/�O���0[fk;-���"��r���\s����c�����9�6�o����i������V�n�`���"x7Y��&I�	�rB�?Fc,p9����v
�u�����J���1����b)���.�#�|��6�@�| �:qb���hCa�/���L�R/��P&=;������u�����$	�4������%��t�[�e��~���,6 ���{3a}w{YX'�O����T|,o��@����_�e�b[���L0>C�����r��[��m�z��pDA-�"�1q�dd[r���)�<*gF��B�>�g��3��*L�����l/g��!����u0�F��nE�R�c%v(ko�:p��d����:`��U���[�Nu�G���%OV�`Ys����5�
n-��r�����S�@c:�W6V�����M`3�:S�
pkC>����s���xb�)E���0��6��D�Q[,�}�K�0�3�<����n�n��o�]��jh&�c�$O�AD��l$��!��c�`����$���VA1�"���3b���V�x �b`�i�>$B��n���o��Y9�L�'��l�n����(��h:;������Kq\yx�"}��7���Q��4jl��k�:�b&N���>���9A�t��+�nK�ZyyIf������S;���1��)�N��r���f��F
T�~���Vg�d��tI��\y��sy��?�3P&-���d��B�0��h7���t�h��OJ}�	h�wp�p�O� �F���.NA#��C�a�^������!��O�+ua�C+�:�\������������
m�Gt~	
�w�b�@6�O�?������Kx:J���X /�Nl�Q<�?�)�	{N"x�"6Q.w-.��)� ���hl���P�XD�XD�����������,���7_�UA���W�sj������;�C�wI
0�d�����Q��-q���Y�X�F)�l�P�'
%��O�/�&��TK�~^)H@b���L�-
���Z'j����PD���u����wY��%A+�s�d�L�[� 9����v��9���S�^����ZT~E�Q1�S�.g�d�GI�	��]��+)�]�\���j�RZ!N� 7�|��_�/[1�fi�;���}ug�E
b�G�N>~��|��I�!�:�f��Y��gH�Tq3��yu������e.��+��F��4��]>T,�%��y4�{)�F���Vfj�������]-K�SzC��7]]+�y�7"+B����-�Rf������3����������)3��AJ�&[7�;Co���n�Yy4����#�����t����&����\��4&�n��4��!�^�V���n0��H�<��pF�F^K�&
����2�~�N{�����E�O���,�"��gV�����yU�*cyS�g��j��o��/
�$� �Eqz����*
����`�C�v@D���92��Ixg3���K��!A�j�s
c�xC�7_�y������I(�����J����v�dItlo�-%�C�����W%�{��.�ve��B�U���K�rY�C��e���IS��R�WNW���c�8��1�b��g���
��I�����X���,�.�,����=d������k�BN�DJ�� \��!�����NyA��D�#��X� mT-�N:,x>������Cj�B��G�+�^��%��f*����6D�Z��tc���\r3���� �hj���sL-�=��i���1����9ln��VgF�_��vi(-$OEF�P���3����b���gC���T��Eq���!�^%u�jfe��nD^���w���O)��d�p��f7�;~��}{����)}g�y�J�q����:s��X'O���(r��udfY.�`��(L �P�����m[���!M�5J��v<bz^5��H6S���S1��B��6�"f�op!-dY8f���+o�1�j&[TE���n�E�/gTe���m�������W/w�ZI^��3I:+��9%�%"e�4����n��l�����?�8=������ef�N�F��7�/t�}b��S���%)�K���J�7����n�9�M5���++u
�m6meQ���^�����=��f�=�q�.KW�D������*�2��E8�K�VSu���O�{y)��+ ���x�~���{�N��M����1��v4�l]�V��5��������E[[qm{�pw_�k�����j�:���r�<���A��e�C�7:����W�������?{��+.�x�R
�(j���k5���)�V���@7zL��_u�Y�����>�5DM�I����Y%����Y��:��|q|~����3c�!���Y3e������^�pkk���n���9���������wH��o=1���Tl��~D�O��t�����[��x����$���}5;A7L����w�"�S��v�5:��4z��a��0<+�l �>VQ��������!��|+0a9a�^'�iX"�$���c�!����J����@��e� �!I�`������|`($�c��#�>FY2�d�t�l�����W�����X��^M����=�8�l�q9��[�pmo�3�9��\d��e�2A���h�(9���{�`����k������~�w����2��,C���ud�������&�
wwp��Y�=w�
�4[�������-i&��8gQP����S�B����:�5�����{��?�M]�dW��
��ao$
�c����pE�T��;�����!���q�x��'quGc��FS����W���a�`	s�X��ocw����4�~5vd@	!���i��F���
�Y��x���-��-9 ��n����uy����!�I��qh9�j�@�.������n�/�u>�2t��l
�d>���pDZ�:3����2��������r���x�����O!�+��I��-fY�*��T������U���t���������$�a�f�_�����;���b����=y���a�Fw��������>~�F��������N�����3���C}_�Aq�A���D����mR��A�@��l�&!?{%a.���go�@w
��Hn����Rl����X��<{���*�}_H�
�H\s�J�;��i:?�)z��TK��K�
M	R��Wy{j��Mz�v��B�[�R�|�(��/s3��H���lN�v�8�M��?W��s{)�X|F�5��fe�����K�t��ia���T��'n=q"M�s����i�8l����6�.*m�k��E=ei4�2����������0Md`cnS�gu�z��o��V
���������S�#�l�n7M�"Sk�����{��^1c����?��,ye�|6�r�f1c,��������"� ��n�z�������Q�Z��c ����9.A�xM8H���c�S���Y���;Z^jK�Ys�
7�z[x����T���6��Xl���IM�J��<��G��E��"E�6k�;{�������4/q��i�!����F��^j	{���eT����L;`�1}�&�2d�D�X�9�G���`�b��0���2������2�9^��H�k�^���a;nmm��Z��~;���l!�xM�A��^m
~��olo��dL^�.��u@pK��Y�E����:���/�M����(�j����m�o��6���Jf�F�*�x�&<4��0���>�}Q>A�5�i$cbCV���!�l����3����J\���/6�$^���1�Y�A��U���W1��M����D��cR�N
J�������>_g2�a>FD�p���8)$���8�	
�}d�R'�R�Y"��J9	 8�Q���\#���NK��������?FRJ�g������r�om�v����V:gv*'������:��M�Nc<������av�3��d�O�b�����SC�f�II9����U��;���H��:���F�
nn�@/�������M,O���f�@�U-��=V��f8���
�|��*w��e�v�����\�.�<���gq.���9���Dh(
��+���K��G���qoS`A��N��Mwv�	8%�q��pH��
+Cx����1�����e�1^���q
����n��`^^!���������u�j<nG
�6�qlxT�����4���G��U����h��c��F��^)l\�ob<�V�tLb���w�����7wD7��;�>�����P��<���xu�����-��dSF�IT9�
el��:����W�$X>p���Cz�K*��$�F��
q��8�4y�ck��n�����R��]=��hL<�,����i�5��X��n���bNs*�93�������N�C��q'�}��L�Jo�u^��`�W;�S5���>��Tw�� �����F
/K�)b"+����������J�U��+�7D��p�J�
��xr5����+�So������F
�C������-�)�Gy���>G`���!�02l��VO����{(�t��KE%hj�^��7��W��f��A�C,���"���.��z] Zx��{�C����72��C�!\��R}��5�R���8M3r�fIW�����T5Z��/�G��K��
1�E7��x���c��T<���r
�����"�
���d����;��E�*����O��_��^tmL0TpA\E��2����E~#r��M�COe�<�e����"��VcO�.���)�(j����9�xwz�S�P�}��jN��8����u'��~�v�~y}����SW���� DP��U��W����} ��Z�������W�'���B����_N��;���e}�T?�{���L]�����j�=�!f��1��[���3�P$P;�T���w��Zf�r!�NCQ_b������8�|�$I������D���7������]��?�"�����SrkA������2q�1���io��0s1��H
'Q�nc@�"�QX�{��A���M�:n)�J�e��^<�aQwG�x�f��FL����hP.�EZ�U�M��1�� T�	/i
����q&�\��S�����I��&��b.p�Z�A�G�Eo9��P��b�'g/���R,�*<������S�����#���#��}��a�k���YJ;�J�X@�>���Q�]5�o�o;1�7�a�&���/�$��9a�E��3�
��� ����M^F8�C�:�gh��{�m
$�'���F�:������^5LA������?lp�(/����Ps������E��H����w|m���IZ�;`f(�����P_4�o���7c�#[�W�=B��������
w|a���X�f�c�y,#�Y�~kr')�� YFmJ��$���`�w����]4iC)8[�S������w������??����������*>��k���U�%��67LZ0|���r#�����U��BkJ/*�������P;)}���	7�rE���D�����nn�-��'o��84��kxk��t���F�e<t���j|���6��Ovg&����LH�i#�|��Qwd�=	�t��Ni�`n�1��J�I��+�;�-��7�����Z}?F�~`VG]��z���*����+r����:������dn�E���p+�x��!�M��>Z=J�����������z��>#�����TX�,2�6����_��PP���DTd[1Ed�hh���a����e[��q{�Q\�g����v�z,��p����t�)(��x�X�b��Bc
�x�1���l������Sl�����5?&���[`���w��]���c�g���o1����������aIY��*J����i�;���J�M��ko���I���v��Vj�=����?(���
�`Q/�K� ���x����tz�4�Y�v��>{i����-�=K4����(�vj�"�~i�c����������z%H��t������x��E�B��`4����y�C�>����B��FR�;��L�nU��f�E�ine�H�d���������j-�Fa��M��RZC��������J��^��[��������������&=��s�����1���l:S-_l�H���-.������Y�:�z��F���2(�+����V��.����2��[L��[�Y�.H�	�f����
��4��������������c��
Y�fr�:�!R����iu��%������ ���
���RU>�����H�6��8��v��:�]b+��kL�t�	�==Ub�I�FZ��FY�a�b�W��t9�c����[�fh�qd���	��_�`
-��[u����-q�X41��/����d|�
j�qB��{��T�CN���~�����"���n�:��![C�����
�O
��S�aw�&P�5���<��<+�
�}���������\@#p��h5��J��mvy�*���0�?�
s��B�>3)����d�����w�`��ZV���	t�x_!�x�r��#6<�o�)���t��4�/SOOg�a����9*���D��9Hx/.�z��~tD��������Y��L��N����r�.F��$����?����?�&��f]� �&2-����ke*u$`9[�u����Q
�]+�:=;�#�`s?�4/�O_A���S��xB&����xs��]�N���8��zY���1���Gaa����� �$z(eo0�'f���`�`�9X��Z�G����M��������P}Xt�!g����Mcv��*l��$)A�s��������#'�������7���"�Hz�-�I���>�I��2�9�J���X���D�	�&������5�!��������qvI����xI#��!�����o6�_b����S�l<=����0�=�i�p����lw��
}{��]y�E��J9����'�a	C������]���{�~A`��3���1�<������tp?���x"���C:�eY�3_S# 7~5�v{%�>z�~��?:��yO��t8�G���#N��8�a::C��Y�]�gSRb,t���KpS��k-g�Z�v�s�53�Fm��%_�S�������m���V��/������bJ1t�hlo�7j�{�;������$���~�W�|�8(�W�����Z������7_=�;$���q���I��?�j_�F���� 4o34_6���~��n5�����(n��O��@tOvi���aB��X&�w���K�K�	��\�!+�5'���tVjo��?����������>B,����,����|5��>H��MH.������t��5���zE4����
Iyv2��=6���D�cc^?�6���������.��L?g�2��e�9�,o8��(j��S�tz�f���j�
bq/OM������g�� �'h���i���	����X[����[�����BV��T�����}��w����GE���f�����Q���;8���#�������~�@���R����=���/%�A��<�����;�H��������������O-vW��K%'�T}�2��`B^^0��y,����H����H��C5��%����������s|tz�R����F�xu�-����|���������v+��`��>��s
��$����6���h�7���	���	98�Ohx����?��a:iz�� 	e8����0'�C�.7��S�d;<�en�n/��wg����Z����h^w������1^�O���0�^�`,� ����
*l}�����&B�hDc�����~��N��0j��
���Gkoq�e��{����Q
�\r_�Pc3�������5�wv�����S�9����#j�?F��hp:�#`�y�,����k��%������B2S=�X-�5-�bd�yH}�i����d Np>��b�ra�������8������E8��bZ1���,k�q����2���������6���V�~�)�Q��p��aC��i#����&jt�
�)�Q�~��u�ci�"��-��w'�'����[qq����@�7v�%=%���)@LE?��U,������qzv��n��B�p�|�e��^�����:f�-��������������|���V��,O.��q���������"E	��(�X�r������v��[Gp%1��T���O�k���ym
`��_?��;�p�T��<1� �������v����5�j��e��{�,�������7���^W!�E�Z}������xzg���n���N�������c&-��YSR:zq.k���T��N��
�
�YNsO���tFAv������<C����a��d��=6���KC}���������Xo����^� ?8(�!�X~hJE�CY�$�4���!g�K�� e����b ���A��=��{"�����&K~�$@�������I9d������m��.�d��9��ne��]e���j)R]������<@��[��%�[`����S��B%Gw�������N�����Pc���Y�_*!����p��m����-
jqW��H���l��@��(y7�Q�R�R��I6�wHz�p���t�J�3��m�xtx�@�e��s�!~�����?|������]/�J�A��>���dJ\���w��]�qx�_����F���x�glE�ZT��������z�vfO��#�z��!��;?�`����W3�x��4�!�q��!I.h���;�������_��iSXb�X
d����P?P��Z��!P��r�c��C�U_�z>��[&�1����@-�T�Tj���t���L�������M�y����C��>1���?�Nw�5���ht Q�^P������
n���
N���uP6T���'��PZ�5�s2i\��3��k��1�$�@em���1��L7'��^Y�y�����s]������"<{���36�{��mf|�)oTK�y�0�n0�>4Im���,z���_@����[���n���A�;������K^��g=�������~�%���5��.yY�?����S�xb���M��B�������5��W*��*$�|�f�P��������,|��A{8	��������,O��l��R��Q6��VC
�QJ�����/`�u�3�c�9k1��yfx9��	���aY{I����Q�������Q(qJ��h�U��h�I�&O��!Nm�~�*D� l���$�Vo:��+�O��������T�X����2�)���W&��"�3�����O����z�VoT���uQ�=�����o,��i��������6��z���3��M �s�7�+Db�>s����L��$e
������'���sc}�i���������{�+���>�!,,uf��N;/v��%jf|$��aC|~Or
�:�>e1���v�V�����FM�kG;�������u���Wy~��	g��QW����,7��Fh�;��zm:p{P��GP^2�0Z)u�|-�j����%�0z�-if&�4����>8x�P��W��z��h������Or �b���Q�����6�m?j��}! E���R��h��`3��R��}����O+��3+��c������E�O������{�.� �ci0���C���u��P����[8��F�+l�fhl8K���E�M���9����qmGoo���:]1�P���z���!f�jv�������{�3�<��z-�r���Z������^]�A�)C)����9A�$?��_���pr������=�3o�ht�0f=�>�\|��lJ��l�!�k�i�0��>��!�is{{�����`F�P�@���]a�Z�������5���S����I��ak�C�)��i�)|V$8�H7���������<|�8�F]c���R��4��x�������<M�-����#x���%�}���6u�uF��(q�\�m�;d�B�n��w35�2�S����2�&�{,�v\olo���H��U�j=�m�r0(f�P
���|F����^�9g�|��,IE?�'w���o1=�<���P��&p=�i�Y�w>��3m�UIW5�C���>i��z��[��*�gZ�d,�����������!�CU=��{yD���n���t"~����(���4�K�|��_���<�H��|I�\��$��~�����$�����������������������|�����8YI�1��@I�����Q�e�0eJm��5�>�D���3\�Z�[�=j�?
�D	y�
G��
����Q��/?�"o�ue�ue�ue���*���B�*��*��*���*��|c�VYW�K�[e]Ye]Ye]Ye]Ye]Ye]��^VYWVYWVYWVYWVYWVYWVYWVYWVYWVYWVYWVYW��+��+��+��+��+)x]e]Ye]Ye]Ye]Ye]Ye]Ye]��U��U��U���@������������������������������������������������������������������*�Jp\9�00���\e]Ye]�����������������������w`M+��=�#������+����ht�N��|����Y��Y��Y����(Z�����f���\P��.����*��*��W`�C��$W9t��:j���{�q�:�La
��I��r�v�����{�V��T�*�|�i&%�N� ��9<��2��/�'V8�p2�X���
���cX�j4�q�U-S��W��m2�j�v��c�jF �����O�^��H�p'b�~�����E�;��@E���a���h�����n{2����}��1��M�iV���.�l���x0���
��q@(���c2���5XX�?���<�����G��f�5���#����>�+���(���@��(4�A��0�Ce�j��e���\	;��N����v������7���b�
��FeO����x6��/oR�cgn�C�����z�O?��~;���^��;����{��f.4P�@��\�����a'_j.�y+'Wco��\���5+��B����	j��J����P
����#
��	d�M�����$3�����H�����t��01;9����"��,��(�Y��	:�X�Ecb�bz��9b2�'����������t���^A[��{�x��Y�Ag���WH�E�u.�r����d��p���cX���&|��]����[V@����[f����[R���D�[R0��v{��m��A�����N����aC�=l��`P�{��l�^9�rY���k�a�_-=�W��;*���eZ~P�����p1�20�Ez�@�'5��7���MB�<|t���&3��CD�YjP����Yz��	��(/����`,_1�����,/
��b�,3h����,1����#�<P,��
��5"^|��������_1$��C��
6����������������������V�ws����l������t��=���F��Gg�+�9�M���_����/*.��Rn�-��W�;RqK���,�����M����%���,�	
#164Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#160)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 2:10 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

new patch attached, change pushed to github.

+ /* GUC variables */
+ static bool	pretty_print_var = false;
+ #define SET_PRETTY_PRINT_VAR(x)		((pretty_print_var) ? \
+ 									 ((x) | PrettyPrint) : (x))

I think that this is not a great idea. I think that we should do away
with the GUC, but keep the function hstore_print() so we can pretty
print that way. I don't believe that this falls afoul of the usual
obvious reasons for not varying the behavior of IO routines with a
GUC, since it only varies whitespace, but it is surely pretty
questionable to have this GUC's setting vary the output of hstore_out,
an IMMUTABLE function.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#165Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andres Freund (#161)
Re: jsonb and nested hstore

Andres Freund wrote:

On 2014-02-26 16:23:12 -0500, Andrew Dunstan wrote:

+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool *) arg = true;

Should be NULL, not 0.

No, the compiler doesn't like that for int values.

Yes, please disregard, I misread. I think I wanted actually to say that
the test for arg should be arg != NULL, because we don't usually do
pointer truth tests (which I personally find odd, but well).

Pointer validity tests seem to be mostly a matter of personal
preference. I know I sometimes use just "if (foo)" and other times "if
(foo != NULL)". Both idioms are used inconsistently all over the place.
We even have a PointerIsValid() macro.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#166Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Peter Geoghegan (#164)
Re: jsonb and nested hstore

Peter Geoghegan wrote:

On Wed, Feb 26, 2014 at 2:10 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

new patch attached, change pushed to github.

+ /* GUC variables */
+ static bool	pretty_print_var = false;
+ #define SET_PRETTY_PRINT_VAR(x)		((pretty_print_var) ? \
+ 									 ((x) | PrettyPrint) : (x))

I think that this is not a great idea. I think that we should do away
with the GUC, but keep the function hstore_print() so we can pretty
print that way. I don't believe that this falls afoul of the usual
obvious reasons for not varying the behavior of IO routines with a
GUC, since it only varies whitespace, but it is surely pretty
questionable to have this GUC's setting vary the output of hstore_out,
an IMMUTABLE function.

I don't see this in the submitted patch. What's going on?

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#167Peter Geoghegan
pg@heroku.com
In reply to: Alvaro Herrera (#166)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 5:06 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

I think that this is not a great idea. I think that we should do away
with the GUC, but keep the function hstore_print() so we can pretty
print that way. I don't believe that this falls afoul of the usual
obvious reasons for not varying the behavior of IO routines with a
GUC, since it only varies whitespace, but it is surely pretty
questionable to have this GUC's setting vary the output of hstore_out,
an IMMUTABLE function.

I don't see this in the submitted patch. What's going on?

I'm working off the Github branch here, as of an hour ago, since I was
under the impression that the patches submitted are merely snapshots
of that (plus I happen to strongly prefer not dealing with patch files
for something this big). Which submitted patch?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#168Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#167)
Re: jsonb and nested hstore

On 02/26/2014 08:09 PM, Peter Geoghegan wrote:

On Wed, Feb 26, 2014 at 5:06 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

I think that this is not a great idea. I think that we should do away
with the GUC, but keep the function hstore_print() so we can pretty
print that way. I don't believe that this falls afoul of the usual
obvious reasons for not varying the behavior of IO routines with a
GUC, since it only varies whitespace, but it is surely pretty
questionable to have this GUC's setting vary the output of hstore_out,
an IMMUTABLE function.

I don't see this in the submitted patch. What's going on?

I'm working off the Github branch here, as of an hour ago, since I was
under the impression that the patches submitted are merely snapshots
of that (plus I happen to strongly prefer not dealing with patch files
for something this big). Which submitted patch?

It's in the nested hstore patch. I've been splitting this into two
pieces. See
</messages/by-id/530D0646.8020407@dunslane.net&gt; for
the latest hstore piece.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#169Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#157)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 3:45 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/26/2014 11:39 AM, Merlin Moncure wrote:

On Wed, Feb 26, 2014 at 12:05 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 02/26/2014 09:57 AM, Merlin Moncure wrote:

What is not going to be so clear for users (particularly without good
supporting documentation) is how things break down in terms of usage
between hstore and jsonb.

Realistically? Once we get done with mapping the indexes and operators,
users who are used to Hstore1 use Hstore2, and everyone else uses jsonb.
jsonb is nothing other than a standardized syntax interface to hstore2,
and most users will choose the syntax similar to what they already know
over learning new stuff.

The problem is that as of today, they are not done and AFAICT will not
be for 9.4.

Well, we plan to push to have the indexes and operators available as an
extension by the time that 9.4 comes out.

Why can't this whole thing be shipped as an extension? It might well
be more convenient to have the whole thing packaged as an extension
than to have parts of it in core and parts of it not in core.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#170Peter Geoghegan
pg@heroku.com
In reply to: Robert Haas (#169)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 6:29 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Why can't this whole thing be shipped as an extension? It might well
be more convenient to have the whole thing packaged as an extension
than to have parts of it in core and parts of it not in core.

That's a good question. I think having everything in contrib would
make it easier to resolve the disconnect between jsonb and hstore. As
things stand, there is a parallel set of functions and operators for
hstore and jsonb, with the former set much larger than the latter. I'm
not terribly happy with that.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#171Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#170)
Re: jsonb and nested hstore

On 02/26/2014 09:43 PM, Peter Geoghegan wrote:

On Wed, Feb 26, 2014 at 6:29 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Why can't this whole thing be shipped as an extension? It might well
be more convenient to have the whole thing packaged as an extension
than to have parts of it in core and parts of it not in core.

That's a good question. I think having everything in contrib would
make it easier to resolve the disconnect between jsonb and hstore. As
things stand, there is a parallel set of functions and operators for
hstore and jsonb, with the former set much larger than the latter. I'm
not terribly happy with that.

The jsonb set will get larger as time goes on. I don't think either of
you are thinking very clearly about how we would do this. Extensions
can't call each other's code. So the whole notion we have here of
sharing the tree-ish data representation and a lot of the C API would go
out the window, unless you want to shoehorn jsonb into hstore. Frankly,
we'll look silly with json as a core type and the more capable jsonb not.

Not to mention that if at this stage people suddenly decide we should
change direction on a course that has been very publicly discussed over
quite a considerable period, and for which Teodor and I and others have
put in a great deal of work, I at least am going to be extremely annoyed
(note the characteristic Australian used of massive understatement.)

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#172Stephen Frost
sfrost@snowman.net
In reply to: Andrew Dunstan (#171)
Re: jsonb and nested hstore

* Andrew Dunstan (andrew@dunslane.net) wrote:

The jsonb set will get larger as time goes on. I don't think either
of you are thinking very clearly about how we would do this.
Extensions can't call each other's code.

Yeah, that was puzzling me too.

Agree with the rest of your comments as well.

Thanks,

Stephen

#173Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#171)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 7:42 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

The jsonb set will get larger as time goes on. I don't think either of you
are thinking very clearly about how we would do this. Extensions can't call
each other's code. So the whole notion we have here of sharing the tree-ish
data representation and a lot of the C API would go out the window, unless
you want to shoehorn jsonb into hstore. Frankly, we'll look silly with json
as a core type and the more capable jsonb not.

When are you going to add more jsonb functions? ISTM that you have a
bunch of new ones right here (i.e. the hstore functions and
operators). Why not add those ones right now?

I don't understand why you'd consider it to be a matter of shoehorning
jsonb into hstore (and yes, that is what I was suggesting). jsonb is a
type with an implict cast to hstore, that is binary coercible both
ways. Oleg and Teodor had at one point considered having the ouput
format controlled entirely by a GUC, so there'd be no new jsonb type
at all. While I'm not asserting that you should definitely not
structure things this way (i.e. have substantial in-core changes), it
isn't obvious to me why this can't work as an extension, especially if
doing everything as part of an extension helps the implementation.
Please point out anything that I may have missed.

Speaking from a Heroku perspective, I know the company places a huge
value on jsonb. However, I believe it matters not a whit to adoption
whether or not it's an extension, except insofar as having it be an
extension helps the implementation effort (that is, that it helps
there be something to adopt), or hinders that effort.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#174Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Christophe Pettus (#147)
Re: jsonb and nested hstore

On 02/26/2014 09:17 AM, Christophe Pettus wrote:

On Feb 25, 2014, at 1:57 PM, Hannu Krosing <hannu@2ndQuadrant.com> wrote:

It is not in any specs, but nevertheless all major imlementations do it and
some code depends on it.

I have no doubt that some code depends on it, but "all major implementations" is
too strong a statement. BSON, in particular, does not have stable field order.

First, BSON is not JSON :)

And I do not really see how the don't preserve the field order - the
structure
is pretty similar to tnetstrings, just binary concatenation of datums
with a bit
more types.

It is possible that some functions on BSON do not preserve it for some
reason ...

Cheers

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic O�

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#175Robert Haas
robertmhaas@gmail.com
In reply to: Andrew Dunstan (#171)
Re: jsonb and nested hstore

On Wed, Feb 26, 2014 at 10:42 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Why can't this whole thing be shipped as an extension? It might well
be more convenient to have the whole thing packaged as an extension
than to have parts of it in core and parts of it not in core.

That's a good question. I think having everything in contrib would
make it easier to resolve the disconnect between jsonb and hstore. As
things stand, there is a parallel set of functions and operators for
hstore and jsonb, with the former set much larger than the latter. I'm
not terribly happy with that.

The jsonb set will get larger as time goes on. I don't think either of you
are thinking very clearly about how we would do this. Extensions can't call
each other's code. So the whole notion we have here of sharing the tree-ish
data representation and a lot of the C API would go out the window, unless
you want to shoehorn jsonb into hstore. Frankly, we'll look silly with json
as a core type and the more capable jsonb not.

It's not very clear to me why we think it's a good idea to share the
tree-ish representation between json and hstore. In deference to your
comments that this has been very publicly discussed over quite a
considerable period, I went back and tried to find the email in which
the drivers for that design decision were laid out. I can find no
such email; in fact, the first actual nested hstore patch I can find
is from January 13th and the first jsonb patch I can find is from
February 9th. Neither contains anything much more than the patch
itself, without anything at all describing the design, let alone
explaining why it was chosen. And although there are earlier mentions
of both nested hstore and jsonb, there's nothing that says, OK, this
is why we're doing it that way. Or if there is, I couldn't find it.

So I tried to tease it out from looking at the patches. As nearly as
I can tell, the reason for making jsonb use hstore's binary format is
because then we can build indexes on jsonbfield::hstore, and the
actual type conversion will be a no-op; and the reason for upgrading
hstore to allow nested keys is so that jsonb can map onto it. So from
where I sit this whole thing looks like a very complicated exercise to
try to reuse parts of the existing hstore opclasses until such time as
jsonb opclasses of its own. But if, as Josh postulates, those
opclasses are going to materialize within a matter of months, then the
whole need for these things to share the same binary format is going
to go away before 9.4 is even out the door. That may not be a good
enough reason to tie these things together inextricably. Once jsonb
has its own opclasses, it can ship as a standalone data type without
needing to depend on hstore or anything else.

I may well be missing some other benefit here, so please feel free to
enlighten me.

Not to mention that if at this stage people suddenly decide we should change
direction on a course that has been very publicly discussed over quite a
considerable period, and for which Teodor and I and others have put in a
great deal of work, I at least am going to be extremely annoyed (note the
characteristic Australian used of massive understatement.)

Unless I've missed some emails sent earlier than the dates noted
above, which is possible, the comments by myself and others on this
thread ought to be regarded as timely review. The basic problem here
is that this patch wasn't timely submitted, still doesn't seem to be
very done, and it's getting rather late. We therefore face the usual
problem of deciding whether to commit something that we might regret
later. If jsonb turns out to the wrong solution to the json problem,
will there be community support for adding a jsonc type next year? I
bet not. You may think this is most definitely the right direction to
go and you may even be right, but our ability to maneuver and back out
of things goes down to nearly zero once a release goes out the door,
so I think it's entirely appropriate to question whether we're
charting the best possible course. But I certainly understand the
annoyance.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#176David E. Wheeler
david@justatheory.com
In reply to: Robert Haas (#175)
Re: jsonb and nested hstore

On Feb 27, 2014, at 3:54 AM, Robert Haas <robertmhaas@gmail.com> wrote:

It's not very clear to me why we think it's a good idea to share the
tree-ish representation between json and hstore. In deference to your
comments that this has been very publicly discussed over quite a
considerable period, I went back and tried to find the email in which
the drivers for that design decision were laid out. I can find no
such email; in fact, the first actual nested hstore patch I can find
is from January 13th and the first jsonb patch I can find is from
February 9th. Neither contains anything much more than the patch
itself, without anything at all describing the design, let alone
explaining why it was chosen. And although there are earlier mentions
of both nested hstore and jsonb, there's nothing that says, OK, this
is why we're doing it that way. Or if there is, I couldn't find it.

FWIW, It was discussed quite a bit in meatspace, at the PGCon unconference last spring.

Unless I've missed some emails sent earlier than the dates noted
above, which is possible, the comments by myself and others on this
thread ought to be regarded as timely review. The basic problem here
is that this patch wasn't timely submitted, still doesn't seem to be
very done, and it's getting rather late.

The hstore patch landed in the Nov/Dec patch fest, sent to the list on Nov 12. The discussion that led to the decision to implement jsonb was carried out for the week after that. Here’s the thread:

/messages/by-id/528274F3.3060403@sigaev.ru

There was also quite a bit of discussion that week in the “additional json functionality” thread.

/messages/by-id/528274D0.7070709@dunslane.net

I submitted a review of hstore2, adding documentation, on Dec 20. Andrew got the patch updated with jsonb type, per discussion, and based on a first cut by Teodor, in January, I forget when. v7 was sent to the list on Jan 29. So while some stuff has been added a bit late, it was based on discussion and the example of hstore's code.

I think you might have missed quite a bit of the earlier discussion because it was in an hstore thread, not a JSON or JSONB thread.

We therefore face the usual
problem of deciding whether to commit something that we might regret
later. If jsonb turns out to the wrong solution to the json problem,
will there be community support for adding a jsonc type next year? I
bet not.

Bit of a red herring, that. You could make that argument about just about *any* data type. I realize it's more loaded for object data types, but personally I have a hard time imagining something other than a text-based type or a binary type. There was disagreement as to whether the binary type should replace the text type, and the consensus of the discussion was to have both. (And then we had 10,000 messages bike-sheadding the name of the binary type, naturally.)

You may think this is most definitely the right direction to
go and you may even be right, but our ability to maneuver and back out
of things goes down to nearly zero once a release goes out the door,
so I think it's entirely appropriate to question whether we're
charting the best possible course. But I certainly understand the
annoyance.

Like the hstore type, the jsonb type has a version bit, so if we decide to change its representation to make it more efficient in the future, we will be able to do so without having to introduce a new type. Maybe someday we will want a completely different JSON implementation based on genetic mappings or quantum superpositions or something, but I would not hold up the ability to improve the speed of accessing values, let alone full path indexing via GIN indexing, because we might want to do something different in the future. Besides, hstore has proved itself pretty well over time, so I think it’s pretty safe to adopt its implementation to make an awesome jsonb type.

Best,

David

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#177Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/27/2014 01:56 AM, Peter Geoghegan wrote:

I don't understand why you'd consider it to be a matter of shoehorning
jsonb into hstore (and yes, that is what I was suggesting).

Because the course Andrew is following is the one which *this list*
decided on in CF3, no matter that people who participated in that
discussion seem to have collective amnesia. There was a considerable
amount of effort involved in implementing things this way, so if Hackers
suddenly want to retroactively change a collective decision, I think
they should be prepared to pitch in and help implement the changed plan.

One of the issues there is that, due to how we handle types, a type
which has been available as an extension can never ever become a core
type because it breaks upgrading, per the discussion about hstore2. For
better or for worse, we chose to make json-text a core type when it was
introduced (and XML before it, although that was before CREATE
EXTENSION). This means that, if we have jsonb as an extension, we'll
eventually be in the position where the recommended json type with all
the features is an extension, whereas the legacy json type is in core.

However, we had this discussion already in November-December, which
resulted in the current patch. Now you and Robert want to change the
rules on Andrew, which means Andrew is ready to quit, and we go another
year without JSON indexing.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#178Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#161)
Re: jsonb and nested hstore

On 02/26/2014 05:45 PM, Andres Freund wrote:

On 2014-02-26 16:23:12 -0500, Andrew Dunstan wrote:

On 02/10/2014 09:11 PM, Andres Freund wrote:

Is it just me or is jsonapi.h not very well documented?

What about it do you think is missing? In any case, it's hardly relevant to
this patch, so I'll take that as obiter dicta.

It's relevant insofer because I tried to understand it, to understand
whether this patch's usage is sensible.

O n a quick reread of the header, what I am missing is:
* what's semstate in JsonSemAction? Private data?
* what's object_start and object_field_start? Presumably object vs
keypair? Why not use element as ifor the array?
* scalar_action is called for which types of tokens?
* what's exactly the meaning of the isnull parameter for ofield_action
and aelem_action?
* How is one supposed to actually access data in the callbacks, not
obvious for all the callbacks.
* are scalar callbacks triggered for object keys, object/array values?
...

You realize that this API dates from 9.3 and has been used in numerous
extensions, right? So the names are pretty well fixed, for good or ill.

semstate is private data. This is at least implied:

* parse_json will parse the string in the lex calling the
* action functions in sem at the appropriate points. It is
* up to them to keep what state they need in semstate. If they
* need access to the state of the lexer, then its pointer
* should be passed to them as a member of whatever semstate
* points to.

object_start is called, as its name suggests, at the start of on object.
object_field_start is called at the start of a key/value pair.

isnull is true iff the value in question is a json null.

scalar action as not called for object keys, but is called for scalar
object values or array elements, in fact for any value that's not an
object or array (i.e. for a (non-key) string, number, true, false, null).

You access json fragments by pulling them from the lexical object.
jsonfuncs.c is chock full of examples.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#179Merlin Moncure
mmoncure@gmail.com
In reply to: Josh Berkus (#177)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 1:11 PM, Josh Berkus <josh@agliodbs.com> wrote:

However, we had this discussion already in November-December, which
resulted in the current patch. Now you and Robert want to change the
rules on Andrew, which means Andrew is ready to quit, and we go another
year without JSON indexing.

How we got here is not the point. All that matters is what's going to
happen from here. Here are the facts as I see them:

1) we've worked ourselves into a situation where we're simultaneously
developing two APIs that do essentially exactly the same thing (hstore
and jsonb). Text json is not the problem and is irrelevant to the
discussion.

2) The decision to do that was made a long time ago. I complained
loudly as my mousy no-programming-only-griping voice would allow here:
http://postgresql.1045698.n5.nabble.com/JSON-Function-Bike-Shedding-tp5744932p5746152.html.
The decision was made (and Robert cast one of the deciding votes in
support of that decision) to bifurcate hstore/json. I firmly believe
that was a mistake but there's no point in revisiting it. Done is
done.

3) In it's current state jsonb is not very useful and we have to
recognize that; it optimizes text json but OTOH covers, maybe 30-40%
of what hstore offers. In particular, it's missing manipulation and
GIST/GIN. The stuff it does offer however is how Andrew, Josh and
others perceive the API will be used and I defer to them with the
special exception of deserialization (the mirror of to_json) which is
currently broken or near-useless in all three types. Andrew
recognized that and has suggested a fix; even then to me it only
matters to the extent that the API is clean and forward compatible.

Here are the options on the table:
a) Push everything to 9.5 and introduce out of core hstore2/jsonb
extensions to meet market demand. Speaking practically, 'out of core'
translates to "Can't be used" to most industrial IT shops. I hate
this option but recognize it's the only choice if the code isn't ready
in time.

b) Accept hstore2 but push jsonb on the premise they should be married
in some way or that jsonb simply isn't ready. I'm not a fan of this
option either unless Andrew specifically thinks it's a good idea. The
stuff that is there seems to work pretty well (again, except
deserialization which I haven't tested recently) and the jsonb
patterns that are in place have some precedent in terms of the text
json type.

c) Accept hstore2 and jsonb as in-core extensions (assuming code
worthiness). Since extensions can't call into each other (this really
ought to be solved at some point) this means a lot of code copy/pasto.
The main advantage here is that it reduces the penalty of failure
and avoids pollution of the public schema. I did not find the
rationale upthread that there was a stigma to in-core extensions in
any way convincing. In fact I'd go further and suggest that we really
ought to have a project policy to have all non-SQL standard functions,
operators and types as extensions from here on out. Each in-core type
introduction after having introduced the extension system has left me
scratching my head.

d) The status quo. This essentially means we'll have to liberally
document how things are (to avoid confusing our hapless users) and
take Andrew at his word that a separate extension will materialize
making jsonb more broadly useful. The main concern here is that the
market will vote with their feet and adopt hstore API style broadly,
sticking us with a bunch of marginally used functions in the public
namespace to support forever.

My personal preference is c) but am perfectly ok with d), particularly
if there was more visibility into the long term planning. Good
documentation will help either way and that's why I signed up for it.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#180Peter Geoghegan
pg@heroku.com
In reply to: Josh Berkus (#177)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 11:11 AM, Josh Berkus <josh@agliodbs.com> wrote:

Because the course Andrew is following is the one which *this list*
decided on in CF3, no matter that people who participated in that
discussion seem to have collective amnesia. There was a considerable
amount of effort involved in implementing things this way, so if Hackers
suddenly want to retroactively change a collective decision, I think
they should be prepared to pitch in and help implement the changed plan.

I think you've completely misunderstood my remarks. For the most part
I agree that there are advantages to having hstore and jsonb share the
same tree representation, and this may be where Robert and I differ.
My concern is: Having gone to that considerable amount of effort, why
on earth does jsonb not get the benefit of the hstore stuff
immediately, since it's virtually the same thing? What is it that
we're actually being asked to wait for?

Let me be more concrete about what my concern is right now:

postgres=# select '{"foo":{"bar":"yellow"}}'::jsonb || '{}'::jsonb;
?column?
--------------------------
"foo"=>{"bar"=>"yellow"}
(1 row)

I put in jsonb, but got out hstore, for this totally innocent use of
the concatenation operator. Now, maybe the answer here is that we
require people to cast for this kind of thing while using jsonb. The
problems I see with that are:

1. It's pretty ugly, in a way that people that care about jsonb are
particularly unlikely to find acceptable. When you mix in GIN/GiST
compatibility to the mix, it gets uglier.

2. Don't we already have a much simpler way of casting from hstore to json?

One of the issues there is that, due to how we handle types, a type
which has been available as an extension can never ever become a core
type because it breaks upgrading, per the discussion about hstore2. For
better or for worse, we chose to make json-text a core type when it was
introduced (and XML before it, although that was before CREATE
EXTENSION). This means that, if we have jsonb as an extension, we'll
eventually be in the position where the recommended json type with all
the features is an extension, whereas the legacy json type is in core.

I take issue with characterizing the original json type as legacy
(it's json, not anything else - jsonb isn't quite json, much like
BSON), but leaving that aside: So? I mean, really: what are the
practical consequences of packing everything as an extension? I can
see some benefits to doing it, but like Robert I have a harder time
seeing a cost.

To be clear: I would really like for jsonb to have parity with hstore.
I don't understand how you can argue for it being unfortunate that the
original json may occupy a privileged position as a core type over
jsonb on the one hand, while not also taking issue with jsonb clearly
playing second fiddle to hstore. Wasn't the whole point of their
sharing a binary representation that that didn't have to happen?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#181Peter Geoghegan
pg@heroku.com
In reply to: Robert Haas (#175)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 3:54 AM, Robert Haas <robertmhaas@gmail.com> wrote:

So I tried to tease it out from looking at the patches. As nearly as
I can tell, the reason for making jsonb use hstore's binary format is
because then we can build indexes on jsonbfield::hstore, and the
actual type conversion will be a no-op; and the reason for upgrading
hstore to allow nested keys is so that jsonb can map onto it.

I think that a typed, nested hstore has considerable independent
value, and would have had just the same value 10 years ago, before
JSON existed. I'm told that broadly speaking most people would prefer
the interface to speak JSON, and I'd like to give people what they
want, but that's as far as it goes. While I see problems with some
aspects of the patches as implemented, I think that the reason that
the two types share a binary format is that they're basically the same
thing. It might be that certain facets of the nested hstore
implementation reflect a need to accommodate jsonb, but there are no
ones that I'm currently aware of that I find at all objectionable.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#182Peter Geoghegan
pg@heroku.com
In reply to: Merlin Moncure (#179)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 1:28 PM, Merlin Moncure <mmoncure@gmail.com> wrote:

3) In it's current state jsonb is not very useful and we have to
recognize that; it optimizes text json but OTOH covers, maybe 30-40%
of what hstore offers. In particular, it's missing manipulation and
GIST/GIN. The stuff it does offer however is how Andrew, Josh and
others perceive the API will be used and I defer to them with the
special exception of deserialization (the mirror of to_json) which is
currently broken or near-useless in all three types. Andrew
recognized that and has suggested a fix; even then to me it only
matters to the extent that the API is clean and forward compatible.

It's missing manipulation (in the sense that the implicit cast
sometimes produces surprising results, in particular for operators
that return hstore), but it isn't really missing GiST/GIN support as
compared to hstore, AFAICT:

postgres=# select * from foo;
i
-------------------------------
{"foo": {"bar": "yellow"}}
{"foozzz": {"bar": "orange"}}
{"foozzz": {"bar": "orange"}}
(3 rows)

postgres=# select * from foo where i ? 'foo';
i
----------------------------
{"foo": {"bar": "yellow"}}
(1 row)

postgres=# explain analyze select * from foo where i ? 'foo';
QUERY PLAN
---------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on foo (cost=12.00..16.01 rows=1 width=32) (actual
time=0.051..0.051 rows=1 loops=1)
Recheck Cond: ((i)::hstore ? 'foo'::text)
Heap Blocks: exact=1
-> Bitmap Index Scan on hidxb (cost=0.00..12.00 rows=1 width=0)
(actual time=0.041..0.041 rows=1 loops=1)
Index Cond: ((i)::hstore ? 'foo'::text)
Planning time: 0.172 ms
Total runtime: 0.128 ms
(7 rows)

Now, it's confusing that it has to go through hstore, perhaps, but
that's hardly all that bad in and of itself. It may be a matter of
reconsidering how to make the two work together. Certainly, queries
like the following fail, because the parser thinks the rhs string is
an hstore literal, not a jsonb literal:

postgres=# select * from foo where i @> '{"foo":4}';
ERROR: 42601: bad hstore representation
LINE 1: select * from foo where i @> '{"foo":4}';
^
DETAIL: syntax error, unexpected STRING_P, expecting '}' or ',' at end of input
LOCATION: hstore_yyerror, hstore_scan.l:172

Other than that, I'm not sure in what sense you consider that jsonb is
"missing GIN/GiST". If you mean that it doesn't have some of the
capabilities that I believe are planned for the VODKA infrastructure
[1]: http://www.pgcon.org/2014/schedule/events/696.en.html
new nested structure, that is hardly a criticism of jsonb in
particular.

[1]: http://www.pgcon.org/2014/schedule/events/696.en.html

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#183Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/27/2014 01:28 PM, Merlin Moncure wrote:

How we got here is not the point. All that matters is what's going to
happen from here. Here are the facts as I see them:

Well, it certainly matters if we want it in this release.

As far as I can tell, moving jsonb to contrib basically requires
rewriting a bunch of code, without actually fixing any of the bugs which
have been discussed in the more technical reviews. I'm really unclear
what, at this point, moving jsonb to /contrib would improve.

On 02/27/2014 04:27 PM, Peter Geoghegan wrote:

I think that a typed, nested hstore has considerable independent
value, and would have had just the same value 10 years ago, before
JSON existed. I'm told that broadly speaking most people would prefer
the interface to speak JSON, and I'd like to give people what they
want, but that's as far as it goes. While I see problems with some
aspects of the patches as implemented, I think that the reason that
the two types share a binary format is that they're basically the same
thing. It might be that certain facets of the nested hstore
implementation reflect a need to accommodate jsonb, but there are no
ones that I'm currently aware of that I find at all objectionable.

We discussed this with Oleg & Teodor at pgCon 2013. From the
perspective of several of us, we were mystified as to why hstore2 has
it's own syntax at all; that is, why not just implement the JSONish
syntax? Their answer was to provide a smooth upgrade path to existing
hstore users, which makes sense. This was also the reason for not
making hstore a core type.

But again ... we discussed all of this at pgCon and in
November-December. It's not like the people on this thread now weren't
around for both of those discussions.

And it's not just that "broadly speaking most people would prefer
the interface to speak JSON"; it's that a JSONish interface for indexed
heirachical data is a Big Feature which will drive adoption among web
developers, and hstore2 without JSON support simply is not. At trade
shows and developer conferences, I get more questions about PostgreSQL's
JSON support than I do for any new feature since streaming replication.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#184Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#182)
Re: jsonb and nested hstore

On Feb 27, 2014, at 5:31 PM, Peter Geoghegan <pg@heroku.com> wrote:

Now, it's confusing that it has to go through hstore, perhaps, but
that's hardly all that bad in and of itself.

Yes, it is. It strikes me as irrational to have jsonb depend on hstore. Let's be honest with ourselves: if we were starting over, we wouldn't start by creating our own proprietary hierarchical type and then making the hierarchical type everyone else uses depend on it. hstore exists because json didn't. But json does now, and we shouldn't create a jsonb dependency on hstore.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#185Craig Ringer
craig@2ndquadrant.com
In reply to: Josh Berkus (#183)
Re: jsonb and nested hstore

On 02/28/2014 09:54 AM, Josh Berkus wrote:

On 02/27/2014 01:28 PM, Merlin Moncure wrote:

How we got here is not the point. All that matters is what's going to
happen from here. Here are the facts as I see them:

Well, it certainly matters if we want it in this release.

As far as I can tell, moving jsonb to contrib basically requires
rewriting a bunch of code, without actually fixing any of the bugs which
have been discussed in the more technical reviews. I'm really unclear
what, at this point, moving jsonb to /contrib would improve.

It's also make it a lot harder to use in other extensions, something
that's already an issue with hstore.

It should be a core type.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#186Peter Geoghegan
pg@heroku.com
In reply to: Craig Ringer (#185)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 6:02 PM, Craig Ringer <craig@2ndquadrant.com> wrote:

It's also make it a lot harder to use in other extensions, something
that's already an issue with hstore.

What do you mean?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#187Stephen Frost
sfrost@snowman.net
In reply to: Peter Geoghegan (#186)
Re: jsonb and nested hstore

* Peter Geoghegan (pg@heroku.com) wrote:

On Thu, Feb 27, 2014 at 6:02 PM, Craig Ringer <craig@2ndquadrant.com> wrote:

It's also make it a lot harder to use in other extensions, something
that's already an issue with hstore.

What do you mean?

Extensions can't depend on other extensions directly- hence you can't
write an extension that depends on hstore, which sucks. It'd be
preferrable to not have that issue w/ json/jsonb/whatever.

Yes, it'd be nice to solve that problem, but I don't see it happening in
the next few weeks...

Thanks,

Stephen

#188Peter Geoghegan
pg@heroku.com
In reply to: Stephen Frost (#187)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 6:08 PM, Stephen Frost <sfrost@snowman.net> wrote:

On Thu, Feb 27, 2014 at 6:02 PM, Craig Ringer <craig@2ndquadrant.com> wrote:

It's also make it a lot harder to use in other extensions, something
that's already an issue with hstore.

What do you mean?

Extensions can't depend on other extensions directly- hence you can't
write an extension that depends on hstore, which sucks. It'd be
preferrable to not have that issue w/ json/jsonb/whatever.

I think it depends of what you mean by "depend". The earthdistance
extension "requires" 'cube', for example, "a data type cube for
representing multidimensional cubes". Although I am aware of the
lengths that drivers like psycopg2 go to to support hstore because
it's an extension, which is undesirable.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#189Stephen Frost
sfrost@snowman.net
In reply to: Peter Geoghegan (#188)
Re: jsonb and nested hstore

* Peter Geoghegan (pg@heroku.com) wrote:

On Thu, Feb 27, 2014 at 6:08 PM, Stephen Frost <sfrost@snowman.net> wrote:

Extensions can't depend on other extensions directly- hence you can't
write an extension that depends on hstore, which sucks. It'd be
preferrable to not have that issue w/ json/jsonb/whatever.

I think it depends of what you mean by "depend". The earthdistance
extension "requires" 'cube', for example, "a data type cube for
representing multidimensional cubes". Although I am aware of the
lengths that drivers like psycopg2 go to to support hstore because
it's an extension, which is undesirable.

What earthdistance does is simply use the 'cube' data type- that's quite
different from needing to be able to make calls from one .so into the
other .so directly. With earthdistance/cube, everything goes through
PG.

Thanks,

Stephen

#190Peter Eisentraut
peter_e@gmx.net
In reply to: Josh Berkus (#177)
Re: jsonb and nested hstore

On 2/27/14, 2:11 PM, Josh Berkus wrote:

This means that, if we have jsonb as an extension, we'll
eventually be in the position where the recommended json type with all
the features is an extension, whereas the legacy json type is in core.

Well that wouldn't be a new situation. Compare geometry types vs
postgis, inet vs ip4(r). It's not bad being an extension. You can
iterate faster and don't have to discuss so much. ;-)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#191Peter Geoghegan
pg@heroku.com
In reply to: Josh Berkus (#183)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 5:54 PM, Josh Berkus <josh@agliodbs.com> wrote:

And it's not just that "broadly speaking most people would prefer
the interface to speak JSON"; it's that a JSONish interface for indexed
heirachical data is a Big Feature which will drive adoption among web
developers, and hstore2 without JSON support simply is not. At trade
shows and developer conferences, I get more questions about PostgreSQL's
JSON support than I do for any new feature since streaming replication.

I work for Heroku; believe me, I get it. I'd go along with abandoning
nested hstore as a user-visible thing if I thought it bought jsonb
something and I thought we could, but I have doubts about that.

I understand why the nested hstore approach was taken. It isn't that
desirable to maintain something like a jsonb in parallel, while also
having the old key/value, untyped hstore. They are still fairly
similar as these things go. Robert said something about re-using op
classes rather than waiting for new op classes to be developed, but
why do we need to wait? These ones look like they work fine - what
will be better about the ones we develop later that justifies their
independent existence? Why should we believe that they won't just be
copied and pasted? The major problem is that conceptually, hstore
"owns" them (which is at least in part due to old hstore code rather
than nested hstore code), and so we need a better way to make that
work. We need some commonality and variability analysis, because
duplicating large amounts of hstore isn't very appealing.

As far as I can tell, moving jsonb to contrib basically requires
rewriting a bunch of code, without actually fixing any of the bugs which
have been discussed in the more technical reviews. I'm really unclear
what, at this point, moving jsonb to /contrib would improve.

These are all of the additions to core, excluding regression tests and docs:

***SNIP***
 src/backend/catalog/system_views.sql  |    8 +
 src/backend/utils/adt/Makefile        |    2 +-
 src/backend/utils/adt/json.c          |   44 ++--
 src/backend/utils/adt/jsonb.c         |  455 ++++++++++++++++++++++++++++++++
 src/backend/utils/adt/jsonb_support.c | 1268
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/backend/utils/adt/jsonfuncs.c     | 1159
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 src/include/catalog/pg_cast.h         |    4 +
 src/include/catalog/pg_operator.h     |   12 +
 src/include/catalog/pg_proc.h         |   44 +++-
 src/include/catalog/pg_type.h         |    6 +
 src/include/funcapi.h                 |    9 +
 src/include/utils/json.h              |   15 ++
 src/include/utils/jsonapi.h           |    8 +-
 src/include/utils/jsonb.h             |  245 +++++++++++++++++
 **SNIP**

It's not immediately obvious to me why moving that into contrib
requires much work at all (relatively speaking), especially since
that's where much of it came from to begin with, although I grant that
I don't grok the patch.

Here is the line of reasoning that suggests to me that putting jsonb
in contrib is useful:

* It is not desirable to maintain some amount of common code between
hstore (as it exists today) and jsonb. This is of course a question of
degree (not an absolute), so feel free to call me out on the details
here, but I'm of the distinct impression that jsonb doesn't have that
much of an independent existence from hstore - what you could loosely
call "the jsonb parts" includes in no small part historic hstore code,
and not just new nested hstore code (that could reasonably be broken
out if we decided to jettison nested hstore as a user-visible thing
and concentrated on jsonb alone, as you would have us do). In other
words, Oleg and Teodor built nested hstore on hstore because of
practical considerations, and not just because they were attached to
hstore's perl-like syntax. They didn't start from scratch because that
was harder, or didn't make sense.

* We can't throw hstore users under the bus. It has to stay in contrib
for various reasons.

* It hardly makes any sense to have an in-core jsonb if it comes with
no batteries included. You need to install hstore for this jsonb
implementation to be of *any* use anyway. When you don't have the
extension installed, expect some really confusing error messages when
you go to create a GIN index. jsonb is no use on its own; why not just
make it all or nothing?

Another way of resolving this tension might be to push a lot more of
hstore into core than is presently proposed, but that seems like a
more difficult solution with little to no upside.
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#192Peter Eisentraut
peter_e@gmx.net
In reply to: Andrew Dunstan (#171)
Re: jsonb and nested hstore

On 2/26/14, 10:42 PM, Andrew Dunstan wrote:

Extensions can't call each other's code.

That's not necessarily so.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#193Peter Eisentraut
peter_e@gmx.net
In reply to: Stephen Frost (#187)
Re: jsonb and nested hstore

On 2/27/14, 9:08 PM, Stephen Frost wrote:

Extensions can't depend on other extensions directly- hence you can't
write an extension that depends on hstore, which sucks.

Sure they can, see transforms.

(Or if you disagree, download that patch and demo it, because I'd like
to know. ;-) )

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#194Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#191)
Re: jsonb and nested hstore

On 02/27/2014 10:09 PM, Peter Geoghegan wrote:

* It hardly makes any sense to have an in-core jsonb if it comes with
no batteries included. You need to install hstore for this jsonb
implementation to be of *any* use anyway.

This is complete nonsense. Right out of the box today a considerable
number of the json operations are likely to be considerable faster.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#195Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#194)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 7:27 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 02/27/2014 10:09 PM, Peter Geoghegan wrote:

* It hardly makes any sense to have an in-core jsonb if it comes with
no batteries included. You need to install hstore for this jsonb
implementation to be of *any* use anyway.

This is complete nonsense. Right out of the box today a considerable number
of the json operations are likely to be considerable faster.

We need the hstore operator classes to have something interesting.
That's what those people at trade shows and developer conferences that
Josh refers to actually care about. But in any case, even that's kind
of beside the point.

I'm hearing a lot about how important jsonb is, but not much on how to
make the simple jsonb cases that are currently broken (as illustrated
by my earlier examples [1]/messages/by-id/CAM3SWZR2mWUNFoQdWQmEsJsvaEBqq6jhfCM1Wevwc7r=tPFuRw@mail.gmail.com, [2]/messages/by-id/CAM3SWZSLybxywH6p2pGhHFGZMzkHqBWkfr83mrzQVsoyqFB9xw@mail.gmail.com -- Peter Geoghegan) work. Surely you'd agree that those
are problematic. We need a better solution than an implicit cast. What
do you propose? I think we might be able to fix at least some things
with judicious use of function overloading, or we could if it didn't
seem incongruous to have to do so given the role of the hstore module
in the extant patch.

[1]: /messages/by-id/CAM3SWZR2mWUNFoQdWQmEsJsvaEBqq6jhfCM1Wevwc7r=tPFuRw@mail.gmail.com

[2]: /messages/by-id/CAM3SWZSLybxywH6p2pGhHFGZMzkHqBWkfr83mrzQVsoyqFB9xw@mail.gmail.com -- Peter Geoghegan
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#196Stephen Frost
sfrost@snowman.net
In reply to: Peter Eisentraut (#193)
Re: jsonb and nested hstore

Peter,

* Peter Eisentraut (peter_e@gmx.net) wrote:

On 2/27/14, 9:08 PM, Stephen Frost wrote:

Extensions can't depend on other extensions directly- hence you can't
write an extension that depends on hstore, which sucks.

Sure they can, see transforms.

(Or if you disagree, download that patch and demo it, because I'd like
to know. ;-) )

The issue is if there's a direct reference from one extension to another
extension- we're talking C level function call here. If the extensions
aren't loaded in the correct order then you'll run into problems. I've
not tried to work out getting one to actually link to the other, so
they're pulled in together, but it doesn't strike me as great answer
either. Then there's the questions around versioning, etc...

Presumably, using shared_preload_libraries would work to get the .so's
loaded in the right order, but it doesn't strike me as appropriate to
require that.

And, for my 2c, I'd like to see jsonb as a built-in type *anyway*. Even
if it's possible to fight with things and make inter-extension
dependency work, it's not trivial and would likely discourage new
developers trying to use it.

Thanks,

Stephen

#197Peter Geoghegan
pg@heroku.com
In reply to: Stephen Frost (#196)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 8:05 PM, Stephen Frost <sfrost@snowman.net> wrote:

And, for my 2c, I'd like to see jsonb as a built-in type *anyway*. Even
if it's possible to fight with things and make inter-extension
dependency work, it's not trivial and would likely discourage new
developers trying to use it.

I'm not advocating authoring two extensions. I am tentatively
suggesting that we look at one extension for everything. That may well
be the least worst thing.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#198Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#197)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 8:07 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Feb 27, 2014 at 8:05 PM, Stephen Frost <sfrost@snowman.net> wrote:

And, for my 2c, I'd like to see jsonb as a built-in type *anyway*. Even
if it's possible to fight with things and make inter-extension
dependency work, it's not trivial and would likely discourage new
developers trying to use it.

I'm not advocating authoring two extensions. I am tentatively
suggesting that we look at one extension for everything. That may well
be the least worst thing.

(Not that it's clear that you imagined I was, but I note it all the same).

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#199Joshua D. Drake
jd@commandprompt.com
In reply to: Josh Berkus (#183)
Re: jsonb and nested hstore

On 02/27/2014 05:54 PM, Josh Berkus wrote:

And it's not just that "broadly speaking most people would prefer
the interface to speak JSON"; it's that a JSONish interface for indexed
heirachical data is a Big Feature which will drive adoption among web
developers, and hstore2 without JSON support simply is not. At trade
shows and developer conferences, I get more questions about PostgreSQL's
JSON support than I do for any new feature since streaming replication.

Just to back this up. This is not anecdotal. I have multiple customers
performing very large development projects right now. Every single one
of them is interested in the pros/cons of using PostgreSQL and JSON.

JD

--
Command Prompt, Inc. - http://www.commandprompt.com/ 509-416-6579
PostgreSQL Support, Training, Professional Services and Development
High Availability, Oracle Conversion, Postgres-XC, @cmdpromptinc
For my dreams of your image that blossoms
a rose in the deeps of my heart. - W.B. Yeats

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#200Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#195)
Re: jsonb and nested hstore

On Feb 27, 2014, at 8:04 PM, Peter Geoghegan <pg@heroku.com> wrote:

I'm hearing a lot about how important jsonb is, but not much on how to
make the simple jsonb cases that are currently broken (as illustrated
by my earlier examples [1], [2]) work.

Surely, the answer is to define a jsonb || jsonb (and likely the other combinatorics of json and jsonb), along with the appropriate GIN and GiST interfaces for jsonb. Why would that not work?

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#201Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#200)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 8:23 PM, Christophe Pettus <xof@thebuild.com> wrote:

On Feb 27, 2014, at 8:04 PM, Peter Geoghegan <pg@heroku.com> wrote:

I'm hearing a lot about how important jsonb is, but not much on how to
make the simple jsonb cases that are currently broken (as illustrated
by my earlier examples [1], [2]) work.

Surely, the answer is to define a jsonb || jsonb (and likely the other combinatorics of json and jsonb), along with the appropriate GIN and GiST interfaces for jsonb. Why would that not work?

I'm not the one opposed to putting jsonb stuff in the hstore module!

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#202Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#201)
Re: jsonb and nested hstore

On Feb 27, 2014, at 8:31 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Feb 27, 2014 at 8:23 PM, Christophe Pettus <xof@thebuild.com> wrote:

Surely, the answer is to define a jsonb || jsonb (and likely the other combinatorics of json and jsonb), along with the appropriate GIN and GiST interfaces for jsonb. Why would that not work?

I'm not the one opposed to putting jsonb stuff in the hstore module!

My proposal is that we break the dependencies of jsonb (at least, at the user-visible level) on hstore2, thus allowing it in core successfully. jsonb || jsonb returning hstore seems like a bug to me, not a feature we should be supporting.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#203Craig Ringer
craig@2ndquadrant.com
In reply to: Christophe Pettus (#202)
Re: jsonb and nested hstore

On 02/28/2014 12:43 PM, Christophe Pettus wrote:

My proposal is that we break the dependencies of jsonb (at least, at the user-visible level) on hstore2, thus allowing it in core successfully. jsonb || jsonb returning hstore seems like a bug to me, not a feature we should be supporting.

Urgh, really?

That's not something I'd be excited to be stuck with into the future.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#204Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#202)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 8:43 PM, Christophe Pettus <xof@thebuild.com> wrote:

I'm not the one opposed to putting jsonb stuff in the hstore module!

My proposal is that we break the dependencies of jsonb (at least, at the user-visible level) on
hstore2, thus allowing it in core successfully. jsonb || jsonb returning hstore seems like a bug
to me, not a feature we should be supporting.

Of course it's a bug.

The only problem with that is now you have to move the implementation
of ||, plus a bunch of other hstore operators into core. That seems
like a more difficult direction to move in from a practical
perspective, and I'm not sure that you won't hit a snag elsewhere. But
you must do this in order to make what you describe work; obviously
you can't break jsonb's dependency on hstore if users must have hstore
installed to get a || operator. In short, jsonb and hstore are tied at
the hip (which I don't think is unreasonable), and if you insist on
having one in core, you almost need to have both there (with hstore
proper perhaps just consisting of stub functions and io routines).

I don't understand the aversion to putting jsonb in the hstore
extension. What's wrong with having the code live in an extension,
really? I suppose that putting it in core would be slightly preferable
given the strategic importance of jsonb, but it's not something that
I'd weigh too highly. Right now, I'm much more concerned about finding
*some* way of integrating jsonb that is broadly acceptable.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#205Christophe Pettus
xof@thebuild.com
In reply to: Craig Ringer (#203)
Re: jsonb and nested hstore

On Feb 27, 2014, at 9:12 PM, Craig Ringer <craig@2ndQuadrant.com> wrote:

On 02/28/2014 12:43 PM, Christophe Pettus wrote:

My proposal is that we break the dependencies of jsonb (at least, at the user-visible level) on hstore2, thus allowing it in core successfully. jsonb || jsonb returning hstore seems like a bug to me, not a feature we should be supporting.

Urgh, really?

That's not something I'd be excited to be stuck with into the future.

The reason that we're even here is that there's no jsonb || jsonb operator (or the other operators that one would expect).

If you try || without the hstore, you get an error, of course:

postgres=# select '{"foo":{"bar":"yellow"}}'::jsonb || '{}'::jsonb;
ERROR: operator does not exist: jsonb || jsonb
LINE 1: select '{"foo":{"bar":"yellow"}}'::jsonb || '{}'::jsonb;
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.

The reason it works with hstore installed is that there's an implicit cast from hstore to jsonb:

postgres=# create extension hstore;
CREATE EXTENSION
postgres=# select '{"foo":{"bar":"yellow"}}'::jsonb || '{}'::jsonb;
?column?
--------------------------
"foo"=>{"bar"=>"yellow"}
(1 row)

--

But I think we're piling broken on broken here. Just creating an appropriate jsonb || jsonb operator solves this problem. That seems the clear route forward.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#206Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#204)
Re: jsonb and nested hstore

On Feb 27, 2014, at 9:28 PM, Peter Geoghegan <pg@heroku.com> wrote:

The only problem with that is now you have to move the implementation
of ||, plus a bunch of other hstore operators into core. That seems
like a more difficult direction to move in from a practical
perspective, and I'm not sure that you won't hit a snag elsewhere.

Implementing operators for new types in PostgreSQL is pretty well-trod ground. I really don't know what snags we might hit.

I suppose that putting it in core would be slightly preferable
given the strategic importance of jsonb, but it's not something that
I'd weigh too highly.

I'm completely unsure how to parse the idea that something is strategically important but we shouldn't put it in core. If json was important enough to make it into core, jsonb certainly is.

Honestly, I really don't understand the resistance to putting jsonb in core. There are missing operators, yes; that's a very straight-forward hole to plug.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#207Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#206)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 9:35 PM, Christophe Pettus <xof@thebuild.com> wrote:

The only problem with that is now you have to move the implementation
of ||, plus a bunch of other hstore operators into core. That seems
like a more difficult direction to move in from a practical
perspective, and I'm not sure that you won't hit a snag elsewhere.

Implementing operators for new types in PostgreSQL is pretty well-trod ground. I really don't know what snags we might hit.

I don't find that very reassuring.

I suppose that putting it in core would be slightly preferable
given the strategic importance of jsonb, but it's not something that
I'd weigh too highly.

I'm completely unsure how to parse the idea that something is strategically important but we shouldn't put it in core. If json was important enough to make it into core, jsonb certainly is.

That is completely orthogonal to everything I've said. To be clear:
I'm not suggesting that we don't put jsonb in core because it's not
important enough - it has nothing to do with that whatsoever - and
besides, I don't understand why an extension is seen as not befitting
of a more important feature.

Honestly, I really don't understand the resistance to putting jsonb in core. There are missing operators, yes; that's a very straight-forward hole to plug.

You are basically suggesting putting all of hstore in core, because
jsonb and hstore are approximately the same thing. That seem quite a
bit more controversial than putting everything in the hstore
extension. I doubt that you can reasonably take any half measure
between those two extremes, and one seems a lot less controversial
than the other. This patch already seems controversial enough to me.
It's as simple as that.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#208Andres Freund
andres@2ndquadrant.com
In reply to: Peter Eisentraut (#192)
Re: jsonb and nested hstore

On 2014-02-27 22:10:22 -0500, Peter Eisentraut wrote:

On 2/26/14, 10:42 PM, Andrew Dunstan wrote:

Extensions can't call each other's code.

That's not necessarily so.

I don't think we have portable infrastructure to it properly yet,
without a detour via the fmgr. If I am wrong, what's the infrastructure?

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#209Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#207)
Re: jsonb and nested hstore

On Feb 27, 2014, at 9:59 PM, Peter Geoghegan <pg@heroku.com> wrote:

I don't find that very reassuring.

Obviously, we have to try it, and that will decide it.

I don't understand why an extension is seen as not befitting
of a more important feature.

contrib/ is considered a secondary set of features; I routinely get pushback from clients about using hstore because it's not in core, and they are thus suspicious of it. The educational project required to change that far exceeds any technical work we are talking about here.. There's a very large presentational difference between having a feature in contrib/ and in core, at the minimum, setting aside the technical issues (such as the extensions-calling-extensions problem).

We have an existence proof of this already: if there was absolutely no difference between having things being in contrib/ and being in core, full text search would still be in contrib/.

You are basically suggesting putting all of hstore in core, because
jsonb and hstore are approximately the same thing. That seem quite a
bit more controversial than putting everything in the hstore
extension.

Well, "controversy" is just a way of saying there are people who don't like the idea, and I get that. But I don't see the basis for the dislike.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#210Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Christophe Pettus (#209)
Re: jsonb and nested hstore

On 02/28/2014 09:02 AM, Christophe Pettus wrote:

contrib/ is considered a secondary set of features; I routinely get pushback from clients about using hstore because it's not in core, and they are thus suspicious of it. The educational project required to change that far exceeds any technical work we are talking about here.. There's a very large presentational difference between having a feature in contrib/ and in core, at the minimum, setting aside the technical issues (such as the extensions-calling-extensions problem).

We have an existence proof of this already: if there was absolutely no difference between having things being in contrib/ and being in core, full text search would still be in contrib/.

Although presentation was probably the main motivation for moving
full-text search into core, there was good technical reasons for that
too. Full-text search in contrib had a bunch of catalog-like tables to
store the dictionaries etc, and cumbersome functions to manipulate them.
When it was moved into core, we created new SQL commands for that stuff,
which is much clearer. The json doesn't have that; it would be well
suited to be an extension from technical point of view.

(This is not an opinion statement on what I think we should do. I
haven't been following the discussion, so I'm going to just whine
afterwards ;-) )

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#211Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#209)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 11:02 PM, Christophe Pettus <xof@thebuild.com> wrote:

On Feb 27, 2014, at 9:59 PM, Peter Geoghegan <pg@heroku.com> wrote:

I don't find that very reassuring.

Obviously, we have to try it, and that will decide it.

I don't think that's obvious at all. Anyone is free to spend their
time however they please, but personally I don't think that that's a
wise use of anyone's time.

contrib/ is considered a secondary set of features; I routinely get pushback from clients about using hstore because it's not in core, and they are thus suspicious of it. The educational project required to change that far exceeds any technical work we are talking about here.. There's a very large presentational difference between having a feature in contrib/ and in core, at the minimum, setting aside the technical issues (such as the extensions-calling-extensions problem).

There are no technical issues of any real consequence in this specific instance.

We have an existence proof of this already: if there was absolutely no difference between having things being in contrib/ and being in core, full text search would still be in contrib/.

I never said there was no difference, and whatever difference exists
varies considerably, as Heikki points out. I myself want to move
pg_stat_statements to core, for example, for exactly one very specific
reason: so that I can reserve a small amount of shared memory by
default so that it can be enabled without a restart at short notice.

You are basically suggesting putting all of hstore in core, because
jsonb and hstore are approximately the same thing. That seem quite a
bit more controversial than putting everything in the hstore
extension.

Well, "controversy" is just a way of saying there are people who don't like the idea, and I get that. But I don't see the basis for the dislike.

Yes, people who have the ability to block the feature entirely. I am
attempting to build consensus by reaching a compromise that weighs
everyone's concerns.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#212Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#211)
Re: jsonb and nested hstore

On Feb 27, 2014, at 11:15 PM, Peter Geoghegan <pg@heroku.com> wrote:

I don't think that's obvious at all. Anyone is free to spend their
time however they please, but personally I don't think that that's a
wise use of anyone's time.

I believe you are misunderstanding me. If there are actual technical problems or snags to migrating jsonb into core with full operator and index support, then the way we find out is to do the implementation, unless you know of a specific technical holdup already.

There are no technical issues of any real consequence in this specific instance.

There was no technical reason that json couldn't have been an extension, either, but there were very compelling presentational reasons to have it in core. jsonb has exactly the same presentational issues.

Yes, people who have the ability to block the feature entirely. I am
attempting to build consensus by reaching a compromise that weighs
everyone's concerns.

The thing I still haven't heard is why jsonb in core is a bad idea, except that it is too much code. Is that the objection?

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#213Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#212)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 11:36 PM, Christophe Pettus <xof@thebuild.com> wrote:

There was no technical reason that json couldn't have been an extension, either, but there were very compelling presentational reasons to have it in core. jsonb has exactly the same presentational issues.

There were also no compelling reasons why json should have been an
extension. The two situations are not at all comparable. In any case,
no author of this patch has proposed any solution to the casting
problems described with using the jsonb type with the new (and
existing) hstore operators. For that reason, I won't comment further
on this until I hear a more concrete proposal.

Yes, people who have the ability to block the feature entirely. I am
attempting to build consensus by reaching a compromise that weighs
everyone's concerns.

The thing I still haven't heard is why jsonb in core is a bad idea, except that it is too much code. Is that the objection?

I suspect that it's going to be considered odd to have code in core
that considers compatibility with earlier versions of hstore, back
when it was an extension, with calling stub functions, for one thing.
Having hstore be almost but not quite in core may be seen as a
contortion. Is that really the conversation you'd prefer to have at
this late stage? In any case, as I say, if that's the patch that
Andres or Oleg or Teodor really want to submit, then by all means let
them submit it. I maintain that the *current* state of affairs, where
jsonb isn't sure if it's in core or is an extension will not fly.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#214Andres Freund
andres@2ndquadrant.com
In reply to: Peter Geoghegan (#213)
Re: jsonb and nested hstore

On 2014-02-27 23:54:47 -0800, Peter Geoghegan wrote:

In any case, as I say, if that's the patch that Andres or Oleg or
Teodor really want to submit, then by all means let them submit it.

Just to make that clear, I am not one of the authors, I just did a
couple of light review passes.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#215Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#214)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 12:01 AM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-27 23:54:47 -0800, Peter Geoghegan wrote:

In any case, as I say, if that's the patch that Andres or Oleg or
Teodor really want to submit, then by all means let them submit it.

Just to make that clear, I am not one of the authors, I just did a
couple of light review passes.

Sorry, that was a typo. I meant Andrew.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#216Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#178)
Re: jsonb and nested hstore

On 2014-02-27 15:06:33 -0500, Andrew Dunstan wrote:

You realize that this API dates from 9.3 and has been used in numerous
extensions, right? So the names are pretty well fixed, for good or ill.

Sure. Doesn't prevent adding a couple more comments tho. I've only
noticed this because I opened the header as a reference when reading
your patch. Anyway, do something based on that feedback or not, your
choice ;)

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#217Thom Brown
thom@linux.com
In reply to: Andres Freund (#216)
Re: jsonb and nested hstore

On 28 February 2014 08:12, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-27 15:06:33 -0500, Andrew Dunstan wrote:

You realize that this API dates from 9.3 and has been used in numerous
extensions, right? So the names are pretty well fixed, for good or ill.

Sure. Doesn't prevent adding a couple more comments tho. I've only
noticed this because I opened the header as a reference when reading
your patch. Anyway, do something based on that feedback or not, your
choice ;)

Can I ask why I can do this:

SELECT review %> 'product'->'title' as product_title
FROM rating;

But I can't do this:

SELECT review->'product'->'title' as product_title
FROM rating;

ERROR: operator does not exist: hstore -> hstore
LINE 1: explain select review -> 'product'::hstore ->'title' as prod...

Yet I can do this:

SELECT review::json->'product'->'title' as product_title
FROM rating;

Yours oblivious,
--
Thom

#218Andrew Dunstan
andrew@dunslane.net
In reply to: Thom Brown (#217)
Re: jsonb and nested hstore

On 02/28/2014 07:19 AM, Thom Brown wrote:

On 28 February 2014 08:12, Andres Freund <andres@2ndquadrant.com
<mailto:andres@2ndquadrant.com>> wrote:

On 2014-02-27 15:06:33 -0500, Andrew Dunstan wrote:

You realize that this API dates from 9.3 and has been used in

numerous

extensions, right? So the names are pretty well fixed, for good

or ill.

Sure. Doesn't prevent adding a couple more comments tho. I've only
noticed this because I opened the header as a reference when reading
your patch. Anyway, do something based on that feedback or not, your
choice ;)

Can I ask why I can do this:

SELECT review %> 'product'->'title' as product_title
FROM rating;

But I can't do this:

SELECT review->'product'->'title' as product_title
FROM rating;

ERROR: operator does not exist: hstore -> hstore
LINE 1: explain select review -> 'product'::hstore ->'title' as prod...

Yet I can do this:

SELECT review::json->'product'->'title' as product_title
FROM rating;

I don't think this complaint has anything to do with the text you
quoted, so you've kinda hijacked the thread slightly.

But anyway, I think we've seen enough of these to conclude that the
casts from hstore to jsonb and back should not be implicit. I am fairly
confident that changing that would fix your complaint and the similar
one that Peter Geoghegan had.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#219Thom Brown
thom@linux.com
In reply to: Andrew Dunstan (#218)
Re: jsonb and nested hstore

On 28 February 2014 13:01, Andrew Dunstan <andrew@dunslane.net> wrote:

On 02/28/2014 07:19 AM, Thom Brown wrote:

On 28 February 2014 08:12, Andres Freund <andres@2ndquadrant.com <mailto:

andres@2ndquadrant.com>> wrote:

On 2014-02-27 15:06:33 -0500, Andrew Dunstan wrote:

You realize that this API dates from 9.3 and has been used in

numerous

extensions, right? So the names are pretty well fixed, for good

or ill.

Sure. Doesn't prevent adding a couple more comments tho. I've only
noticed this because I opened the header as a reference when reading
your patch. Anyway, do something based on that feedback or not, your
choice ;)

Can I ask why I can do this:

SELECT review %> 'product'->'title' as product_title
FROM rating;

But I can't do this:

SELECT review->'product'->'title' as product_title
FROM rating;

ERROR: operator does not exist: hstore -> hstore
LINE 1: explain select review -> 'product'::hstore ->'title' as prod...

Yet I can do this:

SELECT review::json->'product'->'title' as product_title
FROM rating;

I don't think this complaint has anything to do with the text you quoted,
so you've kinda hijacked the thread slightly.

Apologies. I'd just given the patches my first test-drive and replied to
the last message on the thread.

But anyway, I think we've seen enough of these to conclude that the casts
from hstore to jsonb and back should not be implicit. I am fairly confident
that changing that would fix your complaint and the similar one that Peter
Geoghegan had.

Thanks.
--
Thom

#220Stephen Frost
sfrost@snowman.net
In reply to: Peter Geoghegan (#198)
Re: jsonb and nested hstore

* Peter Geoghegan (pg@heroku.com) wrote:

On Thu, Feb 27, 2014 at 8:07 PM, Peter Geoghegan <pg@heroku.com> wrote:

I'm not advocating authoring two extensions. I am tentatively
suggesting that we look at one extension for everything. That may well
be the least worst thing.

(Not that it's clear that you imagined I was, but I note it all the same).

Thanks for that clarification- it was useful (for me anyway). For my
2c, while I agree that it would work, I'd still rather see this get into
core for reasons mentioned elsewhere but which I'll echo here- JSON has
become the de-facto data interexchange format on a rather massive scale-
it's become what XML was trying to be, in many ways by being simpler.

While I agree that the comparison to FTS isn't entirely fair, I also
feel that we should still be considering adding new types to core and
not try to push everything out as extensions. To add on to that- I feel
we still have a ways to go before our extension support will be really
*good* (which I certainly hope it to be some day) and I'd rather we not
force that on to the mobs of installations out there who will want
jsonb.

Thanks again,

Stephen

#221Robert Haas
robertmhaas@gmail.com
In reply to: Christophe Pettus (#184)
Re: jsonb and nested hstore

On Thu, Feb 27, 2014 at 8:55 PM, Christophe Pettus <xof@thebuild.com> wrote:

On Feb 27, 2014, at 5:31 PM, Peter Geoghegan <pg@heroku.com> wrote:

Now, it's confusing that it has to go through hstore, perhaps, but
that's hardly all that bad in and of itself.

Yes, it is. It strikes me as irrational to have jsonb depend on hstore. Let's be honest with ourselves: if we were starting over, we wouldn't start by creating our own proprietary hierarchical type and then making the hierarchical type everyone else uses depend on it. hstore exists because json didn't. But json does now, and we shouldn't create a jsonb dependency on hstore.

Right. I think this is one of the smartest things that anyone has
said on this thread. I don't have any objection to the idea of
enhancing hstore to support hierarchical data; I completely understand
the appeal of such a change. Nor do I have any objection to the idea
of a binary-json type in core (or out of core); there are obvious uses
for such a thing.

But what's happened here is not the sum of those two admirable
proposals. hstore has been augmented not only to support hierarchical
data but also with a notion of typed data that matches that of JSON
(except that I think the hstore and jsonb patches may have slightly
different notions as to what constitutes a valid number). The
internal format for jsonb has been contrived to match the
upward-compatible format designed for JSON. And thus jsonb depends on
hstore for the functionality that it isn't able to provide for itself.

Taken individually, none of those decisions seem crazy, but taken
together it's pretty weird. Instead of inventing a new type (jsonb)
designed from the ground up to do what we want, we're, well, we're
doing what Christophe says: creating our own proprietary hierarchical
type and then making the hierarchical type everyone else uses depend
on it. Described in those terms, it's hard for me to believe that
anyone here thinks that's not a strange thing to do.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#222Andrew Dunstan
andrew@dunslane.net
In reply to: Robert Haas (#221)
Re: jsonb and nested hstore

On 02/28/2014 09:27 AM, Robert Haas wrote:

On Thu, Feb 27, 2014 at 8:55 PM, Christophe Pettus <xof@thebuild.com> wrote:

On Feb 27, 2014, at 5:31 PM, Peter Geoghegan <pg@heroku.com> wrote:

Now, it's confusing that it has to go through hstore, perhaps, but
that's hardly all that bad in and of itself.

Yes, it is. It strikes me as irrational to have jsonb depend on hstore. Let's be honest with ourselves: if we were starting over, we wouldn't start by creating our own proprietary hierarchical type and then making the hierarchical type everyone else uses depend on it. hstore exists because json didn't. But json does now, and we shouldn't create a jsonb dependency on hstore.

Right. I think this is one of the smartest things that anyone has
said on this thread. I don't have any objection to the idea of
enhancing hstore to support hierarchical data; I completely understand
the appeal of such a change. Nor do I have any objection to the idea
of a binary-json type in core (or out of core); there are obvious uses
for such a thing.

But what's happened here is not the sum of those two admirable
proposals. hstore has been augmented not only to support hierarchical
data but also with a notion of typed data that matches that of JSON
(except that I think the hstore and jsonb patches may have slightly
different notions as to what constitutes a valid number). The
internal format for jsonb has been contrived to match the
upward-compatible format designed for JSON. And thus jsonb depends on
hstore for the functionality that it isn't able to provide for itself.

Taken individually, none of those decisions seem crazy, but taken
together it's pretty weird. Instead of inventing a new type (jsonb)
designed from the ground up to do what we want, we're, well, we're
doing what Christophe says: creating our own proprietary hierarchical
type and then making the hierarchical type everyone else uses depend
on it. Described in those terms, it's hard for me to believe that
anyone here thinks that's not a strange thing to do.

Well, the time to make these sorts of decisions would have been back in
November. The direction was clear then, if you were paying attention.
But right from the time this came up at pgcon the idea was to leverage
Oleg and Teodor's work. Nobody found it strange then. I'm rather
confused about why it's suddenly strange now.

What you're essentially arguing for is the invention of TWO binary
treeish things, without any argument I have yet seen advanced about why
the first one we have is good enough for hstore but not good enough for
jsonb.

Frankly, it looks to me like you have turned Christophe's argument on
its head, or completely misunderstood his point.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#223Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#221)
Re: jsonb and nested hstore

* Robert Haas (robertmhaas@gmail.com) wrote:

Taken individually, none of those decisions seem crazy, but taken
together it's pretty weird. Instead of inventing a new type (jsonb)
designed from the ground up to do what we want, we're, well, we're
doing what Christophe says: creating our own proprietary hierarchical
type and then making the hierarchical type everyone else uses depend
on it. Described in those terms, it's hard for me to believe that
anyone here thinks that's not a strange thing to do.

I was taking a slightly different perspective on it, though the devil is
almost certainly in the details. I'll be the first to admit that I've
not looked in detail at the patch either and so I've been trying to
avoid commenting on implementation specifics, but I was seeing this from
the perspective that we are building a single hierarchical typed data
store and then providing two interfaces to it. The way we're getting
there is a little awkward, in hindsight, and we'd like to have backwards
compatibility for one of the interfaces (and its on-disk storage), but
I'm not entirely sure that we'd actually end up in a different place
when we reach the end of this road.

Had we implemented jsonb first and then added hstore to it, would much
be different from the result we're getting here beyond the names of the
functions and the backwards-compatibility for the older on-disk format?
Are we really paying a high cost to support that older format?

The specific issues mentioned on this thread look more like bugs to be
addressed or additional operators which need to be implemented for
jsonb (imv, that should really be done for 9.4, but we have this
deadline looming...) along with perhaps dropping the implicit cast
between json and hstore (is there really a need for it..?).

Thanks,

Stephen

#224Joshua D. Drake
jd@commandprompt.com
In reply to: Christophe Pettus (#209)
Re: jsonb and nested hstore

On 02/27/2014 11:02 PM, Christophe Pettus wrote:

On Feb 27, 2014, at 9:59 PM, Peter Geoghegan <pg@heroku.com> wrote:

I don't find that very reassuring.

Obviously, we have to try it, and that will decide it.

I don't understand why an extension is seen as not befitting
of a more important feature.

contrib/ is considered a secondary set of features; I routinely get pushback from clients about using hstore because it's not in core, and they are thus suspicious of it. The educational project required to change that far exceeds any technical work we are talking about here.. There's a very large presentational difference between having a feature in contrib/ and in core, at the minimum, setting aside the technical issues (such as the extensions-calling-extensions problem).

We have an existence proof of this already: if there was absolutely no difference between having things being in contrib/ and being in core, full text search would still be in contrib/.

This is an old and currently false argument. It is true that once upon a
time, contrib was a banished heart, weeping for the attention of a true
prince. Now? Not so much. She is a full on passion flower with the
princes of all the kingdoms wanting her attention.

Joshua D. Drake

--
Command Prompt, Inc. - http://www.commandprompt.com/ 509-416-6579
PostgreSQL Support, Training, Professional Services and Development
High Availability, Oracle Conversion, Postgres-XC, @cmdpromptinc
For my dreams of your image that blossoms
a rose in the deeps of my heart. - W.B. Yeats

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#225Merlin Moncure
mmoncure@gmail.com
In reply to: Stephen Frost (#223)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 8:57 AM, Stephen Frost <sfrost@snowman.net> wrote:

The specific issues mentioned on this thread look more like bugs to be
addressed or additional operators which need to be implemented for
jsonb (imv, that should really be done for 9.4, but we have this
deadline looming...) along with perhaps dropping the implicit cast
between json and hstore (is there really a need for it..?).

Bugs/bad behaviors should be addressed (which AFAICT are mostly if not
all due to implicit casts). "Missing" operators OTOH are should not
hold up the patch, particuarly when the you have the option of an
explicit cast to hstore if you really want them.

Notwithstanding some of the commentary above, some of jsonb features
(in particular, the operators) are quite useful and should find
regular usage (json has them also, but jsonb removes the performance
penalty). The upshot is that with the current patch you have to do a
lot of casting to get 100% feature coverage and that future
improvements to jsonb will remove the necessity of that. Also the
hstore type will be required to do anything approximating the nosql
pattern.

I don't think the extension issue is a deal breaker either way. While
I have a preference for extensions generally, this is nothing personal
to jsonb. And if we can't come to a consensus on that point the patch
should be accepted on precedent (json being in core).

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#226Teodor Sigaev
teodor@sigaev.ru
In reply to: Andrew Dunstan (#158)
Re: jsonb and nested hstore

+ v.size += VARSIZE_ANY(v.numeric) +sizeof(JEntry) /* alignment */ ;

Why does + sizeof(JEntry) change anything about alignment? If it was
aligned before, adding a statically sized value doesn't give any new
guarantees about alignment?

Teodor, please comment.

Because numeric type will be copied into jsonb value. And we need to keep
alignment inside jsonb value. The same is true for nested jsonb (array or object).

+                type = JsonbIteratorGet(&it, &v, false);
+                if (type == WJB_VALUE)
+                {
+                    first = false;
+                    putEscapedValue(out, &v);
+                }
+                else
+                {
+                    Assert(type == WJB_BEGIN_OBJECT || type ==
WJB_BEGIN_ARRAY);
+                    /*
+                     * We need to rerun current switch() due to put
+                     * in current place object which we just got
+                     * from iterator.
+                     */

"due to put"?

I think that's due to the author not being a native English speaker. I've tried
to improve it a bit.

Teodor, please comment if you like.

Pls, fix my English. I mean if we got first element of array/object and it isn't
a scalar value we should do actions pointed by case
WJB_BEGIN_OBJECT/WJB_BEGIN_ARRAY in the same switch without calling iterator.

Teodor, please examine and comment on all comments below this point.

+JsonbValue *
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags,
+                                  uint32 *lowbound, JsonbValue *key)
+{

Functions like this *REALLY* need documentation for their
parameters. And of their actual purpose.

What's actually the uncompressed bit here? Isn't it actually the
contrary? This is navigating the compressed, non-tree form, no?

Functions returns value in JsonbValue form (uncompressed, not just a pointer).
For object it performs search by key and returns corresponding value, for array
- returns matched value. If lowbound is not null then it will be set into
array/object position of found value. And search will be started from *lowbound
position in array/object. That allows some optimizations.

Buffer is a pointer to header of jsonb value. After pointer there is an array of
JEntry and following list of values of array or key and values of object. This
is internal representation for jsonb or hstore without varlena header.
Nested array/object have the same representation. Flags points desired search -
in array or object. For example, If buffer contains array and flags has only
JB_FLAG_OBJECT then function returns NULL.

+    else if (flags & JB_FLAG_OBJECT & header)
+    {
+        JEntry       *array = (JEntry *) (buffer + sizeof(header));
+        char       *data = (char *) (array + (header & JB_COUNT_MASK) * 2);
+        uint32        stopLow = lowbound ? *lowbound : 0,
+                    stopHigh = (header & JB_COUNT_MASK),
+                    stopMiddle;

I don't understand what the point of the lowbound logic could be here?
If a key hasn't been found, it hasn't been found? Maybe the idea is to
use it when testing containedness or somesuch? Wouldn't iterating over
the keyspace be a better idea for that case?

If we has keys (a,b,c,d,e,f,g) and need to search keys e and f, then for second
search we could do in in subset of keys (f,g), we don't need to search in full
set of keys. The idea was introduced in hstoreFindKey() in hstore V2.

+ if (key->type != jbvString)
+ return NULL;

That's not allowed, right?

Right. it should be an Assert or ERROR.

+/*
+ * Get i-th value of array or hash. if i < 0 then it counts from
+ * the end of array/hash. Note: returns pointer to statically
+ * allocated JsonbValue.
+ */
+JsonbValue *
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+    uint32        header = *(uint32 *) buffer;
+    static JsonbValue r;

Really? And why on earth would static allocation be a good idea? Specify
it on the caller's stack if need be. Or even return by value, today's
calling convention will just allocate that on the caller's stack without
copying.
Accessing static data isn't even faster.

Just to prevent multiple palloc(). Could be changed, I don't insist. I saw
problems with a lot of small allocations but didn't see such problems with
static allocations.

+    if (JBE_ISSTRING(*e))
+    {
+        r.type = jbvString;
+        r.string.val = data + JBE_OFF(*e);
+        r.string.len = JBE_LEN(*e);
+        r.size = sizeof(JEntry) + r.string.len;
+    }
+    else if (JBE_ISBOOL(*e))
+    {
+        r.type = jbvBool;
+        r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+        r.size = sizeof(JEntry);
+    }
+    else if (JBE_ISNUMERIC(*e))
+    {
+        r.type = jbvNumeric;
+        r.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+
+        r.size = 2 * sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+    }
+    else if (JBE_ISNULL(*e))
+    {
+        r.type = jbvNull;
+        r.size = sizeof(JEntry);
+    }
+    else
+    {
+        r.type = jbvBinary;
+        r.binary.data = data + INTALIGN(JBE_OFF(*e));
+        r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+        r.size = r.binary.len + 2 * sizeof(JEntry);
+    }

This bit of code exists pretty similarly in several places, maybe consolitate?

findUncompressedJsonbValueByValue(), getJsonbValue() and formAnswer(). But one
has inconvenient difference with skipNested flag. Ok, will fix.

+/****************************************************************************
+ *                      Walk on tree representation of
jsonb                    *
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg,
uint32 level)
+{
+    int            i;

check stack limit.

+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+    if (v)
+        walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *                           Iteration over binary
jsonb                        *
+ ****************************************************************************/

This needs docs.

+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{

Why invent completely independent naming conventions to the previous
functions here?

Suggest it.

+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry * e, bool skipNested)
+{

Imaginatively undescriptive name. But if it were slightly more more
abstracted away from JsonbIterator it could be the answer to my prayers
above about removing redundant code.

formAnswerOfJsonbIteratorGet()?

+static JsonbIterator *
+up(JsonbIterator *it)
+{

Not a good name.

...

+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+    int            res;

recursive, stack depth check.

fixed.

+    switch ((*it)->type | (*it)->state)
+    {
+        case JB_FLAG_ARRAY | jbi_start:

I don't know, but I don't see the point in avoid if (), else if()
... constructs if it requires such dirty tricks.

Will be:
if ((*it)->type == JB_FLAG_ARRAY && (*it)->state == jbi_start)

A bit slower and I don't feel that switch is more worse. But I don't insist.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#227Christophe Pettus
xof@thebuild.com
In reply to: Robert Haas (#221)
Re: jsonb and nested hstore

On Feb 28, 2014, at 6:27 AM, Robert Haas <robertmhaas@gmail.com> wrote:

Taken individually, none of those decisions seem crazy, but taken
together it's pretty weird. Instead of inventing a new type (jsonb)
designed from the ground up to do what we want, we're, well, we're
doing what Christophe says: creating our own proprietary hierarchical
type and then making the hierarchical type everyone else uses depend
on it. Described in those terms, it's hard for me to believe that
anyone here thinks that's not a strange thing to do.

A lot of it is that we're getting really tied up in knots about terminology. Because of the history of the project, it's being approached as "jsonb depends on hstore2", rather than, "We need a binary format, BSON won't cut it, but hstore2 is creating one, so let's use the same for both to avoid duplication of effort."

Put that last way, it's a more sensible decision. My specific concern was "Well, if you want binary json, install hstore" is a very strange presentation to give to customers. Many of the user-facing objections can be solved just by removing the implicit cast from jsonb to hstore, and the remaining operators (if they don't make it into this patch) can be added over time.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#228Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/28/2014 07:39 AM, Joshua D. Drake wrote:

This is an old and currently false argument. It is true that once upon a
time, contrib was a banished heart, weeping for the attention of a true
prince. Now? Not so much. She is a full on passion flower with the
princes of all the kingdoms wanting her attention.

That's one of the more colorful metaphors ever posted on this list. I
don't think we've had language like that since Hitoshi stopped being
active. ;-)

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#229Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#218)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 5:01 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

But anyway, I think we've seen enough of these to conclude that the casts
from hstore to jsonb and back should not be implicit. I am fairly confident
that changing that would fix your complaint and the similar one that Peter
Geoghegan had.

Yes, it will, but I think that that will create more problems than it
will solve (which is not to suggest that an implicit cast is the right
thing). That will require that any non-trivial usage of jsonb requires
copious casting, where nested hstore does not. The hstore module
hardly contains some nice extras that a minority of jsonb users will
be interested in. It contains among other basic things, operator
classes required to index jsonb. All of my examples will still not
work, plus a bunch of cases that currently do work reasonably well.
There'll just be a different error message.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#230Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Geoghegan (#229)
Re: jsonb and nested hstore

Peter Geoghegan <pg@heroku.com> writes:

On Fri, Feb 28, 2014 at 5:01 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

But anyway, I think we've seen enough of these to conclude that the casts
from hstore to jsonb and back should not be implicit. I am fairly confident
that changing that would fix your complaint and the similar one that Peter
Geoghegan had.

Yes, it will, but I think that that will create more problems than it
will solve (which is not to suggest that an implicit cast is the right
thing). That will require that any non-trivial usage of jsonb requires
copious casting, where nested hstore does not. The hstore module
hardly contains some nice extras that a minority of jsonb users will
be interested in. It contains among other basic things, operator
classes required to index jsonb. All of my examples will still not
work, plus a bunch of cases that currently do work reasonably well.
There'll just be a different error message.

We should have learned by now that implicit casts are generally pretty
dangerous things. I think putting in implicit casts as a band-aid for
missing functionality is a horrid idea that we'll regret for a long
time to come. I gather from upthread comments that the patch currently
actually creates implicit casts in *both* directions? That's doubly
horrid/dangerous.

The more I read in this thread, the more I think that jsonb simply
isn't ready. We should put it off to 9.5 so that we can have a
complete implementation without so many rough edges. I'm afraid that
if we ship it as-is, backwards compatibility considerations are going
to prevent us from filing down the rough edges in future.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#231Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/28/2014 06:27 AM, Robert Haas wrote:

Taken individually, none of those decisions seem crazy, but taken
together it's pretty weird. Instead of inventing a new type (jsonb)
designed from the ground up to do what we want, we're, well, we're
doing what Christophe says: creating our own proprietary hierarchical
type and then making the hierarchical type everyone else uses depend
on it. Described in those terms, it's hard for me to believe that
anyone here thinks that's not a strange thing to do.

It certainly seems like a strange thing to do to *me*. However, Oleg
and Teodor were doing the heavy lifting on the heirarchical type -- we
wouldn't even be talking about jsonb without it -- and they were very
negative to JSON. As with many things, this reminds me of a story.

When the BART subway system was being built for the Bay Area in 1970,
the tunnel was planned to go straight under a particular hardware store
in Berkeley. The hardware store owner was convinced that the
construction would destroy his building and his business, and hit the
state with lawsuit after lawsuit to stop the construction. Eventually,
CALtrans caved and added a curve in that section of the BART tunnel to
go around the location of the hardware store. Forty years later, the
hardware store owner is dead, the hardware store is gone (was gone, in
fact, by 1978), but the curve is still there. And that curve forces
BART trains to slow down by 25mph in a spot which is fairly central to
the whole BART system, thus reducing the overall max capacity of the
entire subway system by 10-15%, and thus making thousands of people a
day late for work for the past 40 years.

I think Robert and Christophe are right: we're building a Berkeley BART
Curve. I think there's two courses of action from here which make sense:

A) We move *all* of the important HStore libraries and operators into
core, and make the hstore extension of them just a mapping of what are
essentially jsonb operators to the hstore type (Christophe's suggestion).

B) We make hstore/jsonb a single extension with two types and all of the
requisite operators etc. (Peter's suggestion).

Reasons for (A):

* In-core jsonb would have strongly enhanced adoption value
* jsonb is liable to become one of our most-used types and it would be
strange for it not to be in core
* binary JSON would "just work" for web developers
* the only reason nested hstore is an extension is because hstore was an
extension and we need an upgrade path
* This is essentially the decision we collectively made in November, for
fairly well-argued reasons, and what Andrew has spent 3 months implementing.

Reasons against (A):

* Having a core type and an extension share code is strange.
* Implicit casts between a core type and an extension could cause issues.

Reasons for (B):

* Conceptually simpler.
* Makes a certain degree of bugginess/unfinishedness more acceptable.

Reasons against (B):

* Users would get tripped up by "first, install the postgresql-contrib
package, then do CREATE EXTENSION hstore"
* As cited, many sysadmins block the install of the -contrib package.
* Performance issues for psycopg2 and other drivers which need to look
up type information on each call.
* This requires larger changes to the existing patch, which likely means
missing the bus for 9.4 (and you've seen my blog about that)

Here's the point in particular which makes me very hesitant to endorse
(B) as a solution:

* Once created as an extension, there is no path to ever making jsonb a
core type.

... as long as we have no way to ever move types between extensions and
core, any decision we make on where a type belongs is permanent. This
is one of the things which Andrew's proposal of a Type Registry last
year was intended to solve, but -hackers soundly rejected that proposal,
so we're currently stuck in the proverbial polluted estuary.

My cause, as everyone knows, is adoption. Given that, I'm pretty
strongly in favor of proposal (A); I think a jsonb type which "just
works" will drive twice the adoption that one you have to remember to
install does.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#232Greg Stark
stark@mit.edu
In reply to: Josh Berkus (#231)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 7:12 PM, Josh Berkus <josh@agliodbs.com> wrote:

* As cited, many sysadmins block the install of the -contrib package.

Of course the more you put things in core the more you make this logic
sound reasonable.

--
greg

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#233Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#230)
Re: jsonb and nested hstore

On 02/28/2014 02:00 PM, Tom Lane wrote:

Peter Geoghegan <pg@heroku.com> writes:

On Fri, Feb 28, 2014 at 5:01 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

But anyway, I think we've seen enough of these to conclude that the casts
from hstore to jsonb and back should not be implicit. I am fairly confident
that changing that would fix your complaint and the similar one that Peter
Geoghegan had.

Yes, it will, but I think that that will create more problems than it
will solve (which is not to suggest that an implicit cast is the right
thing). That will require that any non-trivial usage of jsonb requires
copious casting, where nested hstore does not. The hstore module
hardly contains some nice extras that a minority of jsonb users will
be interested in. It contains among other basic things, operator
classes required to index jsonb. All of my examples will still not
work, plus a bunch of cases that currently do work reasonably well.
There'll just be a different error message.

We should have learned by now that implicit casts are generally pretty
dangerous things. I think putting in implicit casts as a band-aid for
missing functionality is a horrid idea that we'll regret for a long
time to come. I gather from upthread comments that the patch currently
actually creates implicit casts in *both* directions? That's doubly
horrid/dangerous.

I agree. I have removed them in my current tree.

The more I read in this thread, the more I think that jsonb simply
isn't ready. We should put it off to 9.5 so that we can have a
complete implementation without so many rough edges. I'm afraid that
if we ship it as-is, backwards compatibility considerations are going
to prevent us from filing down the rough edges in future.

Well, the jsonb portion of this is arguably the most ready, certainly
it's had a lot more on-list review.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#234Tom Lane
tgl@sss.pgh.pa.us
In reply to: Josh Berkus (#231)
Re: jsonb and nested hstore

Josh Berkus <josh@agliodbs.com> writes:

... This requires larger changes to the existing patch, which likely means
missing the bus for 9.4 (and you've seen my blog about that)

Yeah. I realize you're gung-ho about getting jsonb into 9.4 in some
form, and I recognize that getting better JSON support is important.
But I wonder how carefully you've thought about the damage it'll do
if what ships in 9.4 is a weird, hard-to-use mishmash. I'd much
rather see us take the time to get it right than to ship something
that's basically a kluge. And having a core type that depends on
an extension for critical functionality is certainly nothing but a
kluge. As an example, you're arguing that some sysadmins won't permit
installation of contrib modules. (Let's pass over the question of
how true or sane that is.) If they won't allow hstore to be installed,
and jsonb is crippled in consequence, where does that put us for
adoption purposes? I'd argue that it's worse than not shipping jsonb
yet at all.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#235Peter Geoghegan
pg@heroku.com
In reply to: Josh Berkus (#231)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 11:12 AM, Josh Berkus <josh@agliodbs.com> wrote:

I think Robert and Christophe are right: we're building a Berkeley BART
Curve. I think there's two courses of action from here which make sense:

A) We move *all* of the important HStore libraries and operators into
core, and make the hstore extension of them just a mapping of what are
essentially jsonb operators to the hstore type (Christophe's suggestion).

B) We make hstore/jsonb a single extension with two types and all of the
requisite operators etc. (Peter's suggestion).

I agree with that dichotomy. I pointed this out a couple of times
already. I think the only reasonable way to deal with the casting
problems are to have parallel sets of operators and functions for
each, and to do that you really need one of those two things.

Reasons against (B):

* This requires larger changes to the existing patch, which likely means
missing the bus for 9.4 (and you've seen my blog about that)

This seems very dubious. I highly doubt it. A big part of the reason
why I favor (B) is because I think just the opposite. Tom's remarks
just now are consistent with that.

My cause, as everyone knows, is adoption. Given that, I'm pretty
strongly in favor of proposal (A); I think a jsonb type which "just
works" will drive twice the adoption that one you have to remember to
install does.

I don't think that's true. I used to work as a consultant, and I had a
number of fairly conservative clients. I don't ever recall there being
a restriction on installing a contrib package. If indeed any DBA does
operate under such a restrictive regime, then that's probably not the
kind of user that this feature is for anyway.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#236Andres Freund
andres@2ndquadrant.com
In reply to: Andrew Dunstan (#233)
Re: jsonb and nested hstore

On 2014-02-28 14:45:29 -0500, Andrew Dunstan wrote:

Well, the jsonb portion of this is arguably the most ready, certainly it's
had a lot more on-list review.

Having crossread both patches I tend to agree with this. I don't think
it's unrealistic to get jsonb committable, but the hstore bits are
another story.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#237Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#234)
Re: jsonb and nested hstore

On 02/28/2014 02:46 PM, Tom Lane wrote:

Josh Berkus <josh@agliodbs.com> writes:

... This requires larger changes to the existing patch, which likely means
missing the bus for 9.4 (and you've seen my blog about that)

Yeah. I realize you're gung-ho about getting jsonb into 9.4 in some
form, and I recognize that getting better JSON support is important.
But I wonder how carefully you've thought about the damage it'll do
if what ships in 9.4 is a weird, hard-to-use mishmash. I'd much
rather see us take the time to get it right than to ship something
that's basically a kluge. And having a core type that depends on
an extension for critical functionality is certainly nothing but a
kluge. As an example, you're arguing that some sysadmins won't permit
installation of contrib modules. (Let's pass over the question of
how true or sane that is.) If they won't allow hstore to be installed,
and jsonb is crippled in consequence, where does that put us for
adoption purposes? I'd argue that it's worse than not shipping jsonb
yet at all.

That hasn't been the way we've done things in the past. We're frequently
incremental. New features sometimes take several releases to mature.
Taking an example from close by, this will be the third release with
Json, and it's got a bunch of spiffy new stuff, but there's at least one
more round to go (what Merlin calls Manipulation functions), which I'm
rather hopeing someone other than me will see fit to implement.

As for what Peter suggests, I just can't bring myself to do anything
that would require people to say "Oh, you want jsonb? You have to load
hstore." It would be plain embarrassing.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#238Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#237)
Re: jsonb and nested hstore

Andrew Dunstan <andrew@dunslane.net> writes:

That hasn't been the way we've done things in the past. We're frequently
incremental. New features sometimes take several releases to mature.

That's perfectly fair. What I don't want to see is a user-visible
dependency from jsonb to hstore. I think that'll be a mess that will
take years to undo. I'd rather say "sorry, that functionality isn't
there yet for jsonb" than have such a dependency.

Maybe we're in violent agreement.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#239Merlin Moncure
mmoncure@gmail.com
In reply to: Andrew Dunstan (#233)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 1:45 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Well, the jsonb portion of this is arguably the most ready, certainly it's
had a lot more on-list review.

That is definitely true. Also, the jsonb type does not introduce any
new patterns that are not already covered by json -- it just does some
things better/faster (and, in a couple of cases, a bit differently) so
there's a safe harbor. The implicit casts snuck in after the review
started -- that was a mistake obviously (but mostly with hstore). The
side argument of 'to extension or not' is just that. Make a decision
and commit this thing.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#240Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 02/28/2014 11:19 AM, Greg Stark wrote:

On Fri, Feb 28, 2014 at 7:12 PM, Josh Berkus <josh@agliodbs.com> wrote:

* As cited, many sysadmins block the install of the -contrib package.

Of course the more you put things in core the more you make this logic
sound reasonable.

Touche'!

However, the problems with admins not wanting to install -contrib aren't
really about what's in or not in -contrib. They're about:

a) it's another package
b) they don't understand what's in it
c) it's called "contrib" which implies that these are
untested/unreviewed scripts, or somehow relates to hacking on Postgres,
both of which were true historically
d) there's some wierd/unstable dependancies for certain contrib modules
(UUID in particular)
e) some vendors don't make contrib available because of the encryption
thing (pgcrypto)

All of the above are worth fixing, but we don't have a proposal on the
table to do so.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#241Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#238)
Re: jsonb and nested hstore

On 02/28/2014 03:19 PM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

That hasn't been the way we've done things in the past. We're frequently
incremental. New features sometimes take several releases to mature.

That's perfectly fair. What I don't want to see is a user-visible
dependency from jsonb to hstore. I think that'll be a mess that will
take years to undo. I'd rather say "sorry, that functionality isn't
there yet for jsonb" than have such a dependency.

Maybe we're in violent agreement.

Maybe we are.

There's actually no real dependency. In fact, the dependency is the
other way. The jsonb patches I have been posting could be committed and
pass every regression test and we'd have useful better performance for
some operations. Every json function has an analog in jsonb except the
generator functions (to_json and friends), and they use the same parser
so they accept exactly the same input. The only "dependency" is if you
want to be able to use some advanced indexing and other functionality,
for which we don't currently have jsonb equivalents of the new hstore
operators, because we ran out of time. Then you can get this
functionality by casting the data to hstore (assuming we also have
nested-hstore committed) and using its operators. But that's no more a
dependency than it is for any other type for which you can leverage this
functionality (e.g. any record type).

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#242Christophe Pettus
xof@thebuild.com
In reply to: Josh Berkus (#240)
Re: jsonb and nested hstore

On Feb 28, 2014, at 1:03 PM, Josh Berkus <josh@agliodbs.com> wrote:

However, the problems with admins not wanting to install -contrib aren't
really about what's in or not in -contrib.

I'll also mention that an increasingly large number of people are running PostgreSQL in an environment where they don't get to pick what packages are installed on their server (RDS, for example). Telling them that something is in -contrib can very well be telling them "You can't have it" in those cases.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#243Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#242)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 1:31 PM, Christophe Pettus <xof@thebuild.com> wrote:

I'll also mention that an increasingly large number of people are running PostgreSQL in an environment where they don't get to pick what packages are installed on their server (RDS, for example). Telling them that something is in -contrib can very well be telling them "You can't have it" in those cases.

Amazon RDS Postgres has hstore.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#244Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#241)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 1:25 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

The only "dependency" is if you want to be able to use some advanced
indexing and other functionality, for which we don't currently have jsonb
equivalents of the new hstore operators, because we ran out of time. Then
you can get this functionality by casting the data to hstore (assuming we
also have nested-hstore committed) and using its operators. But that's no
more a dependency than it is for any other type for which you can leverage
this functionality (e.g. any record type).

I don't think hstore-style indexing is "advanced"; it's the main
reason for there being a jsonb, in my view. Anyway, this is where I
have a hard time understanding what you intend for jsonb for 9.4. You
ran out of time for writing jsonb operator classes, and so you can use
the hstore ones, which work fine. But, if we didn't run out of time,
how would the jsonb operator classes differ from the hstore ones? Is
there something inferior about the hstore operator class as compared
to a hypothetical jsonb operator class, other than the superficial
need to cast?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#245Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#244)
Re: jsonb and nested hstore

On Feb 28, 2014, at 1:35 PM, Peter Geoghegan <pg@heroku.com> wrote:

I don't think hstore-style indexing is "advanced"; it's the main
reason for there being a jsonb, in my view.

jsonb is significantly faster than json even without indexing; there are plenty of reasons to have jsonb even if we don't initially have indexing operations for it.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#246Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#243)
Re: jsonb and nested hstore

On Feb 28, 2014, at 1:34 PM, Peter Geoghegan <pg@heroku.com> wrote:

Amazon RDS Postgres has hstore.

Just observing that putting something in -contrib does not mean every installation can automatically adopt it.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#247Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#245)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 1:38 PM, Christophe Pettus <xof@thebuild.com> wrote:

jsonb is significantly faster than json even without indexing; there are plenty of reasons to have jsonb even if we don't initially have indexing operations for it.

That may be true, although I think that that's still very disappointing.

In order to make a rational decision to do the work incrementally, we
need to know what we're putting off until 9.5. AFAICT, we have these
operator classes that work fine with jsonb for the purposes of
hstore-style indexing (the hstore operator classes). Wasn't that the
point? When there is a dedicated set of jsonb operator classes, what
will be different about them, other than the fact that they won't be
hstore operator classes? A decision predicated on waiting for those to
come in 9.5 must consider what we're actually waiting for, and right
now that seems very hazy.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#248Christophe Pettus
xof@thebuild.com
In reply to: Peter Geoghegan (#247)
Re: jsonb and nested hstore

On Feb 28, 2014, at 2:12 PM, Peter Geoghegan <pg@heroku.com> wrote:

AFAICT, we have these
operator classes that work fine with jsonb for the purposes of
hstore-style indexing (the hstore operator classes).

That assumes that it is acceptable that jsonb be packaged in the hstore extension. To put it mildly, there's no consensus on that point; indeed, I think there's consensus that's a non-starter.

--
-- Christophe Pettus
xof@thebuild.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#249Peter Geoghegan
pg@heroku.com
In reply to: Christophe Pettus (#248)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 2:40 PM, Christophe Pettus <xof@thebuild.com> wrote:

On Feb 28, 2014, at 2:12 PM, Peter Geoghegan <pg@heroku.com> wrote:

AFAICT, we have these
operator classes that work fine with jsonb for the purposes of
hstore-style indexing (the hstore operator classes).

That assumes that it is acceptable that jsonb be packaged in the hstore extension. To put it mildly, there's no consensus on that point; indeed, I think there's consensus that's a non-starter.

No, it assumes nothing at all. It's a very simple question.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#250Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#236)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 2:01 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-28 14:45:29 -0500, Andrew Dunstan wrote:

Well, the jsonb portion of this is arguably the most ready, certainly it's
had a lot more on-list review.

Having crossread both patches I tend to agree with this. I don't think
it's unrealistic to get jsonb committable, but the hstore bits are
another story.

hm, do you have any specific concerns/objections about hstore?

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#251Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#250)
Re: jsonb and nested hstore

On 2014-03-03 08:57:59 -0600, Merlin Moncure wrote:

On Fri, Feb 28, 2014 at 2:01 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-28 14:45:29 -0500, Andrew Dunstan wrote:

Well, the jsonb portion of this is arguably the most ready, certainly it's
had a lot more on-list review.

Having crossread both patches I tend to agree with this. I don't think
it's unrealistic to get jsonb committable, but the hstore bits are
another story.

hm, do you have any specific concerns/objections about hstore?

I've listed a fair number in various emails, some have been addressed
since I think. But just take a look at the patch, at least last when I
looked, it was simply far from ready. And it's quite a bit of code, so
it's not something that can be addressed within 5min.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#252Oleg Bartunov
obartunov@gmail.com
In reply to: Andres Freund (#251)
Re: jsonb and nested hstore

Andres,

you can always look at our development repository:
https://github.com/feodor/postgres/tree/hstore - hstore only,
https://github.com/feodor/postgres/tree/jsonb_and_hstore - hstore with jsonb

Since we were concentrated on the jsonb_and_hstore branch we usually
wait Andrew, who publish patch. You last issues were addressed in
both branches.

Oleg

PS.

We are not native-english and may not well inderstand your criticism
well, but please try to be a little bit polite. We are working
together and our common goal is to make postgres better. Your notes
are very important for quality of postgres, but sometimes you drive us
...

On Mon, Mar 3, 2014 at 7:00 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-03-03 08:57:59 -0600, Merlin Moncure wrote:

On Fri, Feb 28, 2014 at 2:01 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-02-28 14:45:29 -0500, Andrew Dunstan wrote:

Well, the jsonb portion of this is arguably the most ready, certainly it's
had a lot more on-list review.

Having crossread both patches I tend to agree with this. I don't think
it's unrealistic to get jsonb committable, but the hstore bits are
another story.

hm, do you have any specific concerns/objections about hstore?

I've listed a fair number in various emails, some have been addressed
since I think. But just take a look at the patch, at least last when I
looked, it was simply far from ready. And it's quite a bit of code, so
it's not something that can be addressed within 5min.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#253Andres Freund
andres@2ndquadrant.com
In reply to: Oleg Bartunov (#252)
Re: jsonb and nested hstore

Hi Oleg,

On 2014-03-03 19:17:12 +0400, Oleg Bartunov wrote:

Since we were concentrated on the jsonb_and_hstore branch we usually
wait Andrew, who publish patch. You last issues were addressed in
both branches.

I'll try to have look sometime soon.

We are not native-english and may not well inderstand your criticism
well, but please try to be a little bit polite. We are working
together and our common goal is to make postgres better. Your notes
are very important for quality of postgres, but sometimes you drive us
...

I am sorry if I came over as impolite. I just tried to point at things I
thought needed improvement, and imo there were quite some. A patch
needing polishing isn't something that carries shame, blame or
anything. It's just a state a patch can be in.

Greetings,

Andres Freund

PS: Not a native speaker either...

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#254Oleg Bartunov
obartunov@gmail.com
In reply to: Andres Freund (#253)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 7:22 PM, Andres Freund <andres@2ndquadrant.com> wrote:

Hi Oleg,

On 2014-03-03 19:17:12 +0400, Oleg Bartunov wrote:

Since we were concentrated on the jsonb_and_hstore branch we usually
wait Andrew, who publish patch. You last issues were addressed in
both branches.

I'll try to have look sometime soon.

We are not native-english and may not well inderstand your criticism
well, but please try to be a little bit polite. We are working
together and our common goal is to make postgres better. Your notes
are very important for quality of postgres, but sometimes you drive us
...

I am sorry if I came over as impolite. I just tried to point at things I
thought needed improvement, and imo there were quite some. A patch
needing polishing isn't something that carries shame, blame or
anything. It's just a state a patch can be in.

We have not so much time to go deep onto 100th messages threads and sometimes
just lost directions.

Greetings,

Andres Freund

PS: Not a native speaker either...

That's explain all :)

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#255Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Oleg Bartunov (#254)
Re: jsonb and nested hstore

On 04/03/14 04:25, Oleg Bartunov wrote:

On Mon, Mar 3, 2014 at 7:22 PM, Andres Freund <andres@2ndquadrant.com> wrote:

[...]

PS: Not a native speaker either...
That's explain all :)

[...]

I AM a native English speaker born in England - though if you read some
of my postings where I've been particularly careless, you well assume
otherwise!

My Chinese wife sometimes corrects my English, and from time to time she
is right!

To the extent that I've read the postings of non-native English speakers
like Oleg & Andres, I've not noticed any difficulty understanding what
they meant - other than technical issues that would also be the same for
me from gifted native English speakers!

Cheers,
Gavin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#256Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#247)
Re: jsonb and nested hstore

On Fri, Feb 28, 2014 at 2:12 PM, Peter Geoghegan <pg@heroku.com> wrote:

In order to make a rational decision to do the work incrementally, we
need to know what we're putting off until 9.5. AFAICT, we have these
operator classes that work fine with jsonb for the purposes of
hstore-style indexing (the hstore operator classes). Wasn't that the
point? When there is a dedicated set of jsonb operator classes, what
will be different about them, other than the fact that they won't be
hstore operator classes? A decision predicated on waiting for those to
come in 9.5 must consider what we're actually waiting for, and right
now that seems very hazy.

I really would like an answer to this question. Even if I totally
agreed with Andrew's assessment of the relative importance of having
jsonb be an in-core type, versus having some more advanced indexing
capabilities right away, this is still a very salient question.

I appreciate that the "put jsonb in hstore extension to get indexing
right away" trade-off is counter-intuitive, and it may even be that
there is an "everybody wins" third way that sees us factor out code
that is common to both jsonb and hstore as it exists today (although
I'm not optimistic about that). I would like to emphasize that if you
want to defer working on hstore-style jsonb operator classes for one
release, I don't necessarily have a problem with that. But, I must
insist on an answer here, from either you or Oleg or Teodor (it isn't
apparent that Oleg and Teodor concur with you on what's important):
what did we run out of time for? What will be different about the
jsonb operator classes that you're asking us to wait for in a future
release?

I understand that there are ambitious plans for a VODKA-am that will
support indexing operations on nested structures that are a lot more
advanced than those enabled by the hstore operator classes included in
these patches. However, surely these hstore operator classes have
independent value, or represent incremental progress?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#257Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 03/03/2014 04:50 PM, Peter Geoghegan wrote:

I understand that there are ambitious plans for a VODKA-am that will
support indexing operations on nested structures that are a lot more
advanced than those enabled by the hstore operator classes included in
these patches. However, surely these hstore operator classes have
independent value, or represent incremental progress?

Primary value is that in theory the hstore2 opclasses are available
*now*, as opposed to a year from now.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#258Peter Geoghegan
pg@heroku.com
In reply to: Josh Berkus (#257)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 4:57 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 03/03/2014 04:50 PM, Peter Geoghegan wrote:

I understand that there are ambitious plans for a VODKA-am that will
support indexing operations on nested structures that are a lot more
advanced than those enabled by the hstore operator classes included in
these patches. However, surely these hstore operator classes have
independent value, or represent incremental progress?

Primary value is that in theory the hstore2 opclasses are available
*now*, as opposed to a year from now.

Well, yes, that's right. Although we cannot assume that VODKA will get
into 9.5 - it's a big project. Nor is it obvious to me that a
VODKA-ized set of operator classes would not bring with them exactly
the same dilemma as we now face vis-a-vis hstore code reuse and GIN
operator classes. So it seems reasonable to me to suppose that VODKA
should not influence our decision here. Please correct me if I'm
mistaken.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#259Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 03/03/2014 05:07 PM, Peter Geoghegan wrote:

Primary value is that in theory the hstore2 opclasses are available
*now*, as opposed to a year from now.

Well, yes, that's right. Although we cannot assume that VODKA will get
into 9.5 - it's a big project. Nor is it obvious to me that a
VODKA-ized set of operator classes would not bring with them exactly
the same dilemma as we now face vis-a-vis hstore code reuse and GIN
operator classes. So it seems reasonable to me to suppose that VODKA
should not influence our decision here. Please correct me if I'm
mistaken.

I would agree with you.

Andrew was onsite with a client over the weekend, which is why you
haven't heard from him on this thread.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#260Peter Geoghegan
pg@heroku.com
In reply to: Josh Berkus (#259)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 5:09 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 03/03/2014 05:07 PM, Peter Geoghegan wrote:

Primary value is that in theory the hstore2 opclasses are available
*now*, as opposed to a year from now.

Well, yes, that's right. Although we cannot assume that VODKA will get
into 9.5 - it's a big project. Nor is it obvious to me that a
VODKA-ized set of operator classes would not bring with them exactly
the same dilemma as we now face vis-a-vis hstore code reuse and GIN
operator classes. So it seems reasonable to me to suppose that VODKA
should not influence our decision here. Please correct me if I'm
mistaken.

I would agree with you.

Good. Hopefully you also mean that you recognize the dilemma referred
to above - that the hstore code reuse made a certain amount of sense,
and that more than likely the best way forward is to work out a way to
make it work. I'm not immediately all that concerned about what the
least worst way of doing that is (I just favor putting everything in
hstore on practical grounds). Another way to solve this problem might
be to simply live with the code duplication between core and hstore on
the grounds that hstore will eventually be deprecated as people switch
to jsonb over time (so under that regime nothing new would ever be
added to hstore, which we'd eventually remove from contrib entirely,
while putting everything new here in core). I don't favor that
approach, but it wouldn't be totally unreasonable, based on the
importance that is attached to jsonb, and based on what I'd estimate
to be the actual amount of code redundancy that that would create
(assuming that hstore gets no new functions and operators, since an
awful lot of the hstore-local code after this patch is applied is new
to hstore). I wouldn't stand in the way of this approach.

In my view the most important thing right now is that before anything
is committed, at the very least there needs to be a strategy around
getting hstore-style GIN indexing in jsonb. I cannot understand how
you can have an operator class today that works fine for hstore-style
indexing of jsonb (as far as that goes), but that that code is out of
bounds just because it's nominally (mostly new) hstore code, and you
cannot figure out a way of making that work that is acceptable from a
code maintenance perspective. If you cannot figure that out in a few
days, why should you ever be able to figure it out? We need to bite
the bullet here, whatever that might actually entail. Can we agree on
that much?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#261Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#256)
Re: jsonb and nested hstore

On 03/03/2014 07:50 PM, Peter Geoghegan wrote:

On Fri, Feb 28, 2014 at 2:12 PM, Peter Geoghegan <pg@heroku.com> wrote:

In order to make a rational decision to do the work incrementally, we
need to know what we're putting off until 9.5. AFAICT, we have these
operator classes that work fine with jsonb for the purposes of
hstore-style indexing (the hstore operator classes). Wasn't that the
point? When there is a dedicated set of jsonb operator classes, what
will be different about them, other than the fact that they won't be
hstore operator classes? A decision predicated on waiting for those to
come in 9.5 must consider what we're actually waiting for, and right
now that seems very hazy.

I really would like an answer to this question. Even if I totally
agreed with Andrew's assessment of the relative importance of having
jsonb be an in-core type, versus having some more advanced indexing
capabilities right away, this is still a very salient question.

(Taking a break from some frantic customer work)

My aim for 9.4, given constraints of both the development cycle and my
time budget, has been to get jsonb to a point where it has equivalent
functionality to json, so that nobody is forced to say "well I'll have
to use json because it lacks function x." For the processing functions,
i.e. those that don't generate json from non-json, this should be true
with what's proposed. The jsonb processing functions should be about as
fast as, or in some cases significantly faster than, their json
equivalents. Parsing text input takes a little longer (surprisingly
little, actually), and reserializing takes significantly longer - I
haven't had a chance to look and see if we can improve that. Both of
these are more or less expected results.

For 9.5 I would hope that we have at least the equivalent of the
proposed hstore classes. But that's really just a start. Frankly, I
think we need to think a lot harder about how we want to be able to
index this sort of data. The proposed hstore operators appear to me to
be at best just scratching the surface of that. I'd like to be able to
index jsonb's #> and #>> operators, for example. Unanchored subpath
operators could be an area that's interesting to implement and index.

I also would like to see some basic insert/update/delete/merge operators
for json/jsonb - that's an area I wanted to work on for this lease but
wasn't able to arrange.

Note that future developments is a major topic of my pgcon talk, and I'm
hoping that we can get some good discussion going there.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#262Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 03/03/2014 06:17 PM, Peter Geoghegan wrote:

Good. Hopefully you also mean that you recognize the dilemma referred
to above - that the hstore code reuse made a certain amount of sense,
and that more than likely the best way forward is to work out a way to
make it work. I'm not immediately all that concerned about what the
least worst way of doing that is (I just favor putting everything in
hstore on practical grounds).

Well, I don't see how this is "on practical grounds" at this point.
Whether we keep jsonb in core or not, we have an equal amount of work
ahead of us. That's why I said the single-extension approach was
"conceptually simpler" rather than "actually simpler". It's easier to
understand, not necessarily easier to implement at this point.

Also, please recognize that the current implementation was what we
collectively decided on three months ago, and what Andrew worked pretty
hard to implement based on that collective decision. So if we're going
to change course, we need a specific reason to change course, not just
"it seems like a better idea now" or "I wasn't paying attention then".

The "one extension to rule them all" approach also has the issue of
Naming Things, but I think that could be solved with a symlink or two.

Another way to solve this problem might
be to simply live with the code duplication between core and hstore on

What code duplication?

the grounds that hstore will eventually be deprecated as people switch
to jsonb over time (so under that regime nothing new would ever be
added to hstore, which we'd eventually remove from contrib entirely,
while putting everything new here in core). I don't favor that
approach, but it wouldn't be totally unreasonable, based on the
importance that is attached to jsonb, and based on what I'd estimate
to be the actual amount of code redundancy that that would create
(assuming that hstore gets no new functions and operators, since an
awful lot of the hstore-local code after this patch is applied is new
to hstore). I wouldn't stand in the way of this approach.

Realistically, hstore will never go away. I'll bet you a round or two
of pints that, if we get both hstore2 and jsonb, within 2 years the
users of jsonb will be an order of magnitude more numerous that then
users of hstore, but folks out there have apps already built against
hstore1 and they're going to keep on the hstore path.

In the discussion you haven't apparently caught up on yet, we did
discuss moving *hstore* into core to make this whole thing easier.
However, that fell afoul of the fact that we currently have no mechanism
to move types between extensions and core without breaking upgrades for
everyone. So the only reason hstore is still an extension is because of
backwards-compatibility.

In my view the most important thing right now is that before anything
is committed, at the very least there needs to be a strategy around
getting hstore-style GIN indexing in jsonb.

I think it's a good idea to have a strategy.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#263Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#261)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 6:54 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

My aim for 9.4, given constraints of both the development cycle and my time
budget, has been to get jsonb to a point where it has equivalent
functionality to json, so that nobody is forced to say "well I'll have to
use json because it lacks function x." For the processing functions, i.e.
those that don't generate json from non-json, this should be true with
what's proposed. The jsonb processing functions should be about as fast as,
or in some cases significantly faster than, their json equivalents. Parsing
text input takes a little longer (surprisingly little, actually), and
reserializing takes significantly longer - I haven't had a chance to look
and see if we can improve that. Both of these are more or less expected
results.

Okay, that's fine. I'm sure that jsonb has some value without
hstore-style indexing. That isn't really in question. What is in
question is why you would choose to give up on those capabilities.

For 9.5 I would hope that we have at least the equivalent of the proposed
hstore classes.

But the equivalent code to the proposed hstore operator classes is
*exactly the same* C code. The two types are fully binary coercible in
the patch, so why delay? Why is that additional step appreciably
riskier than adopting jsonb? I don't see why the functions associated
with the operators that comprise, say, the gin_hstore_ops operator
class represent much additional risk, assuming that jsonb is itself in
good shape. For example, the new hstore_contains() looks fairly
innocuous compared to much of the code you are apparently intent on
including in the first cut at jsonb. Have I missed something? Why are
those operators riskier than the operators you are intent on
including?

If it is true that you think that's a significant additional risk, a
risk too far, then it makes sense that you'd defer doing this. I would
like to know why that is, though, since I don't see it. Speaking of
missing operator classes, I'm pretty sure that it's ipso facto
unacceptable that there is no default btree operator class for the
type jsonb:

[local]/postgres=# \d+ bar
Table "public.bar"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-------+-----------+----------+--------------+-------------
i | jsonb | | extended | |
Has OIDs: no

[local]/postgres=# select * from bar order by i;
ERROR: 42883: could not identify an ordering operator for type jsonb
LINE 1: select * from bar order by i;
^
HINT: Use an explicit ordering operator or modify the query.
LOCATION: get_sort_group_operators, parse_oper.c:221
Time: 6.424 ms
[local]/postgres=# select distinct i from bar;
ERROR: 42883: could not identify an equality operator for type jsonb
LINE 1: select distinct i from bar;
^
LOCATION: get_sort_group_operators, parse_oper.c:226
Time: 6.457 ms

But that's really just a start. Frankly, I think we need to
think a lot harder about how we want to be able to index this sort of data.
The proposed hstore operators appear to me to be at best just scratching the
surface of that. I'd like to be able to index jsonb's #> and #>> operators,
for example. Unanchored subpath operators could be an area that's
interesting to implement and index.

I'm sure that's true, but it's not our immediate concern. We need to
think very hard about it to get everything we want, but we also need
to think somewhat harder about it in order to get even a basic jsonb
type committed.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#264Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#263)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 7:39 PM, Peter Geoghegan <pg@heroku.com> wrote:

But that's really just a start. Frankly, I think we need to
think a lot harder about how we want to be able to index this sort of data.
The proposed hstore operators appear to me to be at best just scratching the
surface of that. I'd like to be able to index jsonb's #> and #>> operators,
for example. Unanchored subpath operators could be an area that's
interesting to implement and index.

I'm sure that's true, but it's not our immediate concern. We need to
think very hard about it to get everything we want, but we also need
to think somewhat harder about it in order to get even a basic jsonb
type committed.

By the way, I think it would be fine to defer adding many of the new
hstore operators and functions until 9.5 (as hstore infrastructure, or
in-core jsonb infrastructure, or anything else), if you felt you had
to, provided that you included just those sufficient to create jsonb
operator classes (plus the operator classes themselves, of course).
There is absolutely no question about having to do this for
B-Tree...why not go a couple of operator classes further?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#265Peter Geoghegan
pg@heroku.com
In reply to: Josh Berkus (#262)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 6:59 PM, Josh Berkus <josh@agliodbs.com> wrote:

Also, please recognize that the current implementation was what we
collectively decided on three months ago, and what Andrew worked pretty
hard to implement based on that collective decision. So if we're going
to change course, we need a specific reason to change course, not just
"it seems like a better idea now" or "I wasn't paying attention then".

I'm pretty sure it doesn't work like that. But if it does, what
exactly am I insisting on that is inconsistent with that consensus? In
what way are we changing course? I think I'm being eminently flexible.
I don't want a jsonb type that is broken, as for example by not having
a default B-Tree operator class. Why don't you let me get on with it?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#266Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#263)
Re: jsonb and nested hstore

On 03/03/2014 10:39 PM, Peter Geoghegan wrote:

On Mon, Mar 3, 2014 at 6:54 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

My aim for 9.4, given constraints of both the development cycle and my time
budget, has been to get jsonb to a point where it has equivalent
functionality to json, so that nobody is forced to say "well I'll have to
use json because it lacks function x." For the processing functions, i.e.
those that don't generate json from non-json, this should be true with
what's proposed. The jsonb processing functions should be about as fast as,
or in some cases significantly faster than, their json equivalents. Parsing
text input takes a little longer (surprisingly little, actually), and
reserializing takes significantly longer - I haven't had a chance to look
and see if we can improve that. Both of these are more or less expected
results.

Okay, that's fine. I'm sure that jsonb has some value without
hstore-style indexing. That isn't really in question. What is in
question is why you would choose to give up on those capabilities.

Who has given up?

I did as much as I could given the time constraints I mentioned. That's
the way Postgres works. People do what they can.

For 9.5 I would hope that we have at least the equivalent of the proposed
hstore classes.

But the equivalent code to the proposed hstore operator classes is
*exactly the same* C code. The two types are fully binary coercible in
the patch, so why delay? Why is that additional step appreciably
riskier than adopting jsonb? I don't see why the functions associated
with the operators that comprise, say, the gin_hstore_ops operator
class represent much additional risk, assuming that jsonb is itself in
good shape. For example, the new hstore_contains() looks fairly
innocuous compared to much of the code you are apparently intent on
including in the first cut at jsonb. Have I missed something? Why are
those operators riskier than the operators you are intent on
including?

You are really jumping at conclusions as to what's in my head,
conclusions that are not justified by anything I have said.

Who said they were riskier? I certainly didn't.

Of course the operators would be the same. We could have them today, by
migrating the exisiting code into core and making the hstore operators
use that code instead. I could probably do it in about a day (if I had a
day to spare). I was actually rather expecting that they would have been
put there for the jsonb type when Teodor moved some code so we could
have a jsonb type. But since he didn't we find ourselves where we are today.

If that's what it will take to get agreement I will try to make it happen.

If it is true that you think that's a significant additional risk, a
risk too far, then it makes sense that you'd defer doing this. I would
like to know why that is, though, since I don't see it.

I don't, as I said. This whole line of speculation has me quite puzzled.

Speaking of
missing operator classes, I'm pretty sure that it's ipso facto
unacceptable that there is no default btree operator class for the
type jsonb:

[local]/postgres=# \d+ bar
Table "public.bar"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-------+-----------+----------+--------------+-------------
i | jsonb | | extended | |
Has OIDs: no

[local]/postgres=# select * from bar order by i;
ERROR: 42883: could not identify an ordering operator for type jsonb
LINE 1: select * from bar order by i;
^
HINT: Use an explicit ordering operator or modify the query.
LOCATION: get_sort_group_operators, parse_oper.c:221
Time: 6.424 ms
[local]/postgres=# select distinct i from bar;
ERROR: 42883: could not identify an equality operator for type jsonb
LINE 1: select distinct i from bar;
^
LOCATION: get_sort_group_operators, parse_oper.c:226
Time: 6.457 ms

Well, the trouble is that the only one that would make sense is one that
did in effect "order by i::json", since it would be weird to have these
different. That might make the ordering slow, but would be easy enough
to add.

But that's really just a start. Frankly, I think we need to
think a lot harder about how we want to be able to index this sort of data.
The proposed hstore operators appear to me to be at best just scratching the
surface of that. I'd like to be able to index jsonb's #> and #>> operators,
for example. Unanchored subpath operators could be an area that's
interesting to implement and index.

I'm sure that's true, but it's not our immediate concern. We need to
think very hard about it to get everything we want, but we also need
to think somewhat harder about it in order to get even a basic jsonb
type committed.

I think you need to be more accepting of the fact that Postgres
development is frequently incremental. Nothing that's been proposed
would prevent future development of the type AFAICT. Enums took us two
or three releases to get to where we are. Arrays took longer. Even a
smallish feature like CSV import is still being tweaked about seven
releases after it was introduced.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#267Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#265)
Re: jsonb and nested hstore

On 03/03/2014 11:20 PM, Peter Geoghegan wrote:

On Mon, Mar 3, 2014 at 6:59 PM, Josh Berkus <josh@agliodbs.com> wrote:

Also, please recognize that the current implementation was what we
collectively decided on three months ago, and what Andrew worked pretty
hard to implement based on that collective decision. So if we're going
to change course, we need a specific reason to change course, not just
"it seems like a better idea now" or "I wasn't paying attention then".

I'm pretty sure it doesn't work like that. But if it does, what
exactly am I insisting on that is inconsistent with that consensus? In
what way are we changing course? I think I'm being eminently flexible.
I don't want a jsonb type that is broken, as for example by not having
a default B-Tree operator class. Why don't you let me get on with it?

You're welcome to submit any code you like. We haven't been secret about
where the code lives. Nobody is stopping you.

What you're not welcome to do, from my POV, is move jsonb into the
hstore extension. I strenuously object to any such plan.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#268Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#267)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 9:05 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

What you're not welcome to do, from my POV, is move jsonb into the hstore
extension. I strenuously object to any such plan.

We both know that that isn't really the point of contention at all.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#269Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#266)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 8:59 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

Okay, that's fine. I'm sure that jsonb has some value without
hstore-style indexing. That isn't really in question. What is in
question is why you would choose to give up on those capabilities.

Who has given up?

I did as much as I could given the time constraints I mentioned. That's the
way Postgres works. People do what they can.

It would be such a small amount of additional effort to add those
operators sufficient to make those operator classes work, relative to
the huge benefits. The code already exists, there isn't terribly much
of it, and is isn't scarier than what you already have. I cannot
fathom how you could choose to not do so, as long as you wanted a
jsonb type in core. It is just not sensible.

But the equivalent code to the proposed hstore operator classes is
*exactly the same* C code. The two types are fully binary coercible in
the patch, so why delay? Why is that additional step appreciably
riskier than adopting jsonb? I don't see why the functions associated
with the operators that comprise, say, the gin_hstore_ops operator
class represent much additional risk, assuming that jsonb is itself in
good shape. For example, the new hstore_contains() looks fairly
innocuous compared to much of the code you are apparently intent on
including in the first cut at jsonb. Have I missed something? Why are
those operators riskier than the operators you are intent on
including?

You are really jumping at conclusions as to what's in my head, conclusions
that are not justified by anything I have said.

Right - they are conclusions justified by what you have not said, and
my attempt to fill in the gaps.

Who said they were riskier? I certainly didn't.

Of course the operators would be the same. We could have them today, by
migrating the exisiting code into core and making the hstore operators use
that code instead. I could probably do it in about a day (if I had a day to
spare). I was actually rather expecting that they would have been put there
for the jsonb type when Teodor moved some code so we could have a jsonb
type. But since he didn't we find ourselves where we are today.

If that's what it will take to get agreement I will try to make it happen.

I'll do it if you really are that strapped for time.

Well, the trouble is that the only one that would make sense is one that did
in effect "order by i::json", since it would be weird to have these
different. That might make the ordering slow, but would be easy enough to
add.

No, it would not be weird to have those be different. In some cases it
would be totally mandatory, as for example when there is a variable
lc_numeric setting that affects the format of numeric. Only text
equality is equivalent to a binary string comparison.

I think you need to be more accepting of the fact that Postgres development
is frequently incremental. Nothing that's been proposed would prevent future
development of the type AFAICT. Enums took us two or three releases to get
to where we are. Arrays took longer. Even a smallish feature like CSV import
is still being tweaked about seven releases after it was introduced.

My objection is that the cost/benefit analysis behind the idea of
excluding the hstore operator classes seem to make no sense.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#270Peter Geoghegan
pg@heroku.com
In reply to: Oleg Bartunov (#252)
Re: jsonb and nested hstore

Hi Oleg,

On Mon, Mar 3, 2014 at 7:17 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

you can always look at our development repository:

I think I found a bug:

[local]/postgres=# \d+ bar
Table "public.bar"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-------+-----------+----------+--------------+-------------
i | jsonb | | extended | |
Indexes:
"f" gin (i)
Has OIDs: no

[local]/postgres=# insert into bar values ('{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}');
INSERT 0 1
Time: 7.635 ms
[local]/postgres=# select * from bar where i @> '{"age":25.0}'::jsonb;
i
---
(0 rows)

Time: 2.443 ms
[local]/postgres=# explain select * from bar where i @> '{"age":25.0}'::jsonb;
QUERY PLAN
-----------------------------------------------------------------
Bitmap Heap Scan on bar (cost=16.01..20.02 rows=1 width=32)
Recheck Cond: ((i)::hstore @> '"age"=>25.0'::hstore)
-> Bitmap Index Scan on f (cost=0.00..16.01 rows=1 width=0)
Index Cond: ((i)::hstore @> '"age"=>25.0'::hstore)
Planning time: 0.161 ms
(5 rows)

[local]/postgres=# set enable_bitmapscan = off;
SET
Time: 6.052 ms
[local]/postgres=# select * from bar where i @> '{"age":25.0}'::jsonb;
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
i | {"age": 25, "address": {"city": "New York", "state": "NY",
"postalCode": 10021, "streetAddress": "21 2nd Street"}, "lastName":
"Smith", "firstName": "John", "phoneNumbers": [{"type": "home",
"number": "212 555-1234"}, {"type": "fax", "number": "646 555-4567"}]}

Time: 6.479 ms
[local]/postgres=# explain select * from bar where i @> '{"age":25.0}'::jsonb;
QUERY PLAN
-----------------------------------------------------
Seq Scan on bar (cost=0.00..26.38 rows=1 width=32)
Filter: ((i)::hstore @> '"age"=>25.0'::hstore)
Planning time: 0.154 ms
(3 rows)

Time: 6.565 ms

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#271Oleg Bartunov
obartunov@gmail.com
In reply to: Peter Geoghegan (#270)
Re: jsonb and nested hstore

Thanks, looks like a bug.

On Tue, Mar 4, 2014 at 12:38 PM, Peter Geoghegan <pg@heroku.com> wrote:

Hi Oleg,

On Mon, Mar 3, 2014 at 7:17 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

you can always look at our development repository:

I think I found a bug:

[local]/postgres=# \d+ bar
Table "public.bar"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-------+-----------+----------+--------------+-------------
i | jsonb | | extended | |
Indexes:
"f" gin (i)
Has OIDs: no

[local]/postgres=# insert into bar values ('{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}');
INSERT 0 1
Time: 7.635 ms
[local]/postgres=# select * from bar where i @> '{"age":25.0}'::jsonb;
i
---
(0 rows)

Time: 2.443 ms
[local]/postgres=# explain select * from bar where i @> '{"age":25.0}'::jsonb;
QUERY PLAN
-----------------------------------------------------------------
Bitmap Heap Scan on bar (cost=16.01..20.02 rows=1 width=32)
Recheck Cond: ((i)::hstore @> '"age"=>25.0'::hstore)
-> Bitmap Index Scan on f (cost=0.00..16.01 rows=1 width=0)
Index Cond: ((i)::hstore @> '"age"=>25.0'::hstore)
Planning time: 0.161 ms
(5 rows)

[local]/postgres=# set enable_bitmapscan = off;
SET
Time: 6.052 ms
[local]/postgres=# select * from bar where i @> '{"age":25.0}'::jsonb;
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
i | {"age": 25, "address": {"city": "New York", "state": "NY",
"postalCode": 10021, "streetAddress": "21 2nd Street"}, "lastName":
"Smith", "firstName": "John", "phoneNumbers": [{"type": "home",
"number": "212 555-1234"}, {"type": "fax", "number": "646 555-4567"}]}

Time: 6.479 ms
[local]/postgres=# explain select * from bar where i @> '{"age":25.0}'::jsonb;
QUERY PLAN
-----------------------------------------------------
Seq Scan on bar (cost=0.00..26.38 rows=1 width=32)
Filter: ((i)::hstore @> '"age"=>25.0'::hstore)
Planning time: 0.154 ms
(3 rows)

Time: 6.565 ms

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#272Peter Geoghegan
pg@heroku.com
In reply to: Oleg Bartunov (#271)
Re: jsonb and nested hstore

On Tue, Mar 4, 2014 at 1:30 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

Thanks, looks like a bug.

I guess this is down to the continued definition of gin_hstore_ops as
an opclass with text storage?:

+ CREATE OPERATOR CLASS gin_hstore_ops
+ DEFAULT FOR TYPE hstore USING gin
+ AS
+ 	OPERATOR        7       @>,
+ 	OPERATOR        9       ?(hstore,text),
+ 	OPERATOR        10      ?|(hstore,text[]),
+ 	OPERATOR        11      ?&(hstore,text[]),
+ 	FUNCTION        1       bttextcmp(text,text),
+ 	FUNCTION        2       gin_extract_hstore(internal, internal),
+ 	FUNCTION        3       gin_extract_hstore_query(internal,
internal, int2, internal, internal),
+ 	FUNCTION        4       gin_consistent_hstore(internal, int2,
internal, int4, internal, internal),
+ 	STORAGE         text;

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#273Teodor Sigaev
teodor@sigaev.ru
In reply to: Peter Geoghegan (#272)
Re: jsonb and nested hstore

I guess this is down to the continued definition of gin_hstore_ops as
an opclass with text storage?:

No, type of this storage describes type of keys. For gin_hstore_ops each key and
each value will be stored as a text value. The root of problem is a JavaScript
or/and our numeric type. In JavaScript (which was a base for json type) you need
explicitly point type of compare to prevent unpredictable result.

select '25.0'::numeric = '25'::numeric;
?column?
----------
t
but
select '25.0'::numeric::text = '25'::numeric::text;
?column?
----------
f

and
select '{"a": 25}'::json->>'a' = '{"a": 25.0}'::json->>'a';
?column?
----------
f

In pointed example inserted value has age: 25 but searching jsonb value has
age:25.0.

+ CREATE OPERATOR CLASS gin_hstore_ops
+ DEFAULT FOR TYPE hstore USING gin
+ AS
+ 	OPERATOR        7       @>,
+ 	OPERATOR        9       ?(hstore,text),
+ 	OPERATOR        10      ?|(hstore,text[]),
+ 	OPERATOR        11      ?&(hstore,text[]),
+ 	FUNCTION        1       bttextcmp(text,text),
+ 	FUNCTION        2       gin_extract_hstore(internal, internal),
+ 	FUNCTION        3       gin_extract_hstore_query(internal,
internal, int2, internal, internal),
+ 	FUNCTION        4       gin_consistent_hstore(internal, int2,
internal, int4, internal, internal),
+ 	STORAGE         text;

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#274Teodor Sigaev
teodor@sigaev.ru
In reply to: Teodor Sigaev (#273)
Re: jsonb and nested hstore

select '{"a": 25}'::json->>'a' = '{"a": 25.0}'::json->>'a';
?column?
----------
f

Although for development version of hstore (not a current version)
# select 'a=> 25'::hstore = 'a=> 25.0'::hstore;
?column?
----------
t

That is because compareJsonbValue compares numeric values with a help of
numeric_cmp() instead of comparing text representation. This inconsistent will
be fixed.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#275Peter Geoghegan
pg@heroku.com
In reply to: Teodor Sigaev (#273)
Re: jsonb and nested hstore

On Tue, Mar 4, 2014 at 2:07 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

No, type of this storage describes type of keys. For gin_hstore_ops each key
and each value will be stored as a text value. The root of problem is a
JavaScript or/and our numeric type. In JavaScript (which was a base for json
type) you need explicitly point type of compare to prevent unpredictable
result.

That's what I meant, I think. But I'm not sure what you mean:

Native Chrome JavaScript.
Copyright (c) 2013 Google Inc
25 == 25
=> true
25 == 25.0
=> true

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#276Peter Geoghegan
pg@heroku.com
In reply to: Teodor Sigaev (#274)
Re: jsonb and nested hstore

On Tue, Mar 4, 2014 at 2:18 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

That is because compareJsonbValue compares numeric values with a help of
numeric_cmp() instead of comparing text representation. This inconsistent
will be fixed.

Cool.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#277Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#276)
Re: jsonb and nested hstore

On Tue, Mar 4, 2014 at 2:18 AM, Peter Geoghegan <pg@heroku.com> wrote:

On Tue, Mar 4, 2014 at 2:18 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

That is because compareJsonbValue compares numeric values with a help of
numeric_cmp() instead of comparing text representation. This inconsistent
will be fixed.

Cool.

Perhaps this is obvious, but: I expect that you intend to fix the
inconsistency by having everywhere use a native numeric comparison.

Thanks
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#278Oleg Bartunov
obartunov@gmail.com
In reply to: Peter Geoghegan (#276)
Re: jsonb and nested hstore

I tried try.mongodb.com

25 == 25.0

true

On Tue, Mar 4, 2014 at 2:18 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Tue, Mar 4, 2014 at 2:18 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

That is because compareJsonbValue compares numeric values with a help of
numeric_cmp() instead of comparing text representation. This inconsistent
will be fixed.

Cool.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#279Teodor Sigaev
teodor@sigaev.ru
In reply to: Peter Geoghegan (#275)
Re: jsonb and nested hstore

Do we have function to trim right zeros in numeric?

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#280Peter Geoghegan
pg@heroku.com
In reply to: Teodor Sigaev (#279)
Re: jsonb and nested hstore

On Tue, Mar 4, 2014 at 2:44 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

Do we have function to trim right zeros in numeric?

I'm not sure why you ask. I hope it isn't because you want to fix this
bug by making text comparisons in place of numeric comparisons work by
fixing the exact problem I reported, because there are other similar
problems, such as differences in lc_numeric settings that your
implementation cannot possibly account for. If that's not what you
meant, I think it's okay if there are apparent trailing zeroes output
under similar circumstances to the numeric type proper. Isn't this
kind of thing intentionally not described by the relevant spec anyway?
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#281Teodor Sigaev
teodor@sigaev.ru
In reply to: Peter Geoghegan (#280)
Re: jsonb and nested hstore

On Tue, Mar 4, 2014 at 2:44 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

Do we have function to trim right zeros in numeric?

Fixed, pushed to github
(https://github.com/feodor/postgres/tree/jsonb_and_hstore). Now it used
hash_numeric to index numeric value. As I can see, it provides needed trim and
doesn't depend on locale. Possible mismatch (the same hash value for different
numeric valye) will rechecked anyway - interested operations set recheck flag.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#282Merlin Moncure
mmoncure@gmail.com
In reply to: Teodor Sigaev (#281)
Re: jsonb and nested hstore

On Tue, Mar 4, 2014 at 6:48 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

On Tue, Mar 4, 2014 at 2:44 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

Do we have function to trim right zeros in numeric?

Fixed, pushed to github
(https://github.com/feodor/postgres/tree/jsonb_and_hstore). Now it used
hash_numeric to index numeric value. As I can see, it provides needed trim
and doesn't depend on locale. Possible mismatch (the same hash value for
different numeric valye) will rechecked anyway - interested operations set
recheck flag.

huh. what it is the standard for equivalence? I guess we'd be
following javascript ===, right?
(http://dorey.github.io/JavaScript-Equality-Table/).

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#283Teodor Sigaev
teodor@sigaev.ru
In reply to: Merlin Moncure (#282)
Re: jsonb and nested hstore

huh. what it is the standard for equivalence? I guess we'd be
following javascript ===, right?
(http://dorey.github.io/JavaScript-Equality-Table/).

right.

But in your link I don't understand array (and object) equality rules. Hstore
(and jsonb) compare function believes that arrays are equal if each
corresponding elements of them are equal.

postgres=# select 'a=>[]'::hstore = 'a=>[]'::hstore;
?column?
----------
t
(1 row)

Time: 0,576 ms
postgres=# select 'a=>[0]'::hstore = 'a=>[0]'::hstore;
?column?
----------
t
(1 row)

Time: 0,663 ms
postgres=# select 'a=>[0]'::hstore = 'a=>[1]'::hstore;
?column?
----------
f

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#284Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: jsonb and nested hstore

On 03/03/2014 09:06 PM, Peter Geoghegan wrote:

On Mon, Mar 3, 2014 at 9:05 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

What you're not welcome to do, from my POV, is move jsonb into the hstore
extension. I strenuously object to any such plan.

We both know that that isn't really the point of contention at all.

Actually, I didn't know any such thing. Just a couple days ago, you
were arguing fairly strongly for moving jsonb to the hstore extension.
You weren't clear that you'd given up on that line of argument.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#285Bruce Momjian
bruce@momjian.us
In reply to: Josh Berkus (#262)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 06:59:37PM -0800, Josh Berkus wrote:

Realistically, hstore will never go away. I'll bet you a round or two
of pints that, if we get both hstore2 and jsonb, within 2 years the
users of jsonb will be an order of magnitude more numerous that then
users of hstore, but folks out there have apps already built against
hstore1 and they're going to keep on the hstore path.

In the discussion you haven't apparently caught up on yet, we did
discuss moving *hstore* into core to make this whole thing easier.
However, that fell afoul of the fact that we currently have no mechanism
to move types between extensions and core without breaking upgrades for
everyone. So the only reason hstore is still an extension is because of
backwards-compatibility.

I have read last week's thread on this issue, and it certainly seems we
are in a non-ideal situation here.

The discussion centered around the split of JSONB in core and hstore in
contrib, the reason for some of these decisions, and what can make it
into PG 9.4.

I would like to take a different approach and explore what we
_eventually_ want, then back into what we have and what can be done for
9.4.

Basically, it seems we have heirchical hstore and JSONB which are
identical except for the input/output syntax. Many are confused how a
code split like that works long-term, and whether decisions made for 9.4
might limit future options.

There seems to be a basic tension that we can't move hstore into core,
must maintain backward-compatibility for hstore, and we want JSONB in
core. Long-term, having JSON in core and JSONB in contrib seems quite
odd.

So, I am going to ask a back-track question and ask why we can't move
hstore into core. Is this a problem with the oids of the hstore data
type and functions? Is this a pg_upgrade-only problem? Can this be
fixed?

Yes, I am ignoring what might be possible for 9.4, but I think these
questions must be asked if we are going to properly plan for post-9.4
changes.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#286Andrew Dunstan
andrew@dunslane.net
In reply to: Bruce Momjian (#285)
Re: jsonb and nested hstore

On 03/05/2014 09:39 AM, Bruce Momjian wrote:

So, I am going to ask a back-track question and ask why we can't move
hstore into core. Is this a problem with the oids of the hstore data
type and functions? Is this a pg_upgrade-only problem? Can this be
fixed?

Yes, pg_upgrade is the problem, and no, I can't see how it can be fixed.

Builtin types have Oids in a certain range. Non-builtin types have Oids
outside that range. If you have a clever way to get over that I'd be all
ears, but it seems to me insurmountable right now.

A year or two ago I made a suggestion to help avoid such problems in
future, but as Josh said it got shot down, and in any case it would not
have helped with existing types such as hstore.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#287Merlin Moncure
mmoncure@gmail.com
In reply to: Bruce Momjian (#285)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 8:39 AM, Bruce Momjian <bruce@momjian.us> wrote:

So, I am going to ask a back-track question and ask why we can't move
hstore into core.

This is exactly the opposite of what should be happening. Now, jsonb
might make it into core because of the json precedent but the entire
purpose of the extension system is stop dumping everything in the
public namespace. Stuff 'in core' becomes locked in stone, forever,
because of backwards compatibility concerns, which are IMNSHO, a
bigger set of issues than even pg_upgrade related issues. Have we
gone through all the new hstore functions and made sure they don't
break existing applications? Putting things in core welds your only
escape hatch shut.

*All* non-sql standard types ought to be in extensions in an ideal world.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#288Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#286)
Re: jsonb and nested hstore

Andrew Dunstan <andrew@dunslane.net> writes:

On 03/05/2014 09:39 AM, Bruce Momjian wrote:

So, I am going to ask a back-track question and ask why we can't move
hstore into core. Is this a problem with the oids of the hstore data
type and functions? Is this a pg_upgrade-only problem? Can this be
fixed?

Yes, pg_upgrade is the problem, and no, I can't see how it can be fixed.

Builtin types have Oids in a certain range. Non-builtin types have Oids
outside that range. If you have a clever way to get over that I'd be all
ears, but it seems to me insurmountable right now.

More to the point:

1. Built-in types have predetermined, fixed OIDs. Types made by
extensions do not, and almost certainly will have different OIDs in
different existing databases.

2. There's no easy way to change the OID of an existing type during
pg_upgrade, because it may be on-disk in (at least) array headers.

We could possibly get around #2, if we could think of a secure way
for array_out and sibling functions to identify the array type
without use of the embedded OID value. I don't know how we could
do that though, particularly in polymorphic-function contexts.

Also, there might be other cases besides arrays where we've embedded
type OIDs in on-disk data; anyone remember?

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#289Merlin Moncure
mmoncure@gmail.com
In reply to: Tom Lane (#288)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 9:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 03/05/2014 09:39 AM, Bruce Momjian wrote:

So, I am going to ask a back-track question and ask why we can't move
hstore into core. Is this a problem with the oids of the hstore data
type and functions? Is this a pg_upgrade-only problem? Can this be
fixed?

Yes, pg_upgrade is the problem, and no, I can't see how it can be fixed.

Builtin types have Oids in a certain range. Non-builtin types have Oids
outside that range. If you have a clever way to get over that I'd be all
ears, but it seems to me insurmountable right now.

More to the point:

1. Built-in types have predetermined, fixed OIDs. Types made by
extensions do not, and almost certainly will have different OIDs in
different existing databases.

2. There's no easy way to change the OID of an existing type during
pg_upgrade, because it may be on-disk in (at least) array headers.

We could possibly get around #2, if we could think of a secure way
for array_out and sibling functions to identify the array type
without use of the embedded OID value. I don't know how we could
do that though, particularly in polymorphic-function contexts.

Also, there might be other cases besides arrays where we've embedded
type OIDs in on-disk data; anyone remember?

composite types.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#290Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#288)
Re: jsonb and nested hstore

On 03/05/2014 10:24 AM, Tom Lane wrote:

Also, there might be other cases besides arrays where we've embedded
type OIDs in on-disk data; anyone remember?

Don't we do that in composites too?

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#291Tom Lane
tgl@sss.pgh.pa.us
In reply to: Merlin Moncure (#289)
Re: jsonb and nested hstore

Merlin Moncure <mmoncure@gmail.com> writes:

On Wed, Mar 5, 2014 at 9:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Also, there might be other cases besides arrays where we've embedded
type OIDs in on-disk data; anyone remember?

composite types.

But that's only the composite type's own OID, no? So it's not really
a problem unless the type we wanted to move into (or out of) core was
itself composite.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#292Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#291)
Re: jsonb and nested hstore

On 03/05/2014 10:30 AM, Tom Lane wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

On Wed, Mar 5, 2014 at 9:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Also, there might be other cases besides arrays where we've embedded
type OIDs in on-disk data; anyone remember?

composite types.

But that's only the composite type's own OID, no? So it's not really
a problem unless the type we wanted to move into (or out of) core was
itself composite.

Sure, although that's not entirely impossible to imagine. I admit it
seems less likely, and I could accept it as a restriction if we
conquered the general case.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#293Bruce Momjian
bruce@momjian.us
In reply to: Merlin Moncure (#287)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 09:19:33AM -0600, Merlin Moncure wrote:

On Wed, Mar 5, 2014 at 8:39 AM, Bruce Momjian <bruce@momjian.us> wrote:

So, I am going to ask a back-track question and ask why we can't move
hstore into core.

This is exactly the opposite of what should be happening. Now, jsonb
might make it into core because of the json precedent but the entire
purpose of the extension system is stop dumping everything in the
public namespace. Stuff 'in core' becomes locked in stone, forever,
because of backwards compatibility concerns, which are IMNSHO, a
bigger set of issues than even pg_upgrade related issues. Have we
gone through all the new hstore functions and made sure they don't
break existing applications? Putting things in core welds your only
escape hatch shut.

*All* non-sql standard types ought to be in extensions in an ideal world.

I have seen your opinion on this but there have been enough
counter-arguments that I am not ready to head in that direction.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#294Robert Haas
robertmhaas@gmail.com
In reply to: Merlin Moncure (#287)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 10:19 AM, Merlin Moncure <mmoncure@gmail.com> wrote:

On Wed, Mar 5, 2014 at 8:39 AM, Bruce Momjian <bruce@momjian.us> wrote:

So, I am going to ask a back-track question and ask why we can't move
hstore into core.

This is exactly the opposite of what should be happening. Now, jsonb
might make it into core because of the json precedent but the entire
purpose of the extension system is stop dumping everything in the
public namespace. Stuff 'in core' becomes locked in stone, forever,
because of backwards compatibility concerns, which are IMNSHO, a
bigger set of issues than even pg_upgrade related issues. Have we
gone through all the new hstore functions and made sure they don't
break existing applications? Putting things in core welds your only
escape hatch shut.

I agree. What concerns me about jsonb is that it doesn't seem very
done. If we commit it to core and find out later that we've made some
mistakes we'd like to fix, it's going to be difficult and
controversial. If it goes on PGXN and turns out to have some
problems, then the people responsible for that extension can decide
whether and how to preserve backward compatibility, or somebody else
can write something completely different. On a theoretical level, I'd
absolutely rather have jsonb in core - not because it's in any way
theoretically necessary, but because JSON is popular and better
support for it will be good for PostgreSQL. But on a practical level
I'd rather not ship it in 9.4 than ship something we might later
regret.

And despite the assertions from various people here that these
decisions were all made a long time ago and it's way too late to
question them, I don't buy it. There's not a single email on this
mailing list clearly laying out the design that we've ended up with,
and I'm willing to wager any reasonable amount of money that if
someone had posted an email saying "hey, we're thinking about setting
things up so that jsonb and hstore have the same binary format, but
you can't index jsonb directly, you have to cast it to hstore, is
everyone OK with that?" someone would have written back and said "no,
that sounds nuts". The reason why input on that particular aspect of
the design was not forthcoming isn't because everyone was OK with it;
it's because it was never clearly spelled out. Maybe someone will say
that this was discussed at last year's PGCon unconference, but surely
everyone here knows that a discussion at an unconference 8 months ago
doesn't substitute for a discussion on-list.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#295Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#294)
Re: jsonb and nested hstore

Robert Haas <robertmhaas@gmail.com> writes:

And despite the assertions from various people here that these
decisions were all made a long time ago and it's way too late to
question them, I don't buy it. There's not a single email on this
mailing list clearly laying out the design that we've ended up with,
and I'm willing to wager any reasonable amount of money that if
someone had posted an email saying "hey, we're thinking about setting
things up so that jsonb and hstore have the same binary format, but
you can't index jsonb directly, you have to cast it to hstore, is
everyone OK with that?" someone would have written back and said "no,
that sounds nuts". The reason why input on that particular aspect of
the design was not forthcoming isn't because everyone was OK with it;
it's because it was never clearly spelled out.

No, that was never the design (I trust). It's where we are today
because time ran out to complete jsonb for 9.4, and tossing the index
opclasses overboard was one of the last-minute compromises in order
to have something submittable.

I think it would be a completely defensible decision to postpone jsonb
to 9.5 on the grounds that it's not done enough. Now, Josh has laid out
arguments why we want jsonb in 9.4 even if it's incomplete. But ISTM
that those are fundamentally marketing arguments; on a purely technical
basis I think the decision would be to postpone. So it comes down
to how you weight marketing vs technical issues, which is something
that everyone is likely to see a little bit differently :-(

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#296Bruce Momjian
bruce@momjian.us
In reply to: Andrew Dunstan (#292)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 10:39:56AM -0500, Andrew Dunstan wrote:

On 03/05/2014 10:30 AM, Tom Lane wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

On Wed, Mar 5, 2014 at 9:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Also, there might be other cases besides arrays where we've embedded
type OIDs in on-disk data; anyone remember?

composite types.

But that's only the composite type's own OID, no? So it's not really
a problem unless the type we wanted to move into (or out of) core was
itself composite.

Sure, although that's not entirely impossible to imagine. I admit it
seems less likely, and I could accept it as a restriction if we
conquered the general case.

OK, so let's look at the general case. Here is what pg_upgrade
preserves:

* We control all assignments of pg_class.oid (and relfilenode) so toast
* oids are the same between old and new clusters. This is important
* because toast oids are stored as toast pointers in user tables.
*
* While pg_class.oid and pg_class.relfilenode are initially the same
* in a cluster, they can diverge due to CLUSTER, REINDEX, or VACUUM
* FULL. In the new cluster, pg_class.oid and pg_class.relfilenode will
* be the same and will match the old pg_class.oid value. Because of
* this, old/new pg_class.relfilenode values will not match if CLUSTER,
* REINDEX, or VACUUM FULL have been performed in the old cluster.
*
* We control all assignments of pg_type.oid because these oids are stored
* in user composite type values.
*
* We control all assignments of pg_enum.oid because these oids are stored
* in user tables as enum values.
*
* We control all assignments of pg_authid.oid because these oids are stored
* in pg_largeobject_metadata.

It seems only pg_type.oid is an issue for hstore. We can easily modify
pg_dump --binary-upgrade mode to suppress the creation of the hstore
extension. That should allow user hstore columns to automatically map
to the new constant hstore oid. We can also modify pg_upgrade to scan
all the user tables for any use of hstore arrays and perhaps composite
types and tell the user they have to drop and upgrade those table
separately.

Again, I am not asking what can be done for 9.4 but what is our final
goal, though the pg_upgrade change are minimal as we have done such
adjustments in the past.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#297Merlin Moncure
mmoncure@gmail.com
In reply to: Bruce Momjian (#293)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 9:52 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Wed, Mar 5, 2014 at 09:19:33AM -0600, Merlin Moncure wrote:

On Wed, Mar 5, 2014 at 8:39 AM, Bruce Momjian <bruce@momjian.us> wrote:

So, I am going to ask a back-track question and ask why we can't move
hstore into core.

This is exactly the opposite of what should be happening. Now, jsonb
might make it into core because of the json precedent but the entire
purpose of the extension system is stop dumping everything in the
public namespace. Stuff 'in core' becomes locked in stone, forever,
because of backwards compatibility concerns, which are IMNSHO, a
bigger set of issues than even pg_upgrade related issues. Have we
gone through all the new hstore functions and made sure they don't
break existing applications? Putting things in core welds your only
escape hatch shut.

*All* non-sql standard types ought to be in extensions in an ideal world.

I have seen your opinion on this but there have been enough
counter-arguments that I am not ready to head in that direction.

The only counter argument given is that this will prevent people from
being able to use extensions because they A: can't or won't install
contrib packages or B: are too stupid or lazy to type 'create
extension json'. Note I'm discussing 'in core extension vs in core
built in'. 'out of core extension' loosely translates to 'can't be
used by the vast majority of systems.

Most corporate IT departments (including mine) have a policy of only
installing packages through the operating system packaging to simplify
management of deploying updates. Really strict companies might not
even allow anything but packages supplied by a vendor like redhat
(which in practice keeps you some versions back from the latest).
Now, if some crappy hosting company blocks contrib I don't believe at
all that this should drive our project management decisions.

Postgresql is both a database and increasingly a development language
platform. Most good stacks have a system (cpan, npm, etgc) to manage
the scope of the installed runtime and it's a routine and expected
exercise to leverage that system.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#298Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#265)
Re: jsonb and nested hstore

On Mon, Mar 3, 2014 at 11:20 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Mon, Mar 3, 2014 at 6:59 PM, Josh Berkus <josh@agliodbs.com> wrote:

Also, please recognize that the current implementation was what we
collectively decided on three months ago, and what Andrew worked pretty
hard to implement based on that collective decision. So if we're going
to change course, we need a specific reason to change course, not just
"it seems like a better idea now" or "I wasn't paying attention then".

I'm pretty sure it doesn't work like that. But if it does, what
exactly am I insisting on that is inconsistent with that consensus? In
what way are we changing course? I think I'm being eminently flexible.
I don't want a jsonb type that is broken, as for example by not having
a default B-Tree operator class. Why don't you let me get on with it?

An excellent question. This thread has become mostly about whether
someone (like, say, me, or in this case Peter) is attempting to pull
the rug out from under a previously-agreed consensus path forward.
But despite my asking, nobody's been able to provide a pointer to any
previous discussion of the points under debate. That's because the
points that are *actually* being debated here were not discussed
previously. I recognize that Josh and Andrew would like to make that
the fault of the people who are now raising objections, but it doesn't
work like that. The fact that people were and are *generally* in
favor of jsonb and hstore doesn't mean they have to like the way that
the patches have turned out.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#299Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#296)
Re: jsonb and nested hstore

Bruce Momjian <bruce@momjian.us> writes:

It seems only pg_type.oid is an issue for hstore. We can easily modify
pg_dump --binary-upgrade mode to suppress the creation of the hstore
extension. That should allow user hstore columns to automatically map
to the new constant hstore oid. We can also modify pg_upgrade to scan
all the user tables for any use of hstore arrays and perhaps composite
types and tell the user they have to drop and upgrade those table
separately.

Yeah, and that doesn't seem terribly acceptable. Unless you think the
field usage of hstore[] is nil; which maybe it is, I'm not sure what
the usage patterns are like. In general it would not be acceptable
at all to not be able to support migrations of array columns.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#300Andres Freund
andres@2ndquadrant.com
In reply to: Merlin Moncure (#297)
Re: jsonb and nested hstore

On 2014-03-05 10:10:23 -0600, Merlin Moncure wrote:

On Wed, Mar 5, 2014 at 9:52 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Wed, Mar 5, 2014 at 09:19:33AM -0600, Merlin Moncure wrote:

*All* non-sql standard types ought to be in extensions in an ideal world.

I have seen your opinion on this but there have been enough
counter-arguments that I am not ready to head in that direction.

The only counter argument given is that this will prevent people from
being able to use extensions because they A: can't or won't install
contrib packages or B: are too stupid or lazy to type 'create
extension json'. Note I'm discussing 'in core extension vs in core
built in'. 'out of core extension' loosely translates to 'can't be
used by the vast majority of systems.

There's the absolutely significant issue that you cannot reasonably
write extensions that interact on a C level. You can't call from
extension to extension directly, but you can from extension to pg core
provided ones.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#301Tom Lane
tgl@sss.pgh.pa.us
In reply to: Merlin Moncure (#297)
Re: jsonb and nested hstore

Merlin Moncure <mmoncure@gmail.com> writes:

*All* non-sql standard types ought to be in extensions in an ideal world.

While there's certainly much to be said for the idea that jsonb should be
an extension, I don't think we have the technology to package it as a
*separate* extension; it'd have to be included in the hstore extension.
Which is weird, and quite a mixed message from the marketing standpoint.
If I understand Josh's vision of the future, he's expecting that hstore
will gradually die off in favor of jsonb, so we don't really want to
present the latter as the ugly stepchild.

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#302Merlin Moncure
mmoncure@gmail.com
In reply to: Andres Freund (#300)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 10:19 AM, Andres Freund <andres@2ndquadrant.com> wrote:

There's the absolutely significant issue that you cannot reasonably
write extensions that interact on a C level. You can't call from
extension to extension directly, but you can from extension to pg core
provided ones.

Certainly. Note I never said that the internal .so can't be in core
that both extensions interface with and perhaps wrap. It would be
nice to have a intra-extension call system worked out but that in no
way plays to the bigger issues at stake. This is all about management
of the public API; take a good skeptical look at the history of types
like xml, json, geo, money and others.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#303Stephen Frost
sfrost@snowman.net
In reply to: Merlin Moncure (#287)
Re: jsonb and nested hstore

* Merlin Moncure (mmoncure@gmail.com) wrote:

*All* non-sql standard types ought to be in extensions in an ideal world.

While I appreciate that you'd like to see it that way, others don't
agree (I certainly don't), and that ship sailed quite a long time ago
regardless. I'm not advocating putting everything into core, but I
agreed with having json in core and further feel jsonb should be there
also. I'm not against having hstore either- and I *wish* we'd put ip4r
in and replace our existing inet types with it.

Thanks,

Stephen

#304Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#295)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:07 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

And despite the assertions from various people here that these
decisions were all made a long time ago and it's way too late to
question them, I don't buy it. There's not a single email on this
mailing list clearly laying out the design that we've ended up with,
and I'm willing to wager any reasonable amount of money that if
someone had posted an email saying "hey, we're thinking about setting
things up so that jsonb and hstore have the same binary format, but
you can't index jsonb directly, you have to cast it to hstore, is
everyone OK with that?" someone would have written back and said "no,
that sounds nuts". The reason why input on that particular aspect of
the design was not forthcoming isn't because everyone was OK with it;
it's because it was never clearly spelled out.

No, that was never the design (I trust). It's where we are today
because time ran out to complete jsonb for 9.4, and tossing the index
opclasses overboard was one of the last-minute compromises in order
to have something submittable.

Well, what I was told when I started objecting to the current state of
affairs is that it was too late to "change course" now, which seemed
to me to imply that this was the idea all along. On the other hand,
Josh also said that there was a plan in the works to ship the missing
opclasses on PGXN before 9.4 hits shelves, which is more along the
lines of what you're saying. So, hey, I don't know.

I think it would be a completely defensible decision to postpone jsonb
to 9.5 on the grounds that it's not done enough. Now, Josh has laid out
arguments why we want jsonb in 9.4 even if it's incomplete. But ISTM
that those are fundamentally marketing arguments; on a purely technical
basis I think the decision would be to postpone. So it comes down
to how you weight marketing vs technical issues, which is something
that everyone is likely to see a little bit differently :-(

I don't have any problem shipping incremental progress on important
features, but once we ship things that are visible at the SQL level
they get awfully hard to change, and my confidence that we won't want
to change this is not very high right now. To the extent that we have
a jsonb that is missing some features we will eventually want to have,
I don't care; that's incremental development at its finest. To the
extent that we have a jsonb that makes choices about what to store on
disk or expose at the SQL level that we may regret later, that's not
incremental development; that's just not being done. Anyone who
thinks that digging ourselves out of a backward-compatibility hole
will be painless enough to justify the marketing value of the feature
has most probably not had to do it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#305Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#301)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

*All* non-sql standard types ought to be in extensions in an ideal world.

While there's certainly much to be said for the idea that jsonb should be
an extension, I don't think we have the technology to package it as a
*separate* extension; it'd have to be included in the hstore extension.
Which is weird, and quite a mixed message from the marketing standpoint.
If I understand Josh's vision of the future, he's expecting that hstore
will gradually die off in favor of jsonb, so we don't really want to
present the latter as the ugly stepchild.

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

I believe the main thing is the opclasses.

My information might be incomplete.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#306Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#301)
Re: jsonb and nested hstore

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

Somewhere upthread, Peter seemed to estimate it at a day, if I
understood correctly. If that's accurate, I'm certainly behind getting
it done and in and moving on. I'm sure no one particularly likes a
bunch of copy/pasteing of code, but if it would get us to the point of
having a really working jsonb that everyone is happy with, I'm all for
it.

It's not clear how much different it would be if we waited til 9.5
either- do we anticipate a lot of code changes beyond the copy/paste for
these?

Thanks,

Stephen

#307Merlin Moncure
mmoncure@gmail.com
In reply to: Tom Lane (#301)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 10:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

*All* non-sql standard types ought to be in extensions in an ideal world.

While there's certainly much to be said for the idea that jsonb should be
an extension, I don't think we have the technology to package it as a
*separate* extension; it'd have to be included in the hstore extension.

I disagree with that. The shared C bits can live inside the core
system and the SQL level hooks and extension specific behaviors could
live in an extension. AFAICT moving jsonb to extension is mostly a
function of migrating the hard coded SQL hooks out to an extension
(I'm probably oversimplifying though).

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

Basically opclasses, operators (particularly search operators) and
functions/operators to manipulate the hstore in place. Personally I'm
not inclined to copy/paste the code. I'd also like to see this stuff
committed, and don't want to hold up the patch for that unless it's
determined for other reasons (and by other people) this is the only
reasonable path for 9.4.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#308Stephen Frost
sfrost@snowman.net
In reply to: Merlin Moncure (#307)
Re: jsonb and nested hstore

* Merlin Moncure (mmoncure@gmail.com) wrote:

On Wed, Mar 5, 2014 at 10:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

*All* non-sql standard types ought to be in extensions in an ideal world.

While there's certainly much to be said for the idea that jsonb should be
an extension, I don't think we have the technology to package it as a
*separate* extension; it'd have to be included in the hstore extension.

I disagree with that. The shared C bits can live inside the core
system and the SQL level hooks and extension specific behaviors could
live in an extension. AFAICT moving jsonb to extension is mostly a
function of migrating the hard coded SQL hooks out to an extension
(I'm probably oversimplifying though).

Yeah, from what I gather you're suggesting, that's more-or-less "move it
all to core", except that all of the actual interface bits end up in an
extension that has to be installed to use what would have to already be
there. I don't see that as any kind of improvement.

Thanks,

Stephen

#309Bruce Momjian
bruce@momjian.us
In reply to: Tom Lane (#299)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:16:01AM -0500, Tom Lane wrote:

Bruce Momjian <bruce@momjian.us> writes:

It seems only pg_type.oid is an issue for hstore. We can easily modify
pg_dump --binary-upgrade mode to suppress the creation of the hstore
extension. That should allow user hstore columns to automatically map
to the new constant hstore oid. We can also modify pg_upgrade to scan
all the user tables for any use of hstore arrays and perhaps composite
types and tell the user they have to drop and upgrade those table
separately.

Yeah, and that doesn't seem terribly acceptable. Unless you think the
field usage of hstore[] is nil; which maybe it is, I'm not sure what
the usage patterns are like. In general it would not be acceptable
at all to not be able to support migrations of array columns.

It would prevent migration of _hstore_ array columns, which might be
acceptable. If we say pg_upgrade can never decline an upgrade, we
basically limit changes and increase the odds of needing a total
pg_upgrade-breaking release someday to re-adjust everything.

I basically think that a split between contrib and core for the
internally same data type just isn't sustainable.

Another conern is that it doesn't seem we are sure if we want JSONB in
core or contrib, at least based on some comments, so we should probably
decide that now, as I don't think the decision is going to be any easier
in the future. And as discussed above, moving something from contrib to
core has its own complexities.

I think we also have to break out how much of the feeling that JSONB is
not ready is because of problems with the core/contrib split, and how
much of it is because of the type itself. I am suggesting that
core/contrib split problems are not symptomatic of data type problems,
and if address/address the core/contrib split issue, the data type might
be just fine.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#310Andrew Dunstan
andrew@dunslane.net
In reply to: Stephen Frost (#306)
Re: jsonb and nested hstore

On 03/05/2014 11:34 AM, Stephen Frost wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

Somewhere upthread, Peter seemed to estimate it at a day, if I
understood correctly. If that's accurate, I'm certainly behind getting
it done and in and moving on. I'm sure no one particularly likes a
bunch of copy/pasteing of code, but if it would get us to the point of
having a really working jsonb that everyone is happy with, I'm all for
it.

It's not clear how much different it would be if we waited til 9.5
either- do we anticipate a lot of code changes beyond the copy/paste for
these?

I think that was my estimate, but Peter did offer to do it. He certainly
asserted that the effort required would not be great. I'm all for taking
up his offer.

Incidentally, this would probably have been done quite weeks ago if
people had not objected to my doing any more on the feature. Of course
missing the GIN/GIST ops was not part of the design. Quite the contrary.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#311Bruce Momjian
bruce@momjian.us
In reply to: Stephen Frost (#306)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:34:10AM -0500, Stephen Frost wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

Somewhere upthread, Peter seemed to estimate it at a day, if I
understood correctly. If that's accurate, I'm certainly behind getting
it done and in and moving on. I'm sure no one particularly likes a
bunch of copy/pasteing of code, but if it would get us to the point of
having a really working jsonb that everyone is happy with, I'm all for
it.

It's not clear how much different it would be if we waited til 9.5
either- do we anticipate a lot of code changes beyond the copy/paste for
these?

What _would_ be interesting is to move all the hstore code into core,
and have hstore contrib just call the hstore core parts. That way, you
have one copy of the code, it is shared with JSONB, but hstore remains
as an extension that you can change or remove later.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#312Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#298)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:11:51AM -0500, Robert Haas wrote:

An excellent question. This thread has become mostly about whether
someone (like, say, me, or in this case Peter) is attempting to pull
the rug out from under a previously-agreed consensus path forward.
But despite my asking, nobody's been able to provide a pointer to any
previous discussion of the points under debate. That's because the
points that are *actually* being debated here were not discussed
previously. I recognize that Josh and Andrew would like to make that
the fault of the people who are now raising objections, but it doesn't
work like that. The fact that people were and are *generally* in
favor of jsonb and hstore doesn't mean they have to like the way that
the patches have turned out.

I am assuming much of this was discussed verbally, and many of us were
not present.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#313Andrew Dunstan
andrew@dunslane.net
In reply to: Bruce Momjian (#309)
Re: jsonb and nested hstore

On 03/05/2014 11:44 AM, Bruce Momjian wrote:

On Wed, Mar 5, 2014 at 11:16:01AM -0500, Tom Lane wrote:

Bruce Momjian <bruce@momjian.us> writes:

It seems only pg_type.oid is an issue for hstore. We can easily modify
pg_dump --binary-upgrade mode to suppress the creation of the hstore
extension. That should allow user hstore columns to automatically map
to the new constant hstore oid. We can also modify pg_upgrade to scan
all the user tables for any use of hstore arrays and perhaps composite
types and tell the user they have to drop and upgrade those table
separately.

Yeah, and that doesn't seem terribly acceptable. Unless you think the
field usage of hstore[] is nil; which maybe it is, I'm not sure what
the usage patterns are like. In general it would not be acceptable
at all to not be able to support migrations of array columns.

It would prevent migration of _hstore_ array columns, which might be
acceptable. If we say pg_upgrade can never decline an upgrade, we
basically limit changes and increase the odds of needing a total
pg_upgrade-breaking release someday to re-adjust everything.

I basically think that a split between contrib and core for the
internally same data type just isn't sustainable.

Another conern is that it doesn't seem we are sure if we want JSONB in
core or contrib, at least based on some comments, so we should probably
decide that now, as I don't think the decision is going to be any easier
in the future. And as discussed above, moving something from contrib to
core has its own complexities.

I think we also have to break out how much of the feeling that JSONB is
not ready is because of problems with the core/contrib split, and how
much of it is because of the type itself. I am suggesting that
core/contrib split problems are not symptomatic of data type problems,
and if address/address the core/contrib split issue, the data type might
be just fine.

Splitting out jsonb to an extension is going to be moderately painful.
The json and jsonb functions share some code that's not exposed (and
probably shouldn't be). It's not likely to be less painful than
implementing the hstore GIN/GIST ops for jsonb, I suspect the reverse.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#314Merlin Moncure
mmoncure@gmail.com
In reply to: Stephen Frost (#308)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 10:43 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Merlin Moncure (mmoncure@gmail.com) wrote:

On Wed, Mar 5, 2014 at 10:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

*All* non-sql standard types ought to be in extensions in an ideal world.

While there's certainly much to be said for the idea that jsonb should be
an extension, I don't think we have the technology to package it as a
*separate* extension; it'd have to be included in the hstore extension.

I disagree with that. The shared C bits can live inside the core
system and the SQL level hooks and extension specific behaviors could
live in an extension. AFAICT moving jsonb to extension is mostly a
function of migrating the hard coded SQL hooks out to an extension
(I'm probably oversimplifying though).

Yeah, from what I gather you're suggesting, that's more-or-less "move it
all to core", except that all of the actual interface bits end up in an
extension that has to be installed to use what would have to already be
there. I don't see that as any kind of improvement.

If you don't then you simply have not been paying attention to the
endless backwards compatibility problems we've faced which are highly
ameliorated in an extension heavy world. Also, you're ignoring the
fact that having an endlessly accreting set of symbols in the public
namespace is not free. Internal C libraries don't have to be
supported and don't have any signficant user facing costs by simply
being there.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#315Bruce Momjian
bruce@momjian.us
In reply to: Andrew Dunstan (#313)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:53:31AM -0500, Andrew Dunstan wrote:

I think we also have to break out how much of the feeling that JSONB is
not ready is because of problems with the core/contrib split, and how
much of it is because of the type itself. I am suggesting that
core/contrib split problems are not symptomatic of data type problems,
and if address/address the core/contrib split issue, the data type might
be just fine.

Splitting out jsonb to an extension is going to be moderately
painful. The json and jsonb functions share some code that's not
exposed (and probably shouldn't be). It's not likely to be less
painful than implementing the hstore GIN/GIST ops for jsonb, I
suspect the reverse.

OK, that's good information. So we have JSONB which ties to a core
type, JSON, _and_ to a contrib module, hstore. No wonder it is so
complex.

I am warming up to the idea of moving hstore internals into core,
sharing that with JSONB, and having contrib/hstore just call the core
functions when defining its data type.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#316David E. Wheeler
david@justatheory.com
In reply to: Andrew Dunstan (#310)
Re: jsonb and nested hstore

On Mar 5, 2014, at 8:49 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

I think that was my estimate, but Peter did offer to do it. He certainly asserted that the effort required would not be great. I'm all for taking up his offer.

+1 to this. Can you and Peter collaborate somehow to get it knocked out?

Incidentally, this would probably have been done quite weeks ago if people had not objected to my doing any more on the feature. Of course missing the GIN/GIST ops was not part of the design. Quite the contrary.

That was my understanding, as well.

Best,

David

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#317Tom Lane
tgl@sss.pgh.pa.us
In reply to: Merlin Moncure (#307)
Re: jsonb and nested hstore

Merlin Moncure <mmoncure@gmail.com> writes:

On Wed, Mar 5, 2014 at 10:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

While there's certainly much to be said for the idea that jsonb should be
an extension, I don't think we have the technology to package it as a
*separate* extension; it'd have to be included in the hstore extension.

I disagree with that. The shared C bits can live inside the core
system and the SQL level hooks and extension specific behaviors could
live in an extension.

That approach abandons every bit of value in an extension, no?
You certainly don't get to fix bugs outside a core-system release cycle.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#318Robert Haas
robertmhaas@gmail.com
In reply to: Bruce Momjian (#311)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:50 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Wed, Mar 5, 2014 at 11:34:10AM -0500, Stephen Frost wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

Somewhere upthread, Peter seemed to estimate it at a day, if I
understood correctly. If that's accurate, I'm certainly behind getting
it done and in and moving on. I'm sure no one particularly likes a
bunch of copy/pasteing of code, but if it would get us to the point of
having a really working jsonb that everyone is happy with, I'm all for
it.

It's not clear how much different it would be if we waited til 9.5
either- do we anticipate a lot of code changes beyond the copy/paste for
these?

What _would_ be interesting is to move all the hstore code into core,
and have hstore contrib just call the hstore core parts. That way, you
have one copy of the code, it is shared with JSONB, but hstore remains
as an extension that you can change or remove later.

That seems like an approach possibly worth investigating. It's not
too different from what we did when we moved text search into core.
The basic idea seems to be that we want jsonb in core, and we expect
it to replace hstore, but we can't get just get rid of hstore because
it has too many users.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#319Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#318)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 12:26:13PM -0500, Robert Haas wrote:

It's not clear how much different it would be if we waited til 9.5
either- do we anticipate a lot of code changes beyond the copy/paste for
these?

What _would_ be interesting is to move all the hstore code into core,
and have hstore contrib just call the hstore core parts. That way, you
have one copy of the code, it is shared with JSONB, but hstore remains
as an extension that you can change or remove later.

That seems like an approach possibly worth investigating. It's not
too different from what we did when we moved text search into core.
The basic idea seems to be that we want jsonb in core, and we expect
it to replace hstore, but we can't get just get rid of hstore because
it has too many users.

Yes. It eliminates the problem of code duplication, but keeps hstore in
contrib for flexibility and compatibility.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#320Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#318)
Re: jsonb and nested hstore

* Robert Haas (robertmhaas@gmail.com) wrote:

On Wed, Mar 5, 2014 at 11:50 AM, Bruce Momjian <bruce@momjian.us> wrote:

What _would_ be interesting is to move all the hstore code into core,
and have hstore contrib just call the hstore core parts. That way, you
have one copy of the code, it is shared with JSONB, but hstore remains
as an extension that you can change or remove later.

That seems like an approach possibly worth investigating. It's not
too different from what we did when we moved text search into core.
The basic idea seems to be that we want jsonb in core, and we expect
it to replace hstore, but we can't get just get rid of hstore because
it has too many users.

This might be valuable for hstore, specifically, because we can't easily
move it into core. I'm fine with that- the disagreement I have is with
the more general idea that everything not-defined-by-committee should be
in shim extensions which just provide basically the catalog entries for
types which are otherwise all in core.

Thanks,

Stephen

#321Merlin Moncure
mmoncure@gmail.com
In reply to: Tom Lane (#317)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:19 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Merlin Moncure <mmoncure@gmail.com> writes:

On Wed, Mar 5, 2014 at 10:24 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

While there's certainly much to be said for the idea that jsonb should be
an extension, I don't think we have the technology to package it as a
*separate* extension; it'd have to be included in the hstore extension.

I disagree with that. The shared C bits can live inside the core
system and the SQL level hooks and extension specific behaviors could
live in an extension.

That approach abandons every bit of value in an extension, no?
You certainly don't get to fix bugs outside a core-system release cycle.

That's core vs non core debate. Just about everyone (including me)
wants json and hstore to live in core -- meaning packaged, shipped,
supported, and documented with the postgresql source code releases.
Only an elite set of broadly useful and popular extensions get that
honor of which json is most certainly one.

Moreover, you give up nothing except the debate/approval issues to get
your code in core. If you want to release off cycle, you can
certainly do that and enterprising users can simply install the
extension manually (or perhaps via pgxn) instead of via contrib.

BTW,This is yet another thing that becomes impossible if you don't
extension (on top of legacy/backwards compatibility issues and general
bloat which is IMNSHO already a pretty severe situation).

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#322Andrew Dunstan
andrew@dunslane.net
In reply to: Bruce Momjian (#315)
Re: jsonb and nested hstore

On 03/05/2014 12:01 PM, Bruce Momjian wrote:

On Wed, Mar 5, 2014 at 11:53:31AM -0500, Andrew Dunstan wrote:

I think we also have to break out how much of the feeling that JSONB is
not ready is because of problems with the core/contrib split, and how
much of it is because of the type itself. I am suggesting that
core/contrib split problems are not symptomatic of data type problems,
and if address/address the core/contrib split issue, the data type might
be just fine.

Splitting out jsonb to an extension is going to be moderately
painful. The json and jsonb functions share some code that's not
exposed (and probably shouldn't be). It's not likely to be less
painful than implementing the hstore GIN/GIST ops for jsonb, I
suspect the reverse.

OK, that's good information. So we have JSONB which ties to a core
type, JSON, _and_ to a contrib module, hstore. No wonder it is so
complex.

Well, "ties to" is a loose term. It's hstore in these patches that
depends on jsonb - necessarily since we can't have core code depend on
an extension.

I am warming up to the idea of moving hstore internals into core,
sharing that with JSONB, and having contrib/hstore just call the core
functions when defining its data type.

Right, at least the parts they need in common. That's how I'd handle the
GIN/GIST ops, for example.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#323Stephen Frost
sfrost@snowman.net
In reply to: Merlin Moncure (#314)
Re: jsonb and nested hstore

* Merlin Moncure (mmoncure@gmail.com) wrote:

On Wed, Mar 5, 2014 at 10:43 AM, Stephen Frost <sfrost@snowman.net> wrote:

Yeah, from what I gather you're suggesting, that's more-or-less "move it
all to core", except that all of the actual interface bits end up in an
extension that has to be installed to use what would have to already be
there. I don't see that as any kind of improvement.

If you don't then you simply have not been paying attention to the
endless backwards compatibility problems we've faced which are highly
ameliorated in an extension heavy world.

We have backwards compatibility "problems" because we don't want to
*break* things for people. Moving things into extensions doesn't
magically fix that- if you break something in a backwards-incompatible
way then you're going to cause a lot of grief for people. Doing that to
everyone who uses hstore, just because it's an extension, doesn't make
it acceptable. On this thread we're already argueing about exactly that
issue and how to avoid breaking things for those users were we to move
hstore into core.

Also, you're ignoring the
fact that having an endlessly accreting set of symbols in the public
namespace is not free. Internal C libraries don't have to be
supported and don't have any signficant user facing costs by simply
being there.

I *really* hate how extensions end up getting dumped into the "public"
schema and I'm not a big fan for having huge search_paths either. As I
mentioned earlier- I'm also not advocating that everything be put into
core. I don't follow what you mean by "Internal C libraries don't have
to be supported" because, clearly, anything released would have to be
supported and if the extension is calling into a C interface then we'd
have to support that interface for that extension *and anyone else who
uses it*. We don't get to say "oh, this C function can only be used by
extensions we bless." We already worry less about breaking backwards
compatibility for C-level functions across PG major versions, but
that's true for both in-core hooks and extensions.

Thanks,

Stephen

#324Josh Berkus
josh@agliodbs.com
In reply to: Josh Berkus (#259)
Re: jsonb and nested hstore

On 03/05/2014 09:26 AM, Robert Haas wrote:

What _would_ be interesting is to move all the hstore code into core,
and have hstore contrib just call the hstore core parts. That way, you
have one copy of the code, it is shared with JSONB, but hstore remains
as an extension that you can change or remove later.

That seems like an approach possibly worth investigating. It's not
too different from what we did when we moved text search into core.
The basic idea seems to be that we want jsonb in core, and we expect
it to replace hstore, but we can't get just get rid of hstore because
it has too many users

Yes, please! This was the original approach that we talked about and
everyone agreed to, and what Andrew has been trying to implement.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#325Peter Geoghegan
pg@heroku.com
In reply to: Robert Haas (#305)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 8:30 AM, Robert Haas <robertmhaas@gmail.com> wrote:

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

I believe the main thing is the opclasses.

Yes, that's right. A large volume of code currently proposed for
hstore2 is much less valuable than those operators sufficient to
implement the hstore2 opclasses. If you assume that hstore will become
a legacy extension that we won't add anything to (including everything
proposed in any patch posted to this thread), and jsonb will go in
core (which is of course more or less just hstore2 with a few json
extras), the amount of code redundantly shared between core and an
unchanged hstore turns out to not be that bad. I hope to have a
precise answer to just how bad soon.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#326Bruce Momjian
bruce@momjian.us
In reply to: Peter Geoghegan (#325)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 10:59:37AM -0800, Peter Geoghegan wrote:

On Wed, Mar 5, 2014 at 8:30 AM, Robert Haas <robertmhaas@gmail.com> wrote:

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

I believe the main thing is the opclasses.

Yes, that's right. A large volume of code currently proposed for
hstore2 is much less valuable than those operators sufficient to
implement the hstore2 opclasses. If you assume that hstore will become
a legacy extension that we won't add anything to (including everything
proposed in any patch posted to this thread), and jsonb will go in
core (which is of course more or less just hstore2 with a few json
extras), the amount of code redundantly shared between core and an
unchanged hstore turns out to not be that bad. I hope to have a
precise answer to just how bad soon.

Can you clarify what hstore2 is? It that the name of a type? Is that
hierarchical hstore with the same hstore name?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#327Josh Berkus
josh@agliodbs.com
In reply to: Peter Geoghegan (#260)
Re: jsonb and nested hstore

On 03/05/2014 11:05 AM, Bruce Momjian wrote:

Can you clarify what hstore2 is? It that the name of a type? Is that
hierarchical hstore with the same hstore name?

hstore2 == nested heirarchical hstore. It's just a shorthand; there
won't be any actual type called "hstore2", by design. Unlike the json
users, the hstore users are going to get an automatic upgrade whether
they want it or not. Mind you, I can't see a reason NOT to want it ...

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#328Bruce Momjian
bruce@momjian.us
In reply to: Peter Geoghegan (#325)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 10:59:37AM -0800, Peter Geoghegan wrote:

On Wed, Mar 5, 2014 at 8:30 AM, Robert Haas <robertmhaas@gmail.com> wrote:

Just out of curiosity, exactly what features are missing from jsonb
today that are available with hstore? How long would it take to
copy-and-paste all that code, if someone were to decide to do the
work instead of argue about it?

I believe the main thing is the opclasses.

Yes, that's right. A large volume of code currently proposed for
hstore2 is much less valuable than those operators sufficient to
implement the hstore2 opclasses. If you assume that hstore will become
a legacy extension that we won't add anything to (including everything
proposed in any patch posted to this thread), and jsonb will go in
core (which is of course more or less just hstore2 with a few json
extras), the amount of code redundantly shared between core and an
unchanged hstore turns out to not be that bad. I hope to have a
precise answer to just how bad soon.

So, now knowing that hstore2 is just hierarchical hstore using the same
hstore type name, you are saying that we are keeping the
non-hierarchical code in contrib, and the rest goes into core --- that
makes sense, and from a code maintenance perspective, I like that the
non-hierarchical hstore code is not going in core.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#329Merlin Moncure
mmoncure@gmail.com
In reply to: Stephen Frost (#323)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:44 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Merlin Moncure (mmoncure@gmail.com) wrote:

On Wed, Mar 5, 2014 at 10:43 AM, Stephen Frost <sfrost@snowman.net> wrote:

Yeah, from what I gather you're suggesting, that's more-or-less "move it
all to core", except that all of the actual interface bits end up in an
extension that has to be installed to use what would have to already be
there. I don't see that as any kind of improvement.

If you don't then you simply have not been paying attention to the
endless backwards compatibility problems we've faced which are highly
ameliorated in an extension heavy world.

We have backwards compatibility "problems" because we don't want to
*break* things for people. Moving things into extensions doesn't
magically fix that- if you break something in a backwards-incompatible
way then you're going to cause a lot of grief for people.

It doesn't magically fix it, but at least provides a way forward. If
the function you want to modify is in an extension 'foo', you get to
put your new stuff in 'foo2' extension. That way your users do not
have to adjust all the code you would have broken. Perhaps for
in-core extensions you offer the old one in contrib for a while until
a reasonable amount of time passes then move it out to pgxn. This is
a vastly better system than the choices we have now, which is A. break
code or B. do nothing.

Also, you're ignoring the
fact that having an endlessly accreting set of symbols in the public
namespace is not free. Internal C libraries don't have to be
supported and don't have any signficant user facing costs by simply
being there.

I *really* hate how extensions end up getting dumped into the "public"
schema and I'm not a big fan for having huge search_paths either.

At least with extensions you have control over this.

mentioned earlier- I'm also not advocating that everything be put into
core. I don't follow what you mean by "Internal C libraries don't have
to be supported" because,

I mean, we are free to change them or delete them. They do not come
with the legacy that user facing API comes. They also do not bloat
the public namespace.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#330Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Merlin Moncure (#329)
Re: jsonb and nested hstore

Merlin Moncure escribi�:

On Wed, Mar 5, 2014 at 11:44 AM, Stephen Frost <sfrost@snowman.net> wrote:

We have backwards compatibility "problems" because we don't want to
*break* things for people. Moving things into extensions doesn't
magically fix that- if you break something in a backwards-incompatible
way then you're going to cause a lot of grief for people.

It doesn't magically fix it, but at least provides a way forward. If
the function you want to modify is in an extension 'foo', you get to
put your new stuff in 'foo2' extension. That way your users do not
have to adjust all the code you would have broken. Perhaps for
in-core extensions you offer the old one in contrib for a while until
a reasonable amount of time passes then move it out to pgxn.

Uhm. Would it work to define a new version of foo, say 2.0, but keep
the old 1.2 version the default? That way, if you want to keep the old
foo you do nothing (after both fresh install and pg_upgrade), and if you
want to upgrade to the new code, it's just an ALTER EXTENSION UPDATE
away.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#331Stephen Frost
sfrost@snowman.net
In reply to: Merlin Moncure (#329)
Re: jsonb and nested hstore

* Merlin Moncure (mmoncure@gmail.com) wrote:

On Wed, Mar 5, 2014 at 11:44 AM, Stephen Frost <sfrost@snowman.net> wrote:

We have backwards compatibility "problems" because we don't want to
*break* things for people. Moving things into extensions doesn't
magically fix that- if you break something in a backwards-incompatible
way then you're going to cause a lot of grief for people.

It doesn't magically fix it, but at least provides a way forward. If
the function you want to modify is in an extension 'foo', you get to
put your new stuff in 'foo2' extension. That way your users do not
have to adjust all the code you would have broken. Perhaps for
in-core extensions you offer the old one in contrib for a while until
a reasonable amount of time passes then move it out to pgxn. This is
a vastly better system than the choices we have now, which is A. break
code or B. do nothing.

I don't see why we can't do exactly what you're suggesting in core.
This whole thread is about doing exactly that, in fact, which is why
we're talking about 'jsonb' instead of just 'json'. I agree that we
don't push too hard to remove things from core, but it's not like we've
had a whole ton of success kicking things out of -contrib either.

I *really* hate how extensions end up getting dumped into the "public"
schema and I'm not a big fan for having huge search_paths either.

At least with extensions you have control over this.

Yeah, but I much prefer how things end up in pg_catalog rather than
public or individual schemas.

mentioned earlier- I'm also not advocating that everything be put into
core. I don't follow what you mean by "Internal C libraries don't have
to be supported" because,

I mean, we are free to change them or delete them. They do not come
with the legacy that user facing API comes. They also do not bloat
the public namespace.

But we actually *aren't* free to change or delete them- which is what I
was getting at. Certainly, in back-branches we regularly worry about
breaking things for users of C functions, and there is some
consideration for them even in major version changes.

Thanks,

Stephen

#332Merlin Moncure
mmoncure@gmail.com
In reply to: Alvaro Herrera (#330)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 2:45 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

Merlin Moncure escribió:

It doesn't magically fix it, but at least provides a way forward. If
the function you want to modify is in an extension 'foo', you get to
put your new stuff in 'foo2' extension. That way your users do not
have to adjust all the code you would have broken. Perhaps for
in-core extensions you offer the old one in contrib for a while until
a reasonable amount of time passes then move it out to pgxn.

Uhm. Would it work to define a new version of foo, say 2.0, but keep
the old 1.2 version the default? That way, if you want to keep the old
foo you do nothing (after both fresh install and pg_upgrade), and if you
want to upgrade to the new code, it's just an ALTER EXTENSION UPDATE
away.

Certainly. The important point though is that neither option is
available if the old stuff is locked into the public namespace.
Consider various warts like the array ('array_upper' et al) API or geo
types. We're stuck with them. Even with jsonb: it may be the hot new
thing *today* but 5 years down the line there's json2 that does all
kinds of wonderful things we haven't thought about -- what if it
displaces current usages? The very same people who are arguing that
jsonb should not be in an extension are the ones arguing json is
legacy and to be superseded. These two points of view IMO are
directly in conflict: if json would have been an extension than the
path to deprecation is clear. Now the json functions are in the
public namespace. Forever (or at least for a very long time).

On Wed, Mar 5, 2014 at 2:46 PM, Stephen Frost <sfrost@snowman.net> wrote:

I don't see why we can't do exactly what you're suggesting in core.

Because you can't (if you're defining core to mean 'not an
extension'). Functions can't be removed or changed because of legacy
application support. In an extension world, they can -- albeit not
'magically', but at least it can be done.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#333Stephen Frost
sfrost@snowman.net
In reply to: Merlin Moncure (#332)
Re: jsonb and nested hstore

* Merlin Moncure (mmoncure@gmail.com) wrote:

On Wed, Mar 5, 2014 at 2:46 PM, Stephen Frost <sfrost@snowman.net> wrote:

I don't see why we can't do exactly what you're suggesting in core.

Because you can't (if you're defining core to mean 'not an
extension'). Functions can't be removed or changed because of legacy
application support. In an extension world, they can -- albeit not
'magically', but at least it can be done.

That simply isn't accurate on either level- if there is concern about
application support, that can apply equally to core and contrib, and we
certainly *can* remove and/or redefine functions in core with sufficient
cause. It's just not something we do lightly for things living in
either core or contrib.

For an example, consider the FDW API, particularly what we did between
9.1 and 9.2.

Thanks,

Stephen

#334Merlin Moncure
mmoncure@gmail.com
In reply to: Stephen Frost (#333)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 4:24 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Merlin Moncure (mmoncure@gmail.com) wrote:

On Wed, Mar 5, 2014 at 2:46 PM, Stephen Frost <sfrost@snowman.net> wrote:

I don't see why we can't do exactly what you're suggesting in core.

Because you can't (if you're defining core to mean 'not an
extension'). Functions can't be removed or changed because of legacy
application support. In an extension world, they can -- albeit not
'magically', but at least it can be done.

That simply isn't accurate on either level- if there is concern about
application support, that can apply equally to core and contrib, and we
certainly *can* remove and/or redefine functions in core with sufficient
cause. It's just not something we do lightly for things living in
either core or contrib.

For an example, consider the FDW API, particularly what we did between
9.1 and 9.2.

Well, we'll have to agree to disagree I suppose. Getting back on
topic, the question is 'what about jsonb/hstore2'? At this point my
interests are practical. I promised (heh) to bone up the docs. I'm on
vacation this weekend so it's looking like around sometime late next
week for that. In particular, it'd be helpful to get some kind of
read on the final disposition of hstore2.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#335Peter Geoghegan
pg@heroku.com
In reply to: Bruce Momjian (#328)
Re: jsonb and nested hstore

On Wed, Mar 5, 2014 at 11:32 AM, Bruce Momjian <bruce@momjian.us> wrote:

So, now knowing that hstore2 is just hierarchical hstore using the same
hstore type name, you are saying that we are keeping the
non-hierarchical code in contrib, and the rest goes into core --- that
makes sense, and from a code maintenance perspective, I like that the
non-hierarchical hstore code is not going in core.

Yeah.

It's hard to justify having a user-facing hstore2 on the grounds of
backwards compatibility, and giving those stuck on hstore the benefit
of all of these new capabilities. That's because we *cannot* really
preserve compatibility, AFAICT. Many of the lines of the patch
submitted are due to changes in the output format of hstore, and the
need to update the hstore tests' expected results to reflect these
changes. For example:

*************** select slice(hstore 'aa=>1, b=>2, c=>3',
*** 759,779 ****
(1 row)

select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']);
! slice
! --------------------
! "b"=>"2", "c"=>"3"
(1 row)

select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']);
! slice
! ---------------------
! "b"=>"2", "aa"=>"1"
(1 row)

select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']);
! slice
! -------------------------------
! "b"=>"2", "c"=>"3", "aa"=>"1"
(1 row)

  select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']))
--- 777,797 ----
  (1 row)

select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']);
! slice
! ----------------
! "b"=>2, "c"=>3
(1 row)

select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']);
! slice
! -----------------
! "b"=>2, "aa"=>1
(1 row)

select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']);
! slice
! -------------------------
! "b"=>2, "c"=>3, "aa"=>1
(1 row)

I could believe that there was at least something to be said for a
user-visible hstore2 if the new capabilites were available without
breaking application code, but it seems that isn't the case. Anyone
who is going to have to deal with some of this kind of incompatibility
one way or the other might as well switch over to jsonb entirely. When
changing aspects of our output format like this, the impact on users
is more or less a step function (of the number of small changes).

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#336Josh Berkus
josh@agliodbs.com
In reply to: Peter Geoghegan (#260)
Re: jsonb and nested hstore

On 03/05/2014 09:07 PM, Peter Geoghegan wrote:

It's hard to justify having a user-facing hstore2 on the grounds of
backwards compatibility, and giving those stuck on hstore the benefit
of all of these new capabilities. That's because we *cannot* really
preserve compatibility, AFAICT. Many of the lines of the patch
submitted are due to changes in the output format of hstore, and the
need to update the hstore tests' expected results to reflect these
changes. For example:

Thank you for checking that. Teodor's goal was that new-hstore be 100%
backwards-compatible with old-hstore. If we're breaking APIs, then it
doesn't really work to force users to upgrade the type, no?

Teodor, are these output changes things that can be made consistent, or
do we need separate hstore and hstore2 datatypes?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#337Teodor Sigaev
teodor@sigaev.ru
In reply to: Josh Berkus (#336)
Re: jsonb and nested hstore

Thank you for checking that. Teodor's goal was that new-hstore be 100%
backwards-compatible with old-hstore. If we're breaking APIs, then it

That's true. Binary format is fully compatible unless old hstore value has more
than 2^28 key-value pairs (256 mln which is far from reachable by memory
requirements). The single issue is a GiST index, GIN index should be recreated
to utilize new features.

doesn't really work to force users to upgrade the type, no?

Teodor, are these output changes things that can be made consistent, or
do we need separate hstore and hstore2 datatypes?

Introducing types in hstore causes this incompatibility - but I don't think
that's huge or even big problem. In most cases application does quoting (sets
"1" instead of just 1) to preserve SQL-injection and to protect hstore-forbidden
characters in hstore. Keys leaves untouched - it could be only a string.

That's possible to introduce GUC variable for i/o functions which will control
old "bug-to-bug" behavior. IMHO, this is much better option that stopping hstore
development or split hstore to two branches.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#338Peter Geoghegan
pg@heroku.com
In reply to: Teodor Sigaev (#337)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 12:23 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

That's possible to introduce GUC variable for i/o functions which will
control old "bug-to-bug" behavior. IMHO, this is much better option that
stopping hstore development or split hstore to two branches.

A GUC that controls i/o functions is generally considered to be an
unacceptable hack.

In what sense are we really stopping hstore development if hstore2
lives as jsonb? I have a hard time imagining someone dealing with the
incompatibility that a user-facing hstore2 would introduce, while
still preferring hstore syntax over json syntax given the choice.
There are very rich facilities for manipulating json available in
every programming language. The same is not true of hstore.

Having looked at the issue today, I think that the amount of redundant
code between a hstore2 in core as jsonb and hstore1 will be
acceptable. The advantages of making a clean-break in having to
support the legacy hstore disk format strengthen the case for doing so
too.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#339Teodor Sigaev
teodor@sigaev.ru
In reply to: Peter Geoghegan (#338)
Re: jsonb and nested hstore

In what sense are we really stopping hstore development if hstore2
lives as jsonb? I have a hard time imagining someone dealing with the
incompatibility that a user-facing hstore2 would introduce, while
still preferring hstore syntax over json syntax given the choice.
There are very rich facilities for manipulating json available in
every programming language. The same is not true of hstore.

It's true for perl. Syntax of hstore is close to hash/array syntax and it's easy
serialize/deserialize hstore to/from perl. Syntax of hstore was inspired by perl.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#340Peter Geoghegan
pg@heroku.com
In reply to: Teodor Sigaev (#339)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 1:32 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

It's true for perl. Syntax of hstore is close to hash/array syntax and it's
easy serialize/deserialize hstore to/from perl. Syntax of hstore was
inspired by perl.

I understand that. There is a module on CPAN called Pg::hstore that
will do this; it appears to have been around since 2011. I don't use
Perl, so I don't know a lot about it. Perhaps David Wheeler has an
opinion on the value of Perl-like syntax, as a long time Perl
enthusiast?

In any case, Perl has excellent support for JSON, just like every
other language - you are at no particular advantage in Perl by having
a format that happens to more closely resemble the format of Perl
hashes and arrays. I really feel that we should concentrate our
efforts on one standardized format here. It makes the effort to
integrate your good work, in a way that makes it available to everyone
so much easier.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#341Oleg Bartunov
obartunov@gmail.com
In reply to: Peter Geoghegan (#338)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 12:43 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Mar 6, 2014 at 12:23 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

That's possible to introduce GUC variable for i/o functions which will
control old "bug-to-bug" behavior. IMHO, this is much better option that
stopping hstore development or split hstore to two branches.

A GUC that controls i/o functions is generally considered to be an
unacceptable hack.

In what sense are we really stopping hstore development if hstore2
lives as jsonb? I have a hard time imagining someone dealing with the
incompatibility that a user-facing hstore2 would introduce, while
still preferring hstore syntax over json syntax given the choice.
There are very rich facilities for manipulating json available in
every programming language. The same is not true of hstore.

Having looked at the issue today, I think that the amount of redundant
code between a hstore2 in core as jsonb and hstore1 will be
acceptable. The advantages of making a clean-break in having to
support the legacy hstore disk format strengthen the case for doing so
too.

Heh, let's not to do an implusive decision about hstore2. I agree,
that jsonb has
a lot of facilities, but don't forget, that json(b) has to follow standard and
in that sense it's more constrained than hstore, which we could further
develop to support some interesting features, which will never be implemented
in json(b). Also, it'd be a bit awkward after working on nested
hstore and declaring it
on several conferences (Engine Yard has sponsored part of our hstore
work), suddenly
break people expectation and say, that our work has moved to core to
provide json
some very cool features, good bye, hstore users :( I'm afraid people
will not understand us.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#342Andrew Dunstan
andrew@dunslane.net
In reply to: Oleg Bartunov (#341)
Re: jsonb and nested hstore

On 03/06/2014 08:16 AM, Oleg Bartunov wrote:

On Thu, Mar 6, 2014 at 12:43 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Mar 6, 2014 at 12:23 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

That's possible to introduce GUC variable for i/o functions which will
control old "bug-to-bug" behavior. IMHO, this is much better option that
stopping hstore development or split hstore to two branches.

A GUC that controls i/o functions is generally considered to be an
unacceptable hack.

In what sense are we really stopping hstore development if hstore2
lives as jsonb? I have a hard time imagining someone dealing with the
incompatibility that a user-facing hstore2 would introduce, while
still preferring hstore syntax over json syntax given the choice.
There are very rich facilities for manipulating json available in
every programming language. The same is not true of hstore.

Having looked at the issue today, I think that the amount of redundant
code between a hstore2 in core as jsonb and hstore1 will be
acceptable. The advantages of making a clean-break in having to
support the legacy hstore disk format strengthen the case for doing so
too.

Heh, let's not to do an implusive decision about hstore2. I agree,
that jsonb has
a lot of facilities, but don't forget, that json(b) has to follow standard and
in that sense it's more constrained than hstore, which we could further
develop to support some interesting features, which will never be implemented
in json(b). Also, it'd be a bit awkward after working on nested
hstore and declaring it
on several conferences (Engine Yard has sponsored part of our hstore
work), suddenly
break people expectation and say, that our work has moved to core to
provide json
some very cool features, good bye, hstore users :( I'm afraid people
will not understand us.

Oleg,

I hear you, and largely agree, as long as the compatibility issue is
solved. If it's not, I think inventing a new hstore2 type is probably a
lousy way to go.

For good or ill, the world has pretty much settled on wanting to use
json for lightweight treeish data. That's where we'll get the most
impact. Virtually every programming language (including Perl) has good
support for json.

I'm not sure what the constraints of json that you might want to break
are. Perhaps you'd like to specify.

Whatever we do, rest assured your work won't go to waste.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#343Bruce Momjian
bruce@momjian.us
In reply to: Andrew Dunstan (#342)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 09:33:18AM -0500, Andrew Dunstan wrote:

I hear you, and largely agree, as long as the compatibility issue is
solved. If it's not, I think inventing a new hstore2 type is
probably a lousy way to go.

For good or ill, the world has pretty much settled on wanting to use
json for lightweight treeish data. That's where we'll get the most
impact. Virtually every programming language (including Perl) has
good support for json.

I'm not sure what the constraints of json that you might want to
break are. Perhaps you'd like to specify.

Whatever we do, rest assured your work won't go to waste.

OK, just to summarize:

JSONB and everything it shares with hstore will be in core
hstore-specific code stays in contrib
hstore contrib will create an hstore type to call contrib and core code
9.4 hstore has some differences from pre-9.4

The question is whether we change/improve hstore in 9.4, or create an
hstore2 that is the improved hstore for 9.4 and keep hstore identical to
pre-9.4. That last option looks an awful like the dreaded VARCHAR2.

What can we do to help people migrate to an hstore type that supports
data types? Is there a function we can give them to flag possible
problem data, or give them some function to format things the old way
for migrations, etc. If they are going to have to rewrite all their old
data, why bother with a backward-compatible binary format? Is it only
the client applications that will need to be changed? How would we
instruct users on the necessary changes?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#344Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#343)
Re: jsonb and nested hstore

Bruce Momjian <bruce@momjian.us> writes:

OK, just to summarize:

JSONB and everything it shares with hstore will be in core
hstore-specific code stays in contrib
hstore contrib will create an hstore type to call contrib and core code
9.4 hstore has some differences from pre-9.4

I've got a problem with the last part of that. AFAICS, the value
proposition for hstore2 largely fails if it's not 100% upward compatible
with existing hstore, both as to on-disk storage and as to application-
visible behavior. If you've got to adapt your application anyway, why
not switch to JSONB which is going to offer a lot of benefits in terms
of available code you can work with?

Although I've not looked at the patch, it was claimed upthread that there
were changes in the I/O format for existing test cases, for example.
IMO, that's an absolute dead no-go.

The question is whether we change/improve hstore in 9.4, or create an
hstore2 that is the improved hstore for 9.4 and keep hstore identical to
pre-9.4. That last option looks an awful like the dreaded VARCHAR2.

I think hstore2 as a separate type isn't likely to be a win either.

The bottom line here is that hstore2 is more or less what we'd agreed to
doing back at the last PGCon, but that decision has now been obsoleted by
events in the JSON area. If jsonb gets in, I think we probably end up
rejecting hstore2 as such. Or at least, that's what we should do IMO.
contrib/hstore is now a legacy type and we shouldn't be putting additional
work into it, especially not work that breaks backwards compatibility.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#345Ronan Dunklau
ronan.dunklau@dalibo.com
In reply to: Andrew Dunstan (#342)
Re: jsonb and nested hstore

Le jeudi 6 mars 2014 09:33:18 Andrew Dunstan a écrit :

On 03/06/2014 08:16 AM, Oleg Bartunov wrote:

On Thu, Mar 6, 2014 at 12:43 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Mar 6, 2014 at 12:23 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

That's possible to introduce GUC variable for i/o functions which will
control old "bug-to-bug" behavior. IMHO, this is much better option that
stopping hstore development or split hstore to two branches.

A GUC that controls i/o functions is generally considered to be an
unacceptable hack.

In what sense are we really stopping hstore development if hstore2
lives as jsonb? I have a hard time imagining someone dealing with the
incompatibility that a user-facing hstore2 would introduce, while
still preferring hstore syntax over json syntax given the choice.
There are very rich facilities for manipulating json available in
every programming language. The same is not true of hstore.

Having looked at the issue today, I think that the amount of redundant
code between a hstore2 in core as jsonb and hstore1 will be
acceptable. The advantages of making a clean-break in having to
support the legacy hstore disk format strengthen the case for doing so
too.

Heh, let's not to do an implusive decision about hstore2. I agree,
that jsonb has
a lot of facilities, but don't forget, that json(b) has to follow standard
and in that sense it's more constrained than hstore, which we could
further develop to support some interesting features, which will never be
implemented in json(b). Also, it'd be a bit awkward after working on
nested
hstore and declaring it
on several conferences (Engine Yard has sponsored part of our hstore
work), suddenly
break people expectation and say, that our work has moved to core to
provide json
some very cool features, good bye, hstore users :( I'm afraid people
will not understand us.

Oleg,

I hear you, and largely agree, as long as the compatibility issue is
solved. If it's not, I think inventing a new hstore2 type is probably a
lousy way to go.

For good or ill, the world has pretty much settled on wanting to use
json for lightweight treeish data. That's where we'll get the most
impact. Virtually every programming language (including Perl) has good
support for json.

I'm not sure what the constraints of json that you might want to break
are. Perhaps you'd like to specify.

I haven't followed the whole thread, but json is really restrictive on the
supported types: a hierarchical hstore could maybe support more types
(timestamp comes to mind) as its values, which is not a valid data type in the
json spec.

Whatever we do, rest assured your work won't go to waste.

cheers

andrew

--
Ronan Dunklau
http://dalibo.com - http://dalibo.org

#346Magnus Hagander
magnus@hagander.net
In reply to: Tom Lane (#344)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 4:25 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Bruce Momjian <bruce@momjian.us> writes:

OK, just to summarize:

JSONB and everything it shares with hstore will be in core
hstore-specific code stays in contrib
hstore contrib will create an hstore type to call contrib and core

code

9.4 hstore has some differences from pre-9.4

I've got a problem with the last part of that. AFAICS, the value
proposition for hstore2 largely fails if it's not 100% upward compatible
with existing hstore, both as to on-disk storage and as to application-
visible behavior. If you've got to adapt your application anyway, why
not switch to JSONB which is going to offer a lot of benefits in terms
of available code you can work with?

Although I've not looked at the patch, it was claimed upthread that there
were changes in the I/O format for existing test cases, for example.
IMO, that's an absolute dead no-go.

The question is whether we change/improve hstore in 9.4, or create an
hstore2 that is the improved hstore for 9.4 and keep hstore identical to
pre-9.4. That last option looks an awful like the dreaded VARCHAR2.

I think hstore2 as a separate type isn't likely to be a win either.

The bottom line here is that hstore2 is more or less what we'd agreed to
doing back at the last PGCon, but that decision has now been obsoleted by
events in the JSON area. If jsonb gets in, I think we probably end up
rejecting hstore2 as such. Or at least, that's what we should do IMO.
contrib/hstore is now a legacy type and we shouldn't be putting additional
work into it, especially not work that breaks backwards compatibility.

(not read up on the full details of the thread, sorry if I'm re-iterating
something)

I think we definitely want/need to maintain hstore compatibility. A
completely separate hstore2 type that's not backwards compatible makes very
little sense.

However, if the new hstore type (compatible with the old one) is the
wrapper around jsonb, rather than the other way around, I don't see any
problem with it at all. Most future users are almost certainly going to use
the json interfaces, but we don't want to leave upgraded users behind. (But
of course it has to actually maintain backwards compatibility for that
argument to hold)

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#347Tom Lane
tgl@sss.pgh.pa.us
In reply to: Magnus Hagander (#346)
Re: jsonb and nested hstore

Magnus Hagander <magnus@hagander.net> writes:

However, if the new hstore type (compatible with the old one) is the
wrapper around jsonb, rather than the other way around, I don't see any
problem with it at all. Most future users are almost certainly going to use
the json interfaces, but we don't want to leave upgraded users behind. (But
of course it has to actually maintain backwards compatibility for that
argument to hold)

Yeah --- all of this turns on whether hstore improvements can be 100%
upwards compatible or not. If they are, I don't object to including them;
I'd have said it was wasted effort, but if the work is already done then
that's moot.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#348Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#347)
Re: jsonb and nested hstore

On 03/06/2014 10:46 AM, Tom Lane wrote:

Magnus Hagander <magnus@hagander.net> writes:

However, if the new hstore type (compatible with the old one) is the
wrapper around jsonb, rather than the other way around, I don't see any
problem with it at all. Most future users are almost certainly going to use
the json interfaces, but we don't want to leave upgraded users behind. (But
of course it has to actually maintain backwards compatibility for that
argument to hold)

Yeah --- all of this turns on whether hstore improvements can be 100%
upwards compatible or not. If they are, I don't object to including them;
I'd have said it was wasted effort, but if the work is already done then
that's moot.

Clearly there are people who want it, or else they would not have
sponsored the work.

We seem to have an emerging consensus on the compatibility issue.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#349Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Tom Lane (#347)
Re: jsonb and nested hstore

On 03/06/2014 05:46 PM, Tom Lane wrote:

Magnus Hagander <magnus@hagander.net> writes:

However, if the new hstore type (compatible with the old one) is the
wrapper around jsonb, rather than the other way around, I don't see any
problem with it at all. Most future users are almost certainly going to use
the json interfaces, but we don't want to leave upgraded users behind. (But
of course it has to actually maintain backwards compatibility for that
argument to hold)

Yeah --- all of this turns on whether hstore improvements can be 100%
upwards compatible or not. If they are, I don't object to including them;

There are reasons for *not* wanting the new hstore2 functionality. If
you don't want nesting, for example, with the new type you're going to
need to add a constraint to forbid that. Ugh. Many applications are
happy with the current functionality, a simple string key/value
dictionary, and for them the new features are not an improvement.

As an analogy, adding significant new functionality like nesting to the
existing hstore type is like suddenly adding the time of day to the date
datatype. It might be useful in many cases. And an existing application
can leave the hour and minute fields zero, so it's backwards-compatible.
But as soon as someone inserts a datum that uses the hour and minute
fields, it will confuse the application that doesn't know about that.

I haven't been following these discussions closely, but for those
reasons, I thought hstore2 was going to be a separate type. I don't
think there are very many applications that would be interested in
"upgrading" from the current hstore to the new hstore2 type. More
likely, the new data type is useful for many applications that couldn't
have used hstore before because it didn't support nesting or was too
loosely typed. And old applications that are already using hstore are
perfectly happy with the status quo.

Let's not mess with the existing hstore datatype. For what it does, it
works great.

Likewise, jsonb is significantly different from hstore2, so it should be
a separate data type. Frankly I don't understand what the problem is
with doing that. I don't have a problem with copy-pasting the common parts.

BTW, now that I look at the nested hstore patch, I'm disappointed to see
that it only supports a few hardcoded datatypes. Call me naive, but
somehow I thought it would support *all* PostgreSQL datatypes, built-in
or user-defined. I realize that's a tall order, but that's what I
thought it did. Since it doesn't, color me unimpressed. It's really not
any better than json, I don't see why anyone would prefer it over json.
Not that I particularly like json, but it's a format a lot of people are
familiar with.

So here my opinion on what we should do:

1. Forget about hstore2
2. Add GIN and GIST operator classes to jsonb, if they're ready for
commit pretty darn soon. If not, punt them to next release.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#350Merlin Moncure
mmoncure@gmail.com
In reply to: Heikki Linnakangas (#349)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 11:28 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

So here my opinion on what we should do:

1. Forget about hstore2
2. Add GIN and GIST operator classes to jsonb, if they're ready for commit
pretty darn soon. If not, punt them to next release.

For #2, would we maintain the hstore syntax for the searching
operators. For example,

SELECT count(*) FROM jsonb_schema WHERE tabledata @> 'columns =>
{{column_name=>total_time}}';

Note the hstore-ish => in the searching operator.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#351Oleg Bartunov
obartunov@gmail.com
In reply to: Tom Lane (#344)
Re: jsonb and nested hstore

Hi there,

Looks like consensus is done. I and Teodor are not happy with it, but
what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

As for the old hstore I think it'd be nice to add gin_hstore_hash_ops,
so hstore users will benefit from 9.4 release. There is no
compatibiliy issue, so I think this could be harmless.

Oleg

On Thu, Mar 6, 2014 at 7:25 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Bruce Momjian <bruce@momjian.us> writes:

OK, just to summarize:

JSONB and everything it shares with hstore will be in core
hstore-specific code stays in contrib
hstore contrib will create an hstore type to call contrib and core code
9.4 hstore has some differences from pre-9.4

I've got a problem with the last part of that. AFAICS, the value
proposition for hstore2 largely fails if it's not 100% upward compatible
with existing hstore, both as to on-disk storage and as to application-
visible behavior. If you've got to adapt your application anyway, why
not switch to JSONB which is going to offer a lot of benefits in terms
of available code you can work with?

Although I've not looked at the patch, it was claimed upthread that there
were changes in the I/O format for existing test cases, for example.
IMO, that's an absolute dead no-go.

The question is whether we change/improve hstore in 9.4, or create an
hstore2 that is the improved hstore for 9.4 and keep hstore identical to
pre-9.4. That last option looks an awful like the dreaded VARCHAR2.

I think hstore2 as a separate type isn't likely to be a win either.

The bottom line here is that hstore2 is more or less what we'd agreed to
doing back at the last PGCon, but that decision has now been obsoleted by
events in the JSON area. If jsonb gets in, I think we probably end up
rejecting hstore2 as such. Or at least, that's what we should do IMO.
contrib/hstore is now a legacy type and we shouldn't be putting additional
work into it, especially not work that breaks backwards compatibility.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#352Andrew Dunstan
andrew@dunslane.net
In reply to: Oleg Bartunov (#351)
Re: jsonb and nested hstore

On 03/06/2014 12:50 PM, Oleg Bartunov wrote:

Hi there,

Looks like consensus is done. I and Teodor are not happy with it, but
what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

We don't normally credit sponsors in commits, but if I'm doing the
commit I promise you guys would certainly get major credit as authors.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#353Oleg Bartunov
obartunov@gmail.com
In reply to: Andrew Dunstan (#352)
Re: jsonb and nested hstore

I meant in Release Notes for 9.4

On Thu, Mar 6, 2014 at 10:26 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 03/06/2014 12:50 PM, Oleg Bartunov wrote:

Hi there,

Looks like consensus is done. I and Teodor are not happy with it, but
what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

We don't normally credit sponsors in commits, but if I'm doing the commit I
promise you guys would certainly get major credit as authors.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#354Josh Berkus
josh@agliodbs.com
In reply to: Robert Haas (#305)
Re: jsonb and nested hstore

On 03/06/2014 07:00 AM, Bruce Momjian wrote:

What can we do to help people migrate to an hstore type that supports
data types? Is there a function we can give them to flag possible
problem data, or give them some function to format things the old way
for migrations, etc. If they are going to have to rewrite all their old
data, why bother with a backward-compatible binary format? Is it only
the client applications that will need to be changed? How would we
instruct users on the necessary changes?

So, from what I've been able to check:

The actual storage upgrade of hstore-->hstore2 is fairly painless from
the user perspective; they don't have to do anything. The problem is
that the input/output strings are different, something which I didn't
think to check for (and Peter did), and which will break applications
relying on Hstore, since the drivers which support Hstore (like
psycopg2) rely on string-parsing to convert it. I haven't
regression-tested hstore2 against psycopg2 since I don't have a good
test, but that would be a useful thing to do.

Hstore2 supports the same limited data types as JSON does, and not any
additional ones.

This makes an hstore2 of dubious value unless the compatibility issues
can be solved conclusively.

Is that all correct? Have I missed something?

On 03/06/2014 09:50 AM, Oleg Bartunov wrote:> Looks like consensus is
done. I and Teodor are not happy with it, but

what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

We don't generally credit companies in the release notes, since if we
started, where would we stop? However, we *do* credit them in the press
release, and I'll make a note of the EY sponsorship, especially since
it's also good PR.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#355Oleg Bartunov
obartunov@gmail.com
In reply to: Josh Berkus (#354)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 10:54 PM, Josh Berkus <josh@agliodbs.com> wrote:
g?

On 03/06/2014 09:50 AM, Oleg Bartunov wrote:> Looks like consensus is
done. I and Teodor are not happy with it, but

what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

We don't generally credit companies in the release notes, since if we
started, where would we stop? However, we *do* credit them in the press
release, and I'll make a note of the EY sponsorship, especially since
it's also good PR.

I think press release is fine. We waited a long time for sponsorship
of our work and EY help was crucial.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#356Daniele Varrazzo
daniele.varrazzo@gmail.com
In reply to: Josh Berkus (#354)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 6:54 PM, Josh Berkus <josh@agliodbs.com> wrote:

The actual storage upgrade of hstore-->hstore2 is fairly painless from
the user perspective; they don't have to do anything. The problem is
that the input/output strings are different, something which I didn't
think to check for (and Peter did), and which will break applications
relying on Hstore, since the drivers which support Hstore (like
psycopg2) rely on string-parsing to convert it. I haven't
regression-tested hstore2 against psycopg2 since I don't have a good
test, but that would be a useful thing to do.

Hello, psycopg developer here. Not following the entire thread as it's
quite articulated and not of my direct interest (nor comprehension).
But if you throw at me a few test cases I can make sure psycopg can
parse them much before hstore2 is released.

FYI I have a trigger that highlights me the -hackers messages
mentioning psycopg, so just mentioning it is enough for me to take a
better look. But if you want a more active collaboration just ask.

Thank you,

-- Daniele

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#357Peter Geoghegan
pg@heroku.com
In reply to: Daniele Varrazzo (#356)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 12:58 PM, Daniele Varrazzo
<daniele.varrazzo@gmail.com> wrote:

Hello, psycopg developer here. Not following the entire thread as it's
quite articulated and not of my direct interest (nor comprehension).
But if you throw at me a few test cases I can make sure psycopg can
parse them much before hstore2 is released.

I don't think that'll be necessary. Any break in compatibility in the
hstore format has been ruled a non-starter for having hstore support
nested data structures. I believe on balance we're content to let
hstore continue to be hstore. jsonb support would certainly be
interesting, though.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#358Daniele Varrazzo
daniele.varrazzo@gmail.com
In reply to: Peter Geoghegan (#357)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 9:10 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Mar 6, 2014 at 12:58 PM, Daniele Varrazzo
<daniele.varrazzo@gmail.com> wrote:

Hello, psycopg developer here. Not following the entire thread as it's
quite articulated and not of my direct interest (nor comprehension).
But if you throw at me a few test cases I can make sure psycopg can
parse them much before hstore2 is released.

I don't think that'll be necessary. Any break in compatibility in the
hstore format has been ruled a non-starter for having hstore support
nested data structures. I believe on balance we're content to let
hstore continue to be hstore. jsonb support would certainly be
interesting, though.

Cool, just let me know what you would expect a well-behaved client
library to behave.

-- Daniele

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#359Josh Berkus
josh@agliodbs.com
In reply to: Robert Haas (#305)
Re: jsonb and nested hstore

On 03/06/2014 12:58 PM, Daniele Varrazzo wrote:

On Thu, Mar 6, 2014 at 6:54 PM, Josh Berkus <josh@agliodbs.com> wrote:

The actual storage upgrade of hstore-->hstore2 is fairly painless from
the user perspective; they don't have to do anything. The problem is
that the input/output strings are different, something which I didn't
think to check for (and Peter did), and which will break applications
relying on Hstore, since the drivers which support Hstore (like
psycopg2) rely on string-parsing to convert it. I haven't
regression-tested hstore2 against psycopg2 since I don't have a good
test, but that would be a useful thing to do.

Hello, psycopg developer here. Not following the entire thread as it's
quite articulated and not of my direct interest (nor comprehension).
But if you throw at me a few test cases I can make sure psycopg can
parse them much before hstore2 is released.

Looks like that won't be necessary, Daniele. But thanks for speaking up!

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#360Bruce Momjian
bruce@momjian.us
In reply to: Oleg Bartunov (#351)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 09:50:56PM +0400, Oleg Bartunov wrote:

Hi there,

Looks like consensus is done. I and Teodor are not happy with it, but
what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

OK, if we are going with an unchanged hstore in contrib and a new JSONB,
there is no reason to wack around JSONB to be binary compatible with the
old hstore format. What sacrifices did we need to make to have JSBONB
be binary compatible with hstore, can those sacrifices be removed, and
can that be done in time for 9.4?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#361Bruce Momjian
bruce@momjian.us
In reply to: Ronan Dunklau (#345)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 04:33:08PM +0100, Ronan Dunklau wrote:

I'm not sure what the constraints of json that you might want to break
are. Perhaps you'd like to specify.

I haven't followed the whole thread, but json is really restrictive on the
supported types: a hierarchical hstore could maybe support more types
(timestamp comes to mind) as its values, which is not a valid data type in the
json spec.

Yes, I can see this as an idea for a new data type that allows
hierarchical storage of key/value pairs where the value can be any
Postgres data type. It wouldn't be called hstore, or hstore2, but
something else.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#362Merlin Moncure
mmoncure@gmail.com
In reply to: Bruce Momjian (#360)
Re: jsonb and nested hstore

On Thu, Mar 6, 2014 at 10:33 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Thu, Mar 6, 2014 at 09:50:56PM +0400, Oleg Bartunov wrote:

Hi there,

Looks like consensus is done. I and Teodor are not happy with it, but
what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

OK, if we are going with an unchanged hstore in contrib and a new JSONB,
there is no reason to wack around JSONB to be binary compatible with the
old hstore format. What sacrifices did we need to make to have JSBONB
be binary compatible with hstore, can those sacrifices be removed, and
can that be done in time for 9.4?

Also,
*) what hstore2 features (if any) that are not already reflected in
the jsonb type are going to be moved to josnb for 9.4?
*) if the answer above is anything but 'nothing', what hstore-isms are
going to be adjusted in the process of doing so? Presumably there
would be same function name changes to put them in the jsonb style but
also the hstore sytnax ('=>') is going to be embedded in some of the
search operators and possibly other things. Is that going change?

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#363Andrew Dunstan
andrew@dunslane.net
In reply to: Bruce Momjian (#360)
Re: jsonb and nested hstore

On 03/06/2014 11:33 PM, Bruce Momjian wrote:

On Thu, Mar 6, 2014 at 09:50:56PM +0400, Oleg Bartunov wrote:

Hi there,

Looks like consensus is done. I and Teodor are not happy with it, but
what we can do :) One thing I want to do is to reserve our
contribution to the flagship feature (jsonb), particularly, "binary
storage for nested structures and indexing. Their work was sponsored
by Engine Yard".

OK, if we are going with an unchanged hstore in contrib and a new JSONB,
there is no reason to wack around JSONB to be binary compatible with the
old hstore format. What sacrifices did we need to make to have JSBONB
be binary compatible with hstore, can those sacrifices be removed, and
can that be done in time for 9.4?

IIRC The sacrifice was one bit in the header (i.e. in the first int
after the varlena header). We could now repurpose that (for example if
we ever decided to use a new format).

Oleg and Teodor made most of the adjustments on the hstore(2) side (e.g.
providing for scalar roots, providing for json typing of scalars so
everything isn't just a string).

Can the architecture be changed? No. If we think it's not good enough we
would have to kiss jsonb goodbye for 9.4 and go back to the drawing
board. But I haven't seen any such suggestion from anyone who has been
reviewing it (e.g. Andres or Peter).

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#364Bruce Momjian
bruce@momjian.us
In reply to: Andrew Dunstan (#363)
Re: jsonb and nested hstore

On Fri, Mar 7, 2014 at 11:35:41AM -0500, Andrew Dunstan wrote:

IIRC The sacrifice was one bit in the header (i.e. in the first int
after the varlena header). We could now repurpose that (for example
if we ever decided to use a new format).

Oleg and Teodor made most of the adjustments on the hstore(2) side
(e.g. providing for scalar roots, providing for json typing of
scalars so everything isn't just a string).

Can the architecture be changed? No. If we think it's not good
enough we would have to kiss jsonb goodbye for 9.4 and go back to
the drawing board. But I haven't seen any such suggestion from
anyone who has been reviewing it (e.g. Andres or Peter).

We are going to be stuck with the JSONB binary format we ship in 9.4 so
I am asking if there are things we should do to improve it, now that we
know we don't need backward compatibility.

If they can be done for 9.4, great, if not, we have to decide if these
suboptimal cases are enough for us to delay the data type until 9.5. I
don't know the answer, but I have to ask the question.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#365Andrew Dunstan
andrew@dunslane.net
In reply to: Bruce Momjian (#364)
Re: jsonb and nested hstore

On 03/07/2014 11:45 AM, Bruce Momjian wrote:

On Fri, Mar 7, 2014 at 11:35:41AM -0500, Andrew Dunstan wrote:

IIRC The sacrifice was one bit in the header (i.e. in the first int
after the varlena header). We could now repurpose that (for example
if we ever decided to use a new format).

Oleg and Teodor made most of the adjustments on the hstore(2) side
(e.g. providing for scalar roots, providing for json typing of
scalars so everything isn't just a string).

Can the architecture be changed? No. If we think it's not good
enough we would have to kiss jsonb goodbye for 9.4 and go back to
the drawing board. But I haven't seen any such suggestion from
anyone who has been reviewing it (e.g. Andres or Peter).

We are going to be stuck with the JSONB binary format we ship in 9.4 so
I am asking if there are things we should do to improve it, now that we
know we don't need backward compatibility.

If they can be done for 9.4, great, if not, we have to decide if these
suboptimal cases are enough for us to delay the data type until 9.5. I
don't know the answer, but I have to ask the question.

AFAIK, there is no sacrifice of optimality. hstore2 and jsonb were
essentially two ways of spelling the same data, the domains were
virtually identical (hstore might have been a bit more liberal about
numeric input).

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#366Bruce Momjian
bruce@momjian.us
In reply to: Andrew Dunstan (#365)
Re: jsonb and nested hstore

On Fri, Mar 7, 2014 at 11:57:48AM -0500, Andrew Dunstan wrote:

If they can be done for 9.4, great, if not, we have to decide if these
suboptimal cases are enough for us to delay the data type until 9.5. I
don't know the answer, but I have to ask the question.

AFAIK, there is no sacrifice of optimality. hstore2 and jsonb were
essentially two ways of spelling the same data, the domains were
virtually identical (hstore might have been a bit more liberal about
numeric input).

OK, it sounds like the adjustments are minimal, like not using the
high-order bit.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#367David E. Wheeler
david@justatheory.com
In reply to: Peter Geoghegan (#340)
Re: jsonb and nested hstore

On Mar 6, 2014, at 1:51 AM, Peter Geoghegan <pg@heroku.com> wrote:

It's true for perl. Syntax of hstore is close to hash/array syntax and it's
easy serialize/deserialize hstore to/from perl. Syntax of hstore was
inspired by perl.

I understand that. There is a module on CPAN called Pg::hstore that
will do this; it appears to have been around since 2011. I don't use
Perl, so I don't know a lot about it. Perhaps David Wheeler has an
opinion on the value of Perl-like syntax, as a long time Perl
enthusiast?

HSTORE was inspired by the syntax of Perl hash declarations, but it is not compatible. Notably, HSTORE the HSTORE can have a value `NULL`, while in Perl hashes it’s `undef`. So you cannot simply `eval` an HSTORE to get a Perl hash unless you are certain there are no NULLs.

Besides, string eval in Perl is considered unsafe. Parsing is *much* safer.

In any case, Perl has excellent support for JSON, just like every
other language - you are at no particular advantage in Perl by having
a format that happens to more closely resemble the format of Perl
hashes and arrays. I really feel that we should concentrate our
efforts on one standardized format here. It makes the effort to
integrate your good work, in a way that makes it available to everyone
so much easier.

I agree. I like HSTORE, but now that JSON is so standard (in fact, as of this week, a *real* standard! http://rfc7159.net/rfc7159), and its support is so much better than that of HSTORE, including in Perl, I believe that it should be priority over HSTORE. I’m happy if HSTORE has the same functionality as JSONB, but given the choice, all other things being equal, as a Perl hacker I will always choose JSONB.

Best,

David

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#368Peter Geoghegan
pg@heroku.com
In reply to: Bruce Momjian (#366)
1 attachment(s)
Re: jsonb and nested hstore

On Fri, Mar 7, 2014 at 9:00 AM, Bruce Momjian <bruce@momjian.us> wrote:

OK, it sounds like the adjustments are minimal, like not using the
high-order bit.

Attached patch is a refinement of the work of Oleg, Teodor and Andrew.
Revisions are mostly my own, although Andrew contributed too.

Changes include:

* Extensive relocation, and moderate restructuring of code. Many
comments added, while many existing comments were copy-edited. Nothing
remains in contrib. jsonb is a distinct, in-core type with no
user-visible relationship to hstore. There is no code dependency
between the two. The amount of code redundancy this turned out to
create (between jsonb and an unchanged hstore) is, in my estimation,
quite acceptable.

* B-Tree and hash operator classes for the core type are included. A
GiST operator class, and two GIN operator classes are also included.
Obviously this is where I spent most time by far.

* Everything else that was in hstore in the last revision (the
complement of the hstore2 opclasses) is removed entirely. The patch is
much smaller. If we just consider code (excluding tests and
documentation), the diffstat seems far more manageable:

***SNIP***
src/backend/catalog/system_views.sql | 8 +
src/backend/utils/adt/Makefile | 10 +-
src/backend/utils/adt/json.c | 42 +-
src/backend/utils/adt/jsonb.c | 461 +++++++++++
src/backend/utils/adt/jsonb_gin.c | 471 +++++++++++
src/backend/utils/adt/jsonb_gist.c | 699 ++++++++++++++++
src/backend/utils/adt/jsonb_op.c | 565 +++++++++++++
src/backend/utils/adt/jsonb_support.c | 1350 +++++++++++++++++++++++++++++++
src/backend/utils/adt/jsonfuncs.c | 1155 +++++++++++++++++++++++---
src/backend/utils/adt/numeric.c | 39 +
src/include/catalog/pg_amop.h | 36 +
src/include/catalog/pg_amproc.h | 18 +-
src/include/catalog/pg_cast.h | 4 +
src/include/catalog/pg_opclass.h | 5 +
src/include/catalog/pg_operator.h | 37 +-
src/include/catalog/pg_opfamily.h | 5 +
src/include/catalog/pg_proc.h | 103 ++-
src/include/catalog/pg_type.h | 8 +
src/include/funcapi.h | 9 +
src/include/utils/json.h | 15 +
src/include/utils/jsonapi.h | 8 +-
src/include/utils/jsonb.h | 245 ++++++
src/include/utils/numeric.h | 1 +
***SNIP***

That's less than 5,000 lines of code, which is actually not terribly
much for a new datatype with voluminous though simple code to
implement each of the new operators. I would like to emphasize that
100% of changes to C files occur at src/backend/utils/adt/* .

On reflection I realize that I didn't like the "type aware" operators
that existed for hstore in prior revisions, and I don't have a sense
that I've made cuts to the patch to reduce the code footprint that I'd
not have made with no constraints on time. While there are one or two
exceptions to this, surprisingly, that's all. Frequently, it's
sufficient to interact with jsonb using the jsonb shadow type system,
and where it isn't then I think that shadow-type-specific operators
are the wrong way to go.

* Extensive additional documentation. References to the very new JSON
RFC. I think that this revision is in general a lot more coherent, and
I found that reflecting on what idiomatic usage should look like while
writing the documentation brought clarity to my thoughts on how the
code should be structured. The documentation is worth a read if you
want to get a better sense of what the patch is about relatively
quickly.

* Operators for jsonb that comprise the default B-Tree operator class
no longer look strange (i.e., they're no longer deliberately ugly to
discourage their use, because unlike with hstore this doesn't make
sense, particularly when sorting jsonb that only the shadow type
system knows to be "raw strings").

* Numerous bug fixes.

* Since preserving on-disk compatibility with hstore1 is no longer an
objective, I was able to remove some code there.

* The jsonb type has test coverage more or less equivalent to the
previous revision where the now-expunged hstore2 type's output was
tested (though there are some more tests that I myself added towards
the end of this past week as part of the process of fixing bugs). I
thank my Heroku colleague Maciek Sakrejda for working on this. This
was largely a mechanical process.

Concerns:

* I would like to have a lot more comments on the GiST code from its
authors, because it's currently quite difficult to follow. A couple of
the bigger GiST support routines currently have zero useful comments.
I would deem it acceptable to lose GiST support entirely for now, if
the 700 line saving made it possible to squeeze this into 9.4. ISTM
that the two GIN operator classes are far more useful, and their code
is quite a bit simpler (apparently the state of the art here is a
system that can query JSON-like structures reasonably well, but has a
global write lock, which to me suggests that GIN represents a better
trade-off).

* That GiST code has bugs, too. I ran out of steam for fixing it
before posting. The basic symptom is I saw is that when creating a
large GiST index, sometimes you'll hit an infinite loop here:

warning: no loadable sections found in added symbol-file
system-supplied DSO at 0x7fffc70eb000
0x0000000000829de5 in hemdistsign (a=0x7fd6c833b260
"*\342\060\331\351b\201\250\002\060\212)\304\337\a*", b=0x17084f0 "")
at jsonb_gist.c:655
655 if (GETBIT(a, i) != GETBIT(b, i))
(gdb)
(gdb)
(gdb) n
656 dist++;
(gdb)
653 LOOPBIT
(gdb)
655 if (GETBIT(a, i) != GETBIT(b, i))
(gdb)
653 LOOPBIT

It's possible that I regress things in a way not captured by the
regression test, and that this is my fault, but whatever the case
let's either get it fixed or cut it for now.

* The jsonb_hash_ops non-default GIN opclass is broken. It has its own
idiosyncratic notion of what constitutes containment, that sees it
only return, say, jsonb arrays that have a matching string as their
"leftmost" element (if we ask it if it contains within it another
array with the same string). Because of the limited number of
indexable operators (only @>), I'd put this opclass in the same
category as GiST in terms of my willingness to forgo it for a release,
even if it did receive a loud applause at pgConf.EU. Again, it might
be some disparity between the opertors as they existed in hstore2 at
one time, and as they exist in the core code now, but I doubt it, not
least since the regression tests didn't pick this up, and it's such a
basic thing. Perhaps Oleg and Teodor just need to explain this to me.

* More generally, I do not understand GiST and GIN to the same extent
as several other people (even after excluding the authors of this
patch). Feedback on those aspects from others would be particularly
useful at this point. Is the text-based storage format (i.e. the
serialization logic for the GiST and GIN default operator classes)
appropriate? I tweaked the serialization logic here a bit, in order to
fix a bug. Overall, most bugs fixed were along the lines of "index
scans are not in agreement with sequential scans", and most fixes were
simple enough. Roughly speaking, on a few occasions opclass support
code needed to be reconciled with jsonb code (or existing core code
that it's implemented in terms of). None of these bugs were difficult
to fix, or in any way scary. Still, as I mentioned there are some open
items here.

* I haven't given the I/O code much attention, nor much of the code
written by Andrew. In general I haven't done all that much with the
parts of the patch that were previously proposed for core inclusion.

* I could have spent more time on testing and breaking things some
more, but didn't want to block on that. The more scrutiny this gets
the better.

Thoughts?
--
Peter Geoghegan

Attachments:

jsonb-10.patch.gzapplication/x-gzip; name=jsonb-10.patch.gzDownload
�b�Sjsonb-10.patch���_G�(�3�+��{m0���c�L�8��!���\}�4��BR46������_U��;�s���a�������'O�$�����i>���?
���,������&gE
��?�{Rk�*�f+�����|����-�:�,�7/���^��O'_?��~*_�-�����?��y����O�~��t&��9�j��y���q�~\,����n���0�O���d���xMc/���`~c'SIF�pz5����4��ON�N��W��l�M'����������?z}����y}
?}z������f�� 8���<.�rd�K
�����&������]��Z���l1�����"��O_$�����4��:����������I�2�z6��
��~�S}<5���g��@~z�&� ��1&�Y�,���4��y*�d������p����7g?��'��b���j�y���av��>�I��r�M��o�������b����>|������t~�t~>��7������b���o��������$9Y/U?8c��`�Oa�8�.����WI���dq�j��Rt Z&���`�\���<^�����B�,�t`f����i���~�F`�tA.`8���\��x����l:��<_N�D��� ����$O�f}���	@ �<3�V������s�2��r����o���b����+@�\Ln<��'���F�%����y�N���X��k&��9��x�^�
�S&�E"Nl���'����@��t"~�J��E6�����B�!�Q����\��3���ov�b���"��-D��F�l.�Q�>������K��8y��2^������<�Q�(j�R}�?d�qr.v�,A4���M%����^e`%��018�Ht��mHG{z���e0~�����������4fi�?�_�#�@�������C�l�!�:�W~#�^`�P,w����<$���=����g�+\d���)m��UVHJ:?���g��q:��E��
B�@�^�w$�����������p|��dt5�����ON��O6�#�t�m��i�-y��b�HK�H7���$N��(������<
���!i���L���z��vG�<�3��{S/���w��P����v�"�������8}�SrWH0w��]i�E�_��C2 R�K/����b<�N�tq���qvnly!�=�gG��k�r�	jIq8�=S�>h�G��r&N�8���$��X�`D�B�nw�9��V��0|��0�� 5' �=D�~_ �������}q��Q�8���[�Z�1�f]&�w�z���?],����g��H��*An�0t�o���������l���j�������W?�*�J�������YiU�?}z��Wp���_�������
_ ��w�&`��<��j��^����C'X��?����3�#W��W���� �H_h���d��M�_3V�����ad�p���4�j0�f��|p�����-��z��HI0�F��&�2im�[��%�f�	��V��;�p~"I���?��{-��y&Dd-i���0�M�M�z��n�>�����R���Q�"���d/��)h8����j8�VH7� �
�g�3�n��B:���`K�t���<��J�K����|���~�T�&�c��&������x���y-�{�g������Tn�{,@�s>��x�:l������+R�sv��R��������c�����x$��yv&V!N����)=?D,�NQ8�f���N�Y:�,��>xd�e���<ph�����8�J�����UX��!�^�w���BNA ������D���2�����j6����Y�Sg
_/�����k2����%��V[{},�2�J�_@��
�,�����^^��RW�Y�<q�����e�g�_w�/�)]�p:Y�	J7��l�.�
�U����0�G��7w"Z�W�	�G������\�S
A'�(0�$��������1i1����Oo����J<���]��O���w���Vv���
�V��7�ak������Z�3���A�[�b�������O�
�G��"e�1�?
h ,,����`����@��a�F��X������TBp's��A���47�o ��>r2L�"3�{�I&����W|@.�� � �
T��V��d#4�����S�g#��O�i2�Nk+��9`��L.R���Uj�ww��x���|��"����JH�h���F�����[������f&C\��)"������KcQ7�����;���g�l<�����')����2���-]$����}I��4M�4��R3��C��=�M�#�x5��>�^�>�D���%.*��������bq�?�N�?m[*J�"1��������
����JW�-��]W������: %=l���t>���l�F<8�g%�n�s��n�$x�x�{]�
�X��z�M�V��:Q�@��C:)��8�\�"�;��H�k8y�l���2�,V�]�m|�.�����'���t|�a���t	S��J������J�l5���a�`A.�����;�y|�l
��@��ZQ��(v�-�\`t���L�H��t�N���f�I����,Q�7�Z�Q6�/�(,�����rN�^�&��A�����������v%�Ys1\����6n�����Z�(D_�}oV��O��r�>#K3�yt���|�~���o�}�>��nh��3vF��\����/��!x��^�bEd�i���T�p���;"�������q�Vy|6�?����U��}�v��)F�L�JPP�����/�b
R��H���`�C�U)�J��Z
���a����v�~�S���2�90���0�$��>B��R�)�A�~ayJ�����~1"X�VRT�
2�H��	aF�ig�rxi}��1
�$����}�T��h/|6��:	�F��Ir>�^%���x&�q���7�$������	,��������?�����P��x�?d��3<r8e�5����Z7�������tA���cXQz�����S���k�9���CO)� �As���I~+��r���b�rH����tjh���`3�
o�l7�P[!R8�|F{�o�YV1�
<=���_k`IR|�Gp�s�A����'�;A��d�	!�Nt�\
�hcJ��D��0�q�ZLLi�����J����w�]�7.k�vYJc&
�z]�W"e(Sy	9Ev�|Z���=���+����{�E_E�>�{��/^�+w_��qN����~�u�*��z}���[W���y���6�ub����[��yO��GEo4�x?��~zs:��#q�]������u^���]�kM?+�K>�)�+_��L��^���B7w
�]q�^���ut�Q~L��"�:e�uV:�����4_�}�c�sz�k����Okf�LO��3���`'��?�*��V�^m�V��r���U�'T�
m�T�����������wrt����PpTRd�����L��yh���n"���v���	�hRA�D�X��u���{�B������|{�~�VL�h���,��Y�9~a(w���
\���/wv]�V;�uZ��������L����v�nH����n��G��cZ|�Om��r���#��T������u���������lg%1�1����o�i�fjn�fHf���!��F����~��NP��|Zpc�`�Z���;o�����L��[�a�+��P:AO�x���� @��-]B:���������z���y{�+4;~�x����(B����,xPt��|1��D��A���G��&�"��"��9���E�{lw�#�A&��J����OxXM���&�����(9�u����������g{I�"�����,!�����_4�S���|6�@�u�k=8�N��g����*�������\�o�����5j�����[@�>wNW[�B*�����%�?b�j_j-�z>�����q��t�ckG��@�%(�T��0E�����8A9KlE������J���`1��C�@t��4d����i���e�)�{$Q�,�����|:_<3��9"��Wi2�	���t�	�@�,�
��z�qz�H&��x/9�*9(�Y�<W�lq9�$��0�z���EM�4����8�h������b��H��������V�����>� �kN��vs(G��}.?����%��8�T�L]�N�?���Y5|���f����^�����~���=�f���	<�x���o�- p�W�o�}���:���[���������������
������~Z�Vn�����R��}��_�%f�Y������L������G��S�r7z7����uw[�6�=��c��,0���������V���Pe�TLm���#��l��w3�u��3����^�s�����4|Y���]��\��<^�*D��k~���s�V�e�3f���Z��@]�8t����u~���9�}d�F���0�30h*U����"rs��/��*nS&���9�8��T��{��vV��������/�K�f��N%���Nw%��'�W�>�E�]��;��I6
�z�iJL��i+��wiv�ivCiv�@�b|i:�5��"���[^v�["��4���H����)��@g\��Y����f�$���,�`o$�������_]o���tx	HwwO�@:�C���k���K����yJ�_���]p��g�L��~�o���s���������q�?�_��a�,���	�+u��BK��e�;����I���/n��MOl��D�6>�mRf�����l1������������m��L�
�p�%��eG�%5�J
sy{�g�l���Y�
���Hnj"�"(��@(��b��OfK%��P^n�&^��s�$�s�����$������KeI���_:��f�1�N�mX&U��Q_a��v�u�lv|�z�o��8TJ����	���I����8nO��v�X,:�W�j� �T�S�����y��B������E`f�E\N��~���"���jEv���,�0�]Y���>}�������3d���v��y������������s�&�	v*nBK,��V�4/L�	TEM����>6�H'c]��w�������j���+����Id��h��T��fu�ivl�����S.������vy��O�)�T���vY�w1���d���Ww{)����n����Z��7I�g,����o��6��^��6v�������nu�*Cyym��>�kC?�/��c�2����Y�R�����N�t�LAwP��������� ���1�fa���+�V\SR�@V%C*�4bW��19Vq_�PE7��`��sA�C�GS�{��mMt�$���tm����!���T��%�H#�.f�� ����)j���4��<��I����N��Y6���~���^�B��|E��HX[�{����h�A��M:���v47d��E���b���'���@��9|�[
�A����$MGr��B�Y�Y!b���\��a:��X��������9��g�`�JdW��Mg���\Y#a�*�`0E&�qI4�X�~'�T/��0!��������C?Xa}~-�Y��yz5������	,���<�f���Q���T��a=�dk0^\N���n����X�\�������M�2R������]H�P�XOD�YE�!Pn}T��z�Hj����n���~a��8�:B�\�y�M��
���H�`��<#�5��+f�C�����hq��y�.J�i.�V<z%�@�4�g��_;[��=WP���2]�O�� �+�br�pk�����e�]=@������>���c&`
�_��W�����`Dw��P����I.uphU��	��\W�,%��T����"�*�h4G������bj����_�.�<�������h�����St�����3��T���a=��S�A:����4��+����yv���7Z�6@B�,=81����v��\,�]+5�L0��$@tR(wi*��TA]`�g h �a,��u9�7�\��g��*�����*��u����u��\4���b��t�GD�,�&�X�/I��@�v@'�1��`>B�)2�-
���2B�-��sU�4��	��-�CtB/��������C��i��/5:0���w�T�	��2��%���+2$4a���V%E���i��f]�S$�XJ���h�G��p�[h�20���z-������D�((��t��T��D0���B�#	���e=:A�)L�B����]���s������#�z���W���o��<�d��^�s�2W����EQ:��XW��x{����5��F�e[(���W)�;�q9y?�~(�Fi(F��~eg���W���A������
��D*����W�����(�nR��-;������1F�TX- iR�Y��G�d��V� ��9���4���$<@�2+��|*�[q���	p[�.�8���H��<��%�g�b	gU&q��YX
z�Ix�q�������!	U���,�L2���rP���p39�M��?���R\"��
r�(|��J�'���2FY�O�4E_�8jekO�bp��B`���3�h�A
��&�A��`.��d?��}*�5�P�\I�|n������7K'�6�1	�N .%�VS�1�.��
3��9O�f�Q�B	������z�c6,R���xZ��J�2���(G����I�D�d2"�s#�>~U�
e���	h@@7P��c��zC���\x�i%uJ-
0�;Q,�l*�$%%�����a�T��*~��E{��
:bf�4tnr��P2��O���_��~�����o�4DF��R���������4>CoJ �Qg]�� �JL?�Q�y]�������7�g��OP��5Bc���Y:S|J'���s9����T}seE��<^f##}I�PK]��P�+�l>F_�]�X���4������&��TV���j��������L��I_
>fW����*��
�X�Zb=����~<F����
����l�wQv�*��7���GP�i��q�Q��@:��I���b9�k���RY6��3����4_����R�r�������,�e:�3)�����f�-��|if�/��s!�������B6�����K��5��(PX�z��)��Tm��mu������*c�4�Q-.�\���m��! v����F�-��{J*t��I����9��[���~�Dz=|��_Q(/(�r+!������W��,g#�(�!��j,�y4��(�,�����}����Z��+O�����:\ME�8��_Q��I����R9����`�)��[�bz%.+1��Jb������u+E��a �A�u�Zu��l�]g#�U�K]I�:-���!��gpXQ�.�*�<T%T�#������!��;`9��
h���~<��(1?�(���q��3�G�����I�%y4`
W�K��5��bd��K�C3B��:��r�	H hQ��VSvW/>��J@��������r��\�F��+FU`.�S��P�F��X��
�k:c1�WF������MN���H�^���.9d���L�*l�q��W_���$� �����<��bY��q����V������?��+C�;��V
�I~j��]������&WqZtZ�b�W��O���S�������c6�B���r���h���P� ZK.m���'[���
��^����Z�@��
iX�p�o�_�.���-���eq���y�����[����jn����2af���T�/i����@JuZ6N��%\�s	E�����(��2��_�J�-��)����L��+E'yJX*k�X��a�����~Mh>�[�P+�
���p"��G������_�q�7
��T�P#�ela�(��_���*�1D���U�v{�o�����{����.ANe�47��)�Qt2��>��=��������d{�����K�8��@�����K�\�Cp�W`-\�<�����YB�4�U%�;����-V�� �:5b��z��e��7�);��V����������?9�>\��)
��?�/������,�>�F���A���b��-5����`]~8�\����t��w��h��(9~���o�,~�
��fY�����o�4E[?������d�N'n���]��g�g���L-6��e�C������Cy���DvG�K�Df�1�<���QC�b����j��5������h(
�B����V��R�uj�����v�s�I>�gh����$��M@���~5�:�
R�������� �Q���c�[I��<���W�j�b���;�
��hX�����v����n�q���U������k����
�\�jr����X�tb_gy_s5�����rC�b���~�����\L��������u�p
������]��+�_���-���S��o�)�=�����j��[��V;���W��W�z�#~�V���Y,G0�Zo��ktj
�R|����k���}�\�,|`
���|�����Iv����'��O��OzK#d1��&q
,1����
������M�Ev�L���O(� ��R�~@�n�����\�����8(����

]+U@�M�+�\AT���p�Y)I������3`�
O+E0���E,�+�}�N����
0�q���7�#���wG���]��/�������V�����
Q[�|�6�Ku�*�{M `���:�*�8���
�
p'J�]���j���H0����=�\�4�L�Y5��QK�����d�������������u��]m��-��=j���<T^���_
~M���#G.�r�$�G{�����4���N���(~���FT>/�+l�M�.��cw�Q�UV��2���38�j�B�����t�7Wh2�jrj��y���Qj9����(�aL��b)���-�/�c���xo�����'pFP�7���_���Fp�}C�@���T�_�sk�8������g�bdF�/0fY�^/��v�2�E��r���?o�7�+P�h,��x:i{�vd���(�h�*����v�3�N��r�l1O�r*���S�V�|^�c�s��^x������q�'�n-�V�G�)�F	+�XO`k�&��V��
����dG�&���h�.�9�V*��03���?����������	w��[ND�u?��)�XH���\���D ��a�X6�7*�K%�����	���h��F��A������r�7t�1�f�LpdY*�j"k�
;e�w�Z���`R����	;���O`��g���wq<f�+f0G��xi����b{P��E���u�r����S����8�y��D���-��{��7��'.'q�>,7���H�*{���5�
o�,��6�K�������=<[%�3��0UY�iDh���Y��x�"t��	_<����zg�!\eO�b������������^��,����V��w��������o~x}xz��5N�[oU�
S(��~���+3v�dR��<�f��W�	�F�q�wG�?�{}��I��W����a��#k:;9�?xu��{�����$y���S����! ��%��PG����Y�^�7��Jx����o~�%�e�B���L
V��.���T���t��H��%x��[*X���j���W�,g�i�O��|b�M��0eR�n:S8��1@x����5��N��@�u���c6���_d���e;p��BZ��F�����?8��F�Ys�����M�{SI7�7��/�<��B�Vi���C�V�v'!�W��"�R?.���3'���DF������7<O'�=�_tZ����7�=�/�V�{�H� ����������h��,�3��/6n��/�������+~��g����U�
A�����"�����j��2���&�����t<�}�_qG���l���>^>��>..r� �?�
���!"�@�T��O��\��z���<]�����&h���g�C�Kt���1*}:S��3��@�������B��V�D��OY_�2�����L������,�X�t}���K�T7��\�<�������q3�/�)~.W�Q��h-�n{h�����Rh����g����#JI�j�������W+uK��
����o����������_a�R9��O7�������O����G�����7��O��mU��=�M6������B(y�'TM|�/��@�������f�����b>�����|'��'B�A������g��~�_`�r1!��<�}1NA
�AZ��+�#��6v�[�>��|�%_';p�������\J_<99�?G[��Jiz�^
g�������j�]? �
6��?}������/[�%O��
�~�_�Y�Qq>����-�%,Q�'��>*��JE�bS$��?=��i�����$�������&��gB��w������z�N��N��^e��J���������r�s�>�)o�+�V�d��%�@s�.�R��L�:�
���c2��S�hG��t���2H�2FH??L1&�$�N>HB.�I�{�� X�n�0'����|�7�6��w_����	���
���/����������"���>�07����rz����`/��s$���LM3$��'[�B7d7b���$1��-9��+�S�'�Q[lU}�-����/���L��`,��nl*M��'�����L.�LJ�/�s������iI������0n��ph�!`��0b��[���)����[�N�D���%A��@���v~���[�������% �H��9<U���>��Jr"3b
�z�PS�}��y�nM�w]LC���q&��{�3����������o������`�p����I�p�#�����_��M6������r(6mKp��!�?��^�����������a�0�lX�!3��v�}����:RA�P���V
��e5�u��v�����76���b��{ �����L�1�9�+M
��������7�^�9~�]�������99>=���
]�42��[������AMH���u-�A���[zu�����1�:��<��5�����*���J�)��{��w����B�w_�y�c�����KB�t��V���6��5*
h	��������L �$�I��(�]��h�Qj�n�Rk�Z����
-A���"���� ��5�{�>�T�����^2@���?��4;�"1U�G��m�u{�!L�'��N��o��#YDI\,s��J�$))]H�]�7Z����P������]����D���9U#����*�5�M��� K�|�Uf0G��z9�I��t\�N�� �g��Bq��l17�Z��ERr�����z.u��]������+�Z �����N�J��}����e���������}��(wt���g���e������$��I�>�����	���q��M2���?,�� �Ng7s���n'�j�YI��U��xz6'/!�q:C��o!;��*��6���w�O�����n������M����������_�R6�o��7]e��\Q[��3�-G�7�Yyff�����J��������B&�D�����3�������`\��P'�A;P.-�E6�4I� A��'X9��y�����@��_�S}b�����z����5����k_�u��B W���
����o���|��t<������\��(���"=��N�m������2��M���q��� Y��{Bf�$���8���P�z���F��	���w���q�k��4�2���:�C+��3��e����d��uxr�������"�0VK|LF�d������L�g.����\���*��D'���o)�	�9�>��D2�N�x�c�\����j ��@)-�����	����S������2�C�h���������z�����$\�0�����������m�z"��)^HfM6����:*�q��s�)Z�`�Ql�
�����w����$�?L�&9{��G����r�x�J����&Z��I�<mg�����w�]���0E��CB 3d%���A����������l{;��L����mW���l�@����a���@�Q���2��K�Mm� ���G@�f�_���@,����HCBF����i!��v�}�����%�='�,��w3C@<s��o�	��Gxr����T
�e���Y�C��/eD M���8�����b�X0�dQ��5u���*q���>�
���f�?cT$������BZpi�L�7T�df?���W�9�6��=�(�B��<r1�&=��&q���B�=�o�q���vN�V���7oN�������t+W,"�q �����!���%*c������!yqr��j�]�K��6Q�uo�����p'z���[z���l�
��������Yy���Jl��d{r[9	��n������\��K�#�T���pi���gp'o�z�{����/=�z�����eb�G�L" �w����7�����C�I�J��g���"v+���;a���3I��%.������|�����eh����*"H�.y�fY]#j�R�
~@r6>y�*���]�B�*�PN�$!����/j^H$��I�� gy9���������Y����$%G�� )�&\�5�a���8p�H�:�J��0�'��4J�X��j�X�E<E�2�?���Tt�'���c+E/A�r"tc+*�*�O~�	��%��9�K��*�:}:��C�L��sW�t$�o``����?��������r�*�|��P�=����<u�$u�LvSI�a������D���\�
�#�tv
����)A�x>�����dR���^K�)�m��P�d+���a��yy�����������O���|�r��%�+!�l)Z)�+���li@E�nG���N@�;aTd��E����o��?�N��f�/ ��������G��:��������PA^5�G���:�/;���/��wLW�{���5g��|�Y�Zww��y�3<�\N�=�������<�iy��CR}�o�<�UR�K�>��J
���-�(:|���C������Tm�Nv�'����:�;0���hmm������0�dX��tm;@�����V����(O�a�A{0 ��}a�D���
�/��������I��EP8�P����j�wh��
f����� t��:�&��)D
��x��{�bmK�G���7H7o�(���C��j{;O������9�o�%��$��0P5F�������v1g��e<F=*�]f��P�V��7�
R��������f�]O����k���}��
��I[r���[B�6\�?7�Q�/J7��w�{$/p��^���������q1������P-'�aN�[��1�/���n��&��"�fhA����8��3�����Q`�@��JuD�s�V������5:EZUfc^�
n�x��Z�L��E�J*S�D�����.�F+����[+����l����J���*�����t`��z@���`�����Y����y7I� Q%��w|t������^}o��|���k)�0]��v_
hh��p�!�5Dw]sXu%���z������(:H��K&�Wdz�`���|l�nO��\��(���&�e{��������pW�]�{�`~!��fR%��m���D�1ff�_bm
H��� ^����g�y��Fq���N���_Q��
r����Pi�e"0�4�Q2U���)����X�-�uRI����7;�BT���@a?��K���{���.�� �p���Q��I�������kZ���Y
��RMI�^ �T�(U�&�F�i��� �t4���G��&�hb$$S�C�2�I6	��5��8��'��4E1R�N0-��Eo1&/������~���v�a�Cfm�XM<������dkK�8�����v�fAV�%yaz��{M4�
7���5+�
��n���^��k�.c88-y��c3���N��������������j��>&�]�1����SipWy�Lt������C��	�8*��
'����j^�<#P�n��~���UH/����W��\�F�6=+F�N�"�l��7�hm��@��_S�X��`���2=�,n�H������g�]U[��r���*!e���q�Z���)��?��������n�.�d�6���P!z,>a80M���d���_{�s����8��]��w r?=�N+C���8�s&��8}Zc�2�����p$K��'hPU�H1p���
���J;�c��N���-�������U����/�P�f#�wH�lj����E���c���������o��1L����{��K�����R�������N99��9�n��n���C�jK���~�?9z��n����dk����!���B��n�_�,x� �3��
�gmu��o�'BrDa��{���|J�?��`����G��hR2�3���������,��F�H�c���W=�����p|����Z12)�L����u������hP8+�s(�����xw	p��������a�����\9����;����*ao�@�k~�ju������DH�e �i���@�i��q����]���>\'�8!���[)
)>���]%�wJWr������Px	R���cO���'��y���^����6X�8�D�0<(�Tj [�����\�-(r����^�RJ?8��p���Z.��E���A�d�f4��@~B��@L��L��g>xZ�U*x�����������9��.��7����.fQ���X���8�E^1���L��nT#j����>�S��R ��pM������E��Uwi�,��=�h�c�#���;��",��|��-%b�:��T%�{`�����8�r[��H�K�|T��:v���p����(�:�}3�V�>1��.*�9TI~���3_���<�--���IE��Zo�o��'G�����R���1��$�=GP�;�9&�V���bMX�m
��H��k�b;�UT���&���Q�c��
��g���}���~c6��6��!�#m������9���&�Nb_��Q���M�!���|���G6$�IA�����|�*c�,���6��p��,���@������o+=�$�i������`UW�������#;���'�~��Y���H�k��$����P�%_������������4�R�:�6/&���/I�
3����J{61j�.oY �����H� ���Gq��?��N������gC����S�c(����!(1v#���v���(�)�u�&o��8�H�~1�1�3�.�_�83�t�%O��K4���U���J��T��}��|VN���*1��a�!@`?�������@��[��'<RT��Z�� 6&�	����Q�j�~�I�.�>&�KQo��i���v���������*O��
Cw[���^�P|}�X�]�&2�|�g`N�b��hfS
��a7�6r>��z D����ka��D��2�{S5�tQ����a$�������2���PP��"	3�#P3.���dWW�(���N�H@����-u�d�B����H*@�!~m��V����*H?q��L��gd+�um�����5��K�d����i�N��KS<U%����N�1�"�Ne�AH����������/~�y�
��!K���M��.��G��_&�������w���\1��[tG������{�W�����%�A\QT|����@}67�$P�3��	T����j���1f&�����������|`v������CA���;m+���#
<I?�o0=�C,x���4'�v����@s����=���Du�Z�����"�v�5��~A]�^�0d%��-nmR�� {�h+���	�,P=1�����>��&x,(�n����/�8�'FO���@�C��L
S��Ex�qc�9O.���V�����P��}aQQ��OtS�������cM���� ����U��T��_�6~���|A��rH{*��,���~�6�K�"�/n��Z�����'�dF���L�����{��E
<AS�	Y�n�����r����j��%~WD'��*�!�:��:�/f]����U�c�~o��
n��V�������?��e�����p��e6�>���GG�_�� Q�O��4�������
��1h�Q�	U��\t����`l������|ZI"-��h�- ���8L2Q�����g�S���F*j�F����.R��%eX�P�#*���A@>�.R/�����U��)��*Gr��Dl��%�V�*���UhE8��
��N��l&���+��9�W��X�c
��a0�hz�1���@Y�����f�6��t>�����G�WZ>j�z����:F��G��#��!�����_��J��/�kg!��E��Q�Dk�bg��
��W�e�a�\AAtL%u;��\�-��P��w�|p�d�������cqO�g�v�9}I�������&<�	�|�}������c`}����:��/��s8�@I�/�fa��{E~7�x-w�|Q�������-���
W���{�/���/��{vr��wz��Y���{��X�bn��+�/5�l�Y�L����)O���SH(*�U�<=9����k�C���d�_$����_�&�,�����H�>���+�[�o?1}n{M�$Dc��5#�]/��/G�?��?=s_?���*����������$�J�T��D��2�z���z���[�G�Iv�oe��������������4��xE>=H?��TpEW�=�&�����*\�T�|1I�Cp!��T�>V�m�'[f�������������d{{������X�@'�����v�(�~��������O�����&y�<��������9��M�@�	������{������`��2����n��������2���$�.&Rm��������[�ZV��P+�g���};($��H9"�GL�I^����Jl��[���l��}�e�l"� �	��$���l��_~{�!��[��12>c�	�*�'	w7���8�
���B��;�k�
?|0%[x����J�;��U��j�_5��Z�W����9�rp@4����o]����|�7�^�8�*���@���r��/~K�_�O���5��U��X����q*��������w�����~?���,�F0����"��������5H.���Xh{^L�B(!3;�y����VA
���c
)��D��Y�L�����p��$��?���������7PH��Y$�<��|#������9
���Wz���bpx���i*��x�-����� �p��L�F��#^�v�J�<��'h#Z�_��b"���s��i�
����0yK�^A����e&��.�%=���{�	���f&�dW3i�LGJ��:����U�ZQ�{�����4`UZj��r|�t!��#h����-G�S�C/����FB*]����������s+?�f���?1�@v2�X�J���k����'�7M�*���@�)�m��WI���X�T��>KY�����ZQF.�i�{�A�:O!�����&�>#��SkiC�j�-��Uc}�^M�7*'�>���_
�����"�"Ae������md3��!�d����#�1�v2�����B��>v�,��[�
�I�R��:��>!��y�_Gg���#WY��4m&���[�@�)b~��S��L�{�Ec�v�cB�k6b�%F�8ik����N���O�!�%������O|;��W0��^�Y�,�T��O��t(��S�*c,��
8�u"����S���aEO���F�CVc�#���[J����������9&��*��6u��`�7a������8��#�j�C��1^� /���d
�0�Bj�)������!+D�n��N�sn���`����p��I��{��2�i�3���5Gu����P��[�qJ������^���?����@6��y,4SYM�@�0�^Kx�4_���z�ji��t,%�
�E���T(�C��H������@����u'�/�n.`�V��B� IOh���Ou���
P�4� �'�l��$R�
�.��@d����9GF:VB�����{�"5Y�!��`��K��U�aCS��������������U�.����lH&�JB�0NO�l0j�G5�$�C���OR��6��"y�
|	�biZ���4](/?s?���!r����p��Dl��w��E6���W+��AYN��G�o�ON��A
��{����J��a��
ZZ�����R~��|I���&�Z�b��-�+M��B���p���,���Y��\��e�@��
�8Jr�c�JC�B���7�_�f���)��%���y*���g���o1E�(y�9�C��p�R/���e��{D�C��!Z���6�D���%:�j�!�Pu�����ck�P�H��h`�1�#�u1&F�
������������%hRFgJ��CNt���t[����?�F=���tw:�kG����U������5~c���`�(HDo��<�X���%���	������N�e;U<��x:��
5}��?��ToX
A��.���(�i7b�%z�+��"��6��YU������Qmk6|���Y�^��Xlf��|����B�4qp�>��Q��yV��l(��2��mA!�?&!z�������#�l��_M�j����`<��F��,UY�i��_���/bq�5�����K����h�">���~2N�dm���W6c%���f,���;��
���O#�?���S��R[���!ZL�x:A�W�Y7��9J|���u���K��'�\v�o �-��Y�^�Ir������}
�0�Sf��^^����#bp����<K~N�~n���s�i�M�g��J}���E��:����6|F�
0��3�~+�����H��n�D�������&k�T����i{��'�c��UR/3�:������	0���&��[�`�0Q���eB|�J��m�^���P�I�9�#��Z�L�m��J#��k�|��I��B����Tl����4�m�|�MZ9���4_�I��6i~�M�m�knz���7�5TM�\���F��-��n���
n��!��_�������g���~��MI.������g�+�K ����>k���jNZ�s�'�<?���H������_�lg����c�U��W�g���{=�/��s3�%��,�=�^B�drST�^�����k�^F���X}}�������
v����q������
A;�+M%ak�$��j�Z��	��z��b�O�?{>\�?3�*W���o�|��4����������\;���A����D|&~���C&�=�@5����@5�2�������!���{�8E�b����D�$~�HF�4�Y��$_�feo:\������_���_����F������a���q�����L!���X
��_����4����7���+`��u@v`���y~f�``Q�k>�~(H������F��O;��D����}`��7�?�r4V��Tk0��<�c��nQ���D�.k�No�Y/i<�ozX�3^[�C	=�H�Z��n��D}������@��(	AQIA���:�4�����9U+����������W��������U�G�s�ia���l�S�����!���l���Nt�H�}�K���C�.�oq*<�vkk��D�	��e�7ZQ�Gi��
���*��&��h;��R0�����N�:����q|�L~s��*�T$������5��R�������Q��3kx�b��h3���o'�_�zt���L�zn�����v���!��7��A�.��M�G8���Io���8X,����F��Q�*��7�t���o��}+��,�%2e�b��T�[�5S��w_��b7��L���)?�����&����owWF�c��1��v�K���dd���,��{:��*$�]�i�LW��,�`�.uzWU�*]\NG$���'o�����b�Px��f�=G�_�W�C��yu�Q6��L��[����$[�*I�i���Q�9��)>:A�P�&��>�On����b��T��3@DA4�3��u�RM\�{��������X�0���Hg$5*c&�tDo�a�����x����H�3f���N�s0r|n��Y0�b?���?��|��O�e���{�)��8�j�oci�����g��d�6X��<���=gk@�	����a���U�}2d���"]��:��k�M||�0����#�������k��E�L��=�8AH|-��t��GB����UF7he��:A�Pe����#D,�-W4��O?�������?K���Ru�R����sH��Jg1�|�S�_�]�y��LT�T^��p�/�~1	UWb}�1h�\��Y����;�Br��/����`��[�dCs���3S��5$G�E�x��1��rn������{�{��N��r�#�����������{���K����b�d��Y6�&�������~����&/\��a�
���������e�������lq5�����q�"j��M����YV��-_;�`�:��?n�� ���������]�/l)GT�7�	��!��Hs����tT�Y��rsd�8�f�m�7A��O��Y�7)0����*}�����S&�HhZb���ad	Y����{��I�����=F��x�����r���?2\�N��LT���E�(Z��_��~^�U���zQ�����U=/\��E95A�����Y��j7p���5�����k�p>��B_��Q������?�4���yA�pr��j��\��G��$����d����[��u�u_+n�A&�zOx��� ���cu1B�v$g�����������@�������b*.�����0Z�2�
�l�	�x�({���������"ZT������Y���'SW;7�T��
�����M|�������Lj_��W/����J,�����~�n��E�5�b�;�CZ�0�k���mV��q����{T4�w����������|l�/]j�[����0�&�	{�b*�r�7��4'y��;�f�x�l����OL`��=���;�8k
�u��5]W��_���!����KL�z���{�����O���9�x�r��z�d�:���&�W�rG�S�2d�������q���
�V���	_>�^b$W����>���Z��U�u�s�6���������G���|��6��KE��N��4�nC6�C�:m�o)����u����-�!�k:�%����^����$����3�]+�����&M�AQM�
����<�/���Z���K�����������y:w������]2.�����'�+>��2�F���t�T���;e��m�H�����R6���N@����!����|z0&e��&^K��S;���*$��P�u`��7B�W/%CMu�F/W�� �=[�AZe�-�$�����'��(HRC:�F�W���Y]�!1!���S�$mWJ�k���%�G�'Dv�%�0p��c�@�	{����@��8_�N�}�>B������P���!�w�'qI����������k)�A���DQ����W��1������{�q%�H�� +vW�?�,�g^�L��H'&���r`����e��t�7	��R��{P�D�i�VH�Cd8N<���<z��Y����K�x`���4U����������cJ�����N�&�����z2^e1�����B%"��X��h	�&,��>����j%��i)7��)�&_��^
$^e���=����L�y!-��N~�d��NSO�$G��g�����4^�����;b�T��S
�J�����Sr��~S?�y��������R	�)z���&3�Ij#e���M2xr.x_[&|��n�� �����&�/�+(i�N�>)@I��m@-�9�� �n�!�����y:� �T&.21(-��&)-P�`9?<	Pu
7Tw�<_^�@����t�A��b���H�i^��K8<�8'�t��������:'��T�>�H�|�<�'�����%1#����6z+����Q
O����wo�}��}����������<ziL�h�_lm�t�Xe}V���h[,r(���P��cv���
��#�����MKO�*��o�(&B0z�s<��T��5�����@v����#>����N*��'�l��TI~�7;IB�{�<��
k&����?��g*9�g����#�B9�Ku ���La����Pv���=�#C�:��P2�MP�Q*.��l�G�
����I�����8���q�U�Uw�����l�H,�|9��^��FNT}^������{��x���	�BP���8+I�cs����6{���p��������T!'�-��+K�����5��������QF�q7���A0���3��$��3���/0��x�`D�	w�T$�L
�,[`s��2�~��n���Ws�d
HOtn0���g�`�+5x.��0#<��;���Q����?��m�l�+��l���@�F��%B���T��I�j�o�o��<�����w��Zc�����>��ct�U&n�����=��x�U
����4�3����|s���GV?v���v��������b�jQ��?|�����
��Q����z���E��od���������
�N����c]�/tjx���SAp����=�S��M0US�&���<Oa����������������%��=�����Q���=�},�����?�/���=��M���@�Gf�k�(>M����u��Z�;�S*����w�O���1��Xk�n�J�#����Y����v��hr����7'�����k�����o�Q_����_����������v���u���X9�O�l��������?�u���z�a0~�W�hg�:�f���!��2�X���������"���\~l������M��m)��'PK\�i0�f�I/2�`)^������1cZ�H��Tv����Fz
�I2��U���X/���Y�Q�h�M��Oli��Rb!�a<E������mV2<c�t�s�3�C�q4�4>�Z�%	�@�,yz�F�d����I�q����^���I���V~��Sn#h
�zKv���f�S(M���^����%-r )�m�=.M���64�M1[�yC��`����}
��;�SG�1P��0���m����e���%����+xxK�f����Y�E�� u�>���u; �N���0�C%1�Md)�e���t`�V�~
���������o(
��j��<g�������p���&������l��)15l�;�i��Dj��u������2�����F���n�
�=4�E|M����������1�~#�0�H6�xy�p���W�G�7��+����`��T�\�+6�������z�R���S�&�*_gKm�2�VL�~�3��`�/�~��C���o W���Q+��7���QK���$>.�z�B�W/��x��P�?�E��28Aet���tQ���y���4�t�����TB:
-E��Q�� �T�A�C:��`�Hz&v��u�
*?��?O8�R�M���)���Fi=���I�������X-��Zr3��"�As%����&
d2�-�,4�n�
	���
3�EG��k(��!�w�83��c�2���6�d>���:��*���>�+�����s��"Kw�������'�����w��I2��9����dqk����w@6�\�D��c��=N�7�2�J5��{0�l�
M��#<�����s�����b<`�p���P�&�^�i����sL�)�O��b!�C��RY?���*�1���bM�F�rP���R\�^�ad+�o��Y��������,��1��(%M��BeOP-�b-73pm�
Q�[j*�De/ �I��3��A E$q���Z�B(��I3k�P� 2�������OI-��F4�v���d4Gc�~����p*2=��%��
�-rCZT'2=�6�)wo�,`��I����%pe&��s��X�F8�rId�G�U�w-����DJ��Z���K%yy���� 4��+�'�s�2"�/�:Yk^@o2����v��D_���{���[����yY]u����a�JN
O�	�Q�V��X����
TQs�i`��uX��g>& ������h��fuL�
H	E�zi�6���.�k|���'�<�&7��Y�\�Ul��DH�I����;]�
�$����������B�;��6Kw��������M'�������u�<�H�c���+���w�����M(���Y�i�^����\�^l�k���r�Ok#���>��BJz��E#!�,8P>��O��������04�-�������������:��+����+��Z�?F��e+�����W$1��8�M���lLj{c�t0���}17I{"e��!e�G���
�1�e`�s$����������
�m=xd"]k����mZE��k�P.PUM�p��pn��
�#a��������G������Q�#�����	���Q�A�����24-�;0X:w��Ii��K�L�Sg9��5�&��t��������
��3Q�W�5;.�����F%��Ny$� ��� ��pg�+!,�~�MFZ�)l�I"!*���I���D��<`F�:��Z����
cb�;���*�#x��YZ���pK����p�����)9rKu��N��+�OE�C����u��RaLZ���.���^J�}�_k��-��dG{����3<�C����
l��v�T1���G�mz��8/��n�
$4�#�Q1��~	�p�,0�y8�����L�a|w����"�>l�"������>�����F�'$�:3b�����}�q�4�H��G�euc�&�!�V��+E��
`p�m��Ev�'��l�=j4p�w�����\%�N����0W�<�8���^��p~K���L����@��N�[izw6+@����KTd��A	l���8E?'���P�-�7+��J2�m�;�v���`���B�5�-d��_��@u5@����������m���>�Y[���Z��P+#��:��������^w)��B*���W��Ay1���~aC����l16���������~��FcNh)�N����@L�/�,�N�$����*��t2����.�k��Yu�#��������S[�^Kr��N��6���xv����_��'��p��4e��v/���r�v9�{��K`��)���C��7 ������u�P��.����H�����t�W�h5��U�^+^�[*2-s����]���C��l�$������2#�^������: +�5���}��H�|A��yh���C��C�X�,10������.|��{��|�@�'w�B6��k�����E������_��q���b�v�a��s�>?Oy��Q�W�Tk!.-�����n�A]�!�t;��r��a���K*�Iy:'��u��^s�kX�kO\�8�l��=�@R����B&b��&��B�����+��]|@��V�O�Q��>�^��@�%d���S�1$}�����aG�D�z�F8� a����tQ1�������xL)�!�#��/���dJf���aT�d,C
]�����J~[�k����u�}���.O�#�����n+��{l	�$������^���zRE�WI�K�]$����nrg�&�L�����a���"6'4	�r���q�w�o��'��z��+��f�4����D����{�r�����:�	@1t�J���+�.�K/���/P��B~$�exVK�8�	�*2������W���&���������r�����1VWrqI����?���)&�����p��r�S^�v,:w�a�{��<��3��Goh'.!e$k7����-K�\&�paa�v?��	)��O6�I���m�V��/TB�
�$2�Z(\k����uBG��&&e���<?n��j&�7h��d�F
�'��WI3:�H"�H������%�����*GH�}�+
�w�Y�Kk��Q�.��kR�n'R����7��4���.���5���0��D�I$�L
�$�d�-��%_�a�O
�.Z���\u��C�c��i�?*�$�.�/�x��=U��#H��>+�C�2��C��4O ���y�_b�7v��~�=�%��������K�q#�{=Mp���Bg����X��s�e���s~��9d�Jg��FG�`���Yh^��[L�29�����IN#;���s.��0��u������`�8��y`����2Bn�Mt��Rg�J���������W�G*�_v�Ml��s(�!�����3P�{|���C������,A�r	f`�����Bj`����M��yKK��a�2I?�;pH=��
N��=�]s��B��r����:��^�)O�#���`I���;f(�$|����|Keps	<pkOQ���l������&���6�2�&��������ec��E:�c/u{��Y:@��T�s`�:��3���)@_h�U���,[X��
���dJ��\C!�q�b�O\�8��H������m���D�f��]��gY+�~�Y�-rbYx�g�mFx)sq���W�V���	��>m����`o5~.�#W`h�0�W�����p����d9�6���9J�9�<p( C�
������Oz:��I��K'C��J�D���1(��%P����������K����
$`���<H��Yt-�ONY�������-�;+����b��V�Q|m��*���G���p)X�����������2	����}��,	��X�/f�s���0$��F��l���Q��>o*�����{_
5��)����7��9��������:��d��,�gX���	��b*C���lKg��� �i�6���h�o�D�+nC�T�La[W�-n���h���+���(��B���A�E��%�����G&c���
����dasr��+V��EUA"�E1&B�Q����:!��@�p�p�b�[�I�V~�ca���q�@Q�AQ%37�mp����P�K}�Z�B}?�~��(iS�\��D\�^�r[
h)7'z���������N��j>y���Hrs�[�K\�ke|�����H�_����2JX�M;�����
o�w�1��y�!.
�(��B�{��U
�Z�m����N��F,�BK���!�&3��R^�Ou}��~������M�w�v��������"�w�y�N{f0����Qh+��\4������qm,U����H�h��H����[����&Q(^jbp�(��zS;t��1��k��]:8�Wxv���t�����0���Y�x|?����_����y([� ����C�.�w�Y�h�LB�����L���(�G"��r�J�4���Y�*N�|�B8����N|Z�T�J�oe���N�����$9�&�,��tT��b������s�������+�w�e�M������S���d#���u��$��Xo2�+z�������NBg+�S�:����O*q�s�t�3z���f�[c�1JXST1BJ������
�������������Y���n����ra��A ���O5F�P(�0�^3w�4`w%��q��,������A����`K2��\�F7��C
�J"@�V���M<������q�%p������&�t���y�z
+V&	S��!9�pRak{�Z���i<O�����yg_�rP���];������`uR�c�(0V	k��������G����zTbu_
�v�468�(}.�H'6"S����r���Yux�}n}xRk�,~2�v�;$G����C���~^b������r.���S��$hus8h��]RFW����XW���N����s���YiH���D�%�z��H������d�n�����-�����Y���A������1]��Yn[m"IB=���]H��<G�jWO
�zO,�����8Q�DN����v�O5v�09��"`6�{��� w	�F�`G��h��a�/2*>�T�a<���Vb�	��W�.O������
����_�O���u;C���Md�p�1K#��r� ���\3�Z0�]c@Am�'$���h�wyq(-��K�aS�(>�����>:��o7�@��7�����6@����9vbz�h� $D�W��N�yL���w�DV�:�A���T��yJ	��&�7teY`�A���wI�tp���9t��y� PZ;���O�+O;a}�7�S5�I��P�EP��bFD�i�����ZZ����B�`g�@�����S�nL��d�bA����e�t�0��^;�����=+A��k�y��j�]:d)K�Ft����>�L���������`����Mu!M��j��=�Y����ebl|����3*s�e���{�%t2a�{Fvy�t�c&�8�-��K��T��q`��T%��/���M|�N\�6n�.x��D|f��c�ov������x�|V�x�`'YU�Q#;	��Nr+[Mrk4(��<���\j_����@l8X�)�`�����;�j�
�&F|��<%��Sa���
�0�!����-��;�:����	 o?P�1��bJ������!��n�����\rE��+�����U�.8mt'���f'J4����'��gv��62�n�Kl���
���O����H}<q���)��3a<�i,t2�$��������K<�_a@]���v�N�Q{�3f@��&�����B��L.!<�Cu�0tv��d>��aF�s��#����;�o�?@�,����gX%�
��[/en5�Rb�y��GE�LG�\�.���/��2����R���V�B���A��L�0t4;S?�z������v)�Al*��KgX7!" .�Q���0:�/�s�+�c��������J�:��(��o�jz��x���0Y9����?C��]HZ�K.���y��D�4�G��n�p�&
��:,|��E(J�0��
VmAb��� �<�NO�2�rX0"HfQ����q5�x�C���&�n���@;{~��������e��W�^�\fG�Z�~##h����3u{�C%�J+�CD}�LK��u���*K��)z���
sI`8��]+����s�	�q��<7��o��Z{�Z1�u���,��yXymy��C��Ff�y�����7��s��NO���)��f}�\d���`�x
�;�k���vw��Q��+ @��N���+t�6����$��l2/G�������X\8���f��e�����K���	�9�4�e����&B���k�e��u����_�p��WT�/��w�f��nJ%��R���+G����{(���~<���+���L���	��+�|���h�I�1M�~fA^�@N�u^z��'4,.����z�m��^bU��w��(_�A��:�8>��k>�>�T����^��2c��MF������8}��)�FO2���mq.p�����a9^�*yz%nT1���Y���5Y���wj�����qt���m�;m��G*o
j?�|<p����h��Ah�w���6��CGSPF�-T!�D������4��vG���F+4O����0�������e��-����k������O�;�e��'���c#?M(���������t����[�����H��s���� ����6���"��v��	�������o�w<����w0�co���>�cx;�J����N����sTC��	1�2t�u���S��������X�F�x��\��]L�o��v>�;�^7�
{9�6�H
Q���&G���t{�����b��0�p��mn�A�q�����A	0��:��,�����
�e� ��f�7J�|�<�<;���M��.8�0�V� vC���$W���������(=W
W�U�~��������*�f]������t~s8� �8������c��������)�[�Sd�0JP$��+��P��%�h-�������=S�Q��A'*U�X���{�S�Yx���d:���n(.��]���\MG�^r�Hf����������GKL��I�;|��.��|�`���0IGr�PV��$�|RT�
$�8`��A�LR�m����lp6��S���ds�b�d����5�k��������qv�)�NCl�����^����:zm�4YB"G����r~bu����L,d�\./Ri�����j����b����r_�J������z�������,h����*g��[tv$��bf����pN?�!*Ni_�H1e���	���2��X�i��Q��O>
������:;%��{W��u��W�>��
�&����Fl�5+	L����G�p���-����t<��Y�Z�~�����G�b��?��y}��]��M�&Z�69/�@F�&�t���s���A���7��������������T��1N��t>���������������:~���N����;�TlmM����bks8��c#o�H.�B�6M����L�V�y���IS{( �0(!�=>�R��H �1�8������f���r���0��D��xl���Y��v����*��J�������m������=e�W����i�C�<����3X��-����	�
�����y(��#�������Id����,�*��d1�u�'i�t��aNb���M�
��X!��maBe���+�5���.L{h�#}�S8�GB����J/2=�
���O��Z$V�1(+��y�\%FlsTm������;<*'G�?�UgE��%���?��>�~��N3E�]���B�v'`fv&�NL����������[I�g�@�S#Y�Y�NJ�I�!����rW�<-���#� q�N`�������%�M�X��(s��%e?�i�y/�I#K}����^�Jh�YQWn�����7���hU+�VW�N����!I�A�pQh�/d9v_��^$�����?J�m:�e�7������`(�NO��^p�����m;�i��j�f�)�=�w��Iv�3����?}��
���l3��,�T�Qit������TZ��� g��z(������>����b�J��e�:���v��������0���*��a~�c�n��I�����
20k�/���6��J�Y_~�&Ky��Y�0���F���P��Q"�B�������{wbN��=!9;�>���+�l��*D.`3�Z������t��vT��3��t��dj;� ����}/3�CHn{��S��Ut�9�S�'�L��|��];�^&����c�;�YUR��'d��P�DP�$�������dG�j�RSz$��^��kZ���u+�^�4��u�o��HR_J�������t=
jh�d��P�/B[�0��r�F�DB�������A1��T4����:h+u���g��J^J�*o����'X��%�":@�A x+m����=}�n6xo����v�~#��������������R���F�i��m��t�����%��N���k����,%��]�\��D������T��������]54lV�hV���]�U��f������n[y��(����i��U�l�'����D��{_���/Vr[�^��({H��j9�?��j���k����4V�4]]���;
�l�{�Q$1��*�:a���f�Sw?��^��?.E����X��BY�n�=����Z�2�qY��l���\}EQ�{�rR���Z�������h�E�%m�Pv�������x����v���<\+�
����],���9������)�����=B�3v��#)��<�!� �+�r�4��N���>D�y�k���F����*d@_����98-�S��/����*:�s�~V���X$&6��f�a��N�S��[�-m<�\�?��7��t2���	�47�TF0Ao�_b�{���x��#�.����YO�H�/eh�&�E��/���e�};�l���@l*���}��T������f��!�H+UulBl��Ju���������7��m��F�Y��I�eo��%�v��3�����`=i����U�'���wn�����=�����)���I�9�=����tkF���'�d#��p��v����N��D���\0�����$��M�C�tfpmj������{N���?	�yz1���i��N#\�'�!��D�1D�v�_-�_���O��,�d���������4�CV��2�@�>U�W�����t���b:&��,�BnpwH���8�(�'�3�����!� |�D_3����`f�h�Fm�!}�ig���g��=5	S)�I�M_�z�Qw_�t�8�>d��)d��;�Z�*�rt�u��^����W�p�L�n�^�t=���P$a�g��Gy<J +��n��<���g��9}I�-�B�P�^�������4�EC5o��/O��}��'yz�G���U�	i&�-;_��X�����5��Z�c�
r��x��4;��wb�])��2B���"�$u:T����}��z������0���V�)�:Dc�W.�={���24��G���V��Kk���<n�bV��^�\U�Bg@�,jo���d�J`�1���:��*�F)���x'��-���a���n$����c�\������W(~���
�b�S�������
H��Te�����^nS~[7t]0%r��_���L�i��H�B�����\&44�8�g�a�� N�|���j�+/������w�9U�[H[���HQn�93�*E'�������v���
�RK?t"��Z>�Z%9�tEYL�[�o�e�m%������Y>����yV����=����F�}m�UG�iz��o��vYP���
������S�H;�p���0�P��$���!�|2A�B5���������9�;�;d9�h�t*������H� ��h��V�%_\�;�n�CJ��TkN����V<�-5�r�za���x�Y�ByM��X��b�<��^La���qu/�CU��	h~��!oh�1�H�4_������/y��V+}0������V�����' r�c��"�qB��%��C�:�<~��_�VF0�+���)q�-[Ip����'�-����C9���t[�)�V��*�F����Ks��WeX�����b�u8���e�����DM�������/����d:��|8�=�B��tg�)��H���dx�
�N��<����Dd��M�d�o����"9SQ�����`���b
�1��U{�^���p�+*v�P2/�������Mg�T~k�B�Wy��>���
�~�.P2�3l��!�>8]����4n,������;4;�h�i����ilO���3��D[���~�
�F�^�5���z���??�
��g�;O�S�w��s5N	�^+	�F�4=���z�`�S�UO���O
p�>@�/�� �W�Eccq5�(�����a�3��������G������1<\�bx���	6��Hy�����rcC��`7��M�=n���VG�%�e�����N��"�k�BWk�l��\�MJ���J'�&t�����g�.��p��2�����]�(�0��{���~��L�_���F���;�`���P����`��tv���Yr`�2�����-9����0����z���`&��C:�^`);���������L�}'���+�4t�m�
��ht7@��R�J,sh����\���r>O'�S:mZ����8!BND������WoO�N�/������i���� �7��
�[}��7h���,��Y�
q1l��T	��	d@d�x��J��,|��X�I=�������W���'�����W[nNZ>M>��q"���6'-hr?a�0������s��B)�#&c�n�'� �����:f���Q[T��x;E��1���:����Rp��'��'*O������_'D;`���~�:�
;m|����FE�B����]�JOJ�=���L���?�n��_�hW�+������i�jn����X��rR��I�p��Lj�K�
[�rY�����"��)��b��.iyv�7.�����f�x�����!m����3UgQ��I�[�T�;�/��3���	��}(���'��W�S�
��F���r!?#�@E�VF�	��U�Uyz���t���4�RdG��Z�L����]��������b|>i�R2L��_�n� f�^?�����NDw���w���*+�������SB1�`�n��#���;e�y���e��{���[��8�V���L�Rl�����H�����%Q�Z�����9�x�-d����}�������H��Gzd���|�B�����\>@�A��3'DR2X4$Fr����(;�g`r0�jD|����#*����Tyj�|3�<?������7	tw���X�J>�$���I��	�%��P��U��J����e�W�����u�����Td�!+"����]1&B9����<��������t�q���SX��.Y������
�(a
*�bZq�@�Yw��:M���C��M������|D&
������^=����w��5m�����
���m���C
,�8=���t������t^Hk�\-���Y�W�-7e������uP���A������15W��Rs�����\�k�
�\ALk��[��%4HA���
���W�B��Nk�jE��v�Z"�����=X'�����6�B6nU_�rZ�]`�yy�A�+���w���%������a=$R��T��`te
Uf�tj:X�'y�O�<5�5,l���S��V��}+���R�R����|�$����N��
�R[����S�f��I���#�cM�j�Y�W�=����U��L~-���Yk��F���BU���V���2W���r�w���h�,��J�Z�����*E���e�\
Rt��,�[
m���i�u���Q�F���$�'��T
��8���')��x��U�|/X)�b���D�-�D�W��Ya�.;z�*��3���5�A8}'Jj�WI�I��,��2�[kC���e_\�.]1�Q�������a~,���v���_�
>T�(6�L�����O�;�;���BH�O&)�H�����������?w7I^�b��'���!T%H���=x���)J���g9�b�a��*'D8)����`X���'���~g���;�5�5�@���Xp� x\L	�I�]
'�[US�U��VK���
J�����U���~����������v��t�b_���������;/� O�����v,B�;����@I��
N�"	�@nf�OGB�JJ��-*1NYe���4L:�����yjj:����Q+�`����D����c�`lC����o������1
��!=AC��F�Ro��V�
�r�R_��R#��r��.o}�**�������w<�k����aFZ��DK� N����y��I�v(���I���0��K���eH��O���C���xV�LfN�&����m��l��y:���������_���y���
r)�)�sT���uM@�\~H���
�,���+�Y
D�Z�9�
���e�J���+�T��]w��P9h���G��;����8]r�$���PgR~��{��|��U�U=�yz�������G���z�N$��R������\��BpHk�;}����w��E���rHr4�^@�n[�^V^65Iv93����,���M�����M��P���;~#��A�D��FL�������8�����,�.�\��f�b�sh�?�� ���M�	�U��������L-
q��>����Q����\}����'��nkf��9
��o@���1Q|&����]�c�6�����M�nV�*��@JZ�V�@n`����n�Z�}��G�m�C�P�!a���-=�$j���O��s������	�5���������@��n���0����t	& ��WW����K��t�*p�Q#�uP���!
YJ�Q���;��!���J��	��i9�����Tb�F���q]��Y2���,sC�2ip|R��,�r���2t�M��,�8I [��2H3�W��!X����
s(eM�Q|��RO)����hz5��@_��Oc5A%I���N���<;
!P�)��[����+����q�E�	?P_\����AsGd����q-I�FK�+��+�|������w#�MS���������,����$%��.�;��Q+���@�a���TZ�|*����)j���\Z�L�
�CY(X,	#��5��������yG����#�	�46[����*��FAF<2,���~Bk����Gl��?]9|%��x�8��l0\����}��NHy5K�k��Yv4�`s����3�d�0�$8k���`t#�����G�����(�hM��.���p$��	���y�����A���r-��K+��"Ux���t���!L@�)#��7+��/����*�gC���1/��y"��=�'��yNxBvS�l�z�)-t��T!���l�?��	1E�@���T������=��`�T����R��\2�R]�tm�w%T���@�lf���RlI�A�������J��l�N����������C��:)z
��t�7Wt�*��r�������"�(~�EP-���:�&#
�BN�SE19�;=��]/K�������6��3���b@kS�^?!e�������	�MS����B�����j!jC�n�9>IT��t����
�=+�����8�vr���L������2���8���{.f!s�B�,�W�^�}�)����^��idR�Y�X��2F� ��z��;����Nl������l�xX�&@��
2�������* I�q��B�$�C��QJS�?MOB��!}��6W�`���1WzJ�vU9�e������q�0�P���F��U���(�!�A+5d�Ax�L��tNu���6j�����C������C%������P]Y�z����������^��.��`3�rD[�v��{��,_�L���������y<7sLRE�P��i���+�pT��%�	�y�9Ut7w�H�~��H�$6�
rh����Tgo�
�$�%Z�UH@*#���gh������r�C&(3��@��lF.��4�����`V4A�M;��_]��%8�oE�K��#���*v�Q��lOX��`"yx^�Q���`�Jy(@f����F9���^�F�f+������9 K�0����c���������k��b���K#��^��\)�KH�|:��������M���p�H��;��f
������j=��K5��9B-���
xX�2`���Y����E�������J��30<��;5!�w�~�H9��M������^���,k���>X 1�����M$�>�������u���u�� ��9��`�By����fr��} QH���H�?��p���h��BEP�7��7��k�T����������wH1ehI�XG9��1�T���;A�xR l��
8�$z:K��[G���u �ubq5�����i<���/�R���0m�Lm�?*�K]��K�+��k+7��&��\f������0���=�����~�4����h����~�%���&��������(�v�D��2���
��B�c���p�Qv��H�"��*R}W�<S��O�z�<�UP�a�������*_�uTD�GE���;��Kj��
j����g�mc�lDBB���<��>tU�����1q���T����;��;6���)f1���Z�D+!W.��.���*Mg�T��K4�R��*<��<}#�0T�MA�	��<�dz��J�W��R'P&7U2���5^�X��(�OR�������ua������.�wv�
{_��,,���`A�!o�����j����A�Dfw��g���G�_)����YC�����P��mY����v�>���!����W�w����
o��`�]p���`w�'�n>#��IG��}�
J+\}
���������?�UO[b�q��������3��e����S|l�K���8��?��;�4���y�>C���h�9��47�9�D�������N���$D�����2EL\��#<!\�p�p�fS�}W��h�=��^ny�����
s/W����[_2��2z�kF��u�]yl6��ABl�zC~����E�"��Z(c��N�v��C�LP��S��a��e�p]o@�x�#+��S O��l�H����#I�H u�C����d��i�N���L�H��u������1	�zLW��+#XN]4�<��G���sZ���{��@�<�������r��������26�
�;[��y�qS��lK�H�9��Z`��T�f��N�� ���A��d,BK{b��L�W�hlm'��
��x�\0�ip�����0X��	�dc���������}��]�x����b���z���|CS��nY�{�D|*��A��	��o�����z�Z>x�]�^������C������d�����L�7���l	����\���h����i!>S���l&�o�0O�t<�lC�j����=s*�V?�w?a���c��5�j�����	Ovw�;�^?i,Z��C]<���
��U��r�������49���`�H�1`�x*M��?�I��t[�(�:i��c��9 w��8���l2/G����������gW���%9\�6��B��l�a�����:bb��Wk&�F��I��j'��t+�n��-x��-SQg-����%��$���l1OUM��t������h�>�j]�U�6 ^L�Q����.�i��MC~S]�������7-�M�}#��y�wA!��.�5�V��o���2��X��k;����|#��'�����V���_���Z��J~$�l�G�*�����u(��P�w�F�����'�����}\4�^��;%Vn�hr�v1��x�M�����H|n��_x����&+($������U1�l��u�NK���Y���V�!
~#TR>h��d�]�i��/�Y[�/	�����m[�/h������5f�n�$�	�6��H#���/��:�+:�A��/�=�E5F��3�N4�a��{��!=q(��������Uo����A��B�W����RS����,_��N�W�����-]�;�z�Ro��7����t����i��t���v�m�$�K&
I���V�\[8��f��p��Z�����j�k�K�S�-�M���%�H�n�M]�q��$�V�"h�6�����v"��i��"��m�U����v}�lW�T��lKlT#T�n�L��Zq��-�T������m@�F��Mh������m��5������������v���=�^�C!����U���(�������G������=������_����j���f�{��i�\���XL��{�d�|FL�tM	�j��.�J��hG��a�#������������i�;E������B��WA�6[%�V��'D��'o�|~ w$�|��I���_��Q^��G���KA�����I�:�.�u���u�2����;($/�,��9~
��f��V����Y�����^����/��m���/m�FQ�
vr.x&K`�~������I)��Z��Hm�a5$�x.xLA��d�?^lb`,A��mp"���/�N�mm�c%	�B��3p��6=�3�u�x/6=�����9�����J���\�+�Mv��v��6�e���Qo�>(���S���Z,���`�K�3�c]�0�]�$l�ZO��|U���E�L�!��R^��l�D��Q��J�����AZ�KND�*�2Pi�Pa����H[�j�K�V
.
nc^6��ixD�������`��Z�\m�8nvn^��xz�9q�*N�� ��B�k�}��l,����������X�����9����,�����:t��_=�D{k����<V_o��_�HP�-�/T����?'��{����`"�_��H=A
��������Ex"��4_5���@��si��+���
�/�6	��`R-	�R�jJ()���?/�F��b��rl����Zp����g!U�3�'���+�����������w��V��l|L'Pbt��`�����D90|0�);�)X0������Mc<.?�q�iI���
0�G9lHG���e1W�����l|���7�J����p�[��7;�Z�Vd�A�V��2kX����eQ�y;(���������;�/����%?md����bl�O����������?0o�����o�ON������QV49~���l�W	����Z)�Eg��	�[�@���V��v�b�T^()!o������'qy�����
n\"R�f�wN����������K��?���T��y�1���t(�]cS\u�$�����N�[WM�k�n&��������O�J����J.G�i�g�A&k�Oo�J��nad��u�{/{���;m���`Yu�,��@Lr�yp��XS�]�����8=�4Mr���a�rKk�viu��d�����Z�9�&.dQS��,�������F��T�%�$3A��o*��%�\�o�Z��F0�*Q-qP������T�Zra�&��IP=����^q��� ��D#i�|tZ��V��|P��Q�hPo	,P�	J����5�Ya����-�
_��RZf)��;�p����Jb�_@X����?��2��JpO�E��K C�9�js���{^.o�%���+��n����c�2@�<��>#[�v�:��������ZdC-r%}�#��A���J:��p��e��,��!�+)!U���L��5��t;���B�a��F&��_)J]���� 
�'G.�N�^��vQ��v	P�qV�:�#�t��\�t����"�p�mu���>��)���D
Sn��C	"�,�B������[�FI���B�]�0��z���"��g�����Z,8��*�B�y�b�
���	�}�}B�zB7���/2�u�5��?}�������+�V������-��v:x}���Q�����E�^c��l6:���?���S��N��I��AN�7�<������.��������S���k��[+3W����m��N��u8KR�b�e&��w�k���l��""�����z����f�K K<��"o� �2�������@���e�����5D���2���U��BK!��qF��O'������(����{����vd����Z����c�Cu!�c��h�#1���/�Eq���S��Y��k���5�j��,��E�������C��E����^�[�e'��hn��A�n<�:���������"dg=5��R���Wy�����R�v@�y�{�l�.%F�)����#��G_o�eE���N�X�,�l�	+W����yl�������2
GFb�:���+�JH�����]��88X����P�*��0�k�*��Vj�u�^y������[o�e���W]��$�*i?��.���F;��Y���!qT��r��N+��<-�"���{[p�nmg}��YP��Z	�g�kk��5��*��N�v����"���TM���44���Sv1�t=&FL���q������k�X�j� ��������#���so�x��m�k��sO�b���U��g�^����#����l��i��������<��P�]�<Mr��W�z��58WL��z���`�D�cbk>'B�AV�<��I�a�������DGT,��q ���3��_?Do�r�����R��R ��6�)���n��������j<���o��G%V��z')����� �eXdI_�g�%�Eva��AQ\n�ZjU`EJ�_���NV[��W/S�����-XQ��h9�XCO
K��)���f�d�!+=�s���������"���d�{[C��a�N��M�4|$*�Iz�{[G��#��g�l��-N���V�%��W���
�~V��Yx�c���q(0}����U2�[Z��������1��:���R��Yq���j�_�XL#�Y����:dI��ZY���fq_Km��Z�@�S�����b�[�;�{[d+��(�}Q-+Y�[V�M���G@��]s'�����g��[�W�2.>�2J�&�����,��."�r�[��)���_���W��sW���6%�����/rD5�@�����7�|���})��<�{z�]p�����}�?@b��'���tf��/V����*I�q����b��v���@~L ���D�"0��?�j��=3Y=�v�N���'��[��F��t��u�?�eh��$��������9���"�GT'����p������o�L�e�$2a��_L�o��&\���_���2#�m=� �}AF(4�GF;K���k�|��`���l�'+����=��#p�@q��+	���[SvA�����,x��w�u#�$XOL���r�����1)�����R�uIr\��Q��j�������o��������O�*�U��$S���w������K�a����m��N�1�&���6�a���tq<9�b��y�=S/�w�/���p����������-������g����,9�@%����9Bi6Nea+���l,��E�<�j�t�/��>bN�����x k^9	�{�����Rz�)$����;��hVl��#��T��a�jL�M�3�`�n�G�#S�K��v����z@�����#ak�N�����`�^�;�S~�oK����~�/�:�k�|]������K@\tJ�-���h�����j�R���\�b��:�S�Pl���Ebm�����hDP{:�By���t`�u�<��z0���{2[��9���,U�/�����,�<Jw�\���(�N�E���	>3��%�M?��}%MeU�,O���4O���,�c6Y����$��n21Q1��k2��XbG�{2�	J�A��y��2���=S�HA�m�X�|���	6�
�}9N�a�����c�Y�@7���������wZ���	+��.��x�F7	���N���?@�'�������y6������t����L�c�&>�Y>k~�N��?�����LE���^�#�v������c�xz.��Bla����]F	�x�`�~; ����(�#�n6���
*}��h����@V_<������?�L������x0G/utA�@j�*] Eg�	?�..��p;��z��z���$o���b���������L�������3<������&%��~:����'p)�e��w�>j�Sl.'�l�3���� h�]n���l-p�W��-��*����������������~�~#��mN���%[���,�M�9>�����)r}`���RqN��w��F�����]�)N� ���V�Nf���������r������WG�76�W|�l�X�E1F������BiXq����)]����h�^�P�t
	�$����y�����-�B�#d��?�F��3���[�����f[���o~x}��������}���a���B��������s$O�~����|w���wXv1���%T��d+y��@j��l����#>��������������%�a1w��
b*��u-�#}d�HP_���������Wk��K,������P�\�l�/��=~���t��_��
j�p
��I�k}�c-�U�,��2�����|�Vu���������dx9��xx��3�|`��8�#�2/�^��"�6�.RR�xkV�V�#��J��
�����<�0X���Oq������PHX0hE3JX|[0�f��}|����y��;�LqNg���Oqr)�x0�U��Lp������4�A/�1~`��d:�e�{�#����,g���m��M�K��Dl& z�O<�D��^<�$`'rqC`p����"�H}�(��
p9y�_������<�}�.�����-��*������������t���q�����n�?�qZ����������Wp ��t�aY���p>�9HP6�6]`[#A�P�9{��������d&�#N`���{��mo��l���<"�Eg��
�^�
[�7���������R&�����o�_��������A6�?=�������q*����b~��9�1�5�Rb=�A�Y_`s?�IuhA|8�������9�,a>y�	G�:k!b���@�".|�#P������`�F8AA)���2OR���<SlOr>\�N�hK��eW�t���
n/�Pq����0"���
l���!��,�7�+���k�gO�JB��Ar)$A1������t)@<��P����r2��jh�OU����e��/93�q����:�����z��g�
6��_6~>�~-�bE�r���b�!Kk~?R��efc~{���-��	W����f�M��T�>5��%~��j
fO��}&�:����x�����-�����$������W��� PkL���8*�N��l����LO��l��/��y���SB��y>�g��+hW2�xC�u���E��.�l"��W�����>���E_�5=�q�����G.&*.Eq#��9&>e������'��t�H��'�9�&1]��<�31��T�����8�f������u�H��R�8,0Q}V,x7i|�<�����a:�X!*v~-��OQe�.�E��rW���Lr:��N����I]������������5�����a���B��RP��`��K|�(
�\� >���<9[�� .'��"->U�(q,�X�� ��8��U
�DN�z��4(���c�B�	O�oxA��d*h�����s 0�	=I��	J��S��l��&���m�������b0_l<O@4���#6���}��&���S��|�D��g7t#��	��-$�O�$��r!X��SH6)o�Z�7��)�tc0��n,�#��n�������1��,�~y���O�_��	���c��� -�IZ�����_�o:.?���M�_���jV�-8c����*��y��%fq�-���W�L-}NU����Q�2�Y����l������U���(3#�
��������_�H���(������@�M�^.��n$�.��5n��W���p���@]�<U�0���m�iT�Z�*	������/��������C�t�?�}'��R��B�k��<��~�h�HG����d�*�d�P����.�o�I�l����C��J������Q�?� 6���Y9l"�b���%����O$o��$s�k�G���_�'���:��3�NoO��3��������TP��75�@�i����f�7��&;Hr����P�-����S�w1�H��R�OG}���?j&&�"Gc���pm��6Jg���'������6���6�i�����?L��
�Z�R�����UD�-��bFW�u��P��@��aF{���@J7�7u>h��99��� ���������|�"�[�t�^��~
��2��x���*�|��v������Q��������6��U�M���i��`S�*&�����'l6�Q:���|0��������7�j�*����d��lL��TWh��hO������� [@ObR���l�-���>X��^�9����/=�l0�/b�����d�p���p:X..��`u��I����a:�`M���-~�W�����}�A�]�W4�C5�����x9�_�4�H3RG6���r5��r��%��������3�`������=���_�v������9r��^A�-������h
G$M��N��*��weK���I�k5�qAku���48�Q2@���
��������HH��9�k�=��zf�%�.����fpLf��q6T��^:
������t��]p��T�0���$4
��qr�.��z�~�
B����D}�zv�BL��eZ���2���n��U�_k�a��e#�s����^�O
���di�����0O�\�"�Y�TwN������
g/6�B$�Y��H�>D���h�|�z�/r C�Vd^mp�"9�C����! �.Q5�8U�6�5z���z7��ft#|*`�X����>�":��c�R��!�p�GqU���c9+s�,�>���y`�M�(Dou�Jj��4����U�d����Z��F���c�}�i��7����v��5z {qxN���W���p�d��_16��q�ct�c<]��l�-��FN�^C������^�TX���Y5�K�-B!����hZ��YP@��Rm���G���5F|/��9W�����������U��������d��������	o�E&�0������5�,���`k�|����=2��U�J@���,��EL`#X��4��M����V��vd�%a|Hiw�Q	1���a����s�
a��jz'��R�V+xIK(�\����O9/����iZ
� �E����	o�������\��nt� .p���:���������mo?�u�\��+�*G��v-���%�}�����#G�n�;���:Q9P������{+f�#f ���kw��0r~U�������b���]��s��rU�g;9��bx������V	�VWC�F��������[d4xBt��E��q�� k�J������S��hUGO���>jT�F�x�����+�g��[�`'�������22io? ��K���.Qt�*!\�����a�7�M�\��@�(L�;_N�u#,W�i�C�
��#[��_Jcg�n������=�'��VL�k8�^��K��v;����WT���U���XFY �I-��w��������hK������
�������R��t�4��"�U��#�b�����g2�'�*U�#�T�^�B��>b�S_$sy
��:��l�r�,h��A��������tK:�M���FY�3%���K��%��4\\O���R�cS���[���v�k>�W%7b/�HR��1���kK�g����1
2����(�<�T�������j�?^������F�F+d�������A�d"J�j�����aX� dv��+�\���h�}�V�*�(V��y�;�^qu,��g����N9����2_����:\-<z
X�4��&b�����VU��ZD��u�F;�-$a�r�1�N��Z�V�*�Z�?��RGG����{��'o�F�X��Udi��|}���khf����M����}bH{�t��Os��v���_����c�d�GT>5ias�`���Ez�U��g�������_���=����N�U���[w/r�����TU�q�"�Z�s�r;
��"[
b�E���0��p�������[��n����mTs���Wr�\8M����>L
5���P�E�A�*(<A�0x=���%��������fji���D���6T �
sB����h��r�(�����

�Q#|���a�]a�o�+�C���MK������p�K�>��(3Y�l}k���D�Z#,�Z%���j���E�d/���K�4|"���w��lRg���1\mp��1�s��lT�5z�~�%���5�!�EX���Pt��A����������F}��K����<$8�w���7����L[d�Qd��hp�=Z@�A���*�N��i���5�\����:<��l�@r���C�g�����������Ft<"�.�����>�
-��nH3t`���#&���;�i��jh�z�:����,/�ry'��4�J��*�2�v���-=�?�|��9p��8r1]`��-mc4Sc j
=��
�Q%��#�2��e�@�����m�����~G�t����F[L_�L�����d��-���!�]��!��{�I�M>m9��F�&d��l�����9h�xR4lk7Gz��/G��A�4�r*W$����������va�M��������x�(�� ���z�)��&�C�u�a2!��-c�\�r��G�8U��&B���pat{�H��g�����YH��I�N�=�Q��1����He��� ����:��)
{������7��.��;�l����K/xOY�n��[�D& 
������9�.9J3_������<�}5�cy2D��S]�!�O��k1��������0is��
����1�z��Dc���
�����K���MJP�^m����c��O�]��3v��T����������}���������~���0��p�K���|����:+�1F�����z�I�*Cb@l���I{�j�y���z���-w�r��Z���0+=_�@V��/���e�����+��#_D~�:L�9������*3�8
	z
�[]c�����U�q��+a�3�:�6����E ��*$\��s�2��������UK�N�\��:�W��\�\(�.zQ��8��z��Q�7(����h�I���;d�?i{�����tp����]O?I��I�p��8S�B�&�l����Xml7�]'�y�4�9>�uYb�l�Er�g�����&�I��g���x��C�����ETR��t�n�w�C!X�T��\uM��m�^#_z���F�,�g�Y(��4=d�C����l|+S��v�)�^�:�5f��J���A!�H�rQ�p���~��k~8bJ���������]���U�1]�{���kM�N�39�:�h���az���8�q��_Y\�tpP<�HA�Z8�G~����_������
��6}�����A,M"
�:Z+	���zJ$�F��P�F�\35��*=&�>�a�i�#��6GU!F�"+X�A���a*���]�������(���6����6�U4�]�
+a��p�0\��!6p��)c�n��J
��9���s��Z�&���y��v�����Y��j��5U�n$��5*Ac�KH�z��0:���M�@�'�a��,�@�����[�D�4��h�\���=?d�)��Vox%B�j�C�7�_-����,A"����T]���a�G��4#QcW9
O��3B�A�+K���?6�����.�EW��T�8��[a|w�g�f7H0KDW�S��2��:Fb���;!�������+f�V^J�AB�@�Y	HS�A�_��p��Fy����d�<�JSC9J�]�������a�P�2l��"�?�j��m|&��c�����.�;t���.eAd�dl[V'�����/��m�R�adJS�qE�%�L���W���h�k�������uQ@#gB�B�!r�p$b��b"a�m�����D�oPS�\;�!�0y�u0�3�GX�N�]b�����;��hLON	�sf�4l����&�h2,�:.���!x�5	M�Vk�}��G�E��M�n����	�c�5��h'}�G8�7��H���P��a/��u=�#� ����G�Y�%�z���G��L{VmLK��{�Y#�@��o�� �z��
�����m�<���w�	�b�&���3>�G�q�H�����S����>��R��k&�t�l8f���i����K)��.D!�2�>�u?~���+lcL��s��!'�.{�9�����h>)�9Nl�;�
�]ZZ�[����:pZMm�����r=������@���	���0���rn'�,���������&�q�d�6��z���:����gtK�d����P|��7x���M��Q�Fcz��gD�h������!E|z�U��%��<�s�iTY0t$2�']1f���q�~���!����bWw=
g�k���
��-�`]���(C�_0q��������h���r\�
j�_.j�����q�d�����Fm��8g*\W���^t8�(�8����A��Z������"U��I�G���6�`9��YO��7��5C
�39��1��^�]mH@���I���
j�a�����S�����D�:
��
�������e1���K����\��jv�x.�����xv��eI;B����u�d��@>L:1����a<�x#�7���hz��|}�������2?�_0��AX]�'��T����P��X^��4\(�Q�:���htBk��v���}������O����v��5]e%*�����,C����h�BTC��;l$��{V�"����K��>�)4w���u+��GD����	��\�`p�������N�M��7K-:t�i���E�s��@��F3����2T�$N�l�J2���q�P(�����������lqw{��7�+�a��C
�9�V�
��jT�3��(���D6g���'oP�G�_`�/�2[�5������8�Q���u�z������+�d�H}��A��@�+F1�X��N�/7����v���F�
�����s����X�a�!	��*�
���4��v�M�I�O2��]wo��2���4��y;1q*��91�������� ��k��s������s�99Qa�����9�G:��Xx�m��F<���?�t-�t��}T�����J��t�&�$�q�Jpq�a=�+B"��5��|I#�=��G^���h:N�K�ehLP����Cp��2��$�Z����d��x�����D��rlu�Jt�`h,��bU#�����\����F7�e�.5r<P�(�N�"O]�,x���Cq����i(P�S�����n���m�����{��#�'��A��'��=oG*A����1k��Q/h���V���G��TA5��;!/��M���o�tkT
�n���'�����si����"wd��)*�0��.���U�t{�,RR�d�b����=�Y����[mT��6b�[u"�l����K}�]�H���a�X��j��v|�9?lV�p�����S���R�p��G��A�$U3�c7����GO����6��=6k�H
W�K7�����������v�������T��d�;��`�v�|�d�f�������c7�E��S�,�����:�y�����Yx���'W����N��;~z�:�w�,��j�i��Q�+���i��v�@����I���lF5�~='��;2t�(�Y���sa���Q�MX��b��\�ZU���8�|���m���'�2&M���\�$���@����� ���9,n�Q�~��c�
L��l��������������YWj�3�C4���}��l�y?��89�1��i�h����A28���	�s<u�Bw�P�Q�2��}�Fh��Y��@�=�m�'?O%��H��������z���������#�D�.|���xq@�7�0s��2y7������Q9��,��aKQ8^3:S������=��Z2�Q�y�p��h��f�FY,�#N�Qg�qG�6���b��)����L��I����Q��H�Ru����h�
�G@�����r�X���T����rF{�FN���uE�~�t����l�R�lu|��V�B"�,�����q�_;C�r����)�s��UKps�cM���6�K�U�94'`f8z�u8'�Dv�y1:��"{�ml\A=T�A4iG%Kd�n�]��3�f%��h�B�����Hi
����]�S�!'�K�����Axt1%B<����p��-���q?�:��QN��]��|6�S)#L�Mm9��3��J�v:,������e:�`��a����L�m�&�L���A���ov�������*$K#T��(|��Al��c�� \k���p\m�����p�p���M��k:�;�!�����&���~��bI�}W���,�Yqa��+�u��9�V[N���%smv�IZ����w++3�f?QT�
x�e���P�QHd���,H7�?�V=c�����X3
�fA��f��Hb� {|���B^�I�O���U/�*-q�B-�V���e����j��4{,�I���C��DM�1���Ga#���U��]U��d�o���<�o���z�x��zT�vW��{�RP�o��i-�)�y��X�ew?�*G�>���,�n����,�������J�!m=/��M@��U�0�*R�Zdr�)�< 1-��R"5������1]�	X&�@�Fy9���?����}$���*b~��*O*R�q!P����s�3������U�t2�V	�<���kf�V�����cmbrM���(����+���,}:#�����,=���a�����3
�:-��E�V��|���i��y����l�������`c�S�n���s!JU���P�������(~���2�g��)��6�G4�/�(s��8Z�"4�����+:��y����Z�j���tX�<�8/#$/�g_��x�L�s����`�:��b��c��������z����Oa&&��*�����y��M|��|8�����-E�����IU8p<Qb2<s�uY�_F�1�l��V�(��Ae&��e�<j�Z�R��H# p<�y�_������4'�G���p��ir����S���\��y���r�q���<"mp}�Fkx&F�C��J@\2�:��M���H�I�Nh5�!��c\��$$$�a�~�[7����<q;����-�����3"x��L%<�'vp����%pN]�rKG��8jl����b���"�H2��K�05��+9r`d�&��=tJ_r��{�H91Uz���=-���v\H{���D�^�Z^��� ����{T�R�D�#�z�PsbM���V+�N)�=���'�	r�Z�G2�k��J����E/?*�E���U ����#Z���  v�a�e8i�V���Lu���(r��$���_4�q��M�n�q���J�h���r;�����0a_js���0��E]�_�PL���j��W���G/��Z]l,��������5`�o��d'���L���t�m�Hf�rQ��:����|?���1l�.����,�����N��<�}�Y#}����
'qoT��u[�-�Yy&����
(��d:�Z^.��%�0�G�+���v&�w�5rCz`�{��q��}$��6I]G6/cbi��U#�,~z3��wxhX�`r�����U�Y�9V8*���-jK��Q�<?�qVw������P�t�+�K�e����F����8F���������c�B��um��P�z�����K	�37�.%�����Q������{��R�O~�_�zaW����"�l�z����d8�U����=�� ���<N�DF�zn������&��������q=v!�D��zh/|�N8�cXev^N������U&uy��_��0PeY����s;�li(�}���4Ag8�,V�^%}��g��ZhQmW�\�O�A�[�"�zC��NL����5��Lm�9n�I'=��A���'[����O�b�<3�tT��`k
��8��ND"��fY��	;��Z��`�p[�+X��jQ��2���k�'y��7����_��$2�����[�\�7c�xB�B�I����~p.�zZW��N ���ns�,�eY�v�Y(�U�=8��Bt�uZ����G�tB4��S�����|�t"$����;06H�6��,���a��B��aF�b(((�����w��.��+�
wK0�����E?X=�Z���k��zn�CB'�������Pa��"��4������$��3�.A�p��p���P�2&��F%�Y��+�h!S�q�����p4��A:����9��e�sD����z���Q|F��B[��kr`#& Z���B��������^���J�"u�KV�����3�)�h7CItLr��c���,��G��&���tC���.x�{�vb
�^���n��
Cs4����3�1g��^H��#%�
�sn��pVv=@�tSsP���m[�+X��hu(X�a��k�l]����z��m��v�A�f4��!�i�������W���O5����>1��)��-#|�)>-9�<�CY�����jlx���R h��IZ��;(t�k�Y��[����u�������"�����Fzr�!�!n���H�L��-���>G}�6�c������c���"#���K�sa�{��o)�5�:��\
-�f6	����q]���3��*O{�jWH�N���2G��'�8�F�Y8Bt����Vh������i�}�>fu�b8WU&/c�C��|����Z����E�����q��C�[�X���<V�Y|�����,��#�/��G��r�����91�nX��!g�v�=���s�`>���#89u9���V�a���9�p���~��:����n@:O��d�\#]��#dyT��9?��	6�1a3C]4N������GsqW�n����V�H
�_��Qoy�b�����Y)
���k���a��|e�;IOw�x���&$��9��w�YjB����1mje-�h�����Tu}�8�����C�S�+6����cn�q��3��.�����{���j�V�����o��L�S���[��enW��;�g�E�d�����#P���n������w����:UV���b�>�qj���={	�3�R���
�a�����S��1nFWC/��"��S�U�[�
-**�Up���)�D4�Y���h�]�A���
T.3G�N0�+��%�U�dB�o�C0|t��E-�,�'L#:�
���W��z�:��tEv�C����Ud\�b*�N�SF{�j�������P��Mwj^UQ%�~��:2����t�u.h��)��(���2�*�%E�yxJ���J2b��^s	�8�[�D�GwbJCJ�N��q-x6%:�`��z��,��P\���46
JwL�dNi�P�?��N��i4��G+�1+��[M�O>U��v~�G� ���6Y������������e4�B$
�E�&>h��F PUe���4] ���)sdr��-K}����n�_OD%I7��}*Pw�4�b�K�U��yYq���9�
IS���IW���Sd�@�i�����������F��2"��J�����:�	<�E9=2	��7���	�<+�	c)��r
T��N��'}f|o����mx�\B���%���x3���t)�df�Ch�|�]'�(����crQ�j�y���1`�\�����E<�����:��\���qm?�0<F���[,�����2O&�	��N�b�A��fwa`��C�^��u�����v�r�g��N�<��j����+�{�]�"i�]o���[��E�,�������0������3��N�5cR���#7�%k-uE������j�E���-B:��~��x!3g��<���a�z,�6s�rR���2�l���`@����r���fhl�'
�3Z���Z��7�26���U����bC]�6
�k�qC�sr���L|!��[����4 3�NB��q��t����D���:�����.u��,��g7_7��c����n�R�~I����`n��&�G����|�[���H��5Q�GKN�^-�[52����,8j=�H$\����:�"�_t��\�M���uWf����>��������(���h�:k
a,������V�l���d����:DWj��T���]��Z�[������dA�t\t���������!�a��
�:��f�^t��U��"���Rn8�s�x��gX�;�.�n5,�xG���Z���[�������4�vA��@8V7l��g�r"�tvks=F�PO������
�#���H���WsR�wm$���xu�������`\h��8�=vk���9����d�u�~���,���^GhFS�z�B'r��L�P���Lx���s����muB���e��M�4u�!R2�pD0�����@��K\��@��H�������
�y7=u�bB��;���s��z8�"���+�K��pK�zBP��u�a<
�L������G��m5-^��<T<����z!��
��<�)���b��$�6Z��j��)�K�u�nxi��k[|4 �:��"��A-��:@��wM������7~�H�U[b@a�?7	yuS�q�7;�8	�4�n7�_pR��Rx0����v�.��SCh���$#��-�^���|�
,�6�zke��6�O
�<�f��$7HXeO^��/�h`��������V�!��4K@�c����[��E�����{�b�
��0�-������m��z�La]��W|%���q�&�@�U&����S���8T�+hF5z��g�J�7C�+?����}ku�!�i8y����4�����W

)9��L��Q9�Ha�_������</�|E"�����s��>�~��&�n6F:�����?/����3�n�DC�x!+~��vv��!?x�kvUp��fA��n��X�v_7Z'������	��Oc7��_�n��^*U�v�'�p"U,CJ����#����2�X�2"zY���������j�����3�#�w�R>-`wZG���ux"[��y�t�v���n^Hh#;^�}�����K��C
�@7��p.��(�.hw�uC��.�����@R�n�%������r����R5'W5Gd`���w�����*[�N��������0�,9|\�T�jy����b��ATi�llq,�C^�[��'�2+4��S���������B�F]Vq�w`a.%^-�������
��	��h�2A���E��;���!����XjV��r���+3�$��`����D�6��	r�W�9Xv{n�)���t*G������������W�A������8)m����@n�{U�]��F]�M�Ce=��>��qrkRSw��"Rr�Q�^�
Nn~^�� m_���f=F7@	B�W
kZ�A41��$�d��K�Ws]<�P�y4�k �s����nB��)�j��  8���X�\'�xe�$W?�i9���K��]����^�[M�������F�^�3yz��Z�N&�������]cd"l+i�����B<c���z!���^���m���H�:!�>����ru2�`���\%�5f"�8du�@�w]�����/bP{u��qM.���I@{_V����R��5Yf����f��\�������u/�G4T���v<wu�m�kh��(�z�N��E��(n�Z�/�Q�����K}�)�!�k�=�����YJ��q��r��:�5B�J+~�����I��c�x����N�0�nga���(�x�L�y��b����[��E���P�����z��,��omq^�X����d5����l�5�����Q�K���������<�LO��^��A��r����G�m�3��q��GW�����	��WfZ�W�{,�=j��RBQ>�^��Vk�m���zdg[�`�P���R�c�3�%�
ZE5��]��_�<P�F���k����LB�:+3�7���5���
����Ka��9�h���9���Jfn�
gv�������rQ�EQ�<��.9l�v��&FS;�����VG	%�)s3�mx'	���.��v-����
	G8�;�2f��j���;���7�$�(�����M������B��[�q�	�>��
n�(�*��|[�U����/��	H	�H�]���v���:q�������u#���i���5�q�����#�)oN/
��8t~"`�8�d���d:�D��!b����5��.7^�����mj�Qd����Lr^�f>�G9|%c�;O{\KH�
��F��Tt��D���K
�p�Z>$��~��9Tz�h�B?���������n(��-GW)t����q#c����o]7�b���c��Z���i�9mn��K�����X������$�s��?7b��t�I��u<����r���L������>����T�;�0�b*�F��u/��,��+�8��@nh���rzV��R���`Z������/:�m"		z=*��w2g��1&��(����XB�h�f�G:�)r}�ZeN���xx9�H������s/����dw�"[$���|�t������b������,.�������lo�\$geZ=����l�&W2��K��|����r�<���-9����&�#tR����/��+�+�,���"����::<Mon>��+�����#��/x�����U=$�����Z2�~�?�n���z���/�������7�*I.F��e9�I�4�L�����t$z�f_%I6�����w&��L����t�,nf)�F4~u��(�}f���_�������+1���}:I6o&Y�G���y}z��S���'o^'�<*	��xo�Akp6dCxE��#��!�@`�������*%��:�����_G�9�/���$���Y:J�y�n�I�#�&��D����>V�b��� ��<��-.1�2M����j���&j�k���l��t��-���b0V��8=�=	��B��(����G^<e��NV�wg��W��GL�T�l�a���k��?�����ed��d8����Ezo�\�^'�5�r>��-9�?/��e�q0J�����Q&�������|���\8�wX��]|��9Fj��N��Xr�{�wA��C}���������"'�������L!\����B���T\��4��&����"�H�'�t4�;s3����2���D��N����_�U|��"���~���,g�t>��&� 9O�������i���*��V`���Wg��qA5�	�]��)������������t>�Kr1������HO.� Tkk��j��ro
x��1h���F�S�6��V��iu�]�����:��./.�l�8P�&���"E�d�E�,X�����U`��,k���<����J��-������~�?QP�5��v���y=H��J�h��h��b-b$�3j�����LRi�%���d=�*>p��#?��+n�%�F}������u��Pv[��S�����b�������c`�$�&vk��u\�����_��������Z��S����(��VI���U��K����T]]
�J���Vi6�.VR/�U�31���������J"'t�S��"�z6��w�q�X�(�e�;���t2JA�
��M������a��\,��,�GDj@����g�{_����,���W�CrL��m$�0�����{����Nrd���MZVl��3c~�tU7����(��x������z���_����/�Lm�q��:S���ueG"�.��i��
��Jp8�����e�x'�bcE��->m�`(���,�0Epk�2�QV�\)|*��\5	�j��$&5.U������\kml��4�?"����j�����v=�3V��v�~(�6.?,5N�X��%Z
g��o�m0�Z����h�*MU`�i���|�TF�}u0V����2��|X��!e�v�����:�[v-5nh�1i�G�8���������v<|
����d���t�1��c�*/W��S�ONC)mT%��+���1�t���L�x�u����}�1����kZ��a[���`���(8s!��
���
��HC��Y��h�4$��	&	P|���Ot^A.K��SoUXG,q��>�!��}�����"��R��-qf]D�\�z��c���e?��6��
��#4i��O�����q��q)5���:uw���G�7�-����5s�������h�����a��T�~|��������V��p]
�[lp�����-��h��������PZx���s��\���W��"�6t�8	UI�0��F]���;������;����NTUK(\B'��(�=�P���(f[@���:y����9�������O�<ce�a�@��*l���� ;�C? k�^�>y�����������=y
Dv�,W���v��g����'��r��mm�0�c[��z��Ol*pJ����-e��sp���L?X��FGwTA5�H�u����}v����$�:
���t>u�?^��]������A ����B�0
���y"����1
�g�j&���m�t�0si�"�L��s���Q���N!����j��]y�z>��R\���([�!���F=@5�h�����5�V��eKH^s(�<��$��qc(�P�eDkV0K�Z�V�V�\�����&-I[l�q�G<�@w��M5\�N|]*J����9�K�h��(�e�����I���4}P7���C�H�4�{�,p��O��&/�>N�&l�{����\D�&@:3�Z$+�.��W#�����&AT����V���R��N�y�P��.�t"�]�X:����������~��m$��H,��r����4�,)�@��Qy�+���>!j�����=3-8�xg:�+�#jJ����<vX��^/m���r�l�Ig�pm���1�I�J��|�+�f��j�n1�F��W�"K��Rq����YR	���5(�r��������G"���l)D�����K��/��a���N�\C�%Td-�t�iW�A�%'��Y�����g7��2o��r����s���}���1lC�_r�6`q��}��fQ��sw����D;�l-�F�H8�&��g�Y�5�N)���������f�2:��ku0�4C�\���^����-�<r��u�*
g
����
^��b;�����(�A	���"�hf�!�'����O����J�QK��]��y{���;���������4]U>+�7\���D�Kt�/�bh�`�V�P�W`{r����B	�0����iF����?���t��}.�.r�e����S))�4���@!i ��m��/L1����h�/�	�/m@S�}�v(�a����%���A���\�P1���������"_��=B����L#4_�3��C��������!32���1>�s@@)>��*����"U�~HQ�T���
�`&/
t������C�W���FY���s�M1��1 n����U�8�&-��?������ ]M[��m��])�.;6.����.���l[��f,�i�v%!D�|Z���h�����]��,/����X������f������x�'�<���#9��
�0T�������zG��]M�$��M��2��]8���L����"����B�42�8�����[z��$EY�9��_�Q���*S��T�)��Jkp��>(�����+����9������G/clr��;��%�6�<�5��[+&�KL��Eb�
��I�:���N��8�U
{�Z�mq��s��.����<�M	?�������T}�n������{s����f
�����)co�`p��������.���;�����<L���&L�����\8J`}`�A�ae1H��GE��a�x�!��4�����"Oc�lt=���c\�,U
��`j�{-��6�o�T���P'�a$,�4Y!]
2���
hea�V����e"��/����R��8��K��X\��kpgk�[���3����d�����������>��wf�� �jB�[3�l
�+j��W"M\,G�)�T��ij�w�[AzO��i��+Z��2XxnT���E��v���5Al���Y^#�6��r��� �@C�HM�j���/���K|�$C���Waj�����UH�����Yi��T���Q97�v)��U�W\+�5��]�0������4���9Y�<���.!�
E6��AI�A��
��9�ph�d���X���������&5��*dvy����	�b���E��7��G[z�l�0����:;��c�������R@��
�q�`!�V��q��Q���	7\�����Z�������.���:��l�b_�r�gv�iw������7�����`��
�u	��
�NS��W<�����Q����3��9�C�H=_�8�����@J�P���w��'���%<&���dD��M �P:��J����������V#<'N�%NW�<�i��)Dy���.��������<���?,W��H�u=h@�
���vex���2�;@���_����f�]�9#���x�s�������+"��.�6����d���V�
������ 78@H�P�_(6AV?���������`J��K���|��}���4�����6�{��Ag�����:����S�w}��w�Q>�L"�G��*\K�������G��^��j������s�a����)����Xe��f/�;������g
�9U�&��k���Li�9
L��(	V�����<9�h���f�P�4��N��z�=�G��h���}�z�a�����������fI����@VP�z�`"E0X�Sk�Q�\
�����J�IZP��\R���Q5�C;��i���7�A��
lj��G��o�VF v����H ��b~2�,70 
��~�������$\��Jx������hlWRF�p�9bl��������[�U^@��J�-��_+�ib�4b��cM�S�[{e�n��,]Y�����I
�����������f����X����0�]��1A��0~z����� x6�
���[nfx~�_�7�#�������?����DW�{���dH���XK:Y,���u��@%JaJ[i@���O KD�-���/x�k(J+
1sV+����P����I�;�����Rc��h��������@@1��+6J��q�9�5my1|�h�����h�f	*BK+Z���)f����ze6���oq	\�X�����P
<�n������|"�Yv8i�I��>������QI��)���A�1��Y�,B����
o�F�L�\X���g!���!I"�����q�����lk��.\���!��M�(�8iT%�������sBMgk{���CrF:���=4^K/��	��*�u����%�����,�(��d�4�X*����M9���RF�p8��Z��3�0y����U�7h9�3]��de����1���U1����"����^<=���_N?j�C�o_?{��/]���o�!�
��\���X�<��z�c�J�m�����q�7�j�@H��-_�a='��]����������f�
����GZw2��$ik��W/��OI:��JZ7��Z�[�vk��!���Em�f}�-��<�����������\��p���������Q��Qpjm���W/��A~���|����0Z���~����t�~?t�Zay	9B���N~�D�*M��K������tj��\
�L��1w=���a�a������[z+�8,�0�$��l�I�����&�h:��[@�T�x�~��s"I��8�f�H�	��D�>1�"�CD4�U�x���YM��_ ��Lt�	g�8�.�<@����N/�D<I��@3������f���NXuu6����
~���R)yK�"z7]9F��(����y��o#���w����g�
=~��d��'������W�	�������[�	��1tYsl�\w��/���W�w�}����(>"x��re#!�Y�Hs�&��D�Q)�����,�*fnp��#�5FV������[F
�2:>�F0�������H���0��VLj,�r���������ow�3g��Nn�%����B�J�_
T�.H�`���9����b�3|P��#���" <���}��������-����-�B2e)�O@*��7�����O�2f�G�����	��M�L!=�B"�;�B�s��W�3+E��.�����j���Ab�y��2��NT���DG���0����������������v�H�V�:�VK;�'��B�q7� ������/�>�i���9PF�����nM��J[�e�p�&ub�^�`���O����3E(x�����C��f���_�PSp�a6��������u!���2kl����f��pJ$��/��2�=1)C~�>�����)XJ������/0��Vj�_��RA���x��5Cw��x�U�A���%?��gEg�Z�R��Q[fE���������2���SU
_��!��)��)�|��<�=��8$�z��^���-)�����02�����e�c9����V��F����1�[E?�^����-���<�+�lm�8�������@7	�PE����g��p-�E���pna���E��F�I�4HAaH�I����a����^b4R��b�K����.�-B�"
����c�1�L{�����x��
�z2���|v7��UF1��LA�����"��Z��"�f-��Z&C
�������0j�|����F��"�$�V���o.��k��/"�=���<��w��{{�J�7�}f�		����<1�be<N�r�s	����2���[���~��m����+�Of�c��O�������w<K6��[��(��DA�A����3�.3�����6buX���d�1����;��Yb*�3K���r0�pW�W�������7��p\��O��&������`k����o,�}�F���k���G&r��l����W9��Z�w0���{W��=8*$��n��xz��j���1(���	������;:�A4�����|S'�$Z��a���w:�/�ppj���A�q�u�������G-�YX��k�^b�0r��������Q�3�VxS����Q���+S\�;K��c'Ub\T8[9nm)EQ�I	I/#((���N�YT�6����� ����%2������y�O����s����3���������w�D�Q[hkn�����j���@2�����>r�b+#LfHlp���DE��=A���.��mZ����wc���6���m:pq���s+�'�
�����Z=����
YX���e.�i�O���q%�*Q�{�2."��)�8��y�q���NL�).���oD�yB���|�5a��ZnN8��L�'D<��1�:��"S��xdny�
8�����mo���a	�uHx��pa�j>
���,��v��6�A)1�)x��f_'%{�:u+GL���+�m�O�G��j�bdP]�0��Y�����8iO�;��\X(Bj*7)��Z:S��v�}���B�	�i����Q���e
Z2WTz�02���!Bo���0���ys�.��m�b�PO+�U�4���%�M3gI�X���C��?8�G��.o1�GTg;_����kqdel�������p���^bi"�*u��Eg9�c�����&k��2E}������7)���j���"��-�%J#�?b�>iic��&U�+iTi�rf�����hh�V��%��+W?����Fk��>�w����;��w�dAHecW�W���ow�t?����lU�0��(��^�>l����!��1�+�v~n��o1LE+7|��Y/���d��[:1S�J��cc�f-U�B'�:����>����/,8[|���3\�}u%�l���������m7���R�����H���Z��J��lQOL�:S���ueG"�.��i��
��Jpw�c�:�e�x'�bcE��j��C��[��)�[[�	�����H�SAF��I�U���1��q�r���^�Z����U� UV��4vf��9��Z]���Cy��p�a�q2����(�j8���x��i����]�H��G�Th��k#]U��%\�c"�b���?����PUlgM?��?������
�_#&
�&gp����O��Wd�p���K�!���������3��r�����vj��i(�-����By���4�����iO�. �p_�a��~��tMk�2l����v6e~+�7�y��T��N�����i����4����H0I�������'V��b�Y�
�
��%����8�����FG]�J�&�Tth>�]D�\�z��c���e?��6��
��#4i�w��*/Y ���J�+�H�������<��YnY�D����=�f��.g�����v'�;`.PQ����X�6��_.�%{�
��A:�[���[�;�S�9;�J������Ew����fg�5K$L�c������r�P��_�����^i7���N�mm|e�c���y����'�O������	g,�D=?��Y{�����7��7/E��|����k ��"�w���GaW:�*~b�A������q���c[��z���i�S 4�_��r�"U"���L��BT���]g�����]3�.KC��������;�
�������w��Qw�.����D@�*���Jb�������]�*C!����j��]y�zf1�
+�e�<$"�����
��~��e~3��J<O��<�0{���D+�f���Q�j�Z�[Us�K�V���$m�	�Ei���
��7m�p�;U�u�(a�"��.�QK�L��:�6�'=x������es���{���G�N���NV�r)�``/y�'��\�p:_�,,�j/�V�HT����V���R��N�y�P��.�t"�]�_:������(W�S��)��/�eB�[z���8���'�xy����V���O���d�s�3����5��Wv#�><vX��^cV�r��
d���1G�l�������'V:����z5�b��R1�BE����F��W��4��kP@��!�aM��B�k���G$~���$����&�����5[BE�"M��v�\rRZ�5MaK}v��=!�6)Q!7��o=7��+�Gli���v0��%o'aj�G9oe�8w��o.;A�s
�&�kA<v5�,���M�ygR?+3
O}�tet:T���`i�b������s_Y�aR�ig���4�)�3"b+pxAY,~0����R���H%@k���a����y�l�]�O�����l����%+���
��3*�������tU���p�S�	/��T���[���V�;�l�q=�:��G1��w����x���"�[���<���IS�<�2
>��L�������&�r�p���64e��k�b���G1+��i������T��t,�s�BDE���<���!&��F|���1����p���d6�$~�����v���f�P��6���_���H��R�%U{���cF$��Kc���4���Cq@Oy'��Q
�������Ds�C��J)mQ�1X5��j����a��9^�������1M��������c��nX8��l~��Ih���fhWB�aJ��#�$�r1�F V�@#2��Q�M%oX-�g�#<�4���{3��H���R��e��.,����:��v5aT�E��z���;3��L��A���Jkp��>(�����+����9������G/clr��;��%�6�<�5��[+&�KL��Eb�
��I�:���N��8�U
{�Z�mq��s��.����<�M	?�������T}�n������{s����f
�����)co�`p��������.���;���y<+x����L��ZS.��>0q� ��2�����(&Dk*^9E�l8� �)���H��0]Or`�$K�y����^@-�
�� o?#����,���4Y!]
2���
hea�V����e"��/����R��8��K��X\��kpggH���..~%��0��%��>g,���3�?�U�b��qdS gXQ�>�i��c9�N�$�� M����KJ��i�:�:uE��^��*����0���;�&���B9:��c��F�R��YV��FI-�P9R����-���0�P�a�U��w7*(oR�3�u�EV�<������I�K�������Z���-�������\}�g��M���b��t�u	�%h(��%
J

j�W8���� @+$ '�a�*(P���.�67�	%U!��kM��5�!�L!�y��G������<�����mYJA"V���Km�B ��0�����*��M��������@���
�����@uI7�O|�T��9��:��������=��� �V��K�T�wr��BD���m��������E����>T������c�qx8
��	5_o|�}xB�y[�cB��NF��2����R�*�;*<(:����{��U7�u�|
Q�g5�i��#�k�.��h��U|3�h]��kg�]�u��L���;��'���~�b�`���0��$���r=�|-:��H5��Mj����=Ym7����}�A��~-�
�/��M���/�b��@#;#�����Dsw;�:j_��=M�G��5�
G��hx���F��3����l��~�]����v�O-�� �
�R5���}k��C��W+�Z����/�\r�d?#xJ�'�'V� ������j�.w2���tN�����Z.!#SZFrNA�="J�U%�?p-O�/�1���-�9�����(��vO��7��a_�w�;i����@�����Y��6l"���^4�H����uT:�)��+�r���1����iET������oZ�2��
kP�������q��������)%=������<�
��E��_�&7&a�-	�������%gNLy0�+b�{8�
16�m��������*/�[k%��b����4�`�i���������\7�@���,����v���ca�S�_���p��v�q�wm,���Hd������x���N��S����f�@!�t�m�L������v�������[C���5^�69C�����gF�d�N��#L�d��/�
��T��b��
Y"���*�(�,0Hj��V�%�;��!�^Tj,*�V�0f�4��(&�v�F��3.1g���&��M�����,AEhiE��:9��$wy�^YA��,n|cO�
��g}��x����3�)����DF[�}*����|����j��A��1��Z��
�(-Wx'�/�LI���#G���!����8�x�H���X��Q��mm��7�H$����'�����Z���N��lm�{Wp/�H������Kc�E��a1�_�����A�<y�����\��E�e����&�3��V-���[�H���^KX~&&�<����*��>4��v�K���,��b:�~��*�;�0�AB�|�����'����'B�Y����g/~#���u����7�&�3���-�&i4��l��6���y�8�g5�.����������~�.���Ok��Lf���>`�����yu��5������$�7�%��Mn���u��n��u{���q��>��Z	]�������������/ZK�b|�\f����(K�(8�6f����o� ����_�zz�
^-OO^?Q����y:���
��qbMX^B��r���� Q��J�y�K������tj��*C���1w=���a�a������[z+�8��&`(I<�����#t�u�M"�t�����,�����D�P�qv��$���}bLE���h
�Q��Kg��
��@6����>&`q ]�y����E��x��]�$f\y��������l���T3��R��6E�>n�r���Q��%�1�����F�G_��
����z��9���O��a�y�z����
�����c����`��C�|!����|����|LG������8	���"F�4�%��J�O��e�P1s�CT��1NvneT�`:j����0��VF�C8��F��>��$�bRc��kD0p�� E�%��|�`�9k�v*p;(���D����@E���N;@�z�:h�m��������)�C���`�s���$Cj�$��/����h��P�LY��A��l�
8B������Qn�0��bB�zS�<S��!��������������J���K%�'/�?&n�a>�����U��x3�&;�#LzD):>� �*5�h {���(��U�������I��Pp��:�������K��}���b�������[�����jY%\��I����W<�c��Sk)*�L
^�/9�/���� ���W<p����:j#�..8�n]HE������1���$�~	}��K�����_OL������c����~
�R�#�1$��|������D���;�{�@l�::Vh����kc��]�Y��w�����s��Y7�x2�a�r��#��T�����CHix
�g�)�6Oo��6��^��a�oK�0���>��������j��X��&�*�������e��V����7fKk5%��;�E[9���:B���^4��E$�T`����d\GGv�9���_F'�Um��q���,
RP��E��s���i��Fq���
��0/'����i���k�����p���|���^�/)�2q���;�#���3��Q��<S���$x���x��Y|����� #��et)�6_��b���}�H2������i��][���Ho���,��������r����bO��mB�)��P�Y������\~�'���!?�$2?�C��~�jd��������#���:���!�+�h����
!+��4�b!Qd�md%����|�,R*���X�x,�i���U�8~��
������Z�����6��p��"��
�.W�f�w��%�wc>�=�k��\�[��zs������l��{pzG>���/<�������TM��dB<I����pW����zqH_y�������K^pyq��/{?�g�9��,FQ����s��C���ra@������(Of�Y�<���/+��W^��]^�!���HZ��C�4}]���F�����=Z����/��e�s��=�����5'S�������k���F�h��E9��������j��fpL+b8du�
��Q^��X�O�8�j�O�=��������'��@�����;kse�.q-cc
�;��w����h>RkF��[����������E��[�Z]��u�]n��J&[�,=
�:w�_�eP
��(z����s{���g�R9~�R������Rf��\saM�++�'Jf��'d��eC���|nm�1�pz�z~'v�������6d�q�kp;�c���bE���a�:���.]�v6��r��:\���s�.��-zn��Z[����+2��d������v���Os9%���.M�
��|r����%�t����lk�l�X�Q�T���K��\��Z�0^���%^����(�U�~��������+2u,V��c���~�X��v�T�T�.������X�X����6n�U�
�BF�s���(n|�l�M�I��
���N[��1��WV &� ="� �;�_&����Y�ze��A�RV����7]�fx��K�����Gy�IC��g[�d��v���h���`�)�H�,aG2�D��{�\�������k���
.::�h����^������;���r��d��+H��:����X����k*b0&f	��e�����qa~��	��uj���C���:����xR�w=#r4Fyg�S���!�*YYsc�t1CK|�s�Q�(���RGa�mF��M��zeyVj�^�j��<�/8z1���D�K�������qo�V��A?-�C.-j���G���;)�i��\�@�P[���I�z+������Oz����S�]���iPJ{
�����	��L��x7��dk����&
��T�0�C :�t�^�t
-���O������z�p4�zGa~�?r�,��|A]�(JV�fn�+����[�*�5O�~�0�LOe=��'-Mmc�>E1	�X�=�M�H���sv���x��IB,�RQ^���v<�M:4X��A<?7b��lZc(4����;MP�W���K�X�T���������u&&�,����bfL���PC�x��M,���	j/�����<g�(�0�����+��%8�>�,��
t�#]U��%��ewe�|��V��h�*�������
���$��
�!>Mk�K#��^Z�[�;�/]�Z����l�����f�a�����"��d������3�$rpC�Xq��t8W�c��8
b�;�������XA�j�Y���*�%��#�r���-�)
�1��s�:���)��.8����p~5�l��O�����6��C��~p�����2�l�P��A��_}����$��}�����0t�����_���d�����z�p�|zC���C��Ok~ >�g/^��zC�{�R������������Q�h�GCS:�*~b>������q�2�c[��x����v���;gc�;���x�n��J�0#}�������C�>�\�![��}xQ���o*byH|v~��q^C&&�������/�r�rE�0�Do@�`�+�q}RTHT{iB$u��g������~5��BL�Z`�o8z�R�������`��l��"�6j���"?Q�Sa���f5a]l����������FF��h�u%�@����\���%r���4��H�?�F|���<`���t�W�Gh����"<v��^ c��r��9*��������W�SV���CLn��	h���f#BjN��EX^���
$�Fa�{=0�Z`�����f��5��+s���]���%S�D �zU��p�@�+���6V�<4���w�%�t�&���y-0�J�Z����X���L����u��m��&��s,��P=_��q���JgI�\Xf��Q��b<;�_���)m��&��=�� �����V����r��QJe����r;r��f����j}?^�"�JPM�������@��!��P�y�[�*M�<Rx	�0�B��$�j�</��h���;��i�TN2��<_`Wa��KA�I�2fQ�&���t��w���2����I�CB���
��s��.�3���������T����1� �z �f@L+�`;��XO�25�KL��r��/h�)8-UP�M���V�U�D���(T59���������jw������v����IS5jFae���E�R��[��re�F�l�����s�20��l���\p��hRyq���#3��r�x<)3�=�l���\>K�D+	��1��q�KwfN��-�UYX�����l$��(��J�J���V��{�����65i�<8�L�*?g�(������A���\]�)�D�] ����y���p ����/un��_z�����)&�LeT�0{S�^%UA��B�7���B�"g��*c%����Tfp�'�(UF��|��G����J��������*u
4��k���mH�a1� �4A2eu�sa
��:����j@_;�guv_��
KOTZ�w��(���Us�jRZq	�\�b�P\��9��(->��^h�R���s�@����|�U��:�w�,)�V�c�%(�Sp����k���f@d����u�4��k,\
�_��fp���kg�]�u�����z��?N��	�x�`GA	\����[�[�g��cg)#���wa<@������j������_�]����8������W;zO���?}�h�Q�7��,�&�W��.��`�������:��4w�bSqjCq��&y4��Qa�fT���2}������{z�h��@
��j�Q*/@�2�Z��J��d��HiN���M1�@I���^o!w������l��=~e�#.,�KklW\��#��O���c�;���B��Z]�j�,�0�d���5Cg\�u�t=�P�kc).BD�����O����vz����� x6�
��p��|FQ�y��������(eC"��+��m:Y,���d���P%^@b^�Gk����g��YS���+"*����IV���+�A&�����]y���6�>��Mr�����$�\=M���!,}���
�0�,��Gks���90a,O�=w�S��xg�C��z�+qpa��!�B�0�]��jZ4�R�"1�����+U6ZZ�PQ�hY8Ea���0��Dd�[��U�Z������N5����{�A#����-������1�]��={Q���;id�+�^��fRk��eC���$�E����t�!,�W����E9�vb���5"�g�klQ���}Eka���O,��d�:���n4P�m�Z�K�1�)��W/�vn����a����A�H����*������8�.��mX��',l��5p5ZN>�j�\�����%����	,ZkjY�X�����M-#T�\4�gy�IU�x��J���
V�Y��HA��&`i������h�
4�8i��4�i��`�
���01��e?,S@+[@/[�_�@%�e�`�����W��Q��Y�D���D���W�2�s�e����i�hqe���T,��c5��k��k�s�_3o^�]��
�l<��T	�-u�
#369Alexander Korotkov
aekorotkov@gmail.com
In reply to: Peter Geoghegan (#368)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 1:18 PM, Peter Geoghegan <pg@heroku.com> wrote:

* The jsonb_hash_ops non-default GIN opclass is broken. It has its own
idiosyncratic notion of what constitutes containment, that sees it
only return, say, jsonb arrays that have a matching string as their
"leftmost" element (if we ask it if it contains within it another
array with the same string). Because of the limited number of
indexable operators (only @>), I'd put this opclass in the same
category as GiST in terms of my willingness to forgo it for a release,
even if it did receive a loud applause at pgConf.EU. Again, it might
be some disparity between the opertors as they existed in hstore2 at
one time, and as they exist in the core code now, but I doubt it, not
least since the regression tests didn't pick this up, and it's such a
basic thing. Perhaps Oleg and Teodor just need to explain this to me.

I din't get comment about "leftmost" element. There is absolutely no
distinguish between array elements. All elements are extracted into same
keys independent of their indexes. It seems to have no change since I wrote
hstore_hash_ops. Could you share test case to illustrate what you mean?

------
With best regards,
Alexander Korotkov.

#370Peter Geoghegan
pg@heroku.com
In reply to: Alexander Korotkov (#369)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 3:00 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

I din't get comment about "leftmost" element. There is absolutely no
distinguish between array elements. All elements are extracted into same
keys independent of their indexes. It seems to have no change since I wrote
hstore_hash_ops. Could you share test case to illustrate what you mean?

I don't have time to post that at the moment, but offhand I *think*
your confusion may be due to the fact that the json_hash_ops opclass
(as I call it) was previously consistent with the behavior of the
other GIN opclass (the default). The problem is that they (well, at
least the default GIN and GiST opclasses) were inconsistent with how
the containment operator behaved in respect of jsonb array elements
generally.

Here is the commit on our feature branch where I fixed the problem for
the default GIN opclass:

https://github.com/feodor/postgres/commit/6f5e4fe9fc34f9512919b1c8b6a54952ab288640s

If it doesn't explain the problem, you may still wish to comment on
the correctness of this fix. I am still waiting on feedback from Oleg
and Teodor.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#371Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#370)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 3:19 AM, Peter Geoghegan <pg@heroku.com> wrote:

I don't have time to post that at the moment, but offhand I *think*
your confusion may be due to the fact that the json_hash_ops opclass
(as I call it) was previously consistent with the behavior of the
other GIN opclass (the default). The problem is that they (well, at
least the default GIN and GiST opclasses) were inconsistent with how
the containment operator behaved in respect of jsonb array elements
generally.

Sorry, I realize now that that must be incorrect. Still, please take a
look at the commit linked to.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#372Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#371)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 3:21 AM, Peter Geoghegan <pg@heroku.com> wrote:

Sorry, I realize now that that must be incorrect. Still, please take a
look at the commit linked to.

To be clear, I mean that my explanation of why this was missed before
was incorrect, not my contention that it's a problem right now (for
whatever reason).

I fat-fingered the URL that linked to the GIN opclass bugfix commit:
https://github.com/feodor/postgres/commit/6f5e4fe9fc34f9512919b1c8b6a54952ab288640

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#373Alexander Korotkov
aekorotkov@gmail.com
In reply to: Peter Geoghegan (#370)
1 attachment(s)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 2:19 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Mon, Mar 10, 2014 at 3:00 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

I din't get comment about "leftmost" element. There is absolutely no
distinguish between array elements. All elements are extracted into same
keys independent of their indexes. It seems to have no change since I

wrote

hstore_hash_ops. Could you share test case to illustrate what you mean?

I don't have time to post that at the moment, but offhand I *think*
your confusion may be due to the fact that the json_hash_ops opclass
(as I call it) was previously consistent with the behavior of the
other GIN opclass (the default). The problem is that they (well, at
least the default GIN and GiST opclasses) were inconsistent with how
the containment operator behaved in respect of jsonb array elements
generally.

Here is the commit on our feature branch where I fixed the problem for
the default GIN opclass:

https://github.com/feodor/postgres/commit/6f5e4fe9fc34f9512919b1c8b6a54952ab288640s

If it doesn't explain the problem, you may still wish to comment on
the correctness of this fix. I am still waiting on feedback from Oleg
and Teodor.

Apparently, there is bug in calculation of hashes. Array elements were
hashed incrementally while each of them should be hashed separately. That
cause an effect of distinguishing array elements by their indexes. Not sure
about when this bug was added.
Fix is attached.

------
With best regards,
Alexander Korotkov.

Attachments:

jsonb-hash-ops-fix.patchapplication/octet-stream; name=jsonb-hash-ops-fix.patchDownload
diff --git a/src/backend/utils/adt/jsonb_gin.c b/src/backend/utils/adt/jsonb_gin.c
new file mode 100644
index f9ab4ce..0fd28cd
*** a/src/backend/utils/adt/jsonb_gin.c
--- b/src/backend/utils/adt/jsonb_gin.c
*************** gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
*** 315,326 ****
  		switch (r)
  		{
  			case WJB_BEGIN_ARRAY:
- 				tmp = stack;
- 				stack = (PathHashStack *) palloc(sizeof(PathHashStack));
- 				stack->next = tmp;
- 				stack->hash_state = tmp->hash_state;
- 				COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
- 				break;
  			case WJB_BEGIN_OBJECT:
  				/* Preserve stack item for key */
  				tmp = stack;
--- 315,320 ----
*************** gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
*** 335,340 ****
--- 329,336 ----
  				break;
  			case WJB_VALUE:
  			case WJB_ELEM:
+ 				stack->hash_state = stack->next->hash_state;
+ 				COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
  				hash_stack_value(&v, stack);
  				path_crc32 = stack->hash_state;
  				FIN_CRC32(path_crc32);
#374Peter Geoghegan
pg@heroku.com
In reply to: Alexander Korotkov (#373)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 3:47 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

Fix is attached.

Could you post a patch with regression tests, please?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#375Alexander Korotkov
aekorotkov@gmail.com
In reply to: Peter Geoghegan (#374)
1 attachment(s)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 3:04 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Mon, Mar 10, 2014 at 3:47 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

Fix is attached.

Could you post a patch with regression tests, please?

Here it is.

------
With best regards,
Alexander Korotkov.

Attachments:

jsonb-hash-ops-fix-2.patchapplication/octet-stream; name=jsonb-hash-ops-fix-2.patchDownload
diff --git a/src/backend/utils/adt/jsonb_gin.c b/src/backend/utils/adt/jsonb_gin.c
new file mode 100644
index f9ab4ce..0fd28cd
*** a/src/backend/utils/adt/jsonb_gin.c
--- b/src/backend/utils/adt/jsonb_gin.c
*************** gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
*** 315,326 ****
  		switch (r)
  		{
  			case WJB_BEGIN_ARRAY:
- 				tmp = stack;
- 				stack = (PathHashStack *) palloc(sizeof(PathHashStack));
- 				stack->next = tmp;
- 				stack->hash_state = tmp->hash_state;
- 				COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
- 				break;
  			case WJB_BEGIN_OBJECT:
  				/* Preserve stack item for key */
  				tmp = stack;
--- 315,320 ----
*************** gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
*** 335,340 ****
--- 329,336 ----
  				break;
  			case WJB_VALUE:
  			case WJB_ELEM:
+ 				stack->hash_state = stack->next->hash_state;
+ 				COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
  				hash_stack_value(&v, stack);
  				path_crc32 = stack->hash_state;
  				FIN_CRC32(path_crc32);
diff --git a/src/test/regress/data/jsonb.data b/src/test/regress/data/jsonb.data
new file mode 100644
index 0672333..90bdc6c
*** a/src/test/regress/data/jsonb.data
--- b/src/test/regress/data/jsonb.data
***************
*** 989,994 ****
--- 989,997 ----
  {"title":"CBC", "status":66, "line":989}
  {}
  {"array":["foo", "bar", "baz"]}
+ {"array":["bar", "baz", "foo"]}
+ {"array":["bar", "baz"]}
+ {"array":["baz", "foo"]}
  {"line":991, "abstract":"BA", "node":"BBB"}
  {"line":992, "disabled":true, "pos":29, "public":false}
  {"state":53, "wait":"CB", "subtitle":"CCC", "line":993, "date":"CAC", "public":false, "coauthors":"BB"}
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index e7a28f3..a916991
*** a/src/test/regress/expected/jsonb.out
--- b/src/test/regress/expected/jsonb.out
*************** SELECT 1 from testjsonb  WHERE j->'array
*** 744,750 ****
   ?column? 
  ----------
          1
! (1 row)
  
  SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']);
   jsonb_exists_any 
--- 744,752 ----
   ?column? 
  ----------
          1
!         1
!         1
! (3 rows)
  
  SELECT jsonb_exists_any('{"a":null, "b":"qq"}', ARRAY['a','b']);
   jsonb_exists_any 
*************** SELECT 1 from testjsonb  WHERE j->'array
*** 1437,1443 ****
   ?column? 
  ----------
          1
! (1 row)
  
  RESET enable_seqscan;
  DROP INDEX jidx;
--- 1439,1447 ----
   ?column? 
  ----------
          1
!         1
!         1
! (3 rows)
  
  RESET enable_seqscan;
  DROP INDEX jidx;
*************** SELECT count(*) FROM testjsonb WHERE j @
*** 1474,1479 ****
--- 1478,1495 ----
       2
  (1 row)
  
+ SELECT count(*) FROM testjsonb WHERE j @> '{"array":["foo"]}';
+  count 
+ -------
+      3
+ (1 row)
+ 
+ SELECT count(*) FROM testjsonb WHERE j @> '{"array":["bar"]}';
+  count 
+ -------
+      3
+ (1 row)
+ 
  SELECT count(*) FROM testjsonb WHERE j ? 'public';
   count 
  -------
*************** SELECT 1 from testjsonb  WHERE j->'array
*** 1498,1510 ****
   ?column? 
  ----------
          1
! (1 row)
  
  RESET enable_seqscan;
  SELECT count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow;
   count 
  -------
!   4784
  (1 row)
  
  SELECT key, count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow GROUP BY key ORDER BY count DESC, key;
--- 1514,1528 ----
   ?column? 
  ----------
          1
!         1
!         1
! (3 rows)
  
  RESET enable_seqscan;
  SELECT count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow;
   count 
  -------
!   4787
  (1 row)
  
  SELECT key, count(*) FROM (SELECT (jsonb_each(j)).key FROM testjsonb) AS wow GROUP BY key ORDER BY count DESC, key;
*************** SELECT key, count(*) FROM (SELECT (jsonb
*** 1532,1553 ****
   subtitle  |   169
   auth      |   168
   abstract  |   161
   age       |     2
-  array     |     1
  (24 rows)
  
  -- sort/hash
  SELECT count(distinct j) FROM testjsonb;
   count 
  -------
!    887
  (1 row)
  
  SET enable_hashagg = off;
  SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
   count 
  -------
!    887
  (1 row)
  
  SET enable_hashagg = on;
--- 1550,1571 ----
   subtitle  |   169
   auth      |   168
   abstract  |   161
+  array     |     4
   age       |     2
  (24 rows)
  
  -- sort/hash
  SELECT count(distinct j) FROM testjsonb;
   count 
  -------
!    890
  (1 row)
  
  SET enable_hashagg = off;
  SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
   count 
  -------
!    890
  (1 row)
  
  SET enable_hashagg = on;
*************** SET enable_sort = off;
*** 1555,1561 ****
  SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
   count 
  -------
!    887
  (1 row)
  
  SELECT distinct * FROM (values (jsonb '{}' || ''),('{}')) v(j);
--- 1573,1579 ----
  SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2;
   count 
  -------
!    890
  (1 row)
  
  SELECT distinct * FROM (values (jsonb '{}' || ''),('{}')) v(j);
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 384bede..afe131f
*** a/src/test/regress/sql/jsonb.sql
--- b/src/test/regress/sql/jsonb.sql
*************** SELECT count(*) FROM testjsonb WHERE j @
*** 335,340 ****
--- 335,342 ----
  SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC", "public":true}';
  SELECT count(*) FROM testjsonb WHERE j @> '{"age":25}';
  SELECT count(*) FROM testjsonb WHERE j @> '{"age":25.0}';
+ SELECT count(*) FROM testjsonb WHERE j @> '{"array":["foo"]}';
+ SELECT count(*) FROM testjsonb WHERE j @> '{"array":["bar"]}';
  SELECT count(*) FROM testjsonb WHERE j ? 'public';
  SELECT count(*) FROM testjsonb WHERE j ?| ARRAY['public','disabled'];
  SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled'];
#376Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#368)
Re: jsonb and nested hstore

On 03/10/2014 05:18 AM, Peter Geoghegan wrote:

On Fri, Mar 7, 2014 at 9:00 AM, Bruce Momjian <bruce@momjian.us> wrote:

OK, it sounds like the adjustments are minimal, like not using the
high-order bit.

Attached patch is a refinement of the work of Oleg, Teodor and Andrew.
Revisions are mostly my own, although Andrew contributed too.

Changes include:

* Extensive relocation, and moderate restructuring of code. Many
comments added, while many existing comments were copy-edited. Nothing
remains in contrib. jsonb is a distinct, in-core type with no
user-visible relationship to hstore. There is no code dependency
between the two. The amount of code redundancy this turned out to
create (between jsonb and an unchanged hstore) is, in my estimation,
quite acceptable.

* B-Tree and hash operator classes for the core type are included. A
GiST operator class, and two GIN operator classes are also included.
Obviously this is where I spent most time by far.

* Everything else that was in hstore in the last revision (the
complement of the hstore2 opclasses) is removed entirely. The patch is
much smaller. If we just consider code (excluding tests and
documentation), the diffstat seems far more manageable:

Thanks for your work on this.

It's just occurred to me that we'll need to add hstore_to_jsonb
functions and a cast to match the hstore_to_json functions and cast.

That should be fairly simple - I'll work on that. It need not hold up
progress with what's in this patch.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#377Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#376)
1 attachment(s)
Re: jsonb and nested hstore

On 03/10/2014 10:50 AM, Andrew Dunstan wrote:

Thanks for your work on this.

It's just occurred to me that we'll need to add hstore_to_jsonb
functions and a cast to match the hstore_to_json functions and cast.

That should be fairly simple - I'll work on that. It need not hold up
progress with what's in this patch.

Here's a patch sans docs for this, to be applied on top of Peter's
patch. It's actually kinda useful as it demonstrates how non-jsonb code
can construct jsonb values directy.

cheers

andrew

Attachments:

hstore-jsonb-funcs.patchtext/x-patch; name=hstore-jsonb-funcs.patchDownload
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 43b7e5f..bf21c65 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -5,7 +5,8 @@ OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
 	crc32.o
 
 EXTENSION = hstore
-DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
+DATA = hstore--1.3.sql hstore--1.2--1.3.sql \
+	hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
 	hstore--unpackaged--1.0.sql
 
 REGRESS = hstore
diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 2114143..9749e45 100644
--- a/contrib/hstore/expected/hstore.out
+++ b/contrib/hstore/expected/hstore.out
@@ -1453,7 +1453,7 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
      1
 (1 row)
 
--- json
+-- json and jsonb
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
                                          hstore_to_json                                          
 -------------------------------------------------------------------------------------------------
@@ -1472,6 +1472,24 @@ select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012
  {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
 (1 row)
 
+select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
+                                         hstore_to_jsonb                                         
+-------------------------------------------------------------------------------------------------
+ {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+(1 row)
+
+select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
+                                              jsonb                                              
+-------------------------------------------------------------------------------------------------
+ {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+(1 row)
+
+select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
+                                 hstore_to_jsonb_loose                                 
+---------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
+(1 row)
+
 create table test_json_agg (f1 text, f2 hstore);
 insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
        ('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql
new file mode 100644
index 0000000..0a70560
--- /dev/null
+++ b/contrib/hstore/hstore--1.2--1.3.sql
@@ -0,0 +1,17 @@
+/* contrib/hstore/hstore--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION hstore UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION hstore_to_jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore_to_jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore_to_jsonb(hstore);
+
+CREATE FUNCTION hstore_to_jsonb_loose(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore_to_jsonb_loose'
+LANGUAGE C IMMUTABLE STRICT;
diff --git a/contrib/hstore/hstore--1.3.sql b/contrib/hstore/hstore--1.3.sql
new file mode 100644
index 0000000..995ade1
--- /dev/null
+++ b/contrib/hstore/hstore--1.3.sql
@@ -0,0 +1,550 @@
+/* contrib/hstore/hstore--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION hstore" to load this file. \quit
+
+CREATE TYPE hstore;
+
+CREATE FUNCTION hstore_in(cstring)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_out(hstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_recv(internal)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_send(hstore)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE hstore (
+        INTERNALLENGTH = -1,
+        INPUT = hstore_in,
+        OUTPUT = hstore_out,
+        RECEIVE = hstore_recv,
+        SEND = hstore_send,
+        STORAGE = extended
+);
+
+CREATE FUNCTION hstore_version_diag(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_version_diag'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION fetchval(hstore,text)
+RETURNS text
+AS 'MODULE_PATHNAME','hstore_fetchval'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR -> (
+	LEFTARG = hstore,
+	RIGHTARG = text,
+	PROCEDURE = fetchval
+);
+
+CREATE FUNCTION slice_array(hstore,text[])
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_slice_to_array'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR -> (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = slice_array
+);
+
+CREATE FUNCTION slice(hstore,text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION isexists(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION exist(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ? (
+	LEFTARG = hstore,
+	RIGHTARG = text,
+	PROCEDURE = exist,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION exists_any(hstore,text[])
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists_any'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?| (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_any,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION exists_all(hstore,text[])
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists_all'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?& (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_all,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION isdefined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION defined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,text)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_delete'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_delete_array'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,hstore)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_delete_hstore'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR - (
+	LEFTARG = hstore,
+	RIGHTARG = text,
+	PROCEDURE = delete
+);
+
+CREATE OPERATOR - (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = delete
+);
+
+CREATE OPERATOR - (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = delete
+);
+
+CREATE FUNCTION hs_concat(hstore,hstore)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_concat'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR || (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_concat
+);
+
+CREATE FUNCTION hs_contains(hstore,hstore)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_contains'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hs_contained(hstore,hstore)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_contained'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR @> (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '<@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@>',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION tconvert(text,text)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_from_text'
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
+
+CREATE FUNCTION hstore(text,text)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_from_text'
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
+
+CREATE FUNCTION hstore(text[],text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_arrays'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
+
+CREATE FUNCTION hstore(text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_array'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (text[] AS hstore)
+  WITH FUNCTION hstore(text[]);
+
+CREATE FUNCTION hstore_to_json(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS json)
+  WITH FUNCTION hstore_to_json(hstore);
+
+CREATE FUNCTION hstore_to_json_loose(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION hstore_to_jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore_to_jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore_to_jsonb(hstore);
+
+CREATE FUNCTION hstore_to_jsonb_loose(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore_to_jsonb_loose'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION hstore(record)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_record'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
+
+CREATE FUNCTION hstore_to_array(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_array'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %% (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_array
+);
+
+CREATE FUNCTION hstore_to_matrix(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_matrix'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %# (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_matrix
+);
+
+CREATE FUNCTION akeys(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_akeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION avals(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_avals'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION skeys(hstore)
+RETURNS setof text
+AS 'MODULE_PATHNAME','hstore_skeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION svals(hstore)
+RETURNS setof text
+AS 'MODULE_PATHNAME','hstore_svals'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION each(IN hs hstore,
+    OUT key text,
+    OUT value text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME','hstore_each'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION populate_record(anyelement,hstore)
+RETURNS anyelement
+AS 'MODULE_PATHNAME', 'hstore_populate_record'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
+
+CREATE OPERATOR #= (
+	LEFTARG = anyelement,
+	RIGHTARG = hstore,
+	PROCEDURE = populate_record
+);
+
+-- btree support
+
+CREATE FUNCTION hstore_eq(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_eq'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ne(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ne'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_gt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_gt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ge(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ge'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_lt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_lt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_le(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_le'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_cmp(hstore,hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_cmp'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR = (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_eq,
+       COMMUTATOR = =,
+       NEGATOR = <>,
+       RESTRICT = eqsel,
+       JOIN = eqjoinsel,
+       MERGES,
+       HASHES
+);
+CREATE OPERATOR <> (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ne,
+       COMMUTATOR = <>,
+       NEGATOR = =,
+       RESTRICT = neqsel,
+       JOIN = neqjoinsel
+);
+
+-- the comparison operators have funky names (and are undocumented)
+-- in an attempt to discourage anyone from actually using them. they
+-- only exist to support the btree opclass
+
+CREATE OPERATOR #<# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_lt,
+       COMMUTATOR = #>#,
+       NEGATOR = #>=#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #<=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_le,
+       COMMUTATOR = #>=#,
+       NEGATOR = #>#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #># (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_gt,
+       COMMUTATOR = #<#,
+       NEGATOR = #<=#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+CREATE OPERATOR #>=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ge,
+       COMMUTATOR = #<=#,
+       NEGATOR = #<#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR CLASS btree_hstore_ops
+DEFAULT FOR TYPE hstore USING btree
+AS
+	OPERATOR	1	#<# ,
+	OPERATOR	2	#<=# ,
+	OPERATOR	3	= ,
+	OPERATOR	4	#>=# ,
+	OPERATOR	5	#># ,
+	FUNCTION	1	hstore_cmp(hstore,hstore);
+
+-- hash support
+
+CREATE FUNCTION hstore_hash(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_hash'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR CLASS hash_hstore_ops
+DEFAULT FOR TYPE hstore USING hash
+AS
+	OPERATOR	1	= ,
+	FUNCTION	1	hstore_hash(hstore);
+
+-- GiST support
+
+CREATE TYPE ghstore;
+
+CREATE FUNCTION ghstore_in(cstring)
+RETURNS ghstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION ghstore_out(ghstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE ghstore (
+        INTERNALLENGTH = -1,
+        INPUT = ghstore_in,
+        OUTPUT = ghstore_out
+);
+
+CREATE FUNCTION ghstore_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_union(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_same(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gist_hstore_ops
+DEFAULT FOR TYPE hstore USING gist
+AS
+	OPERATOR        7       @> ,
+	OPERATOR        9       ?(hstore,text) ,
+	OPERATOR        10      ?|(hstore,text[]) ,
+	OPERATOR        11      ?&(hstore,text[]) ,
+        --OPERATOR        8       <@ ,
+        OPERATOR        13      @ ,
+        --OPERATOR        14      ~ ,
+        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
+        FUNCTION        2       ghstore_union (internal, internal),
+        FUNCTION        3       ghstore_compress (internal),
+        FUNCTION        4       ghstore_decompress (internal),
+        FUNCTION        5       ghstore_penalty (internal, internal, internal),
+        FUNCTION        6       ghstore_picksplit (internal, internal),
+        FUNCTION        7       ghstore_same (internal, internal, internal),
+        STORAGE         ghstore;
+
+-- GIN support
+
+CREATE FUNCTION gin_extract_hstore(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gin_hstore_ops
+DEFAULT FOR TYPE hstore USING gin
+AS
+	OPERATOR        7       @>,
+	OPERATOR        9       ?(hstore,text),
+	OPERATOR        10      ?|(hstore,text[]),
+	OPERATOR        11      ?&(hstore,text[]),
+	FUNCTION        1       bttextcmp(text,text),
+	FUNCTION        2       gin_extract_hstore(internal, internal),
+	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
+	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
+	STORAGE         text;
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 9daf5e2..dcc3b68 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/hstore'
 relocatable = true
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 65e4438..d106718 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -12,6 +12,7 @@
 #include "libpq/pqformat.h"
 #include "utils/builtins.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/typcache.h"
@@ -1374,3 +1375,167 @@ hstore_to_json(PG_FUNCTION_ARGS)
 
 	PG_RETURN_TEXT_P(cstring_to_text(dst.data));
 }
+
+PG_FUNCTION_INFO_V1(hstore_to_jsonb);
+Datum		hstore_to_jsonb(PG_FUNCTION_ARGS);
+Datum
+hstore_to_jsonb(PG_FUNCTION_ARGS)
+{
+	HStore	   *in = PG_GETARG_HS(0);
+	int			i;
+	int			count = HS_COUNT(in);
+	char	   *base = STRPTR(in);
+	HEntry	   *entries = ARRPTR(in);
+	ToJsonbState *state = NULL;
+	JsonbValue *res;
+
+	res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	for (i = 0; i < count; i++)
+	{
+		JsonbValue key, val;
+
+		key.size = sizeof(JEntry);
+		key.type = jbvString;
+		key.string.len = HS_KEYLEN(entries, i);
+		key.string.val = pnstrdup(HS_KEY(entries, base, i), key.string.len);
+		key.size += key.string.len;
+
+		res = pushJsonbValue(&state, WJB_KEY, &key);
+
+		if (HS_VALISNULL(entries, i))
+		{
+			val.size = sizeof(JEntry);
+			val.type = jbvNull;
+		}
+		else
+		{
+			val.size = sizeof(JEntry);
+			val.type = jbvString;
+			val.string.len = HS_VALLEN(entries, i);
+			val.string.val = pnstrdup(HS_VAL(entries, base, i), val.string.len);
+			val.size += val.string.len;
+		}
+		res = pushJsonbValue(&state, WJB_VALUE, &val);
+	}
+
+	res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
+
+PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
+Datum		hstore_to_jsonb_loose(PG_FUNCTION_ARGS);
+Datum
+hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
+{
+	HStore	   *in = PG_GETARG_HS(0);
+	int			i;
+	int			count = HS_COUNT(in);
+	char	   *base = STRPTR(in);
+	HEntry	   *entries = ARRPTR(in);
+	ToJsonbState *state = NULL;
+	JsonbValue *res;
+	StringInfoData tmp;
+	bool        is_number;
+
+	initStringInfo(&tmp);
+
+	res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	for (i = 0; i < count; i++)
+	{
+		JsonbValue key, val;
+
+		key.size = sizeof(JEntry);
+		key.type = jbvString;
+		key.string.len = HS_KEYLEN(entries, i);
+		key.string.val = pnstrdup(HS_KEY(entries, base, i), key.string.len);
+		key.size += key.string.len;
+
+		res = pushJsonbValue(&state, WJB_KEY, &key);
+
+		val.size = sizeof(JEntry);
+
+		if (HS_VALISNULL(entries, i))
+		{
+			val.type = jbvNull;
+		}
+		/* guess that values of 't' or 'f' are booleans */
+		else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't')
+		{
+			val.type = jbvBool;
+			val.boolean = true;
+		}
+		else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f')
+		{
+			val.type = jbvBool;
+			val.boolean = false;
+		}
+		else
+		{
+			is_number = false;
+			resetStringInfo(&tmp);
+
+			appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
+
+			/*
+			 * don't treat something with a leading zero followed by another
+			 * digit as numeric - could be a zip code or similar
+			 */
+			if (tmp.len > 0 &&
+				!(tmp.data[0] == '0' &&
+				  isdigit((unsigned char) tmp.data[1])) &&
+				strspn(tmp.data, "+-0123456789Ee.") == tmp.len)
+			{
+				/*
+				 * might be a number. See if we can input it as a numeric
+				 * value. Ignore any actual parsed value.
+				 */
+				char	   *endptr = "junk";
+				long		lval;
+
+				lval = strtol(tmp.data, &endptr, 10);
+				(void) lval;
+				if (*endptr == '\0')
+				{
+					/*
+					 * strol man page says this means the whole string is
+					 * valid
+					 */
+					is_number = true;
+				}
+				else
+				{
+					/* not an int - try a double */
+					double		dval;
+
+					dval = strtod(tmp.data, &endptr);
+					(void) dval;
+					if (*endptr == '\0')
+						is_number = true;
+				}
+			}
+			if (is_number)
+			{
+				val.type = jbvNumeric;
+				val.numeric = DatumGetNumeric(
+					DirectFunctionCall3(numeric_in, CStringGetDatum(tmp.data), 0, -1));
+				val.size += VARSIZE_ANY(val.numeric) +sizeof(JEntry);
+			}
+			else
+			{
+				val.size = sizeof(JEntry);
+				val.type = jbvString;
+				val.string.len = HS_VALLEN(entries, i);
+				val.string.val = pnstrdup(HS_VAL(entries, base, i), val.string.len);
+				val.size += val.string.len;
+			}
+		}
+		res = pushJsonbValue(&state, WJB_VALUE, &val);
+	}
+
+	res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index 9518f56..5a9e9ee 100644
--- a/contrib/hstore/sql/hstore.sql
+++ b/contrib/hstore/sql/hstore.sql
@@ -331,11 +331,15 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
 
--- json
+-- json and jsonb
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
 select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 
+select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
+select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
+select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
+
 create table test_json_agg (f1 text, f2 hstore);
 insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
        ('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
#378Peter Geoghegan
pg@heroku.com
In reply to: Alexander Korotkov (#375)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 4:19 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

Here it is.

So it looks like what you have here is analogous to the other problems
that I fixed with both GiST and GIN. That isn't surprising, and this
does fix my test-case. I'm not terribly happy about the lack of
explanation for the hashing in that loop, though. Why use COMP_CRC32()
at all, for one thing?

Why do this for non-primitive jsonb hashing?

COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);

Where PATH_SEPARATOR is:

#define PATH_SEPARATOR ("\0")

Actually, come to think of it, why not just use one hashing function
everywhere? i.e., jsonb_hash(PG_FUNCTION_ARGS)? It's already very
similar. Pretty much every hash operator support function 1 (i.e. a
particular type's hash function) is implemented with hash_any(). Can't
we just do the same here? In any case it isn't obvious why the
requirements for those two things (the hashing mechanism used by the
jsonb_hash_ops GIN opclass, and the hash operator class support
function 1 hash function) cannot be the same thing.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#379Alexander Korotkov
aekorotkov@gmail.com
In reply to: Peter Geoghegan (#378)
Re: jsonb and nested hstore

On Tue, Mar 11, 2014 at 5:19 AM, Peter Geoghegan <pg@heroku.com> wrote:

On Mon, Mar 10, 2014 at 4:19 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

Here it is.

So it looks like what you have here is analogous to the other problems
that I fixed with both GiST and GIN. That isn't surprising, and this
does fix my test-case. I'm not terribly happy about the lack of
explanation for the hashing in that loop, though. Why use COMP_CRC32()
at all, for one thing?

Why do this for non-primitive jsonb hashing?

COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);

Where PATH_SEPARATOR is:

#define PATH_SEPARATOR ("\0")

Actually, come to think of it, why not just use one hashing function
everywhere? i.e., jsonb_hash(PG_FUNCTION_ARGS)? It's already very
similar. Pretty much every hash operator support function 1 (i.e. a
particular type's hash function) is implemented with hash_any(). Can't
we just do the same here? In any case it isn't obvious why the
requirements for those two things (the hashing mechanism used by the
jsonb_hash_ops GIN opclass, and the hash operator class support
function 1 hash function) cannot be the same thing.

It's because CRC32 interface allows incremental calculation while hash_any
requires single chunk of memory. I don't think that unfolding everything is
good idea. But we could implement incremental interface for hash_any.

------
With best regards,
Alexander Korotkov.

#380Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#368)
Re: jsonb and nested hstore

Hi,

I've spent a few hours stress-testing this a bit - loading a mail
archive with ~1M of messages (with headers stored in a jsonb column) and
then doing queries on that. Good news - no crashes or any such issues so
far. The queries that I ran manually seem to return sane results.

The only problem I ran into is with limited index row size with GIN
indexes. I understand it's not a bug, but I admit I haven't realized I
might run into it in this case ...

The data I used for testing is just a bunch of e-mail messages, with
headers stored as jsonb, so each row has something like this in
"headers" column:

{
"from" : "John Doe <john@example.com>",
"to" : ["Jane Doe <jane@example.com>", "Jack Doe <jack@example.com>"],
"cc" : ...,
"bcc" : ...,
... various other headers ...
}

The snag is that some of the header values may be very long, exceeding
the limit of 1352 bytes and causing errors like this:

ERROR: index row size 1416 exceeds maximum 1352 for index "gin_idx"

A good example of such header is "dkim-signature" which basically
contains the whole message digitally signed with DKIM. The signature
tends to be long and non-compressible, thanks to the signature.

I'm wondering what's the best way around this, because I suspect many
new users (especially those attracted by jsonb and GIN improvements)
will run into this. Maybe not immediately, but eventully they'll try to
insert a jsonb with long value, and it will fail ...

With btree indexes on text I would probably create an index on
substr(column,0,1000) or something like that, but doing that with JSON
seems a bit strange.

I assume we need to store the actual values in the GIN index (so a hash
is not sufficient), right?

GIST indexes work, but with that I have to give up the significant
performance gains that we got thanks to Alexander's GIN patches.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#381Peter Geoghegan
pg@heroku.com
In reply to: Tomas Vondra (#380)
Re: jsonb and nested hstore

On Tue, Mar 11, 2014 at 3:58 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

ERROR: index row size 1416 exceeds maximum 1352 for index "gin_idx"

All index AMs have similar restrictions.

A good example of such header is "dkim-signature" which basically
contains the whole message digitally signed with DKIM. The signature
tends to be long and non-compressible, thanks to the signature.

I'm wondering what's the best way around this, because I suspect many
new users (especially those attracted by jsonb and GIN improvements)
will run into this. Maybe not immediately, but eventully they'll try to
insert a jsonb with long value, and it will fail ...

The jsonb_hash_ops operator class just stores a 32-bit integer hash
value (it always sets the recheck flag, which only some of the other
default GIN opclass' strategies do). It only supports containment, and
not the full variety of operators that the default opclass supports,
which is why it isn't the default. I think that in practice the
general recommendation will be that when indexing at the "top level",
use jsonb_hash_ops. When indexing nested items, use the more flexible
default GIN opclass. That seems like a pretty smart trade-off to me.

The more I think about it, the more inclined I am to lose GiST support
entirely for the time being. It lets us throw out about 700 lines of C
code, which is a very significant fraction of the total, removes the
one open bug, and removes the least understood part of the code. The
GiST opclass is not particularly compelling for this.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#382Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#381)
Re: jsonb and nested hstore

On Tue, Mar 11, 2014 at 4:41 PM, Peter Geoghegan <pg@heroku.com> wrote:

I think that in practice the
general recommendation will be that when indexing at the "top level",
use jsonb_hash_ops. When indexing nested items, use the more flexible
default GIN opclass. That seems like a pretty smart trade-off to me.

By which I mean: index nested items using an expressional GIN index.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#383Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#381)
Re: jsonb and nested hstore

On 12 Březen 2014, 0:41, Peter Geoghegan wrote:

On Tue, Mar 11, 2014 at 3:58 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

ERROR: index row size 1416 exceeds maximum 1352 for index "gin_idx"

All index AMs have similar restrictions.

Yes, I know and I have no problem with restrictions in general. You may
run into similar issues with btree indexes on text columns with long text,
for example. The thing is that people don't generally index text directly,
because it usually does not make much sense, but using tsvector etc.

But with jsonb it's more likely because indexing is one of the goodies (at
least for me). And the discussions with several people interested in
storing json data I had recently went often like this:

me: It seems we'll have a better json datatype in 9.4.
them: Nice!
me: And it will be possible to do searches on arbitrary keys.
them: Yay!
me: And we actually got pretty significant improvements in GIN indexes.
them: Awesome!
me: But the values you may index need to be less than ~1500B.
them: Bummer :-(
me: Well, you can use GIST then.

A good example of such header is "dkim-signature" which basically
contains the whole message digitally signed with DKIM. The signature
tends to be long and non-compressible, thanks to the signature.

I'm wondering what's the best way around this, because I suspect many
new users (especially those attracted by jsonb and GIN improvements)
will run into this. Maybe not immediately, but eventully they'll try to
insert a jsonb with long value, and it will fail ...

The jsonb_hash_ops operator class just stores a 32-bit integer hash
value (it always sets the recheck flag, which only some of the other
default GIN opclass' strategies do). It only supports containment, and
not the full variety of operators that the default opclass supports,
which is why it isn't the default. I think that in practice the
general recommendation will be that when indexing at the "top level",
use jsonb_hash_ops. When indexing nested items, use the more flexible
default GIN opclass. That seems like a pretty smart trade-off to me.

OK, I'll look into the jsonb_hash_ops - that sounds more or less like what
I was thinking about (and sure, storing hashes makes some operations
impossible to support).

The other thing I was thinking about is introducing some kind of upper
limit for the value length - e.g. index just the first 1kB, or something
like that. My experience is most values are way shorter, or actually
differ in the first 1kB, so this should allow most decisions to be made.
But I'm not really that familiar with how GIN works, so maybe this is
nonsense.

The more I think about it, the more inclined I am to lose GiST support
entirely for the time being. It lets us throw out about 700 lines of C
code, which is a very significant fraction of the total, removes the
one open bug, and removes the least understood part of the code. The
GiST opclass is not particularly compelling for this.

I disagree with that. I see GiST as a simple fallback option for the cases
I described. I wasn't able to create a GIN index because of exceeding the
max item length, but GiST created just fine. It was considerably slower,
but it worked.

Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#384Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#382)
Re: jsonb and nested hstore

On 12 Březen 2014, 0:51, Peter Geoghegan wrote:

On Tue, Mar 11, 2014 at 4:41 PM, Peter Geoghegan <pg@heroku.com> wrote:

I think that in practice the
general recommendation will be that when indexing at the "top level",
use jsonb_hash_ops. When indexing nested items, use the more flexible
default GIN opclass. That seems like a pretty smart trade-off to me.

By which I mean: index nested items using an expressional GIN index.

I'm still not sure how would that look. Does that mean I'd have to create
multiple GIN indexes - one for each possible key or something like that?
Can you give an example?

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#385Oleg Bartunov
obartunov@gmail.com
In reply to: Tomas Vondra (#383)
Re: jsonb and nested hstore

Also, GiST index is faster for create/update operations. I really hope we will
improve jsonb indexing in the next one-two releases. For now I'd suggest people
index expressional indexes to index just interesting keys or use GiST.

On Wed, Mar 12, 2014 at 5:15 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

On 12 Březen 2014, 0:41, Peter Geoghegan wrote:

On Tue, Mar 11, 2014 at 3:58 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

ERROR: index row size 1416 exceeds maximum 1352 for index "gin_idx"

All index AMs have similar restrictions.

Yes, I know and I have no problem with restrictions in general. You may
run into similar issues with btree indexes on text columns with long text,
for example. The thing is that people don't generally index text directly,
because it usually does not make much sense, but using tsvector etc.

But with jsonb it's more likely because indexing is one of the goodies (at
least for me). And the discussions with several people interested in
storing json data I had recently went often like this:

me: It seems we'll have a better json datatype in 9.4.
them: Nice!
me: And it will be possible to do searches on arbitrary keys.
them: Yay!
me: And we actually got pretty significant improvements in GIN indexes.
them: Awesome!
me: But the values you may index need to be less than ~1500B.
them: Bummer :-(
me: Well, you can use GIST then.

A good example of such header is "dkim-signature" which basically
contains the whole message digitally signed with DKIM. The signature
tends to be long and non-compressible, thanks to the signature.

I'm wondering what's the best way around this, because I suspect many
new users (especially those attracted by jsonb and GIN improvements)
will run into this. Maybe not immediately, but eventully they'll try to
insert a jsonb with long value, and it will fail ...

The jsonb_hash_ops operator class just stores a 32-bit integer hash
value (it always sets the recheck flag, which only some of the other
default GIN opclass' strategies do). It only supports containment, and
not the full variety of operators that the default opclass supports,
which is why it isn't the default. I think that in practice the
general recommendation will be that when indexing at the "top level",
use jsonb_hash_ops. When indexing nested items, use the more flexible
default GIN opclass. That seems like a pretty smart trade-off to me.

OK, I'll look into the jsonb_hash_ops - that sounds more or less like what
I was thinking about (and sure, storing hashes makes some operations
impossible to support).

The other thing I was thinking about is introducing some kind of upper
limit for the value length - e.g. index just the first 1kB, or something
like that. My experience is most values are way shorter, or actually
differ in the first 1kB, so this should allow most decisions to be made.
But I'm not really that familiar with how GIN works, so maybe this is
nonsense.

The more I think about it, the more inclined I am to lose GiST support
entirely for the time being. It lets us throw out about 700 lines of C
code, which is a very significant fraction of the total, removes the
one open bug, and removes the least understood part of the code. The
GiST opclass is not particularly compelling for this.

I disagree with that. I see GiST as a simple fallback option for the cases
I described. I wasn't able to create a GIN index because of exceeding the
max item length, but GiST created just fine. It was considerably slower,
but it worked.

Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#386Peter Geoghegan
pg@heroku.com
In reply to: Tomas Vondra (#384)
Re: jsonb and nested hstore

On Wed, Mar 12, 2014 at 6:20 AM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm still not sure how would that look. Does that mean I'd have to create
multiple GIN indexes - one for each possible key or something like that?
Can you give an example?

It could mean that you're obliged to create multiple indexes, yes. For
an example, and to get a better sense of what I mean, look at the
documentation in the patch.

The idea that you're going to create one index on a jsonb, and it's
going to be able to usefully index a lot of different queries doesn't
seem practical for most use-cases. Mostly, people will have fairly
homogeneous json documents, and they'll want to index certain nested
fields common to all or at least a large majority of those documents.

By indexing entire jsonb datums, do you hope to get much benefit out
of the indexed values (as opposed to keys) being stored (in serialized
form) in the GIN index? Because you *are* indexing a large nested
structure as a value. Is that large nested structure going to appear
in your query predicate, or are you just going to subscript the jsonb
to get to the level that's of interest to query that? I'm pretty sure
that people want the latter. Are you sure that your complaint isn't
just that the default GIN opclass indexes values (as distinct from
keys) that are large and unwieldy, and not terribly useful?

I don't think expressional indexes are some kind of unfortunate work
around for a jsonb limitation. I think that they're the natural way to
approach indexing a nested structure in Postgres. MongoDB, for
example, does not magically index everything. You're still required to
make choices about indexing that consider the access patterns.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#387Peter Geoghegan
pg@heroku.com
In reply to: Oleg Bartunov (#385)
Re: jsonb and nested hstore

On Wed, Mar 12, 2014 at 11:57 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

Also, GiST index is faster for create/update operations. I really hope we will
improve jsonb indexing in the next one-two releases. For now I'd suggest people
index expressional indexes to index just interesting keys or use GiST.

When do you ever want to index non-interesting keys?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#388Oleg Bartunov
obartunov@gmail.com
In reply to: Peter Geoghegan (#387)
Re: jsonb and nested hstore

On Thu, Mar 13, 2014 at 12:10 AM, Peter Geoghegan <pg@heroku.com> wrote:

On Wed, Mar 12, 2014 at 11:57 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

Also, GiST index is faster for create/update operations. I really hope we will
improve jsonb indexing in the next one-two releases. For now I'd suggest people
index expressional indexes to index just interesting keys or use GiST.

When do you ever want to index non-interesting keys?

Regular user may just index all keys.

I mean, that json can contains keys, which are not searched, so it's
not needed to index them and save index size. We probably could
provide option in CREATE INDEX to specify what to index and what not
index, but it require planner to know that information.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#389Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#387)
Re: jsonb and nested hstore

On 03/12/2014 04:10 PM, Peter Geoghegan wrote:

On Wed, Mar 12, 2014 at 11:57 AM, Oleg Bartunov <obartunov@gmail.com> wrote:

Also, GiST index is faster for create/update operations. I really hope we will
improve jsonb indexing in the next one-two releases. For now I'd suggest people
index expressional indexes to index just interesting keys or use GiST.

When do you ever want to index non-interesting keys?

The problem is when do you know they are interesting?

One major use case for using treeish data types in the first place is
that you don't know when you're designing the database exactly what
shape the data will be. If you don't know that, then how are you
supposed to know what in it will be interesting? It's somewhat analogous
to full text indexing, where we don't know in advance what phrases or
words will be interesting. Here, a key is the equivalent of a word and a
key path or subpath is the equivalent of a phrase.

Maybe I'm dreaming, since I have no idea how to go about this sort of
indexing, but it's where I'd like to see lots of effort.

I agree with Oleg that we need to be very creative about jsonb indexing.
One of my hopes is that by going down the road we are on, we'll get much
wider interest in this, and that both ideas and money might flow towards
addressing it in a way that we probably wouldn't have seen otherwise.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#390Josh Berkus
josh@agliodbs.com
In reply to: Peter Geoghegan (#338)
Re: jsonb and nested hstore

Andrew, Peter:

Just so I'm clear on the limits here, lemme make sure I understand this:

a) GIN indexing is limited to ~~1500chars

b) The "value", which includes everything other than the top level set
of keys, is one item as far as GIN is concerned.

Therefore: we are limited to indexing JSON where nothing below a
top-level key is more than 1500bytes?

I'm asking for documentation purposes.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#391Peter Geoghegan
pg@heroku.com
In reply to: Andrew Dunstan (#389)
Re: jsonb and nested hstore

On Wed, Mar 12, 2014 at 1:37 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

One major use case for using treeish data types in the first place is that
you don't know when you're designing the database exactly what shape the
data will be. If you don't know that, then how are you supposed to know what
in it will be interesting? It's somewhat analogous to full text indexing,
where we don't know in advance what phrases or words will be interesting.
Here, a key is the equivalent of a word and a key path or subpath is the
equivalent of a phrase.

You don't know exactly how, but you have some idea. The major benefit
is that you can add new things to new documents as the need arises,
and that's not a big deal, nor does it require a migration with DDL.
If we continue to take MongoDB as representative of how people will
use jsonb, they pretty strongly encourage the idea that you have to
have some structure or design. Google "mongodb schema design" to see
what I mean - you'll find plenty. It has more to do with making
querying the data possible than anything else. There is a limited
amount you can do with a bunch of documents that share little in
common in terms of their structure - what does a query (that can use
an index just in principle) even look like there?

The use case you describe here doesn't sound like something similar to
full text search. It sounds like something identical.

In any case, let's focus on what we have right now. I think that the
indexing facilities proposed here are solid. In any case they do not
preclude working on better indexing strategies as the need emerges.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#392Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Geoghegan (#391)
Re: jsonb and nested hstore

On 03/12/2014 04:58 PM, Peter Geoghegan wrote:

In any case, let's focus on what we have right now. I think that the
indexing facilities proposed here are solid. In any case they do not
preclude working on better indexing strategies as the need emerges.

I quite agree, didn't mean to suggest otherwise.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#393Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#386)
Re: jsonb and nested hstore

On 12.3.2014 20:40, Peter Geoghegan wrote:

On Wed, Mar 12, 2014 at 6:20 AM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm still not sure how would that look. Does that mean I'd have to
create multiple GIN indexes - one for each possible key or
something like that? Can you give an example?

It could mean that you're obliged to create multiple indexes, yes.
For an example, and to get a better sense of what I mean, look at
the documentation in the patch.

OK, will do.

The idea that you're going to create one index on a jsonb, and it's
going to be able to usefully index a lot of different queries doesn't
seem practical for most use-cases. Mostly, people will have fairly
homogeneous json documents, and they'll want to index certain nested
fields common to all or at least a large majority of those documents.

I think that's unfounded assumption. Many users actually have very
little control over the documents or queries - a nice example may be the
mail archive, with headers stored in a hstore/jsonb. I have absolutely
no control over the headers or queries.

But I think this is a "feedback loop" too - what if many users actually
want that functionality, but realize that expression indexes are not
sufficient for their needs and thus don't even try (and so we don't hear
about them)?

And my experience is that this is actualy one of the very cool hstore
features - being able to index the whole structure and then do arbitrary
queries over that.

The only reason why I'm looking at jsonb is that it the improved support
for data types (especially arrays).

So I have my doubts about the claims that users have homogenous
documents and only want to index some fields with expression indexes.

By indexing entire jsonb datums, do you hope to get much benefit out
of the indexed values (as opposed to keys) being stored (in serialized
form) in the GIN index? Because you *are* indexing a large nested
structure as a value. Is that large nested structure going to appear
in your query predicate, or are you just going to subscript the jsonb
to get to the level that's of interest to query that? I'm pretty sure
that people want the latter. Are you sure that your complaint isn't
just that the default GIN opclass indexes values (as distinct from
keys) that are large and unwieldy, and not terribly useful?

No, I don't expect a large nested structure to appear in the query. And
I expect most people won't need that, although I can imagine queries @>
doing that (not sure if that checks for equality or 'subset').

But I'm not sure I understand how's this related to my original post?

All I was asking whether it wouldn't be enough to store a hash instead
of the original value, i.e. instead of this:

{"from" : "john@example.com",
"to" : "jack@example.com",
"content-type" : "text/plain; charset=us-ascii",
"dkim-signature" : ".... veeeery long value ...."}

this

{129812 : 29382,
459821 : 1029381,
21083 : 102941,
111390 : 129010292}

which would solve issues with the long values and might still support
the queries (with recheck, of course). I don't know if that's what
jsonb_hash_ops do or if it's even possible / compatible with GIN.

I don't think expressional indexes are some kind of unfortunate work
around for a jsonb limitation. I think that they're the natural way to
approach indexing a nested structure in Postgres. MongoDB, for
example, does not magically index everything. You're still required to
make choices about indexing that consider the access patterns.

For many usecases, expressional indexes are the right tool. But not for
all and I see no reason to just throw some tools away.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#394Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#391)
Re: jsonb and nested hstore

On 12.3.2014 21:58, Peter Geoghegan wrote:

The use case you describe here doesn't sound like something similar to
full text search. It sounds like something identical.

I think this very depends on the definition of full text search.

In any case, let's focus on what we have right now. I think that the
indexing facilities proposed here are solid. In any case they do not
preclude working on better indexing strategies as the need emerges.

+1

Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#395Peter Geoghegan
pg@heroku.com
In reply to: Tomas Vondra (#393)
Re: jsonb and nested hstore

On Wed, Mar 12, 2014 at 2:30 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I think that's unfounded assumption. Many users actually have very
little control over the documents or queries - a nice example may be the
mail archive, with headers stored in a hstore/jsonb. I have absolutely
no control over the headers or queries.

Maybe, but what do you want me to do to help them? Indexing a typical
jsonb field is a bad idea, unless you really do want something
essentially equivalent to full text search (which could be justified),
or unless you know ahead of time that your documents are not going to
be heavily nested. The whole basis of your complaints seems to be that
people won't know that at all.

For many usecases, expressional indexes are the right tool. But not for
all and I see no reason to just throw some tools away.

If the tool you're talking about throwing away is the GiST opclass, I
do not propose to throw that away. I don't think it's important enough
to justify inclusion in our first cut at this, especially given the
fact that the code has bugs, and is quite a bit more complex than GIN.
What's wrong with those reasons?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#396Tomas Vondra
tv@fuzzy.cz
In reply to: Josh Berkus (#390)
Re: jsonb and nested hstore

On 12.3.2014 21:55, Josh Berkus wrote:

Andrew, Peter:

Just so I'm clear on the limits here, lemme make sure I understand this:

a) GIN indexing is limited to ~~1500chars

The exact message I get is this:

ERROR: index row size 1944 exceeds maximum 1352 for index "tmp_idx"

so it's 1352B. But IIRC this is closely related to block size, so with
larger block sizes you'll get different limits. Also, this is a limit on
compressed value, which makes it less user-friendly as it's difficult to
predict whether the row is OK or not :-(

And I just discovered this:

create table tmp (val jsonb);
create index tmp_gin_idx on tmp using gin (val);
insert into tmp
select ('{"z" : "' || repeat('z', 1000000) || '"}')::jsonb;

which tries to insert a well-compressible string ('z' repeated
1e6-times), and fails with this:

ERROR: index row requires 11472 bytes, maximum size is 8191

So I think it's quite difficult to give simple and exact explanation in
the docs, other than "there are limits, but it's difficult to say when
you hit them".

Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#397Stephen Frost
sfrost@snowman.net
In reply to: Tomas Vondra (#396)
Re: jsonb and nested hstore

* Tomas Vondra (tv@fuzzy.cz) wrote:

So I think it's quite difficult to give simple and exact explanation in
the docs, other than "there are limits, but it's difficult to say when
you hit them".

Arrays have more-or-less the same issue...

Thanks,

Stephen

#398Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#395)
Re: jsonb and nested hstore

On 12.3.2014 22:43, Peter Geoghegan wrote:

On Wed, Mar 12, 2014 at 2:30 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I think that's unfounded assumption. Many users actually have very
little control over the documents or queries - a nice example may be the
mail archive, with headers stored in a hstore/jsonb. I have absolutely
no control over the headers or queries.

Maybe, but what do you want me to do to help them? Indexing a
typical jsonb field is a bad idea, unless you really do want
something essentially equivalent to full text search (which could be
justified), or unless you know ahead of time that your documents are
not going to be heavily nested. The whole basis of your complaints
seems to be that people won't know that at all.

Well, I would be quite happy with the GIN indexing without the limit I
ran into. I don't think we need to invent something entirely new.

You're right that the index is pretty futile with a condition matching
field/value combination. But what if I'm doing a query with multiple
such conditions, and the combination matches just a small fraction of
rows? GIN index works with that (and the patches from Alexander improve
this case tremendously, IIRC).

I still don't understand how's this similar to fulltext - that seems
pretty unsuitable for a treeish structure, assuming you can't flatten
it. Which you can't, if the queries use paths to access just parts of
the json value.

For many usecases, expressional indexes are the right tool. But not for
all and I see no reason to just throw some tools away.

If the tool you're talking about throwing away is the GiST opclass, I
do not propose to throw that away. I don't think it's important enough
to justify inclusion in our first cut at this, especially given the
fact that the code has bugs, and is quite a bit more complex than GIN.
What's wrong with those reasons?

Meh, I accidentally mixed two responses :-/

I have no problem with expression indexes, but it's not a good solution
to all problems. I certainly can't use them to achieve what I'd like and
I disagree with your assumptions that it doesn't make sense to index
everything / non-interesting keys, or that the documents have
well-defined structure. I can live with larger / less efficient indexes
on all fields.

Regarding GiST - I understand your concerns about complexity, and you
may be right that not shipping it now is prefferable to shipping it with
bugs. The thing is it doesn't have issues with the value lengths, which
prevents me from using GIN, and although GiST is slower, it's at least
some indexing. But maybe jsonb_hash_ops will work, I haven't tried yet.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#399Bruce Momjian
bruce@momjian.us
In reply to: Peter Geoghegan (#391)
Re: jsonb and nested hstore

On Wed, Mar 12, 2014 at 01:58:14PM -0700, Peter Geoghegan wrote:

The use case you describe here doesn't sound like something similar to
full text search. It sounds like something identical.

In any case, let's focus on what we have right now. I think that the
indexing facilities proposed here are solid. In any case they do not
preclude working on better indexing strategies as the need emerges.

Keep in mind that if we ship an index format, we are going to have
trouble changing the layout because of pg_upgrade. pg_upgrade can mark
the indexes as invalid and force users to reindex, but that is less than
idea.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#400Greg Stark
stark@mit.edu
In reply to: Bruce Momjian (#399)
Re: jsonb and nested hstore

On Thu, Mar 13, 2014 at 6:15 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Wed, Mar 12, 2014 at 01:58:14PM -0700, Peter Geoghegan wrote:

The use case you describe here doesn't sound like something similar to
full text search. It sounds like something identical.

In any case, let's focus on what we have right now. I think that the
indexing facilities proposed here are solid. In any case they do not
preclude working on better indexing strategies as the need emerges.

Keep in mind that if we ship an index format, we are going to have
trouble changing the layout because of pg_upgrade. pg_upgrade can mark
the indexes as invalid and force users to reindex, but that is less than
idea.

Well these are just normal gin and gist indexes. If we want to come up
with new index operator classess we can still do that and keep the old
ones if necessary. Even that seems pretty unlikely from past experience.

I'm actually pretty sanguine even about keeping the GIST opclass. If
it has bugs then the bugs only affect people who use this non-default
opclass and we can fix them. It doesn't risk questioning any basic
design choices in the patch.

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?
--
greg

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#401Greg Stark
stark@mit.edu
In reply to: Greg Stark (#400)
Re: jsonb and nested hstore

Fwiw I have a few questions -- but beware, I'm a complete neophyte
when it comes to jsonb style document databases so these are more
likely to represent misconceptions on my part than problems with
jsonb.

I naively though a gin index on a jsonb would help with queries like
WHERE col->'prop' = 'val'. In fact it only seems to help with WHERE
col ? 'prop'. To help with the former it looks like I need an
expression index on col->'prop' is that right? There doesn't seem to
be an operator that combines both a dereference and value test into a
single operator so I don't think our index machinery can deal with
this. Or am I supposed to use contains and construct a json object for
the test?

I also find it awkward that col->>'prop' returns the json
representation of the property. If it's text that means it's
double-quoted. I would think that a user storing text in a json
property would want a way to pull out the text that json property
represents so he doesn't have to write col->>'prop' = '"foo"' and
doesn't need to strip the quotes (and de-escape the string?) before
displaying the value or passing it through other apis.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#402Alexander Korotkov
aekorotkov@gmail.com
In reply to: Greg Stark (#400)
Re: jsonb and nested hstore

On Thu, Mar 13, 2014 at 1:21 PM, Greg Stark <stark@mit.edu> wrote:

Well these are just normal gin and gist indexes. If we want to come up
with new index operator classess we can still do that and keep the old
ones if necessary. Even that seems pretty unlikely from past experience.

I'm actually pretty sanguine even about keeping the GIST opclass. If
it has bugs then the bugs only affect people who use this non-default
opclass and we can fix them. It doesn't risk questioning any basic
design choices in the patch.

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?

A couple of thoughts from me:
1) We can evade length limitation if GIN index by truncating long values
and setting recheck flag. We can introduce some indicator of truncated
value like zero byte at the end.
2) jsonb_hash_ops can be extended to handle keys queries too. We can
preserve one bit in hash as flag indicating whether it's a hash of key or
hash of path to value. For sure, such index would be a bit larger. Also,
jsonb_hash_ops can be split into two: with and without keys.

------
With best regards,
Alexander Korotkov.

#403Oleg Bartunov
obartunov@gmail.com
In reply to: Alexander Korotkov (#402)
Re: jsonb and nested hstore

On Thu, Mar 13, 2014 at 4:21 PM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

On Thu, Mar 13, 2014 at 1:21 PM, Greg Stark <stark@mit.edu> wrote:

Well these are just normal gin and gist indexes. If we want to come up
with new index operator classess we can still do that and keep the old
ones if necessary. Even that seems pretty unlikely from past experience.

I'm actually pretty sanguine even about keeping the GIST opclass. If
it has bugs then the bugs only affect people who use this non-default
opclass and we can fix them. It doesn't risk questioning any basic
design choices in the patch.

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?

A couple of thoughts from me:
1) We can evade length limitation if GIN index by truncating long values and
setting recheck flag. We can introduce some indicator of truncated value
like zero byte at the end.
2) jsonb_hash_ops can be extended to handle keys queries too. We can
preserve one bit in hash as flag indicating whether it's a hash of key or
hash of path to value. For sure, such index would be a bit larger. Also,
jsonb_hash_ops can be split into two: with and without keys.

That's right ! Should we do these now, that's the question.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#404Greg Stark
stark@mit.edu
In reply to: Oleg Bartunov (#403)
Re: jsonb and nested hstore

Fwiw the jsonb data doesn't actually seem to be any smaller than text
json on this data set (this is avg(pg_column_size(col)) and I checked,
they're both using the same amount of toast space)

jsonb | json
-------+-------
813.5 | 716.3
(1 row)

It's still more than 7x faster in cpu costs though:

stark=# select count(attrs->'properties'->>'STREET') from citylots;
count
--------
196507
(1 row)

Time: 1026.678 ms

stark=# select count(attrs->'properties'->>'STREET') from citylots_json;
count
--------
196507
(1 row)

Time: 7418.010 ms

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#405Andrew Dunstan
andrew@dunslane.net
In reply to: Greg Stark (#401)
Re: jsonb and nested hstore

On 03/13/2014 06:53 AM, Greg Stark wrote:

I also find it awkward that col->>'prop' returns the json
representation of the property. If it's text that means it's
double-quoted. I would think that a user storing text in a json
property would want a way to pull out the text that json property
represents so he doesn't have to write col->>'prop' = '"foo"' and
doesn't need to strip the quotes (and de-escape the string?) before
displaying the value or passing it through other apis.

->> returns dequoted text if the value it points to is a plain string.
If it's not doing that then that's a bug.

andrew=# select jsonb '{"a":"the string"}' -> 'a';
?column?
--------------
"the string"
(1 row)

andrew=# select jsonb '{"a":"the string"}' ->> 'a'
;
?column?
------------
the string
(1 row)

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#406Andrew Dunstan
andrew@dunslane.net
In reply to: Greg Stark (#404)
Re: jsonb and nested hstore

On 03/13/2014 08:42 AM, Greg Stark wrote:

Fwiw the jsonb data doesn't actually seem to be any smaller than text
json on this data set (this is avg(pg_column_size(col)) and I checked,
they're both using the same amount of toast space)

jsonb | json
-------+-------
813.5 | 716.3
(1 row)

That's expected, you save on whitespace, quotes and punctuation and
spend on structural overhead (e.g. string lengths). The actual strings
stored are the virtally the same. Numbers are stored as numerics, which
might or might not be longer. Nulls and booleans are about a wash.

It's still more than 7x faster in cpu costs though:

stark=# select count(attrs->'properties'->>'STREET') from citylots;
count
--------
196507
(1 row)

Time: 1026.678 ms

stark=# select count(attrs->'properties'->>'STREET') from citylots_json;
count
--------
196507
(1 row)

Time: 7418.010 ms

That's also expected, it's one of the major benefits. With jsonb you're
avoiding reparsing the json.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#407Greg Stark
stark@mit.edu
In reply to: Andrew Dunstan (#405)
Re: jsonb and nested hstore

On Thu, Mar 13, 2014 at 1:08 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

->> returns dequoted text if the value it points to is a plain string. If
it's not doing that then that's a bug.

Sorry, I must have gotten confused between various tests. It does seem
to be doing that.

--
greg

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#408Greg Stark
stark@mit.edu
In reply to: Greg Stark (#407)
Re: jsonb and nested hstore

Another question. Is Peter's branch up to date with
jsonb_populate_record() ? From discussions on list it sounds like the
plan was to get rid of the use_json_as_text argument but his patch
still has it.

(Tangentially, I wonder if it wouldn't be possible to make this a
plain cast. I'm not sure but I think it's possible to have a cast to a
polymorphic type and peek at runtime at the record definition to
determine what to do).

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#409Andrew Dunstan
andrew@dunslane.net
In reply to: Greg Stark (#408)
Re: jsonb and nested hstore

On 03/13/2014 10:49 AM, Greg Stark wrote:

Another question. Is Peter's branch up to date with
jsonb_populate_record() ? From discussions on list it sounds like the
plan was to get rid of the use_json_as_text argument but his patch
still has it.

Yes, we're not changing that, and some people like it anyway. The API is
intentionally the same as the legacy json_populate_record API.

(Tangentially, I wonder if it wouldn't be possible to make this a
plain cast. I'm not sure but I think it's possible to have a cast to a
polymorphic type and peek at runtime at the record definition to
determine what to do).

If you can simplify it be my guest.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#410Tomas Vondra
tv@fuzzy.cz
In reply to: Oleg Bartunov (#403)
Re: jsonb and nested hstore

On 13.3.2014 13:28, Oleg Bartunov wrote:

On Thu, Mar 13, 2014 at 4:21 PM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

On Thu, Mar 13, 2014 at 1:21 PM, Greg Stark <stark@mit.edu> wrote:

Well these are just normal gin and gist indexes. If we want to come up
with new index operator classess we can still do that and keep the old
ones if necessary. Even that seems pretty unlikely from past experience.

I'm actually pretty sanguine even about keeping the GIST opclass. If
it has bugs then the bugs only affect people who use this non-default
opclass and we can fix them. It doesn't risk questioning any basic
design choices in the patch.

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?

A couple of thoughts from me:
1) We can evade length limitation if GIN index by truncating long values and
setting recheck flag. We can introduce some indicator of truncated value
like zero byte at the end.
2) jsonb_hash_ops can be extended to handle keys queries too. We can
preserve one bit in hash as flag indicating whether it's a hash of key or
hash of path to value. For sure, such index would be a bit larger. Also,
jsonb_hash_ops can be split into two: with and without keys.

That's right ! Should we do these now, that's the question.

Yeah, those are basically the two solutions I proposed a few messages
back in this thread. I'm pleased I haven't proposed a complete nonsense.

The question whether do that now or wait for 9.5 is a tough one. Doing
both for 9.4 is certainly stretching the commitfest to it's limits :-(

My impression is that while (2) means rather significant implementation
changes in jsonb_hash_ops, (1) is rather straightforward. Is that
correct (e.g. how's the truncation going to work with arrays?).

If that's true, I'd like propose doing (1) for 9.4 and leaving (2) to
9.5. I'm ready to spend non-trivial amount of time testing the changes
required in (1).

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#411Merlin Moncure
mmoncure@gmail.com
In reply to: Peter Geoghegan (#368)
Re: jsonb and nested hstore

On Mon, Mar 10, 2014 at 4:18 AM, Peter Geoghegan <pg@heroku.com> wrote:

* Extensive additional documentation. References to the very new JSON
RFC. I think that this revision is in general a lot more coherent, and
I found that reflecting on what idiomatic usage should look like while
writing the documentation brought clarity to my thoughts on how the
code should be structured. The documentation is worth a read if you
want to get a better sense of what the patch is about relatively
quickly.

The attached documentation is excellent -- wow.

merlin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#412Peter Geoghegan
pg@heroku.com
In reply to: Greg Stark (#400)
Re: jsonb and nested hstore

On Thu, Mar 13, 2014 at 2:21 AM, Greg Stark <stark@mit.edu> wrote:

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?

Personally, I don't think it's a good idea to change the default. I
have yet to be convinced that if you hit the GIN limitation it's an
indication of anything other than that you need to reconsider your
indexing choices (how often have we heard that complaint of GIN before
in practice?). Even if you don't hit the limitation directly, with
something like jsonb_hash_ops you're still hashing a large nested
structure, very probably uselessly. Are you really going to look for
an exact match to an elaborate nested structure? I would think,
probably not.

Now, as Alexander says, there might be a role for another
(jsonb_hash_ops) opclass that separately indexes values only. I still
think that by far the simplest solution is to use expressional
indexes, because we index key values and array element values
indifferently. Of course, nothing we have here precludes the
development of such an opclass.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#413Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#412)
Re: jsonb and nested hstore

On 13 Březen 2014, 23:39, Peter Geoghegan wrote:

On Thu, Mar 13, 2014 at 2:21 AM, Greg Stark <stark@mit.edu> wrote:

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?

Personally, I don't think it's a good idea to change the default. I
have yet to be convinced that if you hit the GIN limitation it's an
indication of anything other than that you need to reconsider your
indexing choices (how often have we heard that complaint of GIN before
in practice?). Even if you don't hit the limitation directly, with

I've never used GIN with anything else than values that built-in full-text
(tsvector), pg_trgm or points, and I suspect that's the case with most
other users. All those types have "naturally limited" size (e.g. words
tend to have very limited length, unless you're Maori, but even there the
longest name is just 85 characters [1]http://en.wikipedia.org/wiki/List_of_long_place_names).

The only place in (core|contrib) where I'd expect this kind of issues is
probably intarray, but it's arguably less frequently used than
tsvector/pg_trgm for example.

So ISTM this is the main reason why we don't see more complaints about the
GIN size limit. I expect that to change with json + "index all" approach.

something like jsonb_hash_ops you're still hashing a large nested
structure, very probably uselessly. Are you really going to look for
an exact match to an elaborate nested structure? I would think,
probably not.

What I find (very) useful is queries that look like this:

SELECT if FROM json_table WHERE json_value @> '{"a" : {"b" : {"c" : 3}}}';

or (without the @> operator) like this:

SELECT if FROM json_table WHERE json_value #>> ARRAY['a', 'b', 'c'] = '3';

or something like that ...

Now, as Alexander says, there might be a role for another
(jsonb_hash_ops) opclass that separately indexes values only. I still
think that by far the simplest solution is to use expressional
indexes, because we index key values and array element values
indifferently. Of course, nothing we have here precludes the
development of such an opclass.

Maybe. I don't have much insight into ho GIN works / what is possible. But
I think we should avoid having large number of opclasses, each supporting
a small fraction of use cases. If we could keep the two we have right now,
that'd be nice.

regards
Tomas

[1]: http://en.wikipedia.org/wiki/List_of_long_place_names

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#414Oleg Bartunov
obartunov@gmail.com
In reply to: Tomas Vondra (#413)
Re: jsonb and nested hstore

VODKA index will have no lenght limitation.

On Fri, Mar 14, 2014 at 3:07 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

On 13 Březen 2014, 23:39, Peter Geoghegan wrote:

On Thu, Mar 13, 2014 at 2:21 AM, Greg Stark <stark@mit.edu> wrote:

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?

Personally, I don't think it's a good idea to change the default. I
have yet to be convinced that if you hit the GIN limitation it's an
indication of anything other than that you need to reconsider your
indexing choices (how often have we heard that complaint of GIN before
in practice?). Even if you don't hit the limitation directly, with

I've never used GIN with anything else than values that built-in full-text
(tsvector), pg_trgm or points, and I suspect that's the case with most
other users. All those types have "naturally limited" size (e.g. words
tend to have very limited length, unless you're Maori, but even there the
longest name is just 85 characters [1]).

The only place in (core|contrib) where I'd expect this kind of issues is
probably intarray, but it's arguably less frequently used than
tsvector/pg_trgm for example.

So ISTM this is the main reason why we don't see more complaints about the
GIN size limit. I expect that to change with json + "index all" approach.

something like jsonb_hash_ops you're still hashing a large nested
structure, very probably uselessly. Are you really going to look for
an exact match to an elaborate nested structure? I would think,
probably not.

What I find (very) useful is queries that look like this:

SELECT if FROM json_table WHERE json_value @> '{"a" : {"b" : {"c" : 3}}}';

or (without the @> operator) like this:

SELECT if FROM json_table WHERE json_value #>> ARRAY['a', 'b', 'c'] = '3';

or something like that ...

Now, as Alexander says, there might be a role for another
(jsonb_hash_ops) opclass that separately indexes values only. I still
think that by far the simplest solution is to use expressional
indexes, because we index key values and array element values
indifferently. Of course, nothing we have here precludes the
development of such an opclass.

Maybe. I don't have much insight into ho GIN works / what is possible. But
I think we should avoid having large number of opclasses, each supporting
a small fraction of use cases. If we could keep the two we have right now,
that'd be nice.

regards
Tomas

[1] http://en.wikipedia.org/wiki/List_of_long_place_names

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#415Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#365)
Re: jsonb and nested hstore

On 03/14/2014 04:52 AM, Oleg Bartunov wrote:

VODKA index will have no lenght limitation.

Yeah, so I think we go with what we have, and tell people "if you're
hitting these length issues, wait for 9.5, where they will be fixed."

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#416Oleg Bartunov
obartunov@gmail.com
In reply to: Josh Berkus (#415)
Re: jsonb and nested hstore

9.5 may too optimistic :)

On Fri, Mar 14, 2014 at 11:18 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 03/14/2014 04:52 AM, Oleg Bartunov wrote:

VODKA index will have no lenght limitation.

Yeah, so I think we go with what we have, and tell people "if you're
hitting these length issues, wait for 9.5, where they will be fixed."

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#417Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Oleg Bartunov (#416)
Re: jsonb and nested hstore

On 15/03/14 08:45, Oleg Bartunov wrote:

9.5 may too optimistic :)

On Fri, Mar 14, 2014 at 11:18 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 03/14/2014 04:52 AM, Oleg Bartunov wrote:

VODKA index will have no lenght limitation.

Yeah, so I think we go with what we have, and tell people "if you're
hitting these length issues, wait for 9.5, where they will be fixed."

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

No tell them to wait for Postgres 12.3.42 - the version that is totally
bug free & implements parallel processing of individual queries! :-)

(With apologies to Douglas Adams)

Cheers,
Gavin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#418Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#365)
Re: jsonb and nested hstore

On 03/14/2014 12:45 PM, Oleg Bartunov wrote:

9.5 may too optimistic :)

Nonsense, you, Teodor and Alexander are geniuses. It can't possibly
take you more than a year. ;-)

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#419Tomas Vondra
tv@fuzzy.cz
In reply to: Josh Berkus (#415)
Re: jsonb and nested hstore

On 14.3.2014 20:18, Josh Berkus wrote:

On 03/14/2014 04:52 AM, Oleg Bartunov wrote:

VODKA index will have no lenght limitation.

Yeah, so I think we go with what we have, and tell people "if you're
hitting these length issues, wait for 9.5, where they will be
fixed."

VODKA may be great, but I haven't seen a single line of code for that
yet. And given the response from Oleg, 9.5 seems ambitious.

I'm not awfully familiar with the GIN code, but based on Alexander's
feedback I presume fixing the GIN length limit (or rather removing it,
as it's a feature, not a bug) is quite straightforward. Why not to at
least consider that for 9.4, unless it turns more complex than expected?

Don't get me wrong - I'm aware it's quite late in the last commitfest,
and if it's deemed unacceptable / endandering 9.4 release, I'm not going
to say a word. But if it's a simple patch ...

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#420Peter Geoghegan
pg@heroku.com
In reply to: Tomas Vondra (#419)
Re: jsonb and nested hstore

On Fri, Mar 14, 2014 at 2:21 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm not awfully familiar with the GIN code, but based on Alexander's
feedback I presume fixing the GIN length limit (or rather removing it,
as it's a feature, not a bug) is quite straightforward. Why not to at
least consider that for 9.4, unless it turns more complex than expected?

Alexander said nothing about removing that limitation, or if he did I
missed it. Which, as I said, I don't consider to be much of a
limitation, because indexing the whole nested value doesn't mean it
can satisfy a query on some more nested subset of an indexed value
datum (i.e. a value in the sense of a value in a key/value pair).

Alexander mentioned just indexing keys (object keys, or equivalently
array elements at the jsonb level), which is a reasonable thing, but
can be worked on later. I don't have much interest in working on
making it possible to index elaborate nested values in key/value
pairs, which is what you're suggesting if I've understood correctly.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#421Peter Geoghegan
pg@heroku.com
In reply to: Oleg Bartunov (#414)
Re: jsonb and nested hstore

For the benefit of anyone that would like to try the patch out, I make
available a custom format dump of some delicious sample data. I can
query the sample data as follows on my local installation:

[local]/jsondata=# select count(*) from delicious ;
count
---------
1079399
(1 row)

[local]/jsondata=# \dt+ delicious
List of relations
Schema | Name | Type | Owner | Size | Description
--------+-----------+-------+-------+---------+-------------
public | delicious | table | pg | 1174 MB |
(1 row)

It's available from:
http://postgres-benchmarks.s3-website-us-east-1.amazonaws.com/jsondata.dump

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#422Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#420)
Re: jsonb and nested hstore

On 14.3.2014 22:54, Peter Geoghegan wrote:

On Fri, Mar 14, 2014 at 2:21 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm not awfully familiar with the GIN code, but based on Alexander's
feedback I presume fixing the GIN length limit (or rather removing it,
as it's a feature, not a bug) is quite straightforward. Why not to at
least consider that for 9.4, unless it turns more complex than expected?

Alexander said nothing about removing that limitation, or if he did I
missed it. Which, as I said, I don't consider to be much of a

Sure he did, see this:

/messages/by-id/CAPpHfds4xmg5zOP+1CtrrqnM6wxhh2A7j11nnJeosa76UoWxyg@mail.gmail.com

Although it doesn't mention how complex change it would be.

limitation, because indexing the whole nested value doesn't mean it
can satisfy a query on some more nested subset of an indexed value
datum (i.e. a value in the sense of a value in a key/value pair).

OK, I'm getting lost in the nested stuff. The trouble I'm running into
are rather unlerated to nesting. For example indexing this fails if the
string is sufficiently long (~1350B if random, more if compressible).

{"key" : "... string ..."}

How's that related to nesting?

Anyway, I'm not talking about exact matches on subtrees. I'm talking
about queries like this:

SELECT doc FROM delicious
WHERE doc @> '{"title_detail" : {"value" : "TheaterMania"}}';

which does exactly the same thing like this query:

SELECT doc FROM delicious
WHERE doc->'title_detail'->>'value' = 'TheaterMania';

Except that the first query can use a GIN index created like this:

CREATE INDEX delicious_idx ON delicious USING GIN (doc);

while the latter does sequential scan. It can use a GiST index too, but
it takes 140ms with GiST and only ~0.3ms with GIN. Big difference.

Alexander mentioned just indexing keys (object keys, or equivalently
array elements at the jsonb level), which is a reasonable thing, but
can be worked on later. I don't have much interest in working on
making it possible to index elaborate nested values in key/value
pairs, which is what you're suggesting if I've understood correctly.

I never asked for indexing elaborate nested values in key/value pairs.
All I'm asking for is indexing of json values containing long strings.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#423Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#421)
Re: jsonb and nested hstore

On 14.3.2014 23:06, Peter Geoghegan wrote:

For the benefit of anyone that would like to try the patch out, I make
available a custom format dump of some delicious sample data. I can
query the sample data as follows on my local installation:

[local]/jsondata=# select count(*) from delicious ;
count
---------
1079399
(1 row)

[local]/jsondata=# \dt+ delicious
List of relations
Schema | Name | Type | Owner | Size | Description
--------+-----------+-------+-------+---------+-------------
public | delicious | table | pg | 1174 MB |
(1 row)

It's available from:
http://postgres-benchmarks.s3-website-us-east-1.amazonaws.com/jsondata.dump

Thanks.

I've been doing some simple queries on this dataset and ISTM there's a
memory leak somewhere in the json code (i.e. something is probably using
a wrong memory context), because this query:

SELECT doc->'title_detail'->'value', COUNT(*)
FROM delicious GROUP BY 1;

results in this:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+
COMMAND

8231 tomas 20 0 5987520 4,645g 6136 R 95,4 60,4 0:37.54
postgres: tomas delicious [local]

I have shared_buffers=1GB and work_mem=64MB, so 4.6GB seems a bit too
much 4.6GB. Actually it grows even further, and then OOM jumps in and
kills the backend like this:

[ 9227.318998] Out of memory: Kill process 8159 (postgres) score 595
or sacrifice child
[ 9227.319000] Killed process 8159 (postgres) total-vm:5920272kB,
anon-rss:4791568kB, file-rss:6192kB

I'm on commit a3115f0d, which is just 2 days old, so I suppose this was
not fixed yet.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#424Andres Freund
andres@2ndquadrant.com
In reply to: Tomas Vondra (#419)
Re: jsonb and nested hstore

On 2014-03-14 22:21:18 +0100, Tomas Vondra wrote:

Don't get me wrong - I'm aware it's quite late in the last commitfest,
and if it's deemed unacceptable / endandering 9.4 release, I'm not going
to say a word. But if it's a simple patch ...

IMNSHO there's no bloody chance for such an addition at this point of
the cycle.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#425Greg Stark
stark@mit.edu
In reply to: Tomas Vondra (#419)
Re: jsonb and nested hstore

On Fri, Mar 14, 2014 at 9:21 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm not awfully familiar with the GIN code, but based on Alexander's
feedback I presume fixing the GIN length limit (or rather removing it,
as it's a feature, not a bug) is quite straightforward. Why not to at
least consider that for 9.4, unless it turns more complex than expected?

Don't get me wrong - I'm aware it's quite late in the last commitfest,
and if it's deemed unacceptable / endandering 9.4 release, I'm not going
to say a word. But if it's a simple patch ...

Well I think the bigger picture is that the cases were we're getting
this error it's because we're expecting too much from the GIN opclass.
It's trying to index entire json objects as individual values which
isn't really very useful. We're unlikely to go querying for rows where
the value of a given key is a specific json object.

As I understand it Peter's right that in its current form the GIN
opclass is only useful if you use it on an expression index on
specific pieces of your json which are traditional non-nested hash
tables. Or I suppose if you're really only concerned with the ?
operator which looks for keys, which is pretty common too.

I had in mind that the GIN opclass would do something clever like
decompose the json into all the path->value tuples so I could do
arbitrary path lookups for values. That might be possible in the
future but it's not what we have today and what we have today is
already better than hstore. I think we're better off committing this
and moving forward with the contrib hstore2 wrapper which uses this
infrastructure so people have a migration path.

I don't think Josh is right to say it'll be "fixed" in 9.5. It'll be
"better" in 9.5 because we have ambitious plans to continue improving
in this direction. But it'll be even better in 9.6 and better again in
9.7. It'll never be "fixed".

--
greg

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#426Peter Geoghegan
pg@heroku.com
In reply to: Tomas Vondra (#423)
Re: jsonb and nested hstore

On Fri, Mar 14, 2014 at 5:10 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm on commit a3115f0d, which is just 2 days old, so I suppose this was
not fixed yet.

Try merging the feature branch now, which will get you commit 16923d,
which you're missing. That was an open item for a while, which I only
got around to fixing a few days ago.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#427Tomas Vondra
tv@fuzzy.cz
In reply to: Greg Stark (#425)
Re: jsonb and nested hstore

On 15.3.2014 02:03, Greg Stark wrote:

On Fri, Mar 14, 2014 at 9:21 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm not awfully familiar with the GIN code, but based on Alexander's
feedback I presume fixing the GIN length limit (or rather removing it,
as it's a feature, not a bug) is quite straightforward. Why not to at
least consider that for 9.4, unless it turns more complex than expected?

Don't get me wrong - I'm aware it's quite late in the last commitfest,
and if it's deemed unacceptable / endandering 9.4 release, I'm not going
to say a word. But if it's a simple patch ...

Well I think the bigger picture is that the cases were we're getting
this error it's because we're expecting too much from the GIN
opclass. It's trying to index entire json objects as individual
values which isn't really very useful. We're unlikely to go querying
for rows where the value of a given key is a specific json object.

Stupid question - so if I have a json like this:

{ "a" : { "b" : "c"}}

the GIN code indexes {"b" : "c"} as a single value? And then takes "c"
and indexes it as a single value too?

Because otherwise I don't understand how the index could be used for
queries with @> '{"a" : {"b" : "c"}}' conditions (i.e. path "[a,b]" with
value "c").

Hmmmm, if that's how it works, removing the size limit would be
certainly more difficult than I thought.

As I understand it Peter's right that in its current form the GIN
opclass is only useful if you use it on an expression index on
specific pieces of your json which are traditional non-nested hash
tables. Or I suppose if you're really only concerned with the ?
operator which looks for keys, which is pretty common too.

Well, depends on how you define useful. With the sample dataset
'delicious' (see Peter's post) I can do this:

SELECT doc FROM delicious
WHERE doc @> '{"title_detail" : {"value" : "TheaterMania"}}';

with arbitrary paths, and I may create a GIN index to support such
queries. And yes, it's much faster than GiST for example (by a factor of
1000).

Yes, the GIN index is quite large (~560MB for a ~1.2GB table).

I had in mind that the GIN opclass would do something clever like
decompose the json into all the path->value tuples so I could do
arbitrary path lookups for values. That might be possible in the
future but it's not what we have today and what we have today is
already better than hstore. I think we're better off committing this
and moving forward with the contrib hstore2 wrapper which uses this
infrastructure so people have a migration path.

Yes, it's better than hstore - no doubt about that. The hierarchy and
data types are great, and hstore has the same size limitation.

I don't think Josh is right to say it'll be "fixed" in 9.5. It'll be
"better" in 9.5 because we have ambitious plans to continue
improving in this direction. But it'll be even better in 9.6 and
better again in 9.7. It'll never be "fixed".

I don't dare to say what will be in 9.5 (not even thinking about the
following versions).

Assuming the GIN will remain for 9.4 as it is now (both opclasses), it
would be nice if we could improve this in 9.5. I can live with custom
opclasses in an extension, if there are some ...

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#428Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#365)
Re: jsonb and nested hstore

On 03/14/2014 06:44 PM, Tomas Vondra wrote:

Stupid question - so if I have a json like this:

Not a stupid question, actually. In fact, I expect to answer it 400 or
500 times over the lifespan of 9.4.

{ "a" : { "b" : "c"}}

the GIN code indexes {"b" : "c"} as a single value? And then takes "c"
and indexes it as a single value too?

I don't know that "c" is indexed separately.

Because otherwise I don't understand how the index could be used for
queries with @> '{"a" : {"b" : "c"}}' conditions (i.e. path "[a,b]" with
value "c").

Hmmmm, if that's how it works, removing the size limit would be
certainly more difficult than I thought.

Precisely. Hence, the Russian plans for VODKA.

Well, depends on how you define useful. With the sample dataset
'delicious' (see Peter's post) I can do this:

SELECT doc FROM delicious
WHERE doc @> '{"title_detail" : {"value" : "TheaterMania"}}';

with arbitrary paths, and I may create a GIN index to support such
queries. And yes, it's much faster than GiST for example (by a factor of
1000).

Yes, the GIN index is quite large (~560MB for a ~1.2GB table).

State of the art, actually. In MongoDB, the indexes are frequently
several times larger than the raw data. So if ours are 50% the size,
we're doing pretty good.

On 15.3.2014 02:03, Greg Stark wrote:

I don't think Josh is right to say it'll be "fixed" in 9.5. It'll be
"better" in 9.5 because we have ambitious plans to continue
improving in this direction. But it'll be even better in 9.6 and
better again in 9.7. It'll never be "fixed".

Oh, no doubt. The important thing is that 9.4 will significantly
broaden the class of applications for which our JSON support is useful,
and allow us to remain relevant to an increasingly NoSQLish developer
base. We're both showing progress and delivering features which are
actually useful, even if they still have major limitations.

Plus, you know, those features are useful to *me*, so I'm keen on them
personally.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#429Peter Geoghegan
pg@heroku.com
In reply to: Tomas Vondra (#427)
Re: jsonb and nested hstore

On Fri, Mar 14, 2014 at 6:44 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

Well, depends on how you define useful. With the sample dataset
'delicious' (see Peter's post) I can do this:

SELECT doc FROM delicious
WHERE doc @> '{"title_detail" : {"value" : "TheaterMania"}}';

with arbitrary paths, and I may create a GIN index to support such
queries. And yes, it's much faster than GiST for example (by a factor of
1000).

If you know ahead of time the entire nested value you can. So, if you
attach some other data to the "TheaterMania" document, you had better
know that too if you hope to write a query like this. You also have to
index the entire table, where presumably with a little thought you
could get away with a much smaller index. That strikes me as not very
useful.

Yes, the GIN index is quite large (~560MB for a ~1.2GB table).

With the default opclass, without an expressional index, 100% of the
data from the table appears in the index. Why do you think that's
quite large?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#430Greg Stark
stark@mit.edu
In reply to: Tomas Vondra (#427)
Re: jsonb and nested hstore

On Sat, Mar 15, 2014 at 1:44 AM, Tomas Vondra <tv@fuzzy.cz> wrote:

Because otherwise I don't understand how the index could be used for
queries with @> '{"a" : {"b" : "c"}}' conditions (i.e. path "[a,b]" with
value "c").

Hm, some experimentation here shows it does indeed work for queries
like this and works quite nicely. I agree, this contradicts my
explanation so I'll need to poke in this some more to understand how
it is that this works so well:

explain select j->'tags'->>'name' from osm where j @>
'{"tags":{"waterway":"dam"}}' ;
QUERY PLAN
------------------------------------------------------------------------
Bitmap Heap Scan on osm (cost=139.47..19565.07 rows=6125 width=95)
Recheck Cond: (j @> '{"tags": {"waterway": "dam"}}'::jsonb)
-> Bitmap Index Scan on osmj (cost=0.00..137.94 rows=6125 width=0)
Index Cond: (j @> '{"tags": {"waterway": "dam"}}'::jsonb)
Planning time: 0.147 ms
(5 rows)

stark=# select j->'tags'->>'name' from osm where j @>
'{"tags":{"waterway":"dam"}}' ;
?column?
-----------------------------------------

Alpine Dam
Bell Canyon Dam
Big Rock Dam
Briones Dam
Cascade Dam
Gordon Valley Dam
Kimball Canyon Dam
Moore Dam
Nicasio Dam
Novato Creek Dam
Ryland Dam
Vasona Dam
Warm Springs Dam
Crystal Dam
....
(248 rows)

Time: 6.126 ms

--
greg

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#431Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#429)
Re: jsonb and nested hstore

On 15.3.2014 06:40, Peter Geoghegan wrote:

On Fri, Mar 14, 2014 at 6:44 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

Well, depends on how you define useful. With the sample dataset
'delicious' (see Peter's post) I can do this:

SELECT doc FROM delicious
WHERE doc @> '{"title_detail" : {"value" : "TheaterMania"}}';

with arbitrary paths, and I may create a GIN index to support such
queries. And yes, it's much faster than GiST for example (by a
factor of 1000).

If you know ahead of time the entire nested value you can. So, if
you attach some other data to the "TheaterMania" document, you had
better know that too if you hope to write a query like this. You also
have to index the entire table, where presumably with a little
thought you could get away with a much smaller index. That strikes me
as not very useful.

Sure, I need to know some basic rules / do assumptions about the
structure of the json document. In other words, schemaless databases are
difficult to query.

For example when storing mail message headers (i.e. the example I've
used before), I do know that the json document is rather well structured
- it's not nested at all, and all the values are either scalar values
(mostly strings), or arrays of scalars.

So it looks like this

{
"from" : "john.doe@example.com",
"to" : ["jane.doe@example.com", "jack.doe@example.com"],
...
}

So the schema is rather well defined (not the exact keys, but the
structure certainly is).

Let's say I want to allow arbitrary searches on headers - I can't
support that with expression indexes, because there's like a zillion of
possible headers and I'd have to create an expression index on each of
them separately.

But I can support that with a single GIN index ...

Yes, the GIN index is quite large (~560MB for a ~1.2GB table).

With the default opclass, without an expressional index, 100% of the
data from the table appears in the index. Why do you think that's
quite large?

That wasn't meant as a complaint. I have no problem with the index size
(If we can make it smaller in the future, great! But I can live with the
current index sizes too.)

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#432Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#426)
Re: jsonb and nested hstore

On 15.3.2014 02:15, Peter Geoghegan wrote:

On Fri, Mar 14, 2014 at 5:10 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

I'm on commit a3115f0d, which is just 2 days old, so I suppose this was
not fixed yet.

Try merging the feature branch now, which will get you commit 16923d,
which you're missing. That was an open item for a while, which I only
got around to fixing a few days ago.

Ok, that seems to be working fine.

T.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#433Alexander Korotkov
aekorotkov@gmail.com
In reply to: Peter Geoghegan (#370)
Re: jsonb and nested hstore

I've noticed two commits on github.

commit b8199ee3c2506ab81b47a0b440363fc90c0d6956
Author: Peter Geoghegan <pg@heroku.com>
Date: Wed Mar 19 02:02:16 2014 -0700

For jsonb_hash_ops, hash less

By limiting the GIN entries to the least-nested level, the delicious.com
sample JSON dataset index shrinks in size from 382MB to 255MB without
any apparent downside.

commit 2cea5213dba011625fc0d5c6b447e838080087b1
Author: Peter Geoghegan <pg@heroku.com>
Date: Wed Mar 19 02:13:42 2014 -0700

Revert "For jsonb_hash_ops, hash less"

This might be workable with another approach, but leave it for now. This
reverts commit b8199ee3c2506ab81b47a0b440363fc90c0d6956.

Besides implementation, what the idea was here? For me, it's impossible to
skip any single element, because it's possible for query to include only
this element. If we skip that element, we can't answer corresponding query
no more.

------
With best regards,
Alexander Korotkov.

#434Peter Geoghegan
pg@heroku.com
In reply to: Alexander Korotkov (#433)
Re: jsonb and nested hstore

On Thu, Mar 20, 2014 at 5:32 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

Besides implementation, what the idea was here? For me, it's impossible to
skip any single element, because it's possible for query to include only
this element. If we skip that element, we can't answer corresponding query
no more.

This had something to do with an alternative notion of containment. I
wouldn't have stuck with such a radical change without consulting you.
I reverted it, and am not going to argue for the idea right now.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#435Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#412)
Re: jsonb and nested hstore

On Thu, Mar 13, 2014 at 3:39 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Mar 13, 2014 at 2:21 AM, Greg Stark <stark@mit.edu> wrote:

It does sound like the main question here is which opclass should be
the default. From the discussion there's a jsonb_hash_ops which works
on all input values but supports fewer operators and a jsonb_ops which
supports more operators but can't handle json with larger individual
elements. Perhaps it's better to make jsonb_hash_ops the default so at
least it's always safe to create a default gin index?

Personally, I don't think it's a good idea to change the default.

I must admit that I'm coming around to the view that jsonb_hash_ops
would make a better default. Its performance is superb, and I think
there's a strong case to be made for that more than making up for it
not supporting all indexable operators - the existence operators just
aren't that useful in comparison.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#436Greg Stark
stark@mit.edu
In reply to: Peter Geoghegan (#435)
Re: jsonb and nested hstore

On Fri, Mar 21, 2014 at 7:23 AM, Peter Geoghegan <pg@heroku.com> wrote:

I must admit that I'm coming around to the view that jsonb_hash_ops
would make a better default. Its performance is superb, and I think
there's a strong case to be made for that more than making up for it
not supporting all indexable operators - the existence operators just
aren't that useful in comparison

Is there any \d command that would display a nice list of which operators a
given operator class actually supports? It's kind of hard to determine
whether a proposed index would actually be useful for your queries without
it.

--
greg

#437Tomas Vondra
tv@fuzzy.cz
In reply to: Peter Geoghegan (#435)
Re: jsonb and nested hstore

On 21.3.2014 08:23, Peter Geoghegan wrote:

On Thu, Mar 13, 2014 at 3:39 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Thu, Mar 13, 2014 at 2:21 AM, Greg Stark <stark@mit.edu> wrote:

It does sound like the main question here is which opclass should
be the default. From the discussion there's a jsonb_hash_ops
which works on all input values but supports fewer operators and
a jsonb_ops which supports more operators but can't handle json
with larger individual elements. Perhaps it's better to make
jsonb_hash_ops the default so at least it's always safe to create
a default gin index?

Personally, I don't think it's a good idea to change the default.

I must admit that I'm coming around to the view that jsonb_hash_ops
would make a better default. Its performance is superb, and I think
there's a strong case to be made for that more than making up for it
not supporting all indexable operators - the existence operators
just aren't that useful in comparison.

I don't think that's how we should choose the default operator class.
Wouldn't an operator class supporting wider range of functionality be a
better fit, as we don't really know what are the users are going to do?

You might be right that existence operators are used less frequently
than conditions on values, how big the difference is? And do we gain
something by using jsonb_hash_ops by default in the end?

Say an application does one '?' query per 100 '@>' queries. If the
default opclass does not support '?' queries (forcing a seqscan), the
total duration may easily be much higher than with the default opclass.

I like that jsonb_hash_ops produces smaller indexes (~50% compared to
jsonb_ops on the delicious dataset), and that it's faster (2-5x on the
simple queries I've tried). But is that worth the risk?

Keeping jsonb_ops as the default seems better / safer to me.

regards
Tomas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#438Peter Geoghegan
pg@heroku.com
In reply to: Tomas Vondra (#437)
Re: jsonb and nested hstore

On Sun, Mar 23, 2014 at 11:10 AM, Tomas Vondra <tv@fuzzy.cz> wrote:

Keeping jsonb_ops as the default seems better / safer to me.

That's what I did.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#439Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#428)
Re: jsonb and nested hstore

On Fri, Mar 14, 2014 at 9:17 PM, Josh Berkus <josh@agliodbs.com> wrote:

On 03/14/2014 06:44 PM, Tomas Vondra wrote:

Stupid question - so if I have a json like this:

Not a stupid question, actually. In fact, I expect to answer it 400 or
500 times over the lifespan of 9.4.

{ "a" : { "b" : "c"}}

the GIN code indexes {"b" : "c"} as a single value? And then takes "c"
and indexes it as a single value too?

I don't know that "c" is indexed separately.

Because otherwise I don't understand how the index could be used for
queries with @> '{"a" : {"b" : "c"}}' conditions (i.e. path "[a,b]" with
value "c").

Hmmmm, if that's how it works, removing the size limit would be
certainly more difficult than I thought.

Precisely. Hence, the Russian plans for VODKA.

Have these plans been shared publicly somewhere? Got a link?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#440Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#365)
Re: jsonb and nested hstore & VODKA

On 03/31/2014 09:34 AM, Robert Haas wrote:

On Fri, Mar 14, 2014 at 9:17 PM, Josh Berkus <josh@agliodbs.com> wrote:

Precisely. Hence, the Russian plans for VODKA.

Have these plans been shared publicly somewhere? Got a link?

Nothing other than the pgCon proposal. Presumably we'll know at pgCon,
unless one of them replies to this.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers