Command Triggers, patch v11

Started by Dimitri Fontainealmost 14 years ago115 messages
#1Dimitri Fontaine
dimitri@2ndQuadrant.fr
1 attachment(s)

Hi,

Please find attached the latest version of the command triggers patch,
in context diff format, with support for 79 commands and documentation
about why only those, and with some limitations explained.

I also cleaned up the node function support business that was still in
there from the command rewriting stuff that we dropped, and did a merge
from tonight's master branch (one of a few clean merges).

This is now the whole of it, and I continue being available to make any
necessary change, although I expect only minor changes now. Thanks to
all reviewers and participants into the previous threads, you all have
allowed me to reach the current point by your precious advice, comments
and interest.

The patch implements :

- BEFORE/AFTER ANY command triggers
- BEFORE/AFTER command triggers for 79 documented commands
- regression tests exercised by the serial schedule only
- documentation updates with examples

That means you need to `make installcheck` here. Installing the tests in
the parallel schedule does not lead to consistent output as installing a
command trigger will impact any other test using that command, and the
output becomes subject to the exact ordering of the concurrent tests.

The only way for a BEFORE command triggers to change the command's
behaviour is by raising an exception that aborts the whole transaction.

Command triggers are called with the following arguments:

- the “event” (similar to TG_WHEN, either 'BEFORE' or 'AFTER')
- the command tag (the real one even when an ANY trigger is called)
- the object Id if available (e.g. NULL for a CREATE statement)
- the schema name (can be NULL)
- the object name (can be NULL)

When the trigger's procedure we're calling is written in C, then another
argument is passed next, which is the parse tree Node * pointer.

I've been talking with Marko Kreen about supporting ALTER TABLE and such
commands automatically in Londiste: given that patch, it requires
writing code in C that will rewrite the command string. It so happens
that I already have worked on that code, so we intend on bringing
support for ALTER TABLE and other commands in Skytools 3 for 9.2.

I think the support code should be made into an extension that both
Skytools and Slony would be able to share. The extension code will be
able to adapt to major versions changes as they are released. Bucardo
would certainly be interested too, we could NOTIFY it from such an
extension. The design is yet to be done here, but it's clearly possible
to implement such a feature given the current patch.

The ANY trigger support is mainly there to allow people to stop any DDL
traffic on their databases, then allowing it explicitly with an ALTER
COMMAND TRIGGER ... SET DISABLE when appropriate only. To that
end, the ANY command trigger is supporting more commands than you can
attach specific triggers too.

It's also possible to ENABLE a command trigger on the REPLICA only
thanks to the session_replication_role GUC. Support for command
triggers on an Hot Standby node is not provided in this patch.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

command-trigger.v11.patch.gzapplication/octet-streamDownload
�PHO�<ks�����_��Mk9�-?�<�3�D��cK�$���f4��xL�*������_`_\R�e;I��Ob���X��b������u#������65��k��Ofa#����4z�l�F�0M���&;X��I���.��l����%����{r�
��|���"f�$�����C6�Q��AA���1�F����	��5h�m�
�^�{~nwZ�^������1o:���_��i�����"�����F�#p�:�lv�����h���yA4���mr�F���P��)�8\����0�S�F����un��W���,���{S@��tJ�O�A I<&.����Us��1:/"���Y��?�������m>mJM=O���,V����8�I7[D��L�~����*����G�QH�����]�=����#)H�nsL��u�������Ae���� )��FXQ�(�u>M\`���&�Ap��FdD	L���OQ�~�����)��~����e=�����ea�������r!�I�sE�tO��P�e^�Q��Zo�lu�19��]%'���ed��s�Qx#��XD"�+��6��K���32�X��d@�MH.��kJ�(�W��B�([b���8�!���e����,H�!�rOj�`�@������@ ��9g%4ovt��	��Q��Q��CI�T�P�����"��i�M�@L��z)�|tVj�=��eJ#�2�@��S�����3�(�<���8�H�hA��z8dYQ���O�x6�F`��(8�������E4�<�����L.��3\�Y
���q�� v��%:X0���t1��6�� �5E���F49>z`	�\��k��P�3$qzmOE�X��;{{oX4v��^�1�����:�����K	��x���c������?����j����t'�7�KV�m��\��:�����?����s��}����E��[��r�~u��0�E��tZ��|�H�,��	���%��$�+?�E��";;;�����^�t5�ki��~�G��.�9��N���������}�KO.;��R���������N��f�}��j��Z��&�����r�K�H����9-��;-����?]:�fi����/���e������S�����n	�f����`�P�{�	�B�L�Y�js~���������%���n��}z��B���3�(����v�S.�jW&����Qn����j&�������;���g�X���R��R���O��$���'{V�^��B�D�
�eQ�%I)8S��`LY�$���k�zI�����k�A�l�V�h���d�v�j���y�%��e���wS�^R����(��Z�a�"/)��y�,�.M�R��,�WMoT2�������*�/TTH>��|���t�P;�2z�B_�)��@���zI�
0
���$�n�w ����������Pg������k����w��V��y����L���P��� �)"���CQ:��=6�^0�H����Cm�����VT)�f��r����y�S7�8��5�����B�C+���4&�����'N�;�XX�w�`PI��C�2�M��6�'�i�Dy��:�<�`�A�(��V�1sGq�2^,�[o�jr`� �0V���G���J6a<�U�KBX��'���;�d�w��[�*r��4���E�U��Z������&0*�������q�
��<�� 1$Z��\u�8����� Q,B�������37����w����`�)��DOD����9�E�7b)�R�g��3�,�iHc
0c�k'���<d�#�0�S�:�2Q�O�1<S��?��:<�5�?e�Q�����*X��bc�3\�v���PgA�%�8��p�s�fq��)Y��7D�nO���!�q��$��b��~6��=���$���,��t�,��_}�6�$����<�a107jc��0j@��l�������4s8Sq�7�G$���%�V�G�����/e
K�-a<r�[�ySX�5P������L�C�a��������������h6����k�)��n�,DA$74H�������;�<4���a����
���3>����e�Al�O��n|���7�F�:�S`�����Y
�����KL����E���0���p�Wh.��J��y]n�t#�&�y��9���[ b�{}R��7v���A�E�%4t��T2�����3yy���4���i{�WnY�8�����~�����EN���9�or*��r���n�i��O\��$Hv'<V�j��[���8���ZMSsMS~#u���B����Yd�S!#c�������)��|�������o�sKGK�X���|WQ��'KK�DX��W�� �7
��]
?�iu�@�������~�*}�}G���bw�V�	��XF<C�"
���
r�	
Ko���?O����Ei(/��:83��4��t�syvf�Z�aF�r-Px��������G#�7^
%`7
c���-���v\,�A�I����[6���Iltg�m���j��q>�|
���r�y�B��az���������0��7��,�@��|[�PpCxG9m�q���-:�x0������!G):���/w���;�E��EQg-Q�����&��VB8�py���/�}'a|R<dg�RzW����W��*���<z�c�N���1��kZ����nu��p#658X�6�^as���Z���g�H]F)��'�{ ����������s��A7�%��8�!��X�����������)�a
+�Q�=Wn/���������U�g�Z<-�"�*�V�b!�#�������5�z��Ej6ps��:H��(��jYRs
3�R���/�y�o�E�C�u���R�(&�n�dPV+Dz:Xt[Hs�Xe������B^OQ[JZ[����c�
hBg�L|X����]���YZ��fF���R�j�)DPg�I<���5�����Y�v3?�q�!@�S�&P�V���F].c�J��,��B�.�z�s��N��q����&��}��14��Nr�n�����\p$7�T|�������Mx����nN��?~U� F+Xn;�3Zq4]y������+�_�����<���)�V=:��
�,]`��1�.��3�Z�KV�#ZaVDP�A�P����Q���}�>b��������E�nt��R�tG�M���g�7��2��Q�CK�D�n�b_��� �n�����A��;������a�V1'���X(�L2�}`���c*�e�UY�f�r.���%q���������D����L���_Pb���S���Y�k���"�Q)�o�iy���/����B�./Zb������0�U�V�0�NBm9g��#Rs+�����1�:2&�ny�8��"��}�.��C�(9��/�������F���$�U���I����FZ��&�V-�������AtM���b����M��i�=�G�9�)��
m#�u��_39�fr~������5��k&��L�/$�s�3\����Y8���Zj~�}G�R�\��C�^�$x�x�P��H��{�����x���i�p�%I����o�S~�!X����x�L�
����`-�Y����G��\���_y�"����B������Y�rc�`	��4��\����O���k��E�6-e~R��>�����3KA9U�)�	����� U��+����Y��}W�o	v����d,+i/
��Oa��fVd���>��h��le�����
�|�h3*�]�S�"=";�A���OW��X^�St�&�p[P�lQ�|�o={��-������2�_`�H��X8��?��X,��;�L&��*�9�K���<���9(��J�v�Z{�/�������g����Y�C��	��EJDa5)��A�(�\l=5O�������=�wMj�|�ieeJxi�,���76h����e��B���bm}��u��S���:p1��O}Cn�yd�C�bop'{�y��(��&#}Qe�	XuFie6�)��b7���,T��Uf�:�"�����R��O���5M�z����a�F^Y��(��������h�p~�����)OlwM�IBR�%���
(7�����^�o"�$�5�a���ro��i�,��Uw�H373����`�� ��^1k�!A8����2� 0�����J@��N�v���w�)���k�z�uc�)�8��'��������]�F����_
�D�@�aKdl��@�����
��xo�2^1��"���e����I����7+�IVj��JA��!�#��/���0&�iN>/�O2^�x�	.`|��lyJ.���l������*T?��(r���3`|�i)i�I7�'�s�
�:�J���zr��m����}�7�j��f��&������P��9��C��->Rya�S=�����|2t�4s�3%���J��Z�z����<7�xS������B5������x���${Fo�X�j�	��!����T#!M��O�s`�p`���-�'�$�������_�<xh���L�Q&Q���_���K�D?7�������P^b����]�}^���8��N_�
�C�U��eS=�|+�#��F����{3��JV�#�v����ThV�ZF���-��H�Cj
�)��-$�+����U]mL�jq(�����oo�+Q��I�����w;<�_����
sYX90F/n�����L����%���-��M��
�*�������	't��={����gbq����������,p�B�o����.?�z����&'qB�I���&=�]�����"��)�P��'
�g�Z�^F7<���9��%"�����rN������0V�x�l��=�����|�����?�,2��dB��LEL�����*fY�Ku�����s ~��!E�u)D\�l�O(c����-z��]_�N�6wW���KD66�}�%�~�u=~x��f��e����Y��y&q�o��p1Y�������Gx��y��k��T�	M/��K��=��������	������'^�o��U�/yY����S��D$E����"<�D��/��-��������xq�@�?[#)����J^�6�h�)"}���E-��+��m���Z0���$�0xT5?Rw>��!�H��*�1E������k:��E��	���y�����g�w�e@��� ��Q�}g�:��J��a�FYJ;�1F�7?����4����''�n���T�����l��&^�E@[0�p
GtDXXW�[�<�����H.��s2#w��v���Y|r
�ess,����O��a0&�o�����x5�����������u{.�T�S�q��D��#M���o�Z�3�4B����e�"���?�]6����x�y�.1Lg����N$*[���y[�"�_�]M�@�J����v4����2�ZdX�i�Y�4��U��4(:�[����'1�;�����6��6�&��LNB�r��z�{^3j�e��m�k7<r)�g������E	��I�A�{b���5#�����^���'�^����jQ������P�h���������Z��L���G�����H��������mq:��Y���G��}�h�/@"��
��xu\�����U07<�!�����j���b)N����9��[�e|!�X��8�w��8����0x#/�7�P����z�=�
��6������\�:G��dj�Ho�q�	�<�&^��W�T��O�xh��O2�$'A}������N,����\����W�J�-��(�"�6�n�t��l\�{��L��Ou^G����	�L��_�*�	�����4�N6O���@��',��|R�����_�����v�E���J`�k&�Y^,�C�wV@�X���G�����'���(x,�����M��O�l!d�XZi�5�yA5�F�P|J���g���uC�����9�
�p�OV������>����	�H�(BG['�hX������\��x���TXUh�����������m[������L"7���%9�~T[I}�X�i�o��~�D���EU��zf�|�w] A��e���9g7	���������C�%-:��tt���y#�9s���U���LX����tk�rU���m���v>�������<&��\��A���>E���>�K��w>��eN:F%�K"�M/h��}�)��Nc���9�Q���]srK_d,�<)�H��<8L(����i��	���&T�����;t_�N������
�����cX2�u���-����I9���3��u2A���������s�]���������C-����b�Q�l��l�����
��ml�(7���f]�����4�a���7��+m���d���r�:�p�GJ6%.�����1�@�.�����y�;8�s�?G��\c�tA������g�5v�|%����;�E���D��u~��Y���g�T�/�g�.�gE.��ftLn=���
v��vM�[mjf"������#~�"���H����z��k���������N����%mj$Z�d}{�f@�Z��U��)�D�<��}w���x����7�r��[�h^>���F����`&q���4�FS���+�oz��48qH���0���n�1�����\G�t]��Q�@G���g�4q�h�K��`���PN4Zph���
^����0I]R��g�K����O{s$��O�,�����i_��	89h��6
Qb����'�e(Z�)����V����
@3���wH@2�	���uD~�{W�`[w�q~t�2��D���A�[�o(��0S�g@�������!]���m_��#�Mp���]�ZTn{�BP@���ao
�'���r����H�I���<�+�D���e)KV���c���KsN;�\%�t����Gfe~�~�,��w�;8������[Y�I�h�:����+�C�iy�j�����n���0�,	�6��7�l��7�I�������%~�y�s]bZR�F��,J(H'��jq] cQe�
��G�RsI��k%���:����By�0�����9F7�uk;T��x^��@��Fo�)Ep���� i�V�@��I0,/h������5�mi2�H;�����L��q6�?�2��(cTF�/�,X�o�N
��iv����Q�Xv
'>�J6*iE&W�n�]���	�L�}��^[����5/�4���3I��s��*w@Qd�)��)G?,������X�}��!�S���'����Ha���_�<�,���W�\�Gg�����{s����w��P��dQ��&H
�b�+�8>�s�`�I��^�m/�������S=��v�2������\N��"��p���#/���9�h�JO|	i0:iu3���<Lx���a�m�G�q�)����,�����#}y
matd�z9�7�0M�T%��j��no�yO��q=7��0wx��~�2j�D��c���)����m��W�u�Y��cO@c
#��AO�T�;�������-���0��~��0�|~��pa�F�H�����RM�y��hP����f��Q�������G�||�����������\��B���s�;���pr������+bE��Z�2Qb=3�'�
!V��n����&����n�����	?������5G��o��+�j�&�+4..�id{ys5~)�-d�)��It����/���e~eW9�����i��.`��%1�������=1�*NSt����t������X��v}�{�D��/�pn7����>Sxq�'%iP��P���W�����f�0�J���!@rm��T�:+�i~n����:z�?v��c���6��w��'�|���{�.M�T��\�r�k���6��q���I��`�M/]�^w�=o{�
��O~��"P�4�������T��$�B�J��V�����p9�}rdN���Yh����P����5\�2�bx�N���I����U�vV���$��i�hk�+9���s�VdL�>���������C#o���_I��=������K[�0Of���4R� ����E&o�`k5�^k��-ty��n=6J1Do�L�$���$y��]����6$�/5d�/�@����?��9�tew������}q}�B@�^��d����c����<2*�E��
���f�m4;`C��I���z��in
��.��h��������#z�z&������\+ �H��}�'��t�[����=�T�bhW�_����D{M�&c�����EQp5��F���V�E|SN�[�-t�AW����X�V)B���_��]N�$��Y� hk��*�����������-��1T.��!�A�6���
X�T��B�L���j[S�� i��O���/�~�ze,��D�H#�\�~a>�dm�m��4���+��hcM��j���`��S����L)�?"�r�K�����N;g�����s��6��dx����5�}$J�q�QR��d��{�Y���,)[����gn�[�\�?��_��\�:����5�U��6���l��n�bC�@��c�Q��������U��l�Mj�be�"���a��U��-9��d��*��n�����)����U�J���#�X�9m,������k�pv�b-���vb��f����BE@[�G���ss9&�3��a�����l�t�;��!����Ho��~w>���%c�$x�s�evBS��:���P�PM�Th�Z��V���b7�'0w�������?KE��|��v��������]���o&
.V�L5s�y;��;�M����>���T�_�TH�?�"I��ff���C�� ���X��F0�#���e�]�����b!�Z�����Tk�6V6��V����b���2
�3�QH���E��HCgc�m8�����$��&L�>�Ln�W�K�:���X���������E�(��)zB�sb)��C�O�B�Pogw������C���@���x�s��n���v�����b�KNU#����?T��x���O)���$�������,"��-����!��[�s�L���a��=n�1�"�{��XK������!������\��M�!,�$���C����HD`����V���	q��Z*�	!��.���g8ST�*.�����!�&*�Uw �����k����}kk�u�#Ujq��!��J�op�.prK�Gq"ey]������)}S�O�=��Kd������V������7�
���H��Al��Y�j��V+UCO�dW�f.J�K��g��Ua���#����$��7�{v%��O&�9�-��`�W�E����*���i��L�?����b�'�l�U�,�	,M�zl�����+
T�-
}-+�*�����V�������$V�-��N�������P�5I�II%��I&Sa��3V��\�cH��Q�\�C���]�i��cE�&��Vk���V��"��$�~[�D��7������cK
s5S��gNW���Q�	�������)[#�u���Zd��J�����������N��^� � ������l��YD"8���i��y>'EED����
��#��R�_���mW�S�}���`��8�������,H�TF�l�4&��yZ�����2��IaF��b��VQ�'u�����������[E����K�����g���J���W�W�:e�����G��t����h��^&o
���u�/�������K#J���Rfs���\��M�.��~��Q�$�����=����&�����-D�1�������N�$�j��3"�k��+�4��v��'����#���p���X>�#*Z���hy����������E�.�C����(ygl����|��U�m
�|�	��x�VGxH�������I-������Q��Z����(lW��W�M�R��^}t,B��9RX%�����������"�����
�GU�ox���A�i�V�Wn�^�N��/#��
3���V,���?�����;�7���'�Hp��v�}�7��'��)��-�R�����K��������=sgU2i6���b��cQab�).��=1�����W�?��*���q,od�i��0h��&p?O4���5H��Os��P��X�����=NE���p���V��;����yu��9k}&��'����!-�O�Uwdk��Y�M7/ULp�����b���bs�ORy��T&���x��#��m������'�R���������O7�n�l�7$�x�gS��FDhc��f�����B��~�Q�R��l�8��vIT��n�P�rL���P��vbvh#���MCI�{k�������-~,���OI|�����k��O����k��=rWQ����ZB��[n�VPB�?��C���6�6����~����~��Xn�����>�����������M�g���=�G��H�"�i������i0�p��;�%�$R�C?�Z���'*�D�����7�P4�/��h������5���ki��h!22�<L�H��<`����}�#�<Y�Sv���e"v��LA����Z%4F��(��?���v2�	*n����	6=��h��I�N�'������W�=��C�����I}�8��d�S_H���}&f�����q�[3��A \�[�s�����&v�w�Z��'��U�!�(�S:��`G�6�<��Oh=B�k���.o���?eS�Dj,!r8�����&=�F��G�#�b��5�491�j�6Ad�i��ZS!����X����R���j=���4NUSs���Y*�Vh���&��z���'��U���~l�������������J*�Y�7r�6�a�����p���9R�=Z��X���}�
Ge�:��_�Z����	�����.��t��n6��2�]��
`Ls�h�l�u��zD�q����<�|1������wg�Ki1$\�O���9�������!NM��������t�H�4�Y�LQ���KP,3fp:
��3}LxIn�����#j�rJN���M���;_�y���>p5�QVb���m��|m�g���(���t[Z�m|#����Br��J���u\����[:��k[��tFh�C�p;�m�6pu2��z[`h�-����;NI
XpUC_�^^����d�gZ���l�o�������\���{}�
P��v���=H#�FL��,���roo����pl��n�^^��q�9n���Q)������T�
Q�d�i\d��Rx�S�b��u����F�k������?	,���0b�>�l�|���};�Q������$�j��*�����e,>dCD9dI��1�!�s�v,�!�� V�e�!����.����!�At���O������^"(V��!(?��5�1�K�fA��f���bR�k�we?+Um������[�N^����!�:���B'��dW���}.j,�.�G�8M��^����
�_��k��V��V�������>5�\$&h�{��V:���N�>������K�c1��QO���J}FR�vp�a�:�d-6��F�$\pY6D8���V�=WZ�<�s��� J?b���7���h����x���&gL�`��3����<����6o�P����s�S�h.G3�9�����7�����C5�Z��,P.���6e��#��{�Mh��z��K�����t]w��' ^�����xu�����:��?-F_c+��q���(��V�T�L�Yb"�
nb��I��l "hk�j���rab�u�{���[mV6�4-,\]R�hJ���L�g�`g�S
��\7�����?��M0zW��!����4���?����7����;]�P�V}	V�\�����n�k
d�j���fmM�y��L��A����PL?]���)���h�GQ~m�����6��K�63u��A_m�V)�u#��7�q6�����]��l���fl��%�;A�e�~ 3@@�>��pI�p)�xcYd `G��a��[��`�1�y����wN��M���np����x�Z��]d��[n]/����B�zp��k���
��^/����!��>0z�p�8��0��@�����91�������FK_U�HT
�T��U����qL���s�D�4���'~�����-1|X/'�Z��zG8<�NO�z��?zo$�sg	����mw�}��T��@�=P���"���O�O��C�����G[���k�'9�IqJ�
��"�C�`�W�%^�����f\��M*���dG�v�����D��`tY����-8,&�B-�q��uv1"��u���E��%�c���������dJg�$Tn��>���O#�������`1A"&]2������MVC#��WM����DB&j3W��(V��FU/����'��_�&��[�g�����
}!ruRq.�Z��������5�>������2jL�@N�hq5�;v���wO����������u��SxHR�A8����@q��j���6�;��d��p����;`Sf�PG������X>��"���j������Rh�K�8����#f�RZ�&��H���"o�H'*����Y���G%���`�9�*<��6XR����2+u�+�i"��f�^��52��\����$ck�]�����O����*��(�|�^�enM��_��o����6����	�2����v1
g��I��SDW�L��������mId����E����N��JP����s�tjx���Q�
��;���g�)��?G�`���yA�����������^��f��X$\<j/���6��:I<B*7�a��-
_x�f�Y�	e/��A�E�Q\�Z�������1>p���/G�DA�np *�xh�X��.8��9���0gy�ZPbT�����	8VQ����~�n���NU�
���j�gj�����s�,&�?sY!�.1st2��n�#�-��gG?����d{�$+�p�_�;�%�$r��	��?L�:�B��T9b=����fJ��
2���[�@*\�dLW�B��P����C
�}�+',��d�	�8��*q���G���hq�e��&a�?�'+��*�3r
������]
cB�+�A����f�`'��x9��z\H�MR�gW
��4�}��%��_����A�=Q�a��� mc3�!�;���*�}�T..�$���'w��cB�O�1.���1dD5����P^k1��A��������:���o�@<�O�}xpt���n��l1Y_E��+)����ux�dg=�n@��������3(����-)�/����w�����3���	�����X5�|���@�3�w(pK0*�p��9�x#4gM\�S�����Wx$��8t�4�BLNt�B�R������`yA
�rI����d)��&�X��@`{��;�?�wZz.i������N{' d�<<������f��0qp��R������\].1-��#��rt#��	����wsz����C&(t����@h8�J+�Yg_GCya���uBU�5V���D�~q�^�*��|/a<$�/
<��:���`�:�����?�XF�~i�9������N���A5���F��a�fUR��-�������c�za)������C�
�`Pq�����?���8YS��$���:]K����G'#�a�q���}�n��b����3����[���S,��/�b%:��.�~L0
>|�8yi����C��~���V�_eA~+�7�|�h���X�?�X��A&Q
���fn���?>��<�cc7	4�rc�%�� �"F	S>�G�%l��,���t�"S�em�[�m ��%`��\��p�,~H�5�7jD�d�� R��IOMZ ��F��-���\G6!�:���Z�9�4�����<Z�F���������q�k�s����T*D��bd9�,7�^�d�����n��d�OJ���m;�������7��	|�%�E8y?v>|x��dF����d:p�'��@09�9���u�=�����)*�o�n*�M�A$����)�`E�������#�wQ&���2"������N�1��N4�G�%H
4��3D1W/|!o�gO��(>GL?v��
n9�-�T�I9����k���	'����P��48�4��c���fVR2�.����.K"���#��>���8'�Q���w�PR������*(���iV���S1c����GZ������{vrt����>2+p��j��/��v��S�Fy��h�����L�U��\��W|���:���r���J$�T��2H�Z�af��d�vr,�����1
@��8@7�(5��H5����b*~]p�rr���t�)����8la��y(A����oI�O�������H��� ��=&��`;�rM��iN�`Y|m�����c�)��vK����P8+t	��OA������YzX����*��l�Q�dy�}���|�z��������P�������h�Y�D������9���`���]���uQ:YdJ��{:��2�]�&��0R:4����?�C���x%��w�E�8�<vc�K��+�|��'���'C���g��wR��-��O~����?z������kZ���8��:M9�`�C^�!_���s|�9������
S���._w�C���Ww���v���[rC5'�g�%#�����s�����V�0@=�.G��4���z���%`�
2�@n����gjW�' ��b��$�g�_H��zA�&��$�������.���h����UW��>J����b��\�,:�2^�f�I����0t��ntQ��`0����/��n?u���G%����hc5��[������f�Y�L�>��O����H����83`?���d���=8Ao�A����s5�n��bg���{��c���_y���$>�|1��m
�Ys�������2lFl��
��bD��6�)Dw
��>����@l��<�Dn���J<"WQ�i�U�b����1�{;��F5�pv'#��yJ�r�z�U
B�B�b����r;�4�B$}V���@mP��~���6��(8h!����j#�i�XM��*�	���gQ�3�BH08��<z�8���������&���Kr^�K��Z�nD��	�y8��W�kN)��bE [R�V��f�W{*��$)�e�^�=����k��zh�v&��E~��:�I�������
�D���%�X����>�%����.�?�����NM�����d�2Q�V�GRk_�W�6���_�K!;�6����E;6�� ��{.��P��K������u�����j�1n��B�H����K�V���D�5���Y���Z"6T�R:{���^Z}s):H�5��sE��k,��K�n"Y`0^)�������abY���"��=�X96@����$��d��C�)��3�3��k�[�v��k�����c����f�	i����?l�Ce��z�lCTU��(�5-|������6����'�)=���"?��<�}��]u�Vt�+�S��v5���~)����M������C<���Y���NjT�E��%	��V����?Z
�n��3P�zc21���W����G�!��I)'w����w�/�	�cDF�D��)�t`�^kqt��E@�"�mc�Bu7�t��sE��D���\oDs�P�����<�#���TM�Y0/(����0q���N"/���_"����@��O��{|�B�P���	l*jD�1����SQ���4	���o���q������x=�����/��&:��N!��W�H�a�SV	p+m���%�a��d�h�j���Hg5��T�9XC8���cB	�$�=�����.�J��K4[�SJ�o�
g(,5��������`�ddd�x0�EJ�D�3]9�:�e�zL�:v�s�����o��X��]lv��PB�������=v0S�����Uc����2)�R�}���p�����x-G7�����6���Q_�T	q81������=I5R��+�&�O�����YO��)��Y"1���LxR��Ep����[&�t�����Su�X(~�����r��{H�s����{����;u�!��a2|����7W���e�s9	�V��s��;�.G�����7��} ��r����"�6��E��Q�?W7��1�*�r��.G�[�����P~���!�o8�� �<����g8���#ceI�w�G������CS�7�*4�j�s�0����G�p7Sz�J	+���|�g���9	�c[����\��K/@����r�!?��c�v��S#��'>����Q��V���S�����;��#��A���e2B�)t�i\��#������k$9��b�W�a{��]C"���)8��J= ����C[`=0p&$'��S��s.�!e���|<��v�'_��+���tO~V�QH)�.2�K��s9!����Ir(q&Ir��g[Yg�1�c[x��fd�2�%��)k������T}R���d}�O��Z��{��A
�7���j�����VU�8�*��J���>z&��o��H������Qj�lX����$q�'&�0a%� ��(:��lHwO�y|t�6��k:��8���~T�T[�dS���Z���7�"�����#k�|�@��@�_f�5>"^��ic�/�%��%��P�]�(k����]��5���;a�������L��z�06;�����)D��/���P#0����b�C�&�em9��<G����%��'���^��������b0�}:�M;��	���1���0��"+�97�}G}L�������F��}�^������p:�q8]�����Z�o8�K���X�)���9��@tE��k��hb(���1�:���v?�������F�$��������:�����5�= 1�<,e�v����@����)�of��D�1����(�]�����d�9�����1{������#���,6u�Y��1VJ���l--��#���s#1�:/]^���X�C�M��u����o� �����cN�A�0����[���)�����$pH��D��M>��i�.Q�����r��&��Q��{S&Q��#�X���P�u#&���]�����Z��E�HbIn�pL�8bT�K�D�P��o��yH�d����wU���+�&v���&n�
'm7��S�LE:u��J �C�
���20����
q`��&��t�(��2�*P�d"�I�)��P������U�C�>b(����=��$�({��E��a�|U���&l��HE���7���v.���K��+*A�2]�<�������<��d��$NC�����n��,nF
����(^���c�0�	S��8�SCK��G�GO,���/��\#?����BK�|+$s��Rq��r���������uZ��x����[1�)���IdDJ�4��p�e�_�GNN�
4I���4�����&	$8k�0W;�������2���
�\�����	V�E�q������3rt%0a5��� �2����of����0�AQ���8����-�B+��p�-�S��0��]����T�g+b7,���(���[HW	��i���P���`�S5�}��
m2���y�U%�5��w��6I�����[m�� ����`O�'��k��/����<c)�y�:����Vf~>{�r�S>E{�cW�5z���Q6R=���^�fYQ*&�Z�O���K���2��+Q^���~fF�g�!��}5��=R2�7���ID�y�2v��|2As�V�W>�t��2�;M����(
&k^��]n"�5�iM�\k��������J��"�M�("�
.�����B�Gb6�	2�}-��B����X�Ly�=�������k�Z6
\K��#�Ew�D��%��=t�=�a����&�-�%7�p�07V�y����f�r�%�5��Q��W��+��[i��j���C���-�*EcV��"~��@<����I�@���@�]��W����+�`-Dx����I/�!���\v�UQsi,���F�A#'��d%��t��?��w���[;����t���������/��r����=��LRq�zq��8��5����f8� ����Jy�����s"
 �[�2��>&��hdF��Vn���V'�a�?����BW/Z�%�{�����yi]�[�R���
���bg�efa��h��6��Ry(��NA��<�����_Yn}zC���U�������$.x7m.�.2�h��E�)J����n�Q��>��]]�(:u���@���7��|,�2f��������n#y�)Z9������;��[m6��o��b���P���
�_�L�.�w�GRaF�&�(���A5�J
���p��}�k�����Y"u[S7�wRA���-��i5���t�1q�[�K:��������tQ��[�v�R����3��+z�����]���z��sa�7��M�������B�3]��U��V0�=G���>���������ff����L�4
a�����^�V���J��H_fY�\�|��|��R������5n���S���q�}��i��,fZ�5�Z�c���y��b��r�EY�\�YQ�RTj?*	wrK�a�;w���w���4h���E��vk��n�Vo��F]��q�!����W������^t$���T�orG1F�{�/�Rd]�u8<n!��R������t\<����
'�Q�Z��h��VY��.1��S�f/�LH�����,�n��@%����-���Y�1�B�q-�*�����%z+e�^i&�'��
��|���\RM�����
�},��n7���I5J���;��G�����a%�������X����|��8qU�^zE��b���s=(�g)���0�"-���0�M��j���\�x���&h}���R��z��0e��?I�����v�Qvb'7Y�����4]*�!��8Ot<����� �_�H���y]V��?�gG'��eiT�?b@�@�S��U
H�Y�xY�/I�M>��#]�5�5��
����7	.,
��y���>����������8%T�0x�����ZU�.�&O�/���H��
��s�FSziFQ@u�`�Nv���:�%������)��r�P���$|�Dn��9~��k��D�����;���j.������(\�������^��6\��z��4�L�4?�0	��<Y�'S�<m����%�����n�H�lU��yT����b�|�S =���y ���g��t��t����*S��~�+��~u�=�3`��Y�����[HX/r������Y7^�S"-�S��.
3�������`��} 
L�>V<l6�J��)�%E�Zt�N�����.�`���%�mZT�4�j��k��^f�CH�C��=BfK6����O�v���[��i,a�
����B]�+<,2D^a����"�}
��M��v������������R��V���wizf�2������2��
"GZ�s��Q��r����
����O�G�=q�Z��Y�g�tqt�����j��n 
��\?rc�>��vVh�O�\A
��.�$]6�&x������� <w�e�����<�7Hbk:�Xa��}A��
9�=��r�������V7��yn�qS?g�{�u�Z���f���

+������ �����ZukqH�cO��xQ�������c'�kB/e�U|��;n�F	�oF���W��J�o�V\��M��'�����2�k�o����e��ZP*���>o>\9�f+����,N�|��;�P���`��!p~��c���]S��"�{W��`6J��W����������U*����W�z,R��#�M�/.��dL#�[L�
�!��'��U�VM��p����PM���AY�������|��U�t�g�SUY;yU���l|a�_0-NU�1����.a;�-|,B��M���j|���Z�m�u�����G��*�%��L�������T������/���W��h���)����|~6��o0�/*�e��x�����'�F�.C
@y�� ��c
H���>�hlKM��x�_�?Cc���2�vb������|� ��F�PV�)q"�s,��Q���O����Q	�D����6�xn�e4���g���H_=d�����4CB`y���*z�G>Nb�=��������=g�aZ���W����D���B|��a����\`I�HiW���,�f�mi��W|Odu��L�N���("��pz��C�K=������/��*,�\����D�mW�.�]s�Z�ev�R4H��������<�9}�E���D�LS,D�/_�j�I�	��'V%x��V���k��@Ui����}|����Cs)�g��1�9ty%M��!K���q��@�O@r��$[���j������U���h�p�����{�9���U4yN�Bh�`u�xx�����?\'�?����W��"3f���9cU'�f_�^f��4�_�-0��TI����������>��u�)#?�V�h!0>Q"��	�����e��\����k
���
L:��hhH
��Z�4q���yD)�D��Gt��O�#R��~�`���r����$�������UiX�1r�+���$R�����!��z�J��u���EEQ�&����G��U�O7M[k,O�U4PP�Z�U�����b��b�>�^�rm��/'��u�X��-B���Z�^.����(��3�z�,wkMv4���L��`9���>*]�
�����%g�`*-�����N.�rYw����K�������@���#f���oK�^�M!��*5m���3��QA�c���bGc"x����������t|��������E=�j�����C�-$�9���'�������A���?-wPcq��z������'�
����������k�YN���>�Es�{'���<�g�����������:| 
~�11��9H�E8�q��9:��9��~o���������y�d�6���'�G��l��qu��So[Z��bvg�����F�����!�<'�������m?��(��*yyT�@D���,������+@��Yt�udA�ep5/of��w��N�bZ����j��W������f��V�|T����I���"�+"���s/����z�)�����_Cl$���f�k�]��Xc�k��x�>
E�r�!m�O��`[�*g��qi��qdQt���P!�T:>�t�@y)��!����~F��z�������[��k�N��1y��������K�q�:��o�����&�[�
Z�(����TE�j�����R�X�_if_A<k���)��i4c{��*���V����V/�,�(�7�4u9W������O��&��7�4�x�i�p�L ���	���D%����D��B,����jU��#�Y�������BZ	�#1�y�QB\�0��D�!�z���w��pA�/��7>'�
�,���@��(L�����������ZE���N�L" M8��y!p�2����[�\*��Xo�'����U�VA/��&���9:���������T�yA��D���4�Jgb��,��_n��ZiU�v+�P!�|���7F���tMSR������
zc���&jZc�](���;[�t5��`Kp�����u)Qu������%�Z�%,��v_�����F���1�Q�*bp�o�V`��BHH:^D����q�IF�,�h����l��8�����}���.�/�i��6���vO^g�f%����[�������K?�5��M�$e��{{{��O�`����Y��u��6�������%����w"m���%��[����vu�0��'
ntuX����%-[�w��v���_��0���P5K�&�^X�2��V/��:nVpU ����jSo���g��-w����9VC�u���m�W���j0q�������|s������z\�&N���g�:l��f��6��M��,W�f�HE�������+v�kd]�[I�_�O��h7&"k��6A��
���w'����t�==�}��
��mU14'����:�Q�(�c��1��/)�7����o�m�
=l]���������s�(��`����4�M�v�w�8�c-��i�����*�������"��b�0S��iN���P���t�U��Z��Vw[q*u\F�����#_T���*����O�2ix��D����b�s6A���nx�]N>�zK�������_X�����?{f�Vv���fH�n��v+�{���*�6��c������:�?@��"���$�:@�:E��3� ���8_�nN�x���v��v������r;qjZ�Q�������������
=z{���1���;
zg?~������a����J�f�������J�D����|���W�K� ����R�{><������������6�);���}�n�}�����-�������&`I�r�����]����XD�z���PbQZ���W�!����U� ��Z�S�c�Z�I�	;���?���tZ�����M�����e=�!����'�ZU�
<G��}��R�1*qF�T0����-����Uvp�K�y��{�v�z�(��'2#�&��b��8�O[����nH�*�:����H��8vY�r0O�,\>@��`��m����������f���P���fF��+Gf�{Lfj	��7�[�� 2��T����\p�r}mF*s�7����0�^�Nsk�� b��s	�.$�6�Vi�&T�	E9����A�'�A�>����H8:/����%��X5�>�kpu�d�O��R�����.�c:�?AYf�1�%��q��q���e��������������0U)4�����x`����pf	�:���L��	*���>t�z���T�FcV�C���7r�,��f�dH���:�d0+<���N���X�pa;�Yn4@�*L�6�Y��z�(��_���x2��_�O�9����'�Q���+��:�Gz�rR5�=]3����v��0���RY��F�A�����]t���Q�e}�gw2��Sri*�g`��:4�f9����t����}6�����\	&���l�"�H��R���dn�4�?�T�%��a�L�A��VM�[[�@jyw�]F����.5@��J�����a�-��G�
�T�C*��	3���t-�o�����Kyb:3�=<��D�7��t��O���0��x���w�g���-�]�nu� ���<3Z�N9�e(h�G�.�C���"L=�S�3�T!��L����re[0N�����C����(1����?u$����K�.���J� T�[v����k��
X�����L8��R~�����9���7�b��*�'
�
�RK������������x	W2W��!+��^�C>��Df�k&���A=���z$�M/�%��(1���tb>�x�Dm0��\�Et�`[���6���j�_!��VA�S0���71��rW��:�����R{�h4�a�)D:;���]�\�z|qV�o)N��{�M���1WS�vt���3:�/��d�=)$����o�(�L,o#C�!�p�������nk�,3(\��>��pY�@�G��Th����o�VR,+���Y6�e�ZV��C��'\�S����N����J����F���:�0	+L�r�X*�"c��'��*��"�8���HeY+��6������;���x�D����<��"T���S����jA�Q��\���+�Z��8����X ���y��eXG�R�KmG6$�*0���������{��+�f��2R0i���@A,�=g������9�{�z��Gr�5����E�%b0�59����]A��6������~����*z
>wE��� y�:�:�t��g?u"~���=�(�.�&-*���k`)J�"�����Y����qr��R��$��}%��z?�:�GT�mT4�RA-w��1U���[���(��MsKX�wj����������S0K�J=@*�v��Mo��WE���
jU�6��px������w��TJ�g��������+6&�u�T�H]S�3�����j*��iPA���o��'���}.��_U/W�H�&�h�p���)	�g�����6N3�(VpW�����KxC�cjP~�N�`)�{�`Y��=
�LN�����N���E�[�!��c�z�A�^i��[o?�t�����)�����n����
#��}�D}/�%�����t�b�������h���z%7Nr�	�m+�Yi����|�#�Fc�D�5�K��"������W�g�2��t��VZ��:�j�x�HT���#�E��;8��W��]�����9Z�f!i�s���;@0r���Z�?���02+��>GZm�N+|j�k-�����j��6;� C����e�5?���N2}��r���j��J���UA��C!�t��A��V�c^	�JuI!����
�A�T��u��� J�q��5E��Mt3�tNA@@�&��MN^���3�:�����RPp��DQ�;�3���?������L0K�������P��f����LiS�����c�D@4C:��p��+z����X6�'d
�c.���.��.�ad�v�&���l��^��������H���==�Gz!k�>��lj���a7qQ`)@��[�a���@�r��5���&4���Lo�v/�N��x�j^������,���Fj��f�x�j��#�c�?Vx�Rf����z3k��J��U�dp�_S����PN�/,������{�m���k�����>�}q�����������t�����0���x�UH��(��L��Tr������NQ<]E!���=�[�m�d&E��3�5�FG/��]���w���c��<�^��g4�b*C��^�����������h�,����+�&?o��s���������p�}x�b�P�M�D�s��B��0\D��7��<����O�+��L�P.�+�y�B�����C����S���G�)��HR	zO��������^ay�QbBF�'q`���&�*TJc����X�	R3�*�K)F���n\I/3Q[��6k����Y�����J�� �����������������:>�\�D��2��V-�������.HqT�����wHj���:[W�n��&���:n��I���0���/�
�����s��W�d ��u�Y�C��>f����;�Y�{��������qp��p�Q����#���g���l9���'�6��$�ng:�p�����qHy���/(��������B�[�.���C@L���p��a�HHp4no��D,`c�L���9��������v�7����T�?�3%Q�Tkt����f���mr����oC�VC�u��V�N�:��4�������|��<sTr��;
��%^R���4��;����f�E3�F��%�Y13Q�:��\�EM�RA����bi��
���E��X����nz��������l�|T)�J�N����e�N�>��^u{-���D;_��;��')�cM�I�~�;����)���N�s
%��XI�d���+%1#��=�5�g��!�{��������sb�J'������$�x�D:�k��Y�]�������D�f����v��w��`����f��N��~�l`#�.��+��C��Ab�8r���7���Jo�8�YqrJ�"
����B�@���o=`&�:�z��� �B�������>����}F=�c��=�)�iZ���a��}��[3-�Z���"	��<��<+t/c�H:z1�/���|^���}���X��1Zu?��[k4t��?;�N��%~%M\�/vP���gq>W�+h.~���0X':U@-;@��)����@k`��r;����$7Z���i��{�q��;|�c}�����p��K��?�P/����[������X�G��#����	t�-��*�A�25���Z��V���jBd�Y	���M0�S���:��)��%�"��!U���Qh�X%6�1`�6{7�;�/\������cyI�t���	zZ��>H�F����!�<5������je���F�|�-xr���;�s���n5M�T`�$[�*v��=���G'�o��N��M:������g$U��%���M�L����-S0���jODx��.�wH%���k(�W��K�����ueR!��v���h�������}t���q����7|q�a"����������I��ed WP1~��IT�.z}i���=hO�7��h�w#|���${	��a�����#�@"����Q�vi0�EIQ�2�:7��:�W�=H���f]��c�T����g$�4��!�h�mS=�]�C���({�<	�c�5�>���7�!��S�k�^	r�]v�*1��X�Nm��_$K���z��k=V�r4�C��?+�|��hB���Jz����Hz���(�_����(�!�M#.���/7��_��.�Iy]n��I������U�?<����]������#9���j�����h��A��F<j�@K[^�p�#�h�}+�J]�I��_����b�$�����a��0�)��������]�[�Q�bvv_UV����<��i4cY{�n�:U-��I�M�*^i��Z�Y0��cD^oq;�Q�N����~�eIfU�~~3��������S��u�'2��Z.��j��,�9�������7�������SLP��=Q�;E�R.Es�\m��j�x����dWxA���X���*v S�����gU�?d��;[	����@��~Dr_�tn6=��?���a$[��U��P5�*lsKR���P����d��]������W+ph��l��y��5�_�4;���F��k���X��g�0t
���{-��@��s|��lz	d��Y'P�vl���A+n�%�e'smGw�e�L��w��e-�^����%H�"o�LHR�����&�N�1�I�����mg,��A�6E�����sxg}��>���C����gNZ�3"b�h��c�a��+*�w��65����b��\�i��m�n��0���`���.�t
dC;�z�r����>ENQV��8#�wX���d/-�d�������rW�c��GQ���~�E�{o�����a��)�V�\����R��Xi��@��Z�����N����d;I�(5%Y�� cO3	���MR���
��;�,�]q��LHVk�.5��K�8sc���<'���W���������$�M+CF�5`�������h����[w�Ywk�f�Nm8{��Vk��Dx�~]y�3����������	/3_X���w,���-���"����jC�:p�J#�m���}�J��W��Ru�#MS�53u)���2u)"�"SWr�k%��/y�<]/e+�X��q>����?bf���&��S��Q�+���q���z���k�������XNa�r�'�?��e(�x�6�T�2+rBU��������4!�"��v��8��@����%�+>���[o���\��(n�|xu2�'�1���m������,[N�}�0��a������d+�v9
���&��z���[�L����bk*Q�������S��B@Y.As�=�-�3�i�y�'��m�DQ��`���Z++�t\E��~�_��}��f6�so~GJ����F�UtMi0�+�qN��}��-�7i�$;�R�*g��da-NU��
D[U� N��a�
���T��=��M����>@1�x��W��t���
�y�t��I���e����gU�\i�a�W�������5�'�����&
�2�8^�X��p>��f��U�������k�����*`%�A����U1���&�\M������:	�������[��5	7Yn��+3�k�_>����j���*���f�2��q����+��9��I'�3���&z�jy�e���qI+e����{ylc���[�H�	�T,r-���[mh.���j�����q������^�oaW[Mf������c�jm�kFr9�E`��N8Df�ld�.�c���`���g+s�����?�ILV��&���8i=���[���'_9t��&D�w�e�P�ve�,���������)��x������"?i4����g��T��U��Ub���s&���q��f*<\�`� ��.m.2�%�JEd	�|�V{���O�Q
�)��6�^;"��S� ���N��R7d5�]
�6/�6r�0]q�R��&��gfm��&7A4��7�_�}E�������b?����g;X	j#T�t�T�Z�^�Uk���
1�."K�c���9�,6��B%��#Z���_��x��",^<F�h8�����������)yQ}�u�'?��G���������7e���������&��s�C=�/��������.:z����'�6��:�T�Z���k����$K�$p<L>�-��<���m��K���O�C���E�������*�?���|�~C����;���	O��qpI�:H�ErV�du�@(��so�a��	*��SnO2�hJ��s��j/{���`����G�������u0����Sw+���q�*������+Lh��3��l������)����l���I�<[H�e)�f��lI���f���#�������^R�G0��p29�����B�.JW����r}�VM�Pq����0"6�Vw�u��G�/�F2��i��`8g�n\\������^�M���p_�4|���hV*pC��VU��
x�X��w������h��9��v<����=P�M��#�&�a�j
U��[g��;��'�"g����<S��PK����O;eqw����;-X�������Ug.����|T��v1. ,B���wG�8u|�
{�Z"-���N9]�x�S����h-�����9]>������LN��\XD�\�����b��%po�$��\��M�y�a���w��
 �{�k����,���q�8�ye�^���n�������f9q
T�������
Z�m3�q�B��5u�4o4��7T���T'n�Qt�=�~��\]����2�4;��������1�yW�lV2��Y�B��e�5��X&���0H���?��!�u=�m�t?8�L�\�E�T�����������]� �oZ�X1�yf	�;V%�V�7I19��O�)����>����$k�,��4����[�i����F�"�{W����Uj�����E�����(��B[B��K�cu��������D7MXM�R���t�R���&t~�oDA�����/�������qwp������7F��� �:�P�MF�,r<�M�d���T�|4�g�B������s�G
���Py��!`��>|��s�;>��
�������������,&e��o{��g��I�|8�pz�?;����'sM�h4kg�V�t��*+@T2�Cw9�����>�C���x��(r�Z��s Y�_�����]��V*-��X/u��+�����>����`8����Vk�U+���$�n�K�}p9�EI���514�Rf6T�op�eZ������d)sRU�����E�f��I]�
�hh�������sDnV����8���w)��P�8z	V�a�X��n���Kb�g��sP	�7�gkQT�W�y��Q���%0WD�x������$�h��
7�*G�j\������Y�7���7+EMIqp�*�0�]�%P�7f��v1������4TDn_��XK7	�0�MJ���o�Z������[7�P+������Q��$�Uq���g;���q}tH�-V���0�"�
�{{�N���oVt�M+f�2�UnY�F��_�l��(����{v><8;���G��K�����[���0�/�J��,L�4i����F��6�|W)�k�(YDI)�.�Ne���%���c(W4������t��le��B�^N��C!e������mS���y���C����,�����
u�]�Y�ED���'!'^���"����p[u�A�%Q�p�����/Y��b��(��|0�'e��7<s��X�f>-,&�A��	�>�]g����f8��W��~Xj9wu2�W��h�L>�sfp��Qi
�]\f��t���*�c@&�b�t�}�X\�9�j����+�����'��J�����hn��r��qh�e$~���*?!*��;�mwD>��t8��#dJ���-\%��%&��B&��M`�>���TH�O�s�D�����M�]�K�vM�?E�R�4�2�����l��@=�����N�i�w9Q�;Qw&�jO]����q��9�%x��W]����`��S�B��o�i�N��!+W�8�$oq���Q������mV�B�
��?9�;�\F
�r�W�`����3����*�2,p���%�%J�S+���4:n����n;�����9�1�_5���N���hy�_{��p���S���v�'JDj�8����^��8��:n��)d����'	���!�=��%��y��'��R|�� ���n�R��EN��9j�~I���%��9��E�)RY����[��]�����Z�����g-N;\Y5&!LGX��l�jZ�]���T�������v�5���]����$��]H�p9�~w�{E�x%Y�W?�~z�����	���b-�;������%f���&�5:0�fC�y��H$����e���PI~����M����@s�#-mY��{�����5t����4[�X���U��V������u�wG������S��/�"4U�A�`�������{���f��;���w�,(-�\y�,�9���������RS���E�\�e83����������h�]���abE���M�Q��������&#|��	s.���j/k��pVC>�0���	,U�����7���|��J����]�I������	u!Q@1�����S�a�;X`^:`�?��,9�����2�+@f��-`��@%�g��c�?@y<��*j������a��+:�>S�~&�=I���IJm$�X6w`�
�l�����E�����s�^�����	.4����r�8R���.}��OeZ��nj�tP���B(��CKV�g�zQ|���E)��~��r{-W���Q�U\����;�A��#d�Y����Q�z���Zx�PN�^��z0�k������UtZ���������@�������|�<<�=����1
�~L�)��n���6�1^�2�M��[Xj;Q�����:%���}���}������:��wPz����z�k������������Y���\T�!!���i �J���+�� Ojp���O��L��]`y-TT!��&�W�~���1W=D�^�\����<��tc�;�6�&������O��.�n
!.v�p�:�v]3`���59#�d�%��N��r;
���wSJ�hr������?>�[���@~*`�2�$��R��c��&`#���1i�N�vx5*\keCx���v��7�+�nt�{�np��C�; ��9���L��i%���W��7jo���������d����mF#��1�@��{�����1�-�2�����e}�~�/�m�;���/x	��i��[#r��_dV*�R�o�{!y��79B[��.w�������1��c� 'N�
������%�1M����W{hr���-w���e��<Nk����Ce���2�z�>7\�\q;�J-�5`dR�ai9]:���.������}�Vj-���%��N�
\A��p�RP���1��y[LB��E(y������*���HmGS96p�T��3�h�"TK��$�F�^�����@�`��8? POD�2�H�K�FoyTOI��Y\z�� �k5n�����O
.���X�Y�k�1!���d����������5����>>�.�n����G��\�3�k0���'�_Sk6�Z���`!�9 ��|�$�\}&�j�'�	��8������F=�<saY�SQ�(|����{������N~���;���/��)�'��\��3��W�?f��K�Ru�2�Th6c���W�]i��jL�P�?rr�G#'�?�����'0����B��#�BKof
���RF���|o>�NlC�$��v�v�����j0�=q�6��o%��iFv��G�bj.�����,$�#����4��%r����d>�V�
f�<K)�U C'�����6��1R�`flM��q:G�*,t\L��K�OE����j���i���z����|BH�v~v��]����N�oA$�c�H�|���������Z��Nt'��<��P�B��V=�Y�5F�a)|M�)�A�(u����a(#[4�c�^
c�#K����bumV�V��%,k�:nKs�:����@C����(����u\u-:�q<ID�#�����qT�nX�L���9�U�m�RU��BE*�g�bAO�����6(-�W�����PP����	�
��
��zv�O�O��g������0���8�fT�`J%����Y�Q�:9��0rY���/��q+�J=Y�g0�f�u�)�u�.*��F���W�k���u<�i��siq��������6#���7c�gg��=����v��RX�q{��;b�$]1t)�����`���*=�/�Lo������Q���)���nx�m`	��##��D
�boUe~�I����E��
�i
o"4'�VB�Y��h��^!��'25���b.�>���i��&���^&��������sg���|��v��3{e����n���<�I�����=�p'��z���X���y��gCk�����'f��U3�},G�$#!����a)$�������%��q���i�fq����Yb�3<�f����~{:K~�#�*��F�k4 i�����cE�V���+�G���������	����S���^g#�=���T%g������U��	Y�����=�K3��<���^m-�q�
��i�|z[OY�5�Z�����U������<�N�w?iI�+��}����Y�C�8@�;d�b)wS�a2������
����>T���B�����j��%wBzq�A�
=�t�[��	�6�Z�j������M��~������������Z>��~�D|Yv���p�����5X����]+�
-�FTTV��_W\����z��n��������Z�Z��r'o�T�l����j���@{W�����	�H��W�������N[��u����O�<�����Bd����r��<>���l����2�t�~������w5I�Y�+qNg�nT+n����lo��t����A-�!C�����Uw�d�]Rb�������������'*�����q��t1"�?������ho����z�~��~���z�P������f��6cM��]P�a��m(5pfk5u��.�7+
���)���9�r�#��88H����
(p�,>��u�)R����n�gW4[�U�X����"���ta����`>
��yZ�)�;<����Oy����[/�:\
S{�1�Id8��DLwu!����<��X�(�\��&��V����	��'��������l�Wm��;D�V<��Na���V�[KoYK�#������hU�n��ad0��'I��dr�Co��AO��d��/'�/j�\�����:�[�l�Vz3��;X�9T�����!IV8�`����pj�;n�������{��h��k�TI6H5�\'�
o��Ch������LDivm������7v��7���s������/��\���8����$����!�o���{hlwjn�{��s�k���g�	�q���h7�n�������@����v����\P�4R�n,o���W'R92��L�G�\�]7�<�e�F��z����f���{aC�}k���il\T�evQ�
�l�"sZlQ�Ln��o�I����p�c3������cx�e��o�d�F5e�������h���ml�RS�:��UX�Y��8�8upS��+~$w�.��?�-9�J��i���X�]G|m�6�����q�2|jC�M�io�>U�4��
����*o�-T)T~����o��y�J��d��A��b�jZ�MU9�E:G���
R
e�TT�
��%a,��J�\3b���Et%EO}���]��T�����="�$�m��`�z��]��d?���]�I57%7B[ }���A%%�l�=G�J�Wc�����f�5�!X��V��Le]�K��.)X�p���Z��w+,+5�R�����Y�`Qu-Lup�/������<u����$K�
�ODn�}�?y{����'����T$��t/����	n��r�/Ni�����J�>a��-�d����
��o4M�s-p2l:U���)VtO�����Z=
�eh���-��
�=���4��C&����Go������D�|�����i.(�6:�8����|��0�m�4r������px�mS��	zm�����M�"���~������M�\�0��z4k�;�����b@�����MKXK
�����<6� ka���|�_@b���Q.#��4$�������;h�h���7�qd5�e#]I��u�]����������T�FP���B^�Yp*�b��������*Ws�u?����qi������S�@�Z��~�E��3F���^S�W����9��3��*2ny��wS�����T��-�B��������w3���vw��a����6k�B��y���7�n���OY�\F���+�jc��{�\�S�so�����L\���xX��[�xX�=L�Tmc�vl�/@.�{��T�]��}g�OA�<������3��h�����h�H]����H�������8q��y�����
�($�H�C���X�������}��w��dE�1�b">R���:�Y��}"I%c�{���.R�����<�97�l�efGn����j��1�v�]7=����L����v�1c+�R"������V��@W$b��G����y����i�������S!��h��-$H2�"7�nSK������$�_�?C��Yj�FL���@��IT��`:s�-�4���f�x�N�V���n�y_V��'���y�����haI��~�j�g�����)�����G/�&�uk���^t`��+�6z������mj�M����B�u��Y�]�pM�������e	.���c��������6fa���5U�h����!�^�FQp5�}��S�t;��\8�M��Yk��������p�7���pu/������l����x��*�0�}����n�N���p���[����9���Z5�#1^�^�X���A���dw��sL���>�����vj�����Lo>���3��
/��#V|��O��P�*D�Y�m	�2����PZ��sQ������������	�N�rEC�?Tx���T����{du�y�J���o�-1��s���g����'�Ad�M�k�A��d
&�m8��[��R���Y��{B*��{i���(����}��'�p���3�n;Nl\Wd��[�I�Ib�b��Q�Ro��0�?�D�@E3tG�32��(��FL����eY���0��[���W6�2�*u�Bk��u�V�"�V7U��R���
G�B��)�sM*a��L�i&������������+YQ0N�P��R���j�*��:8R������8�|Y!�����$S64���a����UWT������*�T�'�J��T��HU%��C"��jz��&���������*�{����[��Nu=���^�k���(.�f	���������@�C�H,'n
|G���i�#��o����_��5��=����w2��E~Z�,%��o]r��VNw2	GH��G��!$�td�5��"4��r	�5 E}=Tj`�M#�@����4�+y.G�6��(�-bq���8������#G�1 �
���q���#5}�XHR\�(v�@;m����S����������!�5�F��E�d�N��'o.�����%�F�Cbb��C!/x�Kq��L���E��-�Z�K�=�E+���v�a-��	/�����l�}q�jE".Rq�"����l1L�������Tn������t�E9�}�9G*�W(�G�@*u�Z�D�m�K�j]��wL����������7D���I�P�Q.%��7��<��������l�Q�����cNUb8
8���r�:��!d��j5���v`�;ux�?u{o����DAM�}�+5`r���j�9�1�:�n-98*U�SSE�i�4��>T�Z����\�C���7���nde]��B�����tY�E�y���+UAw�������.l����1`�G�:���
G��g9+�<=r��Ng����u�v��D�
�*���v��1�1@��7����s�QJ���{Z�^�V�\R�(�j$d
K?~��W�4+�3�����j��j��su���{�=.~��L��Z�����_��@���X�"�G�N���X\������us�=�7���j8�j��M��*� W:HK;���-��V���Z��'1~Z���9V	}��������a����?�	�#�������H��@��P����fus�j��uV������n�~J��Kl��&@������z�4�c=�����*�����������C|����j~�����p)��Z:�����$�U$�U��da�*��z��'�M�����lD����=~7�.`;o6�M����� �\1%V��#�t����j�[+R��p�j����
��1�<���U[�����M��q�GH��L�����tw;�Wy,���<�{����R�ZX�Z����z$9S���E��;��CtG���Ma������fd�����@n�FY� ��<�uPC1�����~]p�x4t?^�zgg�3��]NQ>����<��5f%�k��cy��F�������?�����O"�]c�9_�9$��:�
�k�����������J�^�{��Ti�AVk�Q�+U_ ]o�Uo��
�"t�����!j��%F?�n�e�����b�L�*�I��-�!)l�
�n��j����`D��U�s2���D��#E������U�7�P"��O��On�>����D�Rm+Z�;�����^��m!J�,pK������@��!�"���
��r���T�s���������&+~}������������T5
��a��o��b�~���8g�"/������X������K�V�������`��J���*R/��h��J�����)��1�Ko���tA��n(O�����o���8�Gz�����LY�$���������'Wh���DNCX�O�:��M�Z���tt�/�q���C�L'����l���!<��\������^:�=��c�q��DQ:%K�06v�,3�_��YH�^F$��j�8n��'-v���qS�n�m@wFF�{A��V��W�7
�����b9h,�7�H7����W�-�NT��|�K9��TH��.��Y�$�*�X���#���82�,���)�[`0�e������N�(~��6��@�v���"���,��ju���-�T�(���p�a���8G���'s��S��_j�s���(��=�@$�B������V\�Wm�W��8��dr^q��L"gKef5�"�nT�wl������@��H�k
�d��f��G<��pC���-Z�B7f�8N9	tS�/g�(�8e$����)�`��p�AM&�glU\��@i��P��	��M�}$���R5���T��h�@���:P���F^���n�6@\���[����_��3�����+����g��5�i,�-\�����`�����k�:����m����P���|_C���������Yf!R$���A�Z�`,(��9����O�1���N��$���_�C��[k���3N�hQ��F���ZG��Q�����#p5���F�D�>Ku�J_�K�F=���H�����{Xb�{����E�
����`�/P3�q~�
���d|"���|
�?��M8.��8�yxG�b�*0i��3`,�����h\b9c��h1�Cg�`���5�p������t,�Ex����
��G�1�b�@~�����Cc�=t�����I��1�cj������&��Xk���k����J������S���Q!	���(�������e��]+����y�����L-1'���D-9M�a6�R���E�{���k�G��&�ff������2�Q���aM<b5W��h��at2���q6�������wo��sG���|�M#�bKA��� :g�>���wcB�c�����y���E��"���L�??��#?�J{����XHg��Tt&bD�
(�w+c�����"����g�f��nN�'7�x����Q�<7�<�t2��2]��w����z�[$���n-�'N�1b�u��	�^@�����"�(#%b��s�^a���� �������*u��^��|P(iHmEz��'|mx;�f�<\N���C�����j�p�~���7������q"nYq�G*�/?�I�	��~�
W�sj'�h?���O��?����z{��e�����O�`�]���|A��U���������P^8�I����������di�����X���J�Z�	���/�oW�{��j���z��������<X����z��P��{�`���`9�����%��Dwp�e���:�5HxX��7����IS�[ow����������H�c�zag����c0_,��
���T%��a��A��!g���#��MhCp���s�?��}����>����l�XC��4�k�:+�`O��v+��|������ZQ'9�}���_Nw�l�a]t^�>ZgvP!���EV����-�����<��+������O��_82?��XC�MH@:���]�Flp^U����YR��{���X������TR�����(��%7=x8��{�zvs������B����>�RR�1��
V*����������O���A���u��H��� ����W�;9��Q�e��ZSEW�n�L���R����1�u�7� v�c�[�^v�������Z�;�Q�B�-�RJ�i,�\�����Q@��H������h�v�sN����!%�"{"�J�mTk����������Z�I��2�b���u�"����� "�-��0�udu���;0���?�}Q�UN�$��������o���_�yZ�R�a�u�.���Z��-�1��c�,N�1f�s�Tx�6M��6��f�9�Y��r����@v)H�7���?j7�V�{��H���	�cE�C�����j���F��6��dT=��Q���6X\����$y��;M��x���1w���]+�VZ"B�<z�*k�Of/V�ncgh����Cl�'���(R����.������!��B��N���
k�K�}�X7�x8x�}[{e|���3x��-�������Vj�A�yx���Q���$����^���N�����I��t��g�}p���64n�TO���KG��p���
���tV��P%��2�����W����*�8&���"�����^��5gh�0f=�62��HdUR�n-�"?%����s���9c����	�=���#J�������kj�> ��}$�<e�|�ap��/q4n��vy�M���K�|$�~Ip����ne����D�B!�3������d���<��qB�X���c��N����c��p���Gw�?H@�g{�����~��!&�,��5�
���-K�	�q�$�����*~���?��#�+}bZ_����^waE���y?�f2��0I��B�������($j}<��f��mH��Q_�GMR5��)�j3�rE^r�&���ny�7�*W]�F#�����D[2�vH
]�����/.d�J��(��1�;��J�`��=�Y��E|����v�z977����D���;���+����A�+[ �*�����8
��9kt�m^y
'�X{s'��N���������4#�����O���v���jI��(�'.�C�2N�vS�fG@��kP7��[��E�P&_E��6�PE����!�>�Go�,�Q8�)q�a�
�c�����%���=yz�j�c�+>-N,H����>�� G�VYD�^��gz����%NQ2jp�?����S.�.Z� �/�����`�-#?i[I�v���c5h�k�&D�C���T�zZm�u�"���w��/���C���]�i�0&SC@�%
44cR:���0�h�Y�?x}78cUZ�'�6Xz���/}�A����Y����q�������1��A::9���X���-Q�j����Y������qE�V��O��+��L���Y�?��?��P���V�p�2�LEP�e*�����e�@]3��9�.�E�)A��C��<�r��=+���=S	�;M�� <%�����%e��jFLwvR�:��c�+�@��=���oY�#���6~�6��@O���?�r��XV�|�������?��L��s��1����N��n���	&w���'��L��-T<K�R�F����BZ�JA�x���sa�����}�����{.'�=='���/�}���]���K=D������X��I�
e��#M�"��n|�~�K�h��������U?�(n�'�Q �r�8��Q�Hl�ZKz���:P{�O*�[1�Vc��{S��%��!��5����(��7V����������N��Da}Q8���1Yk\l�?������gB�I3��I!���3b���u�jt	��5����g���MH�hp(R���[G����?�~�����}���;�m�<���?�M������_��O������o�o������x���?5���������������tr�)�*����p�!�(0o����n��(m��C�������	_��C8+l$���I9��btN�@z�}��Ho��r��i�������{�����{�����u�9���
O��N�Oi��{@�`������`�{?�O���<>�zH[���s����0������X9����y��������\������s<�pv���o��}8�������2)Y���G:9���D����C����w�U���)��� q�z��qM��q�
�����5tq���u���K����u��6����,n��E�zG���x��^��}#U�p�f��������V1�|�EN��'�W~�n��M.����d��������3�ll����T�Z�1��
[�*�\s���b��|b���w�nQ{��^��7UO%��~�1^���;n�QO/��p[���<��j��K��Q��[�7M.JgC��V-�^m�]��,����W�Zn���l����A�?;;��<�
���8�k�R$i*������r��G��P}�r�����g��{�:�,�E���O�qKv6�����;�c���z��zT�Y������P��XV��P����J�9��K�xA:s�Lx��_Hf_4�R��/:��_��&z��8��)r�����Y~���e�/l�5������K�u�������J�lw�9�?��|v��_��.����Gg��!7z��!i�����#&b�y"��������H��`�0�����m����}�r���j�O�����k��`�Y�*e�t�� ���p�an��
l~�����%�	���C�q�����W���'�m�����PZ��i-����o�<�'r9��`$�yh����{���0���]�g��h�k��U(��U���Q���=�����}��u���=�~8!���S������U�{�A������5^e��������������8��������8�����ld�L�y
�N�Y����ptr����&�������w����l���w9�����������Y��e��^����������|g���C[W�2��lBz������������lRd��#:?������[��������3�lTd@C��h�12�a=�xe����E�D/~��1���_�wY��V|��3�w�����|�fuGzs�����?:�9on!�I���"������$&����T����q�$zf\4Zj$[/���2��������n�t���&���89�7�S��;kO�'zf�a�e�m���H#���u�u�xgG6�e�D�U��m,�.�.�.�
��
�f����Az��{ NO���7�.�%��d��x�;�x��F����N�[��2�dp]�.���>���k,������-�V/l���^�:����u��K��Mv��Z�n����.�e����^�'_��=���l�>5X����>���2�e0��O
�w���!���d�����/j���K��6;�]�F��[:�(���v�wt��u+l�Kc�g�,�(�w������u������
|+h����`ek�`e�)Y�e02L����Wi���r���"&^Yh�x�h�I�vm�iRD`�i��x�h{��&�g�	-g��#4�V��s�'I���13��h��ki�J(��cCq<�o�O�������������9�-e�6C�j����R�3����j�E���n&z�u�r�������+JO`�|;	����HM����v48|��o����w2�/c�6��f���Gu����������������h�c� ��gRz.F���C����K���RK'e�T���MWA:&��EW*�&Oc��-�+����r2�N��N�t�]��*[�sz|t���k���O����7;<X��bY�������c��(��\6��#�*���Ys�?m�����&O�����C�!�������W���GY�mVZ��������d���(��x�<��9%��^�����E� ��f�r�_Qa�}����5���0X����!t�?�9�u�C���K�������������su=���T�A��O<�����=rr���
�Ho�����ZJ��?9�]����E��f��|"r�%��6[�y.fi���u��g�E������y��0 �)39/�����8��s���
�����`�x[�9����[��7��8�"g�����M=E+�/,����+"���V���������y��KF��[�5u����0���*@:�
\�H�h����Y��+�o�wd2Z^D��	C��`��"C��[,����]��Y��`~��+���B�����[o7
�9���n����1]vE�{�V��Yj�|3���d)\!��_�YzM��mX�E�!"��[�L�[�-�vf�b.B*e��*��52�Zv��6�Qk��+U��0"*�:����71q����4���~���	WdA��%X�[���Q%�~#����O4�'�pE�x�A��U,Cd>�z��������H�������A9�'�s0V��
�FZ3+6�]Q���y���#�=�;^�����!��X�a6Y^�������{��OQ�����m>^�8\#����2!�"_��G�������WB�����7�n�!��8�G������N�|���#^�Q����HM#j�HG�\q�X�]��!LV��O���:�/������v#*�n\��&�
����A���U���x����%#�w^g���K��~�V��V��d���`2�����m�~%B��/T��J���PL�R��p���TR"���J����my�}��~3X�x���-�S�wZ���m8o����R������F���c3N������h�e�����(�����y������M�SiR�s��u/T��i�q�F������6�SV8�c��t
����"��a@����Z��������?�2�T��D����3 �1X���EF�
K��&�H�� y
,�}Y�Q��I��d'��U��^�2��	�	LV@J�V����[�����j���Y���2y�i��(����J�v��3x&Y']e�����Q�+<T�d+�d+��������������xPb
� ��o�<��]J�8��W{)�`NG���R���Ay���xD��a�9��K�T����5��
)���_����S68��sc�1��R&!)���Lx������+��v��3��0"l��_^�_�]������ji��]T���
K6j�&w�7���M�����&��b�,&��c�ei��x�j:���aBi��P��;n�Z7��;�~�7�������b�����1��"��b;S�5y����������[��_��:�8/�Q~�Y���&<�$�eH��`�4.������u�P��i&�s�/W�w^�]Th�T��v�F�������)����7�\��Da;����J���pO����@���JZVK*���������M����KHq�! �{wv����4�jT��J��*k�5������K��?�8Y�T]2N%cV[�	�+�p����"�+}"�'W&�f�F%�p����-�YX\�`���U�hUQ��P�/���\]���r)����^6Ei+QhU�F��Yo5Zn�iz�!L��/�������
���S���hN):J{����^�[mR�����.j���;�*��nce2�O�rhIg@�;-�S6�j
��0]��e.
�PK����E�.l����u���%���;�T�!t���W��t[����T�n�V����k%�������;�1-$Q�C7JbJ�(�\�&*,^IE35KZjX�&��"�(�}VRE�8�,&��g�u5e
�'�J�(�����l���H{�=8��M�a�Qs;F��o5����I�g�J
��x����3�o!'
~�����A�����7h[\E$[%l�^��	�gJ�0�|���;��t�\=�=I)��Y�NXI-V�v�9zd��y�`�u�R��k�syg��;����������}8R�.96��6�a�)H�q+uL��:>�H���	�~�\������y���xq��5�c�;��\�4N���r2Y5�x?����L#E�3!�M�@L��P�qk�jw<��os�;��E7��<��?�;���cNA�a��NFJ��/��Q+��#/���������b+X�R��z2g#5o8�0�E���IxU�����\gw9��^M�b>��V81������xW��v���+M��z�s�:����Ns��<��.4��?�������t��{�S���=����]��mQ/l��?Cy��/���b��g��8��d}���d$}X�3T��kMc���^]��+���Bn�edy��3w����q����j�T�7 !&q�|d��x:\�������c��3������^s"�h�I��hKp��M&1�P�mAb'��h2�5x[�){6���3�d#���1��l4#���i�<SL�@	l>e�\MlE�o�p	>�R����j�S��v�fAQu#I����"<�#R�5���4y�i<e^k:�G�����P������z��=�.�/oN��<r���[��y��G�q�d��k@m��/)m��f�M�J����ZsMN���%�G@y�K�� ��2[��FJ�N�������^���zC���Z���)���F��J�
�F�����QM��?��|���\�h{,�m����8��s
�=G��\��[
��pv�i�g�S�����wHG�~o�=4���8���$j}f��e0g��m������m�o��N�V#��c�Yh�M��g�����j���hU�����i�PcVZ{f�?�/�@�{���;��*21���v���,��}����1T��u!kmW�\�������R�����
�xM�*�����z���Ju*f�����JX��0�����[��5�"����L~�G��7n=��������g��&mX�Q�,r>O&��bl7<��������� 7-#���L`O�&i�\��<S�K{y �3Z�YD�F�����j�V�[�$�Wwv45���0%�[��k�����J��`��c�����E��<#`	c���T���tv;���99q?N�0��	�^zW�\`\V;�!������������7�2�������N�_Q���59�/BN����`��`��tO�e���(>Z���?�ng>��J*�������P���#$��8���c!�}TR��w�&��[���;km<��N����=$������4
c���a���Y�Mh}���-#����s,��I��\z��p���	��PFp�sk��Z����\J��2��t�2�4�E��ADJ�����	��Ku	��F*������YX�4����b�&��8;{bB�a{;*�K!pU���[�Vu�?��d��
n|s���s����nw������% V^�U���i��z����t�r�t�@*����!�/���s/�E��=;9:y���7�s�U|��w�
d���<
��T��7�N�F�w�3��5�u�3>��'���:3a��1�P$+5(�KG8
��8v�Vg�
�z��5���ry����{�lUs[5=xe���<����bzx���(��F~�E����F�,�_k�\_=�J�^�t�6�0k�x�Q��������U�q��8�����x!FE�����J�����?����?�i�@f2����y_�3��K�������tv6�����jn����sT���0�{���|����=��	�bO��qU�r�J��/�[Y�S�1r�2�������0> /7���A���2�)�������/gw�y���O�vv���q_��q,��#
����_;���G��dEU�j�#wG�s���g���Q��r?g
��)
������p����M�����t���g=���9�W����r�$�Gy���I�rj�������ih�����x�+��=�Z
o��7�WYhW�T�+3��s�������5�}u.��o���!�N?�����Ua<�4N�H)�c�3k.J��^]%��#��]]e����D_���/d��i���$��E�$H�HN�	\�w�"�
�],0�E
x�Xp,��S��k.�G���.(�X����_]�g���w"�k��lt2R�'b�rUo -�������x�5�����G�����U����&��+�
��5Q)�8�p@���z���[^^�H?7��o��M�y��ws?������l�v�z�}gHK��u�^sh��.�3v~�\��p�f�����}}�X�"-��{��l-gJ@n0E�/\������Lx.�#���]"�D�v��5����M,���8�~�t��rY��E�u��]}�8���1q�h_A��v�fH����W�E"G��ad�����1��3r�o���H#g��9���B
�����Q�g� H}�3�vW����w�IC=�r�~�P�cT���"�w��wg������1���A���8���C��	d�v���$�ye�p�7�g��p��^&�����i>����]�UcW��'l�4�[����o���	ai�o����X��z���I\&g!���Z��
����8����8���z�4��_�_����}H��8��j_$��<�k��-B��t�7 ���������������V��
��tJ�@������3>#�
�-���������hs�!>}������z��}������^H���km�L�t������A�+��.E�U!Nh7^"���(EG,n0Cp�E��d�3e�q&����jH�����:�J���������'8�_�K0�:_�r��*��V��N�����U�����8�����t�	V�������j\���T�N���'9��� �f�����iM�]kZ��j�f�M��[��N�������`�Lq��}����Dn���d�AIE�#��T��Bu��j]�.B��=�����8j�T/E�q#%����������9�"�yW�>���+�@����'ik�7�U	=$����}��o��0�Ct���t�u��g��=X������*&�d����f����1$����3V����n��.)�������`H�6�c%V:���>^���z��z`f���������g��s����UB���L�
�U8C�|��I bCAxz��b��)c�|8&�I~�IO�u�f�
����������G����������!k�X��e�g�
��AI���Zb1�6�4���^�/���{9�Q��]	]'���GW(0�W(0�3��m�R~CHQ�����l��������������+}w��N�[l�3z�\���K�*(-��f	L�pl4n�8���s����o����V�F��I�k��"����7���$��)���
�����n�5�i`�$�CDWN�'���s���?�4�����|�iq�H0/oL�O�pA�J�K�d�^�������[d��[>�2�s��'t���'�p�IU����6��������>#�H��vM�`����Ad0��2Or��4��cd��6Z�( F9��P��L�����J1�`a�v����}t��#�&�Y�/��Zm:�GU-Y��J���+4�	�2`M�"t�nx�����{Q|+��
�p�Xe����;�q��/�*�3
'��������_~u�A�TZ�p���)>L�#��RI�":%���[E���o��Z�@�����������������mu����z�FL�����vV-�Z�\s����Z���<$�o�6�ag���r�����+u�#�0k�<�����H�+L��K>��*`��\�4a�����`|��0�c���,�vc����`5����Ii_K�������9L��D�S��d}^+�����$Ki=j#tn{�J�D��
���(k9?�@N�����+1�4Y5_Z��f
����(��,�\������t�\�)��F���0�^T����|�b5��!�����Jw���'���2�?bkr�����r>p��uj��/ZdO�8<��=��k���_�����R��_������3�����"�3���X��*2V
7���J���i~8�����I�ly�-�Hn��io�9�z��������gIM����
�_8�o{����r�V5a���dU:xR�d�S���$�s�mzK�>
�i�&JG&��l����3�q�E�bS�y�/��
��-�,o�r��8S��e�](�Q"=���kM��,����E�?v��cW��B�	�����V}���D�B�)�����y�}�����/����?n��l������d�#�=z�C�����z�,%�����,�2��_d��Gj�q["92C)=c�ie�#M�8Hi���x���X��z�B�u
=@2B���4g����f�>TIv�q�4������������D�b|�:���:�"O��z����fS�j�,JG����?sk"o��*���ns;�=��#;�\�>k����X����r-4�����z�����O���)p-8Q�Z�	Z5��]�^���X�h������m�!"cm!�L��?3��Y�
�9��2�#��[���$���D��IT�_�	%l��h�H4����~�-w�{��U�����:��x�p:��n:f�pl�cyEWK?�/��^�8��9���O�u1*�nG���)2Ky�]��'3;_"���5;nU��/��a@�r�>��2gy}��2�{�J�K!���m�T9�?�1�e��W B��=����~t��)�o������J�s�"���T2gx����i�n��_yx����N��Q.~y�1�"f��!Iv�0#dj��$	��)�p��b�� set�W0���
�3�%;�Sh�8�f�o{`���L�������0�x{���'����M�����k4�N�����E���;?zg����S�!Ep�=��`B�#������:���l���y�d�
����Wa�-c�F�TcVx������T��08w�����������HB��[.�e;�]�X��;�_Nq(0s��X?1��E��W�N�B�|��j��#�������x)*�;�$����#oZ:q���3����u%�'�PuH`��	��:���N��`����,�H��'r����9�TK��k	3����!eIQ
v������/&��1������"�vx�;
Xw����)�^�d�|8==���N
��f�>��w����`���vZ[j�*�����4�T�����D8,�M�E�}�9\����9��B%�65�:���8��X*���A7g���0++t[M[��UkT�Z������������>��%�"�.�@CV<0R�N�U��(��-�%.��G���b�W}�V�j������76g3���n��/��	�7���*�g��j�fKNI�7�Yi�,����JU#�0�'����P(WS�o��;��^1��l��!�#�����R���xiB�YV�Q�L�]����b�3vY�rD`�D31��X�I��jU��L+�L�J:�=���v��N5B,rcO�����S�P��������?����$��d�aFTv�2X$��T^\96$S���T(��M.��wh�g�+������S��j�/�����
�x�q�}
��I�{��������?w�s>�B�U>f�Xa�����nK0����z[��l
�	��8)�(�#��vK�x����cR�=9���m���qd��C_�����P���������|z��(��S����u�\���4������y7	/`�CLG����y7��UC�]`���r��I��;|r<#p`���f���&��I��7��������H8�8j)�a�h?r������7\��J{q`�����+�s�8�/��:uL���C��O�<�7wv�k6�����l���TX�P�Ix����5kh��������-Kqg7y����;�]|��jH�������H :���z�V�}v��_�5]�BA�]�����C �,��~u>���LgA����W�4(�J:&�qP���������<�e`�����&��
��D,���
z�
5��0a
�s`mB�:�3,{�?(����D�krH�3��2���j�Hd�/��*=����@
�l7�"I$n��'��.�'�JCk��.3���������0fueC�PhX[��

�+
�����sPB��s{��a����#o�g��;�T���%������B<,; ���U~�\f��
0��s�E�������J�*�K�:_,CM�y���O/����w�g_CS��	��#~��$���V��?�9�ryD��o8��'��q2S��i�\J�q=�^y?M3�z`�^|�"�!Q�����0y,�1=Oml�L�>���m3T�U�7��o���IR&H������D��g��j�R�"	Mgh�Ex��|��V���t�i��|.��5c�z��S25eg�M�)�������$g@O�Akl�Ar���E�Z����_�W��!�')�IV�"��]��V��(�?��R��2*���3��L9��^�����������~��$P4Q��������G����?#&\O��������^t���14��k��?�L� 3������"%���=n�����e$,�H�*�za�y'�q��8���CiM
|N�%�tvB��)�~B�.H
R;�9��7Q�Qi����y���@����p_dt���w'G0N�*e�5E��]�W������f����r$�Q	F�IH6��P�N��O��}��0���_��5�F�~�����P��K9�T�`��g��x.�z����i�4�S�P�My��()�T���K>P�����	2�w?��g����tG�����!�UNr
������8����Q�h���X������/\��w� �9��3Zy��F�TnE���g'y��?�Y�!G�i.��,~��]��&�����n=�e���������0���GR*��E�/��
t�?�w
��&���d����?��M0�b�g4.>M��J���h(&D�����GKbR�����s��?�����
lH����S|T|�'N����ZA���B�y6a�}��8�_���6�N�j[����P�&����������`Kj��1W.�X|yv!�.<�i&�.�g��vC��2��m��TXe:f�;u�q�$����K�_��a=�������+��`��<��a��O�<`�b�'�'U���KWD��d1��M���I��e0��,_b�|Q������O������G�|�X%,K�z��#�|��
;zj9�c�
���=�r�`t�"T��.�=B-ml����8QE7��%��N�2���d������Z�(a�2����q�����)ew�:���l���RR�@���'8�������y�Sdv����XvFX]V�����L������f[����J��J|��b���E�F���Hg�"bY�0����d
��\�25��"4�.4���9�d��t�&�,����Z?�n���3���/Y�n)��f�h���
Ci�1�k��c
S1+�Y���������je���$c���V���n����i��-���I�8������VHe��V���
��.�I5<Ll`�d�7���7r��1&Q����s�(��v-��N�3	P���1H��o���Z�=��h�5��9��;SI)����'t��
���*�|C��l�"[����E��{aV�1���M�,���Vj��,	vXk~����s��~+B`m��mP�H��2���R�9�.z1H;j��RM�S�i���Z=/N8e��(�c���H������{g�4�lk��/d��!Eb�1�!�&�oJ�Vh��r���B��O��v��v5���f��}2_x�?�����?�@��S����A�/�Dw ~���e���)��xZ���TQH�i���q�s�X��1~��Q6;��R��J�G; ��<�����`�wj�������6��{o�z�
VY{�%�hF<g�P�LF�l������[s��7����L,KK�"H#��)��|���-��aF�9�]��\Zm�x"��,��l�����NFUeK{+V��C=\�&=E3���c���c����l�G��80����^�e��.�.���BR��=((^��K!k��+kz���i�]���������v��i)GP�U�$\G�9PD���������C�#����k����_���]��s�L���K8t.L������!�����hr�"R�1�b�P�oT��!J���;Vp�^�(����K���u�a�}cv�7	#q{WW�����V*���<��^u��C�j��@PFX�>��m�
����\�'6�1qwJ���������J%�����j����['[�Q[����z���|�T��T�s,v����q����<GjO
�9?S��wX^�[�X�X�(���<&�G���r�(��^=za����!~�u�S�O>��`��(�� R�WJ�i6�N'���?��*����:�����B<����s����f���rr����g��e�'u�?�~�n��_��k�{�0�mt'���n~��m��VW��V����&���s�z��~��Y�Z����wL�63[�(��������������-Y�`� ���������&yb��8�3Am`mv���8y�bb��y��bx���nR�m��J���-vL}��9:�D�[c,�w�`�2{�
ZD�7]�s��ST\�7r�It^ax��������8?�����/r��P_[G	�C����w�/5�Z�9�x5�q�����m|6�$�B�j����>��7�2��|���
�o��3���:�����y�C�0��X������(���y`<���lK��c�XF���>��mW�������(a.v�@�d
b0�y�"x���>��F�g1#�K(�*��������+�D��x_��O����
�?�GbL�P'���W"l��F�'������5t�O��]�6b��@���$��k0���ufz
�~�Z��.���I��u�kS�|
{<�=�%S�������m���
�������gZgY���vZ��r���"�t����*�����b���J��[��`'�,A���9��U�L�p��vF�Y��n�����\�a�-���H���O��l2�Y��T���43��z��*�,���r�!����^�t2X�G��:>��
�A�^'}=�����u]�>��F�������]3���m�r�H�*~G���F���5��PJ��gDo�Z�g����dZ���w�={,5�
^]/���3�>`��z�mX2���t�����e]%��^eN�\�K���qN��-%����f6�������t1�/��q!�l�Wx����qaw1���,�����>��t�FN&��J�����t�u�FV����@��u������6L�H����GrV�zg9�H��l+eWr�����T+�� ����0���v-�&������0Z���%cU>�����QV�� �����gFN�rC�SL�
���^v/�kW*M�z5�a��A���}��g�
n��U�0�('s�`��T�aA���~8>�4������;;/��z�d�$�;�x�T3�fK��������<�\���.oD�se{����,\��_v���(D\/��8
����K���VJ
SEa�j�&����<	��u�5{�m�TN����wk��y��pq��L��L�M^������i�W���GQ�4k����dG�20�s;k#��-��|�_{��p�\�?2m@b��w��O J��]M�hc�)lJ:+����tr�!6����-����#+�)|����{���.{�k))2���{7�������s]�s�ofcV���,�,���5m��?������RU�HBv���V#���R�*(T$)�M������d�������^��.��j�������v���"�Q/13�����~�kn
��=�>��E��x�W������h�X����<�����9��n�R�Q��"������N���
2��T����'��S+u���):����)��T�#V���q����C���������iM�R)��Js�Jj�
��h+�Q$�\���T�4�����G,���#�w����p�%����g��O-�;�������*T[���A �'
2fffW3_��Tmu�#��Z�x��z����5l12��:������A�:D}LL>�y0�7�Zc%L�U�r�g	���oS'E>R 5����Fh��?q9c���x���<������f���P��
�L�����UV��	��h`\��'g���� ��J�-�'S`���+TraA���������v�=����sN����S�%��LB�����F#�B=�+R�w���r��i����;8�K���������>�C�~�#��r� �],� "*�1��-��50���)�X����s������;�xb>9��u�N��8k��U���VZ��5G�I�Eu�Z:4kqJ9������F�_�������Jk�3�5���~3����PU��c�U���D��I|eT����^j��AcK�����	�e�q���`��0c�ul�Af�������B�FjM�S�:����V����~[z�}�Y�&6�������l����n]�|d�������x�����}�����k-u��Wxk8�g��������s�=y�����?�_�~�
�����d%��t���4�t�S�N(FG��}AZAKF������k�pGS���Fb�� rn�;�!��:-Xg���j��Se����Sox$l���ay���CE!���O�R
��������~!�O#~��F|�r�lw�����%&�_��K�0_��n����"-���+�w�����[?%��V�w?��utp�}��;��T�e���Xs��>�!Vwo�K��$W�'>;;�s �xDH������H0)�y�_}����%��v���-����������vG�7�����y�{������I�}O6��o�-y��1�03
�<��;��w�`�(�:!#�|8)����l��K/�~S�V�}��Kt��S�S���Oe|)�s�v,�����bGV�M���z?���{o�Nz�)!�f�^a���/Yk���x�C��3G��cH�t�>���"E���	vu�@�R����4Z��8�oh� �2���)#��5�U�&�W��>K:����1���O�8Y��jZ��^��x=�m.;������;Wst[���e��5d����bum�j�&����$9��|����V`�9�h,FT���u���k�\b�o���w?`�,��������3���eLG1�'p�/�+��MfW�oh�E�_��^�W�;��F����p��T�-���O����s.'c����9C�F�{A���%�%����v�����+���e��j\mT�8q��/y4Y���0���?����>k���g�Kio��\����
G��* �>`�/P�G��#���za��o`�3�?^������<��]�����'�I,w�L�Sc��.���.c2��3~������n�}�����KV,_�d�_� �n�Z^���c��s	/EC$|�(�%gOV1uT<�:�1�O|x�??:��vy�Yc����������*��������k~��:��	g��% ����a��o~�|���
�������NF�[,�)�s������.����`�DZ�
�s����x��^����wr��R��3���v3wtr����;���9=;z�=����������y8�����wW.��"6�#N������x�O����@c`����7�B����D�>![���P�E	&|���G���=��+��]4�J����cIA�������k��q� R�s�-t5�H���s$�����0�e������B��c������?�������A�"������������Z�x�3�����`��w1�*�iQ�;z���/9x��T������`�	��HHm���L{J@Z�?�t��/:�Fi-�����K����W���'�	^w���36Cu#������w�����vE ��
�k�z�n:�~�����cH�����	�;�2���"��U�N&���b�����4$l,��G��Ry�Z4{�|	t����P��������R�z�&�:����X��B)���i��{�?[w��7����p)���fjX�&�]�����k�f�`�V���A�n7�&�^<�\�o�;��ES���nB~%�\wF����fB���S���������GH\�(���7-o��9B��z�����e0�&�~����gN�#|���������AW�����-t���Xy���������e�������v?�H=�nt�c@�#��r�^{J+z%
j���:�vJ����q���|���E����o�=!kt4�6�GbM}�N�~l[�:��q9��#/�V�����~E������f�$�c�T5tp�U�V/���P������Z�6����]�c/����
�rZ8���
�u�u�.����'�{$`��Y����g�by5��b�<LQ*�S������u���&a��_<����:N:��D}�&K�yqN���/>�yz��p�!x-��,�M�#��������z��������_�?���������U>������e����xl.�h��>��N���m�P������d5B�	��wZ��}����4��|�J�uF��%<����$�w&3�8/�8�f��u�r�_��Sn���+�e���I5������Z����/r'���u���u~��
��*���������fa(�"��kc�
@p��<X'�V��������u�>��9�E���UC�UY�)k�����p
Kz�V����W��Gs���b)��7�Wb�� ��IB��">����pq��J1xW���G��������Rf�������������5_�-�>����}���'�w��Q�7���v)i�L�{i���C�u���$�5���"�|i~Lku�����Vl2O���UX��:�yk�����G�����B<���/��]
�b������l7X�Fl7���ul��hd�pQ<�|�Y�A����
�-���Fp7$eE��DLu��"vjnG����{��JR��
�����MUJ-�����`��������/��E�kL�d{��_�8J{ouSe����^�tW��� �f������c,�����b�\�%��t��4[����b�7Ou����R�QCY.���W��o�<�7k�^c0����\�2Z�|���a�D�r�I"8������L����-&�(q\���x9��P��
'�U�)��`��������
�6������Y+�3C��8�QW�SU�HN��p|���0��(��61B���me(sE���3���C���h����?8�������5gE6���^�[�H�B�K���8���t
�d��<j�I���u#Vz�?�����7s.�����N7��,�������e�D�����������#�G`]��g��>�eqScZA{P1{�
���������9�Ss���x+���N�����+C����Y���4�n�i*��r����;#T4��)��.�I�9"�j�A��J$CAm�}��@����������/���z�Y����4!j�
�� �����w,
#2Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#1)
2 attachment(s)
Re: Command Triggers, patch v11

On 24 February 2012 22:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Please find attached the latest version of the command triggers patch,
in context diff format, with support for 79 commands and documentation
about why only those, and with some limitations explained.

I also cleaned up the node function support business that was still in
there from the command rewriting stuff that we dropped, and did a merge
from tonight's master branch (one of a few clean merges).

This is now the whole of it, and I continue being available to make any
necessary change, although I expect only minor changes now.  Thanks to
all reviewers and participants into the previous threads, you all have
allowed me to reach the current point by your precious advice, comments
and interest.

The patch implements :

 - BEFORE/AFTER ANY command triggers
 - BEFORE/AFTER command triggers for 79 documented commands
 - regression tests exercised by the serial schedule only
 - documentation updates with examples

That means you need to `make installcheck` here. Installing the tests in
the parallel schedule does not lead to consistent output as installing a
command trigger will impact any other test using that command, and the
output becomes subject to the exact ordering of the concurrent tests.

The only way for a BEFORE command triggers to change the command's
behaviour is by raising an exception that aborts the whole transaction.

Command triggers are called with the following arguments:

 - the “event” (similar to TG_WHEN, either 'BEFORE' or 'AFTER')
 - the command tag (the real one even when an ANY trigger is called)
 - the object Id if available (e.g. NULL for a CREATE statement)
 - the schema name (can be NULL)
 - the object name (can be NULL)

When the trigger's procedure we're calling is written in C, then another
argument is passed next, which is the parse tree Node * pointer.

I've been talking with Marko Kreen about supporting ALTER TABLE and such
commands automatically in Londiste: given that patch, it requires
writing code in C that will rewrite the command string.  It so happens
that I already have worked on that code, so we intend on bringing
support for ALTER TABLE and other commands in Skytools 3 for 9.2.

I think the support code should be made into an extension that both
Skytools and Slony would be able to share. The extension code will be
able to adapt to major versions changes as they are released.  Bucardo
would certainly be interested too, we could NOTIFY it from such an
extension.  The design is yet to be done here, but it's clearly possible
to implement such a feature given the current patch.

The ANY trigger support is mainly there to allow people to stop any DDL
traffic on their databases, then allowing it explicitly with an ALTER
COMMAND TRIGGER ... SET DISABLE when appropriate only.  To that
end, the ANY command trigger is supporting more commands than you can
attach specific triggers too.

It's also possible to ENABLE a command trigger on the REPLICA only
thanks to the session_replication_role GUC.  Support for command
triggers on an Hot Standby node is not provided in this patch.

Hi Dimitri,

I just tried building the docs with your patch and it appears
doc/src/sgml/ref/allfiles.sgml hasn't been updated with the necessary
references for alterCommandTrigger, createCommandTrigger and
dropCommandTrigger.

Also in ref/alter_command_trigger.sgml, you define SQL-CREATETRIGGER.
Shouldn't this be SQL-CREATECOMMANDTRIGGER? And there also appears to
be orphaned text in the file too, such as "Forbids the execution of
any DDL command". And there's a stray </para> on line 299.

I attach updated versions of both of those files, which seems to fix
all these problems.

--
Thom

Attachments:

allfiles.sgmltext/sgml; charset=US-ASCII; name=allfiles.sgmlDownload
create_command_trigger.sgmltext/sgml; charset=US-ASCII; name=create_command_trigger.sgmlDownload
#3Thom Brown
thom@linux.com
In reply to: Thom Brown (#2)
1 attachment(s)
Re: Command Triggers, patch v11

On 24 February 2012 22:32, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Please find attached the latest version of the command triggers patch,
in context diff format, with support for 79 commands and documentation
about why only those, and with some limitations explained.

I also cleaned up the node function support business that was still in
there from the command rewriting stuff that we dropped, and did a merge
from tonight's master branch (one of a few clean merges).

This is now the whole of it, and I continue being available to make any
necessary change, although I expect only minor changes now.  Thanks to
all reviewers and participants into the previous threads, you all have
allowed me to reach the current point by your precious advice, comments
and interest.

The patch implements :

 - BEFORE/AFTER ANY command triggers
 - BEFORE/AFTER command triggers for 79 documented commands
 - regression tests exercised by the serial schedule only
 - documentation updates with examples

That means you need to `make installcheck` here. Installing the tests in
the parallel schedule does not lead to consistent output as installing a
command trigger will impact any other test using that command, and the
output becomes subject to the exact ordering of the concurrent tests.

The only way for a BEFORE command triggers to change the command's
behaviour is by raising an exception that aborts the whole transaction.

Command triggers are called with the following arguments:

 - the “event” (similar to TG_WHEN, either 'BEFORE' or 'AFTER')
 - the command tag (the real one even when an ANY trigger is called)
 - the object Id if available (e.g. NULL for a CREATE statement)
 - the schema name (can be NULL)
 - the object name (can be NULL)

When the trigger's procedure we're calling is written in C, then another
argument is passed next, which is the parse tree Node * pointer.

I've been talking with Marko Kreen about supporting ALTER TABLE and such
commands automatically in Londiste: given that patch, it requires
writing code in C that will rewrite the command string.  It so happens
that I already have worked on that code, so we intend on bringing
support for ALTER TABLE and other commands in Skytools 3 for 9.2.

I think the support code should be made into an extension that both
Skytools and Slony would be able to share. The extension code will be
able to adapt to major versions changes as they are released.  Bucardo
would certainly be interested too, we could NOTIFY it from such an
extension.  The design is yet to be done here, but it's clearly possible
to implement such a feature given the current patch.

The ANY trigger support is mainly there to allow people to stop any DDL
traffic on their databases, then allowing it explicitly with an ALTER
COMMAND TRIGGER ... SET DISABLE when appropriate only.  To that
end, the ANY command trigger is supporting more commands than you can
attach specific triggers too.

It's also possible to ENABLE a command trigger on the REPLICA only
thanks to the session_replication_role GUC.  Support for command
triggers on an Hot Standby node is not provided in this patch.

Hi Dimitri,

I just tried building the docs with your patch and it appears
doc/src/sgml/ref/allfiles.sgml hasn't been updated with the necessary
references for alterCommandTrigger, createCommandTrigger and
dropCommandTrigger.

Also in ref/alter_command_trigger.sgml, you define SQL-CREATETRIGGER.
Shouldn't this be SQL-CREATECOMMANDTRIGGER?  And there also appears to
be orphaned text in the file too, such as "Forbids the execution of
any DDL command".  And there's a stray </para> on line 299.

I attach updated versions of both of those files, which seems to fix
all these problems.

I've just noticed there's an issue with
doc/src/sgml/ref/alter_command_trigger.sgml too. It uses <indexterm
zone="sql-altertrigger"> which should be sql-altercommandtrigger. (as
attached)

--
Thom

Attachments:

alter_command_trigger.sgmltext/sgml; charset=US-ASCII; name=alter_command_trigger.sgmlDownload
#4Thom Brown
thom@linux.com
In reply to: Thom Brown (#3)
Re: Command Triggers, patch v11

On 24 February 2012 22:39, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:32, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Please find attached the latest version of the command triggers patch,
in context diff format, with support for 79 commands and documentation
about why only those, and with some limitations explained.

I also cleaned up the node function support business that was still in
there from the command rewriting stuff that we dropped, and did a merge
from tonight's master branch (one of a few clean merges).

This is now the whole of it, and I continue being available to make any
necessary change, although I expect only minor changes now.  Thanks to
all reviewers and participants into the previous threads, you all have
allowed me to reach the current point by your precious advice, comments
and interest.

The patch implements :

 - BEFORE/AFTER ANY command triggers
 - BEFORE/AFTER command triggers for 79 documented commands
 - regression tests exercised by the serial schedule only
 - documentation updates with examples

That means you need to `make installcheck` here. Installing the tests in
the parallel schedule does not lead to consistent output as installing a
command trigger will impact any other test using that command, and the
output becomes subject to the exact ordering of the concurrent tests.

The only way for a BEFORE command triggers to change the command's
behaviour is by raising an exception that aborts the whole transaction.

Command triggers are called with the following arguments:

 - the “event” (similar to TG_WHEN, either 'BEFORE' or 'AFTER')
 - the command tag (the real one even when an ANY trigger is called)
 - the object Id if available (e.g. NULL for a CREATE statement)
 - the schema name (can be NULL)
 - the object name (can be NULL)

When the trigger's procedure we're calling is written in C, then another
argument is passed next, which is the parse tree Node * pointer.

I've been talking with Marko Kreen about supporting ALTER TABLE and such
commands automatically in Londiste: given that patch, it requires
writing code in C that will rewrite the command string.  It so happens
that I already have worked on that code, so we intend on bringing
support for ALTER TABLE and other commands in Skytools 3 for 9.2.

I think the support code should be made into an extension that both
Skytools and Slony would be able to share. The extension code will be
able to adapt to major versions changes as they are released.  Bucardo
would certainly be interested too, we could NOTIFY it from such an
extension.  The design is yet to be done here, but it's clearly possible
to implement such a feature given the current patch.

The ANY trigger support is mainly there to allow people to stop any DDL
traffic on their databases, then allowing it explicitly with an ALTER
COMMAND TRIGGER ... SET DISABLE when appropriate only.  To that
end, the ANY command trigger is supporting more commands than you can
attach specific triggers too.

It's also possible to ENABLE a command trigger on the REPLICA only
thanks to the session_replication_role GUC.  Support for command
triggers on an Hot Standby node is not provided in this patch.

Hi Dimitri,

I just tried building the docs with your patch and it appears
doc/src/sgml/ref/allfiles.sgml hasn't been updated with the necessary
references for alterCommandTrigger, createCommandTrigger and
dropCommandTrigger.

Also in ref/alter_command_trigger.sgml, you define SQL-CREATETRIGGER.
Shouldn't this be SQL-CREATECOMMANDTRIGGER?  And there also appears to
be orphaned text in the file too, such as "Forbids the execution of
any DDL command".  And there's a stray </para> on line 299.

I attach updated versions of both of those files, which seems to fix
all these problems.

I've just noticed there's an issue with
doc/src/sgml/ref/alter_command_trigger.sgml too.  It uses <indexterm
zone="sql-altertrigger"> which should be sql-altercommandtrigger. (as
attached)

And upon trying to test the actual feature, it didn't work for me at
all. I thought I had applied the patch incorrectly, but I hadn't, it
was the documentation showing the wrong information. The CREATE
COMMAND TRIGGER page actually just says CREATE TRIGGER.... BEFORE
COMMAND <command>, which isn't the correct syntax.

Also the examples on the page are incorrect in the same regard. When
I tested it with the correction, I got an error saying that the
function used had to return void, but the example uses bool. Upon
also changing this, the example works as expected.

--
Thom

#5Thom Brown
thom@linux.com
In reply to: Thom Brown (#4)
Re: Command Triggers, patch v11

On 24 February 2012 23:01, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:39, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:32, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Please find attached the latest version of the command triggers patch,
in context diff format, with support for 79 commands and documentation
about why only those, and with some limitations explained.

I also cleaned up the node function support business that was still in
there from the command rewriting stuff that we dropped, and did a merge
from tonight's master branch (one of a few clean merges).

This is now the whole of it, and I continue being available to make any
necessary change, although I expect only minor changes now.  Thanks to
all reviewers and participants into the previous threads, you all have
allowed me to reach the current point by your precious advice, comments
and interest.

The patch implements :

 - BEFORE/AFTER ANY command triggers
 - BEFORE/AFTER command triggers for 79 documented commands
 - regression tests exercised by the serial schedule only
 - documentation updates with examples

That means you need to `make installcheck` here. Installing the tests in
the parallel schedule does not lead to consistent output as installing a
command trigger will impact any other test using that command, and the
output becomes subject to the exact ordering of the concurrent tests.

The only way for a BEFORE command triggers to change the command's
behaviour is by raising an exception that aborts the whole transaction.

Command triggers are called with the following arguments:

 - the “event” (similar to TG_WHEN, either 'BEFORE' or 'AFTER')
 - the command tag (the real one even when an ANY trigger is called)
 - the object Id if available (e.g. NULL for a CREATE statement)
 - the schema name (can be NULL)
 - the object name (can be NULL)

When the trigger's procedure we're calling is written in C, then another
argument is passed next, which is the parse tree Node * pointer.

I've been talking with Marko Kreen about supporting ALTER TABLE and such
commands automatically in Londiste: given that patch, it requires
writing code in C that will rewrite the command string.  It so happens
that I already have worked on that code, so we intend on bringing
support for ALTER TABLE and other commands in Skytools 3 for 9.2.

I think the support code should be made into an extension that both
Skytools and Slony would be able to share. The extension code will be
able to adapt to major versions changes as they are released.  Bucardo
would certainly be interested too, we could NOTIFY it from such an
extension.  The design is yet to be done here, but it's clearly possible
to implement such a feature given the current patch.

The ANY trigger support is mainly there to allow people to stop any DDL
traffic on their databases, then allowing it explicitly with an ALTER
COMMAND TRIGGER ... SET DISABLE when appropriate only.  To that
end, the ANY command trigger is supporting more commands than you can
attach specific triggers too.

It's also possible to ENABLE a command trigger on the REPLICA only
thanks to the session_replication_role GUC.  Support for command
triggers on an Hot Standby node is not provided in this patch.

Hi Dimitri,

I just tried building the docs with your patch and it appears
doc/src/sgml/ref/allfiles.sgml hasn't been updated with the necessary
references for alterCommandTrigger, createCommandTrigger and
dropCommandTrigger.

Also in ref/alter_command_trigger.sgml, you define SQL-CREATETRIGGER.
Shouldn't this be SQL-CREATECOMMANDTRIGGER?  And there also appears to
be orphaned text in the file too, such as "Forbids the execution of
any DDL command".  And there's a stray </para> on line 299.

I attach updated versions of both of those files, which seems to fix
all these problems.

I've just noticed there's an issue with
doc/src/sgml/ref/alter_command_trigger.sgml too.  It uses <indexterm
zone="sql-altertrigger"> which should be sql-altercommandtrigger. (as
attached)

And upon trying to test the actual feature, it didn't work for me at
all.  I thought I had applied the patch incorrectly, but I hadn't, it
was the documentation showing the wrong information.  The CREATE
COMMAND TRIGGER page actually just says CREATE TRIGGER.... BEFORE
COMMAND <command>, which isn't the correct syntax.

Also the examples on the page are incorrect in the same regard.  When
I tested it with the correction, I got an error saying that the
function used had to return void, but the example uses bool.  Upon
also changing this, the example works as expected.

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order? Also it appears to show
CREATE/ALTER/DROP TYPE_P, and the same for CONVERSION_P and DOMAIN_P.
I'm assuming these are typos? They also appear on DROP COMMAND
TRIGGER.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against. Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

--
Thom

#6Thom Brown
thom@linux.com
In reply to: Thom Brown (#5)
Re: Command Triggers, patch v11

On 24 February 2012 23:43, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 23:01, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:39, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:32, Thom Brown <thom@linux.com> wrote:

On 24 February 2012 22:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Please find attached the latest version of the command triggers patch,
in context diff format, with support for 79 commands and documentation
about why only those, and with some limitations explained.

I also cleaned up the node function support business that was still in
there from the command rewriting stuff that we dropped, and did a merge
from tonight's master branch (one of a few clean merges).

This is now the whole of it, and I continue being available to make any
necessary change, although I expect only minor changes now.  Thanks to
all reviewers and participants into the previous threads, you all have
allowed me to reach the current point by your precious advice, comments
and interest.

The patch implements :

 - BEFORE/AFTER ANY command triggers
 - BEFORE/AFTER command triggers for 79 documented commands
 - regression tests exercised by the serial schedule only
 - documentation updates with examples

That means you need to `make installcheck` here. Installing the tests in
the parallel schedule does not lead to consistent output as installing a
command trigger will impact any other test using that command, and the
output becomes subject to the exact ordering of the concurrent tests.

The only way for a BEFORE command triggers to change the command's
behaviour is by raising an exception that aborts the whole transaction.

Command triggers are called with the following arguments:

 - the “event” (similar to TG_WHEN, either 'BEFORE' or 'AFTER')
 - the command tag (the real one even when an ANY trigger is called)
 - the object Id if available (e.g. NULL for a CREATE statement)
 - the schema name (can be NULL)
 - the object name (can be NULL)

When the trigger's procedure we're calling is written in C, then another
argument is passed next, which is the parse tree Node * pointer.

I've been talking with Marko Kreen about supporting ALTER TABLE and such
commands automatically in Londiste: given that patch, it requires
writing code in C that will rewrite the command string.  It so happens
that I already have worked on that code, so we intend on bringing
support for ALTER TABLE and other commands in Skytools 3 for 9.2.

I think the support code should be made into an extension that both
Skytools and Slony would be able to share. The extension code will be
able to adapt to major versions changes as they are released.  Bucardo
would certainly be interested too, we could NOTIFY it from such an
extension.  The design is yet to be done here, but it's clearly possible
to implement such a feature given the current patch.

The ANY trigger support is mainly there to allow people to stop any DDL
traffic on their databases, then allowing it explicitly with an ALTER
COMMAND TRIGGER ... SET DISABLE when appropriate only.  To that
end, the ANY command trigger is supporting more commands than you can
attach specific triggers too.

It's also possible to ENABLE a command trigger on the REPLICA only
thanks to the session_replication_role GUC.  Support for command
triggers on an Hot Standby node is not provided in this patch.

Hi Dimitri,

I just tried building the docs with your patch and it appears
doc/src/sgml/ref/allfiles.sgml hasn't been updated with the necessary
references for alterCommandTrigger, createCommandTrigger and
dropCommandTrigger.

Also in ref/alter_command_trigger.sgml, you define SQL-CREATETRIGGER.
Shouldn't this be SQL-CREATECOMMANDTRIGGER?  And there also appears to
be orphaned text in the file too, such as "Forbids the execution of
any DDL command".  And there's a stray </para> on line 299.

I attach updated versions of both of those files, which seems to fix
all these problems.

I've just noticed there's an issue with
doc/src/sgml/ref/alter_command_trigger.sgml too.  It uses <indexterm
zone="sql-altertrigger"> which should be sql-altercommandtrigger. (as
attached)

And upon trying to test the actual feature, it didn't work for me at
all.  I thought I had applied the patch incorrectly, but I hadn't, it
was the documentation showing the wrong information.  The CREATE
COMMAND TRIGGER page actually just says CREATE TRIGGER.... BEFORE
COMMAND <command>, which isn't the correct syntax.

Also the examples on the page are incorrect in the same regard.  When
I tested it with the correction, I got an error saying that the
function used had to return void, but the example uses bool.  Upon
also changing this, the example works as expected.

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show
CREATE/ALTER/DROP TYPE_P, and the same for CONVERSION_P and DOMAIN_P.
I'm assuming these are typos?  They also appear on DROP COMMAND
TRIGGER.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

I notice that DROP COMMAND TRIGGER requires the specification of every
command it was created against in order to drop it. So if I had:

CREATE COMMAND TRIGGER test_cmd_trg
BEFORE CREATE SCHEMA,
CREATE OPERATOR,
CREATE COLLATION,
CREATE CAST
EXECUTE PROCEDURE my_func();

I couldn't drop it completely unless I specified all of those commands. Why?

Incidentally, I've noticed the DROP COMMAND TRIGGER has a mistake in the syntax.

DROP COMMAND TRIGGER [ IF EXISTS ] name ON COMMAND command [, ... ] [
CASCADE | RESTRICT ]

Should be:

DROP COMMAND TRIGGER [ IF EXISTS ] name ON command [, ... ] [ CASCADE
| RESTRICT ]

The documentation also needs to cover the pg_cmdtrigger catalogue as
it's not mentioned anywhere.

I'm enjoying playing with this feature though btw. :)

--
Thom

#7Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#2)
1 attachment(s)
Re: Command Triggers, patch v11

Hi,

Please find attached version 12 of the patch, which is fixing docs per
your review. Thanks for your time, comments and fixes!

You can see the patch-on-patch here for quick proof reading:

https://github.com/dimitri/postgres/commit/b7798e8ba6c9bee1f65b233316ae9c08b78e5ddb

Thom Brown <thom@linux.com> writes:

I just tried building the docs with your patch and it appears
doc/src/sgml/ref/allfiles.sgml hasn't been updated with the necessary
references for alterCommandTrigger, createCommandTrigger and
dropCommandTrigger.

Also in ref/alter_command_trigger.sgml, you define SQL-CREATETRIGGER.
Shouldn't this be SQL-CREATECOMMANDTRIGGER? And there also appears to
be orphaned text in the file too, such as "Forbids the execution of
any DDL command". And there's a stray </para> on line 299.

I attach updated versions of both of those files, which seems to fix
all these problems.

Those are in the attached, apart from your editing of the examples para.
A single para is needed around all examples, which was forgotten in my
previous version of the patch, now fixed.

Thom Brown <thom@linux.com> writes:

I've just noticed there's an issue with
doc/src/sgml/ref/alter_command_trigger.sgml too. It uses <indexterm
zone="sql-altertrigger"> which should be sql-altercommandtrigger. (as
attached)

Included.

Thom Brown <thom@linux.com> writes:

And upon trying to test the actual feature, it didn't work for me at
all. I thought I had applied the patch incorrectly, but I hadn't, it
was the documentation showing the wrong information. The CREATE
COMMAND TRIGGER page actually just says CREATE TRIGGER.... BEFORE
COMMAND <command>, which isn't the correct syntax.

Seems like I've forgotten to update the docs when acting on Robert's
suggestion to improve the syntax to CREATE COMMAND TRIGGER. I've now
fixed that.

Also the examples on the page are incorrect in the same regard. When
I tested it with the correction, I got an error saying that the
function used had to return void, but the example uses bool. Upon
also changing this, the example works as expected.

Fixed too.

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order? Also it appears to show

Any reason why? I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

CREATE/ALTER/DROP TYPE_P, and the same for CONVERSION_P and DOMAIN_P.
I'm assuming these are typos? They also appear on DROP COMMAND
TRIGGER.

Yeah I did use an emacs macro to get from the gram.y format to the docs
format, then replaced '_P ' with ''. Should have replaced '_P' really,
now done.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against. Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right? So I'm not sure that's needed here. By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

command-trigger.v12.patch.gzapplication/octet-streamDownload
��HO�=is�F���_1�s"�����VLB
k%J!�$���V ����d��_�\R��8y�*��9z�{������������&��t�$t����$i��-�����x��'�|&/���O���!�(	�4#����;)��$�����#AD�� %~�P/���6[@=%���#B^�����;���$#������9#���a�~T�f4����N]@�4�F��k�������82L�d����������aL�4��f����6#��$TA����7s�nJ�}_�5��7��$�����nU����\
��L�q�k~$�8������K(ldi�%��k�n�!m7�j�%f{��<Q����%r"gE�_%)�&*5KD��)�Je�Y���Df���o�p�xa��X������YM8�$��
�����(	Fen�`�i%�Q��D,�\�P���D�C�mG���e�Q��D�T]�S��@v|z�����d/��������vw�, s���%��"q��D�?�	��F�+�����B�0��	�7�0t�>:��������������
���e��,�Bz�f1��y�w���D��MF�������AA��D���"�n4��)%��")���1�v4�$��;�~pS@��%��n5x�'�8�G� @AM)oDSPj�~��r��F[���O��.��oqD�����[l��P`��H37Y4�+��r
�N~���y�����Pq�<t=����i�f����g��)B���#r�[5IPW�7p���s7�E2d���2Q��*�=���	}��S;[�-��C���g�=uJ���w�r[��8��m����4DsGg9��������'v��J��^�Q�^�(��������o���n��8%s0`4��e8�`�{�TA4%b��m���\����8'��
03�r����b~���(�"�o��������7	pW��J�@>2�#�j������� ��l(�~�`(go�Y���m5G�z�����*-J�Q%@��Zw
�Dn����3�V����G�Q��I��W4�-��J�m��j �����(�)F%#\8���%qH�h>�(�p������U�0F��`��(8��������/-��;G�������3t�i���q�� v��5:�#q�g�9x� 
���K�ft6���������F>w�<���fX����1P\>����@m<�1�\r�Zc���1��d���=t�e�i�0s	��8��T(��Y���Z6����������[�������m����w���~r����E���t.a���&y�1�lX������4<(b��"�����OI���j3�+�j��A�;��.�9?
���{�+7����K��)�_�����������N�2v�{�3
�]{h����Eu�%@$g�����{����Su��/�^���]��rK����}r�wN��r�9�c�+8��OOm;�����oG@UR��N�����k3t�.Nk(�#������e���������������V%�{X��=��C���`��wl�uO%�<�)�#o��#o��o��H���N�4]�� s|�A�xGUHxkEF��%�T���J� �I���5����U}�I���B�xCI����Hu���=`
=em���g]�9�bq�\Z��T�'+��1�%��T�����Zo������Mlg�*&YpP�x�b�R�A�Y�A�9n��Qv�U���^�)I:G���zE�J0J[Z"���s U��������K���������|��QSXd�X(������a�y��
�cb/��>4eW�����Lh�p�_�!����3Z��d�*.Y�@`#^���)������\W)�!��NAW�I���Nb�'��`&,+&sh�����&v�FZ��5>'�Y�DE��&�"�`�E�(�sV�0cw�)k�k���d�90^��
RLU���G���J�^,�U9B��#� 3����L��L���5���X}�� �j�p��z�8�AfS}�7�22I�Y)�Td$��DI#2��[������<a�]����k��\x���nz����0�V�)`|&"�z�J��<���R�v��y3L�i\���
`�R�� '~0Y��i�h��^�7,����y��������.x8x�s�)�R����P��L��`��U��A9�Xb`�v����]�
C�����b}����#���%�'%�V��c����a�|@�B��NLgv�,�K�_�=1�����Zd�)Ic`f�&��I������{	�{��,h�p��0�o�,�|ZC�a?���%
��a,I�m	�����w�X�ML_�9p<U��i��f<!h f�������4��p-0
�m��(���	s��d��=���S�����`6�C� �E�����D����	d>�����
�F@��.�!���Z�G{�v)��oU�kL���e���0��VpxP�;r�%B����|j��|@����������R3�!)aq�[{�Q� �"��|��m�42���<��4V�����;,:7-mmVP�A?aa��b"�`]�869Ffw����
���*6.O�$Hv�,E��H���G�"����]���f���d�������4�t�t��dd�{�P��;���u'4@r�M/,A�����%������{�1%��9!�p��>Q<��k=�V�t�Cz!�w��R��wd������`&v��x�.E
�0d�:
K�x�?O����ei��z��%�L�%MI�z�C��<=����h��B$����8��@��H�C��A@	������|?\?2�X�x�hd����1���l���bX�b4�',���<�*��k�����T��p�s�^����N|����6;"<�QN����v��@�%���kkr�a��{|q�1(;�/(���(�:�F�'�Dn�[	���y��&�$����I��=�(�������o��e6��<�Z`�MGx���i)Z����fu�#68p}����i�Q��q<����%�x{��"<@*`��JXo<9D����
S
8�Lu,���k�y}�|Dp�fX�
��`@��Kn�p|J�i�*�sf��R�N�l��,�rdVx��qYX���P�)���X��T�@��������������/�y�o�E�CKu�W���`$����LO���4���U����U
��X�Z�dme��T��e(�	�A0�a�o�9D���&9�u���d��� S�N�T����N�)���iW��}V�h���X�r���J-a
���f�����	��][!��n��T��/��sw�2��������1cG
Fr���~��\0$7��|��q����
x���iN���?~��&�a��9eN��
����Z���-@����e�������b���#��p�f,<;"����`&��[�O���``xz�A�`@�� �c��q
:M[@��Z��&�p��#�
w<��@Z~c�&�h�{��w��'��V��a��*nB��|������	T�!k���`��y��mFq3�I��}[�����X����
�E1��Qo��$�v�$��#�r��H�/Q�(u�>���>c���s������+�����5-.H?k�����z1���|s��
�������0��-e��E@�8���#�\s�/����11]r�z����VW���>]UM���ZR<������?���6"j�"m�;^Ij��r���"5-�����z]I���"L���mw����������6�S������)��R�����K	���/%�_J8�9%��L��yL��F-^i�VI��3H��S/O�;�[�S����4K�\����=�U�2�%I���Rh$Sv��Y���xT��
�4�Wj�������v�G}��������l"o��+��Z3�"��g��s��%����q=v_�'=u5���yma7��R��I	���S��,)dT��HT�
B�L��i�N|%��{S{In��}��dI�(1��b/�]�X�~!���7�x9�����c�gz�����\F%��;Meb�����O/��!��"|�\0M�`Y
Xa�P>���=/����B��~�.�j$yUnR��Tn�����V��Uy8�K����6���9��+#	{�O����Z�u�����������![����2%��U&h�����b��y�o�=��d�)�w��S���Jo�R�Z�t��@V������%��W�r�������s�`��K�u)bVY���"���HwC�p4��=�'�h|c=�V�(��8�VJ���o�T�@�p�c�y��}�v�����0����e"A7�74�	�-�f�so��"UY�l=bs��[�S-<�a�_���W�����%I��\,�.�\��[nz���D�wT�V���U���n�b�V e��m#������(~e���t�n��da|
���JH���+cN�;(mM���{����r�v�]���q�o��_��o{!4q�8�F����_r�D���Q�Wjv��b���o�5��xo�����"�����3qC|?b��G_5���2v�����#��Ex���+nL%��|^�-V\�q�}-`|����%�Q�k��1{w������INm_>��������t�x�s�^S���A�jC��km�8O��7z���h�o������U�����V��d��D^��T�d�/�;���,K��l_x(�F���uF��������Y����'�,rK0�w&�s��dO���5���R~���/��Z��������b����'&�I�-!@�@�=�E�P��}����(���`�`)F���
@x.�`����1���H��"0Q�u��9D�<J����
|A*�=�G���J-,���p%B��*�O�qo���$���yp
\l{KLRi�18��z|TG��f��Kp���7�$7�����[��f�k�w3�,V(���c���s������R&e������1{���F3~*��KD�;K�����g����u��!t��Y{��[{����.{��xr����,0�B�����F����p{����	
���c��E-i���v�>t$��F���<��y��U��M��������g�m/l��q���)����4V1xM�.=9�?0�����%����^dZ�d���Lft���g2eY��9���g�9�?���"��"&@��'4M�.O�"'�|�����1��E`�������j�}������� �����Z�-�FM�gu�xv�+���c�s���h��h���y��kO~kJ3��6��j���Ck�@r�����s������bm1���������$@Rx�!���,�J�@T�\x��o��W��]��/��g+$��o�7������$~
M0�#Ddm�������O�k��y���H�G�l�����;����<�kY>��`4f��Sd�
�kjm��WC��e� ����\���4��75(�nd�o���X�~�������q��^�a�6��������ft���xt��8�@����������Y����\�1�6�$��&��q�9�e��baFf��A��^|k�=�mNi�|�����Y�c�&�����n�������rE
~�����}])&���|`&g�LS�*��|��������l�."���������T��o6o� $��bl�:���@e��{��7��7dW�B�{��M��7������i�2m}����(�������0���z�)��z+�HF��S28q�+d���ya�RM.M
���fx�+W��7S��G	��i�Fexb�������{�F,9O������aN��xi�?)��ALA���iaH��qHfA�7����"}B 6���J���t�~)��h��u��GR��P������~z����^7J�*��_��������ny��!����h�{w��}�k����8��C9��1�TE&k"lm��!�Y/����������m$����Okgl*Fd�/�&�a$��F5��L�L� �pLAZ�����o]��@)J��dvO,������u-�$�]a��f���<��N��z�c���
��Cm�~0o`d���(_��3��@c���$�X�G�MCy�)%�{Zc����gb��U��;�%fg1eB��(
����5�������!�t`��?F%��`��H�0�bL��"�P���������p�����
��p4��Y�g( �5���:uj�U���	�i1�
p,�G����1��xL��NBA��T�����+�4�C����X��!!m*���T�H��*�
`�;���@K��b�_,��x_Q'l�Q��N����bT���we��P�����	�h1�VR����JG'�[�G1��37;�[U�k���
��*�M�V.W���6����o��!��O�TL�1��r=�����%z����.A����������]�(�xA�������Q0"
�(z�#ot�A-}����x"h�"��0�s�\MC���1���uBe�[���Z�C�5�d�������x�/�	;�%#]gNY��Q����C�|�>���N&��}�Y�v�K�������~ja(���H��:gKcs4NE��oc�F����7��{�	�I�Y�}����V}�MvP������	Gy� S�b�J���|sPD���+~0�����=��s����kLv� ��:�c���7�k�]1_	 !���|Q�nw�+e���b�p�!U��Y��Y�?��:&���rv��vI�[mjj"������=~�"]�]I����z��k���������N����%m�$ZK	d-�-~3�S-������j"�r��;4SL|��W�F�r��V4+A�Q"��F0�s���4Q�����
����%uN�~>&�2�52�w�c�������x]���m�-���l���2M-���"�A1~��Fm������$�&)�A�Rt�p6��
��c����HT�C�,<t������/v����J��H,yGVt��2��|��[���u�fb\q9����&�b��q,�=��]��mQ��e�a��YJgGm�s�:�n���$��L���������t
��}�v�x4�M�g7�B���w���6�5C�$:��N��_Jj����]�y>W�
'��:R���)i�,=����v��J����M�����:]�4��2r��vz�
�v���
��\����t;u%z�%-/Q�6�Muf%���������)��z�
Z��8�C�|%�r��b���\���|�w#�U%|H'��jq] eQe�
�?�Bs*�8	K%���:d�bx!<V
{��F�������Z�:U�)���#���3���jB��x��!�4x+�q�$����OYNG����4�$�|$�H���`&��
�8����paJ�1
#�w,�7�v����4����D/j�v
'>��lT��L�0�����]:�0���C������5+�4���3I�����Js@^d�'��)6?,������X�}��!�S���'���GO���y���y��DE+_Qs��9?���><��s7�8�hn������7A�k~���C��#?�
z�d��������J���M��0���h�+P�xL����Z���3���c�s��l��	i0:iq3���r>&,@����6�#�8���E��9��*�H[�B�C]��c��M�&Q���d���t��?�7�x'�ub��oA�;��K?b5e"��1E��G�Z������2�,Ay���'���Q��/k��S~�����������	?zw�?>�Ca�Pc#S$H��Q�D���<wa4���nMS��(_�q�R��Q8*+������.�'�.������N{�B89�yx�?{��"X�|�(�����@�+��b�m`�uK�V\7z�����������f����!�����p���,Z.�I�
���`�K/o���/e���,9%.�Dwi����K�,s�]�Xv�����_��D��r������B�"h�����7M���?��7~��b#�������'
����l�Km���`KJ��VQ�*0�)�l�3g����+��6��kkn�R�Y	��s]�v��s���������7��?�fS&��+vi��b�����]��4�����&��N���v��������Yx��h@|�cs�"���,����u��V$y��TM�����������!�025�fHCk]L���F�m\��z��|��ld���![���o��[U�g��jM�m��������h�=0��E�0�#]!��;�o$y����J/���^t�&\��yo^IM#52��k�[d���Vs�����B�W���c�|���j�����3�<���AH�f���d�/P	@Y)���?��9�hew������}q}���"������p���
�y�W��H�XE���hv6��F�/��n��{��) �\`�������&S|Q����fci�@��\'n�&�5��Mn��&����C���j��w%�k*5��O��|/���i%�%����/��r���o!������S�0���\h��U��-t9������x9�@����*�����CI����d�{��%}�lcZ����� +4dpv$EW�����I3F?��#���Y�����F�5 �$rm�����-�5�)��$����G��5|[��6��|�O��R2%�
���/�����E�~j�9��t������I�'�K�|����#��P�K���� ;�1f�[Y.�������"/���z��7|q�r��l[�*��VY���>��U�����]�7�(���:����N��0����d{nR@�*3�6
C`0��n�]�$�WU|w�����L��.���������-��E����>OO��6
gw.�����l'z/_`0_��*D4��|4�+=7�c�>����mI��vO����:����\�����w�s�N�P2zK�W�Xf�a'45������E%QeO����E_h��n+6�xs7��
l�y��T�1O����n��[q����!���D��
���fn�"o���vg���~>�F���jvb��N���'_��_�����#6hS���5����:�b�`D�v�������SYl�b[K��OR��j-U��&W���3^^��9Yz��`�5r)�Z�����C�T�p46Y�������&��7a��)FrS�p\����p��/����>��MG�/�E�L��	������>�~���*���?rJ��Exv��.��}.���S�U�K�n�^.9T��(#|���P���zn�?�{_��G�jH|>m���'�Xc�
����9���(�&����]�3.|�Wi��q�0�����P�z)�4��tBsNO�]4H��	���m�J��;��9���`B��uP��3��)��,�����L�I����ul�m������}mk�u�=Uj���!��J�np�.p2K�Oq ey]�l����)|&Q�_�-��KD�����V���V��m��+�e����Xw����r��V��������\���p��p����[G�+��@C�������L���m!O#��.R_�S����+6,��������/6q{p��[���{����D���Z6�(�"G��"����B�R����lE������b�J�����T�k]i���
�-��&)4)��X8�d(��x�����o��1$��l�(B&�����.�l4���"[NM��d��V��"�$m~k����7������cKs5S��gNW���Q�	�������)["�u���Xd��H�����������N�
�^� ����r�e6K�,"����0��<���"�DC��b�I�Cc)��/������G�v:e#����f�:1��*��l�kLT���n���������
3"w3��>�xR��_����kO�b~��'Z�m�������g���J���W�W�:�����G��t�����h��^&o
����u�/�������K�J��C)��u�5:�)b��-�_���(=B�nK�=����&�����-D�1��Y\ze�S:	�����br�����o�J$�x�]/>�=�^
�L���d����(oM� ������
������E�.w ���X��3�y�ed>A�����]>���R<M�!<:�n	����X��^-�������^���Z����(lW��W�M�R��^�wL>�v�1RX%���������4J������t����7<�����4I+�+
�R�n�M
���V�{Yk+���w������������w���@�N�o�Fv���g9�+u��=:�x��|�X��3wV�f�[MO6�9&6��R���Q��^d+�Ut��q,�g�i��0���&p?Oh��k�Y����B�b5WN�3^�q(�<n�3
���0#��������v���Xk����f��@Z�x�wdk��,����J&���I�	}���L1��������R�����	>���o����6�|Ki�j�A�oz'��n��j�����\<�P�i��M��E�����m� �Q�R��l�8x�$����&TEG�����`�N�mD��a��+�yo�����b�����`	\%9�-dc��h��!���b���u��U*u��~!V�-�Z+�B�?�����)�m����������10o�wc���x_�!����&EUV�L��?�l��?:�@��YN=��T,E'���s�i�	.�'Bp��2��>QB'���������|i�GSi�8Vf���_K%y��y�aE2����El9d�����F&��G
���6�t�*�1��E
�@�9�fh��aMPq���fM�i�qFF{�MB�p2>���7�������'w�s.xM��8�t�d�S#�Y����k6��t@�_�C��t�}b+p.>Xmk`�z��5z�.��P	p2��\ 0�
v$r���S|P���#c��ty3d���)(��&Bc�'��Mx�?`�co4~�?&Fq�\3@���jD��f��5R~� )`�����)��[������@�P55�^o��l�vaZQIQo�����H�z2�Z�+����,Zi^�LX�*Q�Y�n�����p#7jS������	���=�������Q�g�pT6�cQB�����
al���~x��b��L��f�~��}/���^��4�����[�,�GdAG�|����X������;�LJ�!�j{����1���,&qh���4�8���%%#E��q��gB�"l��[��b!1��iH���c�Jrs	h��H��	��4�+����N�������������z�<�����7��V\��@<oO�%���m����Tr%.�c:�6����6M����0\�z���m�����a^��C�o����qJ����Q�1����5�|��K���/�qJ7@�V������>�e�����'��nm�����K�$��T(:(���9oI�F��6���-7���,�2�\}�mXO�J��9OF�E��*�G>�/&�_Wj~?n����~��=�I`Y���K��`���m\������m��G%W#�U)�d7F.c�!#"�c�-�Y<���p�����?dwA����h�c��$�E����7>��e;��������0��J���mD<��}����%w� BW3�c2����j������\]Eru��y'�QSN��C��wx!��3��(uo�����l� Np�!��������������r+m-(��OM?�	Z��n��Ny}��j���9�����X�?Eo��2��R����L#���u�R4	��
N:��m���#O��\J,�6���(j�M123*�(m:bT��Y�(���jq`��)/0������b�\��4����>F���yh0���j?#�P��V7<�"��Mm����qo��~So]c�;�BV�.��.4��Dq���8�W�>�I������b���1���g��+���i�O�$�%&"Kp}NbWe�A[��U#|��S�s��CN4�j��	�in����ES0<�d�>�S?s��P�_��)������o�����
���������o����	�(V�|����b��K���ZM��6w�^k �V���4mk"���HDr�"��>��b8tI�^������������W�=u,)5��mf���R������J�o��l��Q
����#����K8%v���@f��|}z���/�R���2�@��"�������>���}x�p9�������s��`/���#+��N}��Z/����V���������
��*��^��CP�}`�2a�q(
aD��VE�sb2,:���
�������`��S�����2��5�|�n=Nh�?:T'O�>�o�7kb��^N�+������px�;>����v-~��H
��<�(�����������( ���C�|@�������>a�?}1BNx��=�2�6X\�>��H�Sz��-B>��zF\�U�
�^l�N� ����n@v�k7`�{ ��N4��F�%�|z����b�)���N\g# b����]�,B�-y��UE�����^�t���J����'L��i��0������9XDL��I������+r��P���Ug�(�;������U��H�U��Z����y�����/m��}�����j�z���2�8�],U���T�VY������m�O35��_ �a������;����'��p��l��x��h����)|�W�A8����@q��j�O!��wK� <G�Z�:��M�i]�N���b�������jX���WFH�/%�PCF�G��+���M2�(��"o�H*����Y�ce��U�NF0���o�LVL)h�Kz������T��s3�W��F����1Y�dta
�kT�B�q���7���e�o�s����	�"�k�����?���	eUN����d���(���*��N]�.x0�o2��#��%�j��2��C8���W�r`�����.�Q��4����p:�s�O^0���Y��(���3/�|���:��#����k��~[�	w��K ���M��N����R�M|X�ux��/<{�������� �>E�Q��Z��;�����1>p�d�/{���6|���@d4��P���5]ps6t_Oa������������	8Vx��+2�m5�����'��%|��K���>KB���}�c0�L��e�p:������[�%������xt�{�������4��|m�_|�%�cwO�f�a���-�(��qH��7WCP"wo�Qg��Rab'}B8��e��%�
R��\y��4��y2$��L����B��[������(�M���Nf:Me"�od�#K9��.���W����6���F���8rd&�8������m\5�K�����L�@#Q��c�[���X�#A��fBw�G�U��b�\\~I���'���c�B�O�>.���>dD�����P\k���A������������o��y>������D���E�b����W���:<���V��zP��N�sy�U�f,>P��#�%�kR�_����'���?
�cg�eGH%N�=���k:��1�@�3�8��%�b�����O����.��c��Q�+<R�b:S�t!&'�|!��g��h�Q:X^P��\R|b��,i��$*k�	��0���������sI;��m]��w�;�G����������^<a&��
W'a�/U��/?�����ry;R�[���+qtN���?�@��+E���0�@�koGoB�aURy�:��8�;\xW�����h\~%J��K��0�W�Hl�{	�!��~i����y����Q��� ����������������y���{��TC��l4��C6��:�o�'������cKy����]���W ��s�@�,����'d�P���
��%��o����4� �;42���[��G��@�kOGU�yO�Kv����_L���������n���1�(X�0q�R1�3����=7�[~��H� �R�������������L��F�)��b�����[d����X����H�Db���*	��'�6PI&���6���E����l�g|	��D��+�:??$���5�X��xxR��IOUZ��+���[�5���tB�u���~s�a���=^H�hQI�3�O�����������R��cc���l<���z�wx��$>��tK�%�}R@�Y���#�;�����
A:xE>���"���>�w�eF�����:p�'���ar�s��;�
zN���Sl9���Tf!���Hvu�{S2��Mq�o��G:�"M���eD�b�����c|�:�����P�����!��s��W�f��p&�T�����p���`�����������cRN8�b�Z7���~��p����C���4��c���fVRo�]���=l]�6D�gd��������9���g�����%���@��V��A9����=MK��u
%�c,4S_�H+��8c�?u�N�N����G&`�z�B��c�����uJ��(���Q3���)�j�{�~���Q������)���)A2M9)�DL�Uf��L&Qj'��+�*���@��f��;���SL���\�C��x��;%\�r�-TT8��%_��$n���KOv�M��I��� ��=&�a`;�tM��iF�`Ylm�����c�)h�vK�����;+t	��O�Qq
��Iu�����U2"C���2��<��������������������=:9�������)�I��sb�����?vwU[�����y�O�K��#j������� FJ������G7(]Y��{��r!�&������Mo<+�|��'���C���g�z5�"<���[�������#{����K����O�q��u��,�y���BL��K�iv�_%Hi�r=g_tY��yU���_��B�����d'�hN���KFP!�z����?���a�&zH]��i��;�8�y��K��d�@n������gjW�% ��b��$�g�_H�H{A�&��$�������.������)��.��}���s_�2=�RXte����Hr-4��a�.�h��;Ag�
�_�	�~��<��Y�R�'���b����u�cW�8v;2�}��R�|i��^	i�qf�~��3����=8Ao|��#b����������
�K�Er�u�@~��F���t��������Ke�\�F��D�`3b#4Vx
#:���N!�SH�������bM��$r@'V���"M#��fV�)H���7����;A�����@*w(���Q� �-(&K��L.�#N#
D�g5�;��:����Z�m������+S�V�����48+����y�?�"������'��
X�
z|o���$����}�!��F������z����B�^�dK��J?����jO�<�$�����G�{��S�������DG�<	�;R!�`�!Z�*Q���t�/��)�}�K�	\�]^+�
��|qW����e"��~��2��\/cm��a�<��B��m��G-�vl�5@�7��\���#���w����C�b��!�F�Q�
Y\#)C0/�XItJ�&��q��4�B�������*~��Oz���7����^�8�i�q�e�z��M�+��.�� ���wXh3���_��E�J*�
���'���A�!����@�����R�5n��_;N�5@g���������Z3��4W��������Y-Y�!��*��}���>��o�^x\��������`��W�y������2O+:����F{��p�0��d�F�&�Z����!������cD'5*�����Ux������-�p7��(
�1��N����[�#k����)w����w�/��	�cDFlD��)~�4`�^kqt��E@Y��1e����[�r���MI"Jkc�7���+���I�P����A
*�&����RN�y�UaL'����/�}n�\ ���e�=>v�l(ui��65�������(��\*�����o���q������x=�����-��&��N!��W�H�a�SV	p+m���%�a��d�h�����Hg5��T5���!��z�1�I~Og�,2����Z�$�5�)!�7R�3��w��w�g?�.�#y�����������O���q��G
=�9��?����&�4s��55|�sM�p�?�;)W������mX�C�U]J�����6����e����x34��Gm\�:j�K���3������$�H
G+G��'���e��'����,����l&<)��"8�Ygr�-��?:A��v�)��R,?f�N�J���=���3Y�ke���p~�n0$v<S�o�^y{s5?�^�;���aq�`0V���p9R��WX�P���^��%�������,�Z����$�����B�qk���-<`RC���*�8��J����W�Z��aD������%q8����]o0OMA���B�XP���J�����7]��L��+%���F�I�l��g$`�m��1�	�X;�^���Se�Cv4�c�v��S%��'�y�D/_+����P�HX���l��[���He
D���z
���5����\���Q��9�FH�?�+������!M���t@�xH���.��8��~�)D�9���AJ}>�_S:�������?q�'?+��(��}��?G��d�����f_��$9�8�$������3����-�j�j3�D����5���S�;�f����d����=d��8�7�$�~c)��P��
��V��*��W)iV�MM��#ie�QO���/*�1�c������U�I��OL�a�J�A��?���PR�!�=������H������'�S�Q�Ru������?4w]n*�D8}��sG���X�O�T���Bk|D�L����_�&J�oK�}��vt�[.�]���������@`����`�������� ��N!Z����|��5�)���<�G:Di"j���l�s\�[ZR��tq��I[��)nH18+Y!J#���x�o��.L�M|�qe�e�>�0��������L8�������Jd�}�V����@A8���.FP�Ez-�7���)}�B���K����@tA��k���b���1<u=2��~8>�?:��z���Hxu����c%u�}�CkL{@���������~��y��`��%�>S��~#\���u�gT&����uF~��Sv�����E�5�`����%<c�������:"m971��Q,�����w����@�!�#��.���-���|rt�9�Fq5R{k�w9�5j����}(���������E�( ?^��+S|ob
��7e�=="�e>��	Q7b����Iv��5�j��$���6
�D�=F���?x�(*b�
<i���Zs��Jz�qe��������U����f�|j��H��4�b9���)�"�D^�V(�4a�&����. ����XA�'����nh��|�m�[9To#�2����SqLR����X���V�W5-k��=�T$�@�x���u��9^�T����R�����a���\��x�LZ���i(b��CE����6��Q{"�'>�q����r����i����������'�n�@x����d�i*����,�
�a�h��9E]�F��^���:���V<��E���P]mI%oD
�4��p�e�_GNN9�
4I���4�����&	$8k�0V;�������2���
�\�����	V�I�q������3rt%0a5��� �2����of����0���8��q(/!�[ ��V*��:[N�>�a����E��8OW�O�����|��o!]$���uWGBm����L����f*���+����D�l�����$����n�����*k�=p������3�LW������`�=k���j����U���O��i�]��h��SG�H�Xt�{1�iE)���iD?����-�?����D
x�Z������9����,4c�H�I�9�\O""�#�����	�3�����+9�1�im
Fi0Y��?$�r��Nk��Z��4D���7T����!lJF9pQ\��tb>�N����hI�2�` b=�?2���0N<2O��C��s�(p-q�����-�JK�{h>{��/��ML[0Jn(2��an���U�+<<, �p�0S`k8������2�Wjm��,���n���y[�U�6�,�y����x&�/5��y�J!va��E3X
V��Z���=,6I�^C��q��V�"��X����&l�FF�%��B\��8:9$�J'��v��������cK'a�_��/���{�
7��������~<_���q��8�8 ����Ry�����s� �[�4��>&��hdz��Vn����'�a�?+���BW%/��%�z�&���yi]�['S������bg�efb��h�:7��Rx(��NB��<�����_�n}zC��6����	���$Nx7m.�."�h���	S���7�j��'N}�Y������V�Gv�����c)�1[����{*�62q���2������,�����f3N��H,���	u{������D��x{� f��`������C��`�
�:�W��k���K��u R�55c|'��H(�B9��s�	.O?� �z��bI��V;9X����,�Rwk�N��meq��c��
���<���+��\�2.���0��UmV��Y@�m��������g�t�p����#e�u��<����:��)�W��!�31����B��5�V)?���� ��k��U�o�]����zTp�����`*��1n���7������Lk��[�7cl��3O��B�Q��(K�3+*U��J�A%a��NnI0z�������n>�����!��&���F3%����n�Q�.�G�f���e����V�Z��O�	:z��H*�7��=�-����)�.�:���HI)�����npV:.���&t���D@�*V�5Zn�U�_[����)��y�'d&�]���[k��C�z����-X��,R��B�r-�*���k�S�V�n��L0fOpm<��5��OpIT�'���+8���H�1
�n����.��>��w�>��3�y�CK��y��%Vk��I��7��oq�*/�����b���w=(�g)���0M#-��R1�M�Hk�����x���&�}���R��z��0e��?I�����v�Qvb'7Y�����4]J�!�aq��x�q6���N*�P�������@����N�%������$2�����^����l)^���l=G�$:jktmo��oKo\�X$K�
2$��}���s�
qJ�4n���%V�M��*.�*O�/����&��x���@�U��YE�y��	�qw�?�����kG[����� B-�= ��q�a�
�����i?����e���6[��\��m=�Q�|�	�*�Y��v1��L���i�t��i~�n��y2�O��yZ��'#KX�k��n�H��UX�yTH�����|�S =&���y ���g��4��d����*U��~e+��~u�=�S`��Ya����[��^�L[�{�{�n��,D&Z�E���M f<	G�-��	�@��=l �x�l��V{S<��Uh�2I��C]���x^�c_�<�iIP��t���.	Z{�y!E���y�-�����?U���n��u���+�q�����WXXd<y�Z���k8&t���[��M{�G�o�����u�:e�Z�n�K+�3���\l,�4�y/k��:��2��[�ulf 
�-X�<@����2�J=������$6��6Q������[���@��B��T����R�@��e�m"��[n�\-h
�sW\�Z� Y���[�$V��1�O�������C!Q��!�-Y�HzJ`5�x�gfQ7��pV��X���N�a��j�����~A�};�X�,�U���>�D>/
ZZT�p�w��zM�����Ot�-R)�����P��_	�M��k��iz��V�5�^&~��M7����� ZJ�������+��l�3<�����[2g��sM�z6��O0~l�A�k�: Z�s���FI~Y��-�������\�"
,��x���"��-���^p�&�`�K1�+��(+�@�Vu[5����7�C�4b�e	�bd�C�aWq���OUf��U1�������b|��8����`B����w��1	��7�N�����j5�U�_4��wH?��a.9}�2���g&������o}y�����G�e�O��T�����|��}��(/����-��r��q���>��7A*���8zu�}�Q������������@�e�tb��e���|��0R�����S�@���.�����i����
���9I�m��\��h�s�x���&z0���kU�i��&�����U���|�D�{�������{������)�
�����:�}���a����\`I�HiS���,�f�mi��U�'�:SP&,�%r���r8�b������jXC�/��J,�\����D�mW�.�]s���Ev�B4H��������Be���G�v��& ��/����`�r(��U^i��j��Z+���4Z�j�>	>��q���\��3��������}Q�_���1��@�M@r��$k���r������U���h�q�����{����Y4yN�BH�`u�xx�����?\'�?����W��"2F���9c�'�f_�^f��4�_�-0���I��D�������>��u�)%?�V�h!0�(}��g
�y�2�y.W�p�5���&
�re4��~��A)M�����<�e�R�#���'�)LT?�z0�Sk�U��Kch���C��*�0�h��v��$�����!��z�
��y���EF��&����G����O7MZk,O�UTPP�Z�U�����b��b�>�^�rm��/'��u�X��-B���Z�^.�������g��jY������?�2����0���|�pu7x�g��K���TZF�����2\���n.����SWk�S����G�$����@���B������q��lNFm�)^���
����'*���g>&���ALin�g�/j1T����$>*k!a��!�-6�����N	���i����0������=yo�L�V��N���_��r���9/���;�X��-�),CG�������������'c;��t!Z�3�Y�����9p�c�����X.�/����@O&�j#[(
a?:�f�����z���f�(�;�M�T3VE%����9�y8����&`u�h��?EU��[��j"���e���5�]�F��#��#
8/���xy3+����pr@��g�V�O����6�E�J���O|��L���'h�VYj���{�����cAT.�b%��@fp5;�]���J���c4�*��i(j��>iM|�v��T9���S��8� ������r���������Bi���^�g+�W]�OkK[����Z���$l��X��������G�C���Z�-����t��Ak�US��*/_�];�xuCJ��W��W�Z��l
�e�����J�O��U�u����1�'���M\��6e����S��Ix�M(�9�h��\�}�cx�"Q���i�!Q������F����Z���q��!�p�s��V��HL��G���g-n<�c�����$\�8�������c�E���^ �J|L�����������\E(��N"M" M8����8D���x�-J.IM������}��Q���[y�����qm��^D�y��� �j��_S�?��31]D��E�[e�VZ��]K"D�)�y����A
U��iB
T��q�\Ao����DIk���	�qc`�c����l	�5�RZ�,E"������dXQ������kY�Q������2F<j�E�������	I���P{3N4�(�EM�z��MGW�pz�o�2�%�%M"����S���������$�T`�x�0��<�Qt���f=����$L��`oo/�)���R��n���\��������F�-�r���{K;�����n�&������k�����oK���=�.W��KW��S�f)��d��\�����#U��
�
��1+Zu�������&{�����i����B�v|��U���L\�l��&}3���k��@�Q���Iw���W�����,W�f��)0������(Qy]�v�N|���s�=I��|���%��	��2�M�{l����	�b9?�uOOg�ttc}[}���i�N�d���(x�1�����������l�7��6����.���������s���`����4�M�v�w�8�c-��i�����*�������"��b�c�~
���s��j��R�R���7D����P��&��A+.��e;�3��@�ksT���e�e���q�����~��l��K������&,��(�se�K�:X�����?{�v���k��������V���q?zUm�/���U��u,���KE��oIu�"u4��g�N��Mq�d��:�d7#L�N�����K��v�����*�2������{?�?Nz���'�=c�)�w��~�2�%���?�����J�f�������J�D����|���V�K� ����R�{><������������6�);���}�n�}�����-�������&`J�r�����]����XD�z���PbQ���YW�.����U��h�����f�h�2�R)v�?�:�������!#���C�'>��z4LC�G'_O����x���� Y�
}T���)g��}	[=����*
�������8�%�0P��Md�M;�0�~����eiS��BUuI�s� P����x��<�g��
�8�l�@�=0�G'��6�Dd�r��7�0
�PX92B�c2SK�6����e���]�R���w�����k3R����&��A���p�[c���U�K�w!�!�	�J6�ZM����<���HO�^G���x�5 <5-86��)���X���_v�$�*�������;�3��e��]2G���]F�|����-?;�z�S�\��z��f�^�g���#i����:���2JhCW�W+J��7f5p.���yx#���<�l�t��^��f�g���I��?,\��e�
P�
��Ma����6�����p<����D�'���a`��{�����+��:�
{z�rR5�=]3����v�t�0���RY��pF�N�����]t���Q�e}�gw2��S2i*`g`��4�f9v��������}6����~��G�i6]�n$@oIz�V27F����U��'�a�L�A��VM�[[�@jy	w�MF����.9@��J�����n�-��{�
�T�]*���	3���t-�o�����Kyb83�=|��D�7��t��O���0��x���w�g���-�]�nu���o�qf� �r*����G�.�A���"=�S�3�T!;�L����re[0N�����A����(1����?u$����K�.���R� T�[6����k��
X�����L8��R~�����9���`�`�x���Z2���Rgrr�Zr�S�u��+�3r��G^��!��@"�	�5_���>��F}�������~��z�|�1�a�f�6�i.�"��^�?[���6���j�_!��ZA�S0���71��rW��:�����R��h4�a�)D:;���]�X��|qV�o)N��{�UU��1VS�zt���3:�/��d�=)$�����o�(�L,o#��Cj�V
�
�5��NY�S��?}�>�2��<���#���(q;�B��XV��A�lD�F��d+�n�O���\I��v����z����~e�u(XaV����"T,E�4:��O*EU��EJq��g���V"�mb%�}ww��_�d�P�q*y@�I������������@J�4nI&Wn�`
���C��@��U��+����J���*���H�U�K=���I�e=���W����e�`��/=��X8{��=H�+�s��8��}��Vs`)��#��lK�`,kpCV�����m���K�.����U�|��X��A�Vu�u�$w�~�D�s�{�Q]"MZ�g>[���(�~�d���wg�w���-d�K�S�P����E�����>�Xn��I�
J�+������x��\�FMl�[���S��H4e�V���\��Y�U*hR�����hzk��*�6�/nP���AE��e��|t=�����R:=�P.��`%�_�1Y����d�5E9���5ZQMeB�B6
j#��A���{R�o��B��U��p5�do�����������A�n�4EN�$`w�n�Q�]���7�;�����J�b��H����#�������~+���+c����B�k��eP�UZ����>��a-���_J2������h���H��d_0Q��|Ius�}?�A��j�����)5�t�^����f�j�JjVj!�p9���Q�X��e�G�Z�H���&��#�i��L=�i��V���Zh�2������5�>����U<ymf��~���YH�\k"j��q8}���Ohu-��J������B��
C�6�eU�ZM��fgdh����5����a�g0"�I����L��[�[i��u�*(=Zh!D�T2h�bq�+���.����BAS�A�E�Z��ZB@xJ�q��5A��Mt3�tNB@@�&�������Y"G,u��g������0�0wRfJ�d;&~<mo�U�`����_�S�d?�Z�����M���_F�����9������H}J@��&��Lt���#[�E���<���.�u����K���\^��������}����Cm����v'��yx�8,��s?�I���q��1�e��1�"�����K���/^����,�?6<�N����*���}a�����������^�����vkU�28�/���MJg�N�/,������{�u���k����1?�mq�����������t����aaR���:��0�z�(��L�bJ9B����v�(����k�����6d0�"[���n��'��&C���;������
���!�
)�����2�::9��=ay%j#�����J�����_���`�s2>���)j
��
�)�(��+�N��E��{3J��]���a�r�����t�3�<U��!z�"(��36*����6%���T����������UX^h|��!#��8�hotP*�1���Sy,��\l%��q^B7������p���z�����f��Vou���~RxY��^��������:>���D��2��V)���+���.HqT$��!�wH�)��[W�n��&���:n��I���0��������Olp�9v��+|o ��u�Y�C��>F���?B[w��0��G�]���8��������)G@!���GL�r�;%�O�m��$�ng:�p�����qHq�����ou��dg_l���U����! &m�A8Y�0o$^p�oo��D���D���s��?
.����6�7����T�?��/Q�T~����a��!���
<V
N��
uxH�������w���H 
�A���'������=����z�����[�%�?]@��o��S�iF��h������$<+f&�S�p�k���Y@*����C,�S�[~��|z�}w�����e��9V*��M����JE_i�I���#��L������!���l�e�<�h�KVx���5� �c��8�@��p��1�0���it�����+I�,QS�R3���[cx�;�r�7�����l0'f�t�??�m(K���7I���(���E-�-�'*$0�z5�����+>�0+6vjux���6��x����^|�\���C�R�F�p^�`���1� NNIhD��S6P���{\���$\gSo]��_H��t]w��' JK���0���$����ML�*����~����zi��2�M$H<8��#���B�2������'���A*��l��'�QJ�����U��(��FCg���l$�[�WR���bu<jxw`�8[]Au��;���:��j���N�mt�Z�e��	�L�&��"��P�O{g��><�������:�W���[l�g�����st��
��b�U-����H�	}�|>�����@�0�bL�bt/Us<����j�*n�&�B?+!���	&w
��S�}2�@8�$\d�;�@�*��
	���W���f��b������}��`,/������3aBKk~��i�h5��<������W-m���h����O�z�w�zN���I�
��dkP���������d���������9�IG?����b��H>Z!�p1��)������7��e���P��H�����N��$�s
%�jAry)l�4���L�����P�?6V��o�����2�yy���/�>LD`�A���";v_Wb^��<�����
*��T6�j�E�/���x����������n�M�X��d/a3��{7;��b$H�3�18*�.
���H)�Q�T��_\��J�;)t��������rQ0Y��@@�@��>�-�m�g�KrH���a��'Apb��&��o?���?d<z�p��+AN���V%f�������d�YYO�s���Z�&5��g����&d�������	���E�9��"�u���b��4�D�*��r#���<������v�4�]m��Ze���n�m������m<�#p@�-w/�������4L�������
<B������t�t�a@����sC�0W�/IozGx�|`4L&�CA��sgp�}�}��V�����V���F�n!`�F�M�F�^��NUs�|�k���W�x�nG�����[�q���� �$��%����_�|b'�86F���p�������m�Z�$~�u�;#f,���M��m�l����}wO�A�NQ��K��w��W[���Z��3/EW�^�5�/�%����Tx7���Y��R�i���Q\o[��|s?"��V:
�
��M����t7��R�*FU��Q��%)�gl(����d��^������W+ph��l��y��5�_�4;���F��k���X��g�0t
���{-��@��s|��lz	d��Y&PDwl���A+n�%�e'cmGw�e�H�lw��e-�^���?�K�z��1#!Ia7��JW�d;��'i����������3��.��E���H��-N��9i�����a��M/4����g��_�����,�v���s��m��)��JB9Sj�	�cP���)�"�`3��MT����}�����rz��0�4���VZ4<���M���+���B�x���8n)W���������/�"uUL�R��/m������b��1�z��	��_�v���jJv���A��f�j�MB���
��;�,�]q��HVk�.5��Ki?s����8'���W���������$�M+��Tk�5+��8�">�����T���Z����p����V9�������g,+���-�
i^f��Z�	��X�
h[��E0�����u�*��F&9�d�q����[�����G���kF�R���E�RDbE�����
�e_��q�^�V����c7|g�[~D/�0���MD���{#gWv���uo�j����[E-&U������On,U���Dm��heT���P��	�^a��3BZE��>@q|#���c,�W8|�k-����G��)�Q�,���`bOfc
J9p������Y����"aJ.��)qWW�(��r���M��V�����b��_��D�ri9{���������\
���{�[�f��L�lObA���3���C�ZV���������63�����l2	.�����"3oq�����`�"!qN��}h�-�7i�$�R�Jg��db�Ne��
D[�� N��a�
���T��}��M����>@1�x��W��t���
�{�t��I���e����g�\��a�W������4�'�����&
���8^�X��p>��f�
:�Y�������k*����J`%�A����U1���&�\M����h�:	�����K��D�kn���Wf�4�|�Y����[�U6��&��e8&���@��GW��sx12�N�g6��M�����L�G���0�8����rylc���[�H�	�P,r-�
��[mh&���jy)#������w�,��"����9��?��������]��T�ww�.2[�d� �vY��mf�@��<[#(6����Mb��,70Pl�����I�����Z��?����S�6�~�\P��kWF���j��==�"����I~�,��F�/��l|��O�HY��J,S�x���iv�z��r�$XW�rD�K���e��R�Y�g>z����z����
/a��V�i����!Mx���N�\��*���F��z9i��8E�nd����3����D�� y��`�v[�v?�
��'�;�r�l+Am�(�n��[����j�9��"���Ed�s�T<G�F�V(�4D�=����Cl<\��",^|F�h8��#������������u�'���#oW�vk�@A���fx	����c����9��������?v���c
�`�����Q�D(v-E�����t[]��b8
����p�S�6{���y�'����{��re�z�}]�����\������3��`�����i�wq\R��R�_�=Y]�>���7G�O�����)�')K	4�h���@��������`q�=�s�Q����:��gd����zg�8O��ZU���4��KC6N��v��j�����\��I��-���o3�H��]�_3�U���T�k�lO)�=|Z8���l�i�
�{!y��\oqB�6S�&iw������xmW����a�����St#�q�4Ar03p7N��n�j��/v��W`�K�/R�Y�
X4+����VV��rx�
X4�w������h�����v<����=P�M��#�&�a�j
E��[g��;��''#g����<S��P����O;e1w�'���;-X�f�����Ug.����|T��v1N ,B���vG��8u|�
[�Z<-���N9��x�S����h-�����1]>������LN��\XD�\�����b��%po�$��\��M�y�a���w��
 �{�k�}���,���q�8�ye�^���n��^7'Ms��6l��]�U��2�f�c���[k��i�h��o(5II�N�����{���;����-}ei6�}'#M�c6r>�����d�]����u��k.�1�(L>Kvav�jo��CX
�rz���vp��Z�
g�N���5���1M';rA��4��b���(w8�PJ�����(�r@3�lU�/���(#�z�*�[��� 3�Y������ZM�D��5�������_Mn�Rs�e��*�D;}��Q8{���`q�l�* ��������t��&���
f�o�~�_)��2?�7� ���c���A�o������;8�����#`�T8��:�P�MF�9��Eo���T��4�g�B������s�G
���Pz��!`��>|��s�;>��
��������������L�������z����p����v�iIO��T�h��|���R.U�.(e
��r$I�Um���!�z�R���.��@���H'�������TZ���\�;W$T���M}�])�,p|94P�����V���I����Typ9�E�yA114�Tf6T�op�ij���/��d)sBU�����I�f��I]�
<�P{�O���5���,�;��
qx?%�Rb��>q������,;��DG������oJ����T����0�K`��V5�8Oj���I��6�7�-*n�U�$��v	D�+��^o�u�nV>5%��I��Hv�B�oL�1�b4�<3�+�i����J�7��n&a�:<4)T����+h�����n��B�W��6�F����V���|������A��!��X��P!@t���x������K��;�^�-C���iE�lT���MKb��9�������B���g����#�h{�l���{o���%������t�����N��}
Li��nC�w��N�F;��I����r�T:<X0���a��}�����p:��le�
��ox9�z�7PV1%�n����o�WZ�}j���
�IN�Pw���'" K���p�I�/a0���u7�V��G�|IT:��3>{c�K��iA��30�d!���]���
���`:V���G����DP�y�@���3E[]�3�����u	�B�]��@��9*$������pT�y��Yg/�`G8�L���	� `[ N)[�?��J�!�#����:SA!@�ag:��;���4�Z"A�?������!dB��s������#����~�L	@����������s���`x���1�(����\,�v��r�mW������Q����"u)�D*J@BW�Y�w�����GW'���;=9Q�;Qw&�jO]����q��9�%x��W��x�t��"��?	����'��EE4��-pzI��~��>z�?	�,���i6pr0w���*R���H��Y��gT4��1UbeX�>2��K|K��Z0��it�vu,���v�!����9�1�_5���N���hy�_{��p���S���v�%"�o���L�?/_j��J7��2�GC������S��K��[������K)�	�r�WjM�R����"&�����$�He��n���H�J�����	Hu�-F�.�����Z�����gMN;\�5&!LGX��l�jZ�^���T����a��v�5��M����$��]H�p9�~w�{E�x%Y�W?�~z�����	���b.�9����F������&�5:0�fC�y��$����e���PA~����M����@��#-lY���[�����54����4[�h��*�r��Z������;��IC�v�~������2���U��w�s�~�^�����N,*a(J�-W�?o��<�)� U@Y�ajpv��8W|���m{��q��_o.�ov$a�X�d�}rT�}u36��A��[#o�������Z�+��U�d��YKQ;T�$�
9�,����zG�2�eg~f��!1{B\HNPGLm�4��t�7��X���'K��������
��zu$2P	����1��<��
j������aj�
u�)G?���$���IJm$��6w��
�l���E��7��������\h��u���q�Ws]�8�����O���������������g@X�-i�ipO�EM����<������L�38�F�Vq�?����:���!f��?>GQ��g�j�]$]9�y��$����eG���(��r����������|~w������dt��h8�e0����n�e��_����r��plBd���R������v�)�������k��m��,������~t��M����}�%<�����G'�"�=�mO�zU��x%���INb�������)����^U��I�������=����Kx~��n6n0��G�N��
�In���w�S`�?X7��
;u8X�z��)��������Z����w�Qm�������wS
�hr������?~�[���@~*��R�$��R��c��*`#��1��F�vx5*\keCx���t��7�+�nt�{�npi�C�;N���u�����e����%�7qlc�X��[���f��6�D��� �6�=��#|dM���r1c��������
~�5���/#8��x���������J%X*5���������w�����x�d�(��ad#r��4��Lq����YO1���3��j��A�������8����i������|���^�CVo���k�+n�Y�%��,�2XZN���r��?Q��mX����e+��Y���Y�����IQ)�Gq��e��-�@�"�<uL]��}n_e�����84�B���gyT�g�I�� 
���yS�
�b�q~@4�0��
d�
�����4�(��4e�����A��j4�N�����\G�������}BV�zY�{��q
����kf!�m|^]\�����4��g�,`JwO����l��VY�B�s@t3�!I69�L2��O>J�p��DKm��z�Y���0��@Q����{������N~������/�SSO@%��g���
�����?,�J�mK�R!���^\�v����1�Ci�������xc�2��@w$6�;��-��5�B�K�9�{2����:�
/�v���k��,�V����[��d�x���HS�����C�pI�5f"�$m�A��.�}�� -'����T0R��YJ���8$��('�,���3bk����9�}Pn��b�
�e0�TD���<Q�6�����W[n]�'�G�Po�gG����lV�d�D��8����������Xk���nD��������Vc�}��l��A_�e�}'�E��x���������W�X����}�l��6+n�����Z��Z�A=�PAC�|!�Q������Zt��x��jGR��sgc��z]�"V��3���n���Q��TZ�����t/���mPR����/I!8�7�1������l �4�`19�8�d)ma
�Wq7��T��J���}'����ur�=h������
^\��Vj�z2��`����p�(���(��1�_��m�����������2J{�j�Wv����n����9Cx�,/L����Ja����?`l�!��4�X���������+*��j��4����vkJ�br�lrxu�a���&<����[/�]dT�>{�*������/�Tn�MPx!1�8����r���:z�\������nK<s���7NC5	����2���8�����;�^���{��{6���3����u������`�������;�U���=��������=Z;����80c������w$N2\����'L��@���Cz���s>.�9
�,�s>��|��lc��0��nOg����c�\�!��@�w�&$�?rCW(���j��vec�hw������<6�Q�r����l����B��������^p����#!�B��*���4���3�O������0���@�����0��Z��5Li�z�ZE�
-������p�s�������'p/����`>��d�A�-r7�&K��QI-1i���C�>�2���
��6:Xr'�����J�����k��5�����l=L���������������^[�G��q�/����;Fv�Q�����MJ:��B��@�"uDFee��u����(�w��������8��%���k�V��m�
�-����[�_he`P�m�P���z5������iZ����]w�!��������8-D$8�8L������f���(�N����G�a@����� �~��J���d�������%��`.,4i��@�mH���aFph����=Yv��X��������A�y���'����9����bDX�����o��n����~���$�+�m�����o����t�f,����
4,�
��lc���n�����J��f�����B1���n�$c��=�
p�,/ ��E�4Z�I�����]8�lU�VYc�v��H���uH��"�Ux�4$_�i)&0����B�G>����o�<�py4L����'� ��%b����'[n=���=�������D�����V5a]�$V�5�C�Yw�����Vr�H�����)�B���vk�-kIq�Y����J�m9��0�%i\�Ny�-�7h�;��1����@��Z#�!�4*���Z�[��V�����pU|k�}�K�N-Xg��1�����n��$E/qi�3Z)!��<UF���uRM,�IY�����f����q�9Q�][izl6d�
�]-�
�����98j{#��K/W��(���:I���f���t��	����)����\���h���~@�G����!����nt2px5>��&���f9y)|h!y7~o���W'R9oN+����r=s�xx�����
�)��	���*�����&���3����8�����r"��I�4���7����	'���
,���V�{�O��c�NH���*����RnV\��
��v����JM�j8\�Va	f�����U\��4���%�����d�*���KScelv��Q�l��j�m���
�W}$��5�T��hc(�����U�P�P������E[�	+�V��c:���5�i�6-Te���9b���R(���Zn���(acQFU��������."+)z��x^�Z��"��44P_�)����
�:�G<��O��nO��~���TsSr#���.�Tb�P�k�j�3d�T�p5V�8��a�[�.�#��������Kr���%���9�X ��n��c��VJ�����5
,������N�ep����'.p���������u�O���pF�	����	q<����dt�o�>����C�`>�z��/O��|�Y(��G}=�M���86�*���u+��T���-���h�"��`
�����l�UT~�
��!�o������Wd��T^a�J������I.��mt�����y���-�x�m��t��*��i�M��&����v�oG4]�S���v������������G����s�=,��o�{����� +��i�@��cSL�&<!)�g9��!V*`�Q�22�M�}Z�~@2��������sY�m�HV�l�����e��~�UmO��YS*�j�(A
�W!+�,8T1Hm��w���*Ws�u?���'qi�����S�@Z��~�E��3z���^C�W����9��3��*2ny��uS�����T��-�B��������w3���6w��a����6k�B��Y���[�}]��,�]F���+�jc��{�\�S�ro�����\���xX��[�xX�}L�Tmc�v��/@.�{����M��}g�OA�<&������3��7GLB��o4�I]����H�������s��J�@W���h^H����;��M����������a$+B����'[(����t������=�A*���lwu�2���x��@�����f3@�(3:r���V��X������ia��L?g�v�M���[��n�����F��"#G<����`�s���
�l/���lf|�g�� ������M-@t��6;��P�/|E���g�	0sn	�'������f���8��qx�S8�Z*cr���}�Y���/V���_�DSZ`l�8���p?C
E���^�<ho4�=5�����
;z���O�X������Q7��@�)��F�e�v��e4����.��
�%�T7~�F���G���2���%�����d>����{�E�����_�#�-��c��7��f��6�>�.��	��{o����{����?F�F����Z�����wg�w�62�q�mFvn��n�b��>$R$r�����xZ�b��b1K*��N�1]��;;�
��c�����s�7�|��g�{^r�G�����d3�0U<5d��%����G`Bi���]x�SK�3�zv�N��&�:`��
�P�]/WR�������H�Q3[�������/��'v��.��{�|��7�Y��/S�0qn����dN�r
����'�Rn����z;��>�w9���{R7��<������)quGZ������$(6j�e� ���C�3
ATT4�@w49#u��R�k�$���Y�9
��]�������	��W���Z����J��)r�
��Bg�V8r��O	��hR	�e20O�0�'G��nF�t\����q2�:m�J�ny�D�����#�
���z���W�R+;���L2eC�m�*�a���ZepE�j������@��p��w:IU�TU�H:�d��^Mo`��Q1��x2z8Rep��#U�v�����:�u������,aY��"x����(�tH���������0M��|�
6�s�����N��g����N��O����n�[��X+�;��#$R��C�z�td�5��"4�nr	�5 E}=Tj��M#�@����4�3y.G�6��(�-bq������t���������u����~���#U}�X���Q���(v��p)��(�a3����?%C�k"$��,��?��;����|���5�/�6*#��(��ry�3\�k�e2��-��l����J��-Z������k2.xQ-��d��#��c��V4'�"�a1��g���0��V�_���%\N�S���N����� \_�(����j�4���,������Y��T�������yo��w
&����\J���o��<��������l������aNVb8
8���2�:���!d�0k5���t`�;ux�?u{o���IEAU�}�5`r���h�9�1�y�����"��)���
|��@*u-��g^H���������M7���b�X!�n����,���<�������;�^����M6����1`�G����G��g+�<=r��Fg��d�u�v��D�
�*���v8�19�1@��7PP����?
��J���{��^\*D.�V\5o
K;.����V���f�^�Wm��j5�xB��:�|��	�'��2&�D�Y�`�j/`�Z�f�_�I��f'�s2,.H�N��	��9���r��4����inF�����$����N+��`K��Ujn�Vm�I��1>b�UB_��#�b1*�#�U����/u��>�p��I���Y����`�Y���@dk��D6m'jG������j�	�km9��8���3����C��b4`��rr��w���)�z!�����^
�����z.�zYg�����D��D�j!��,�!W�SV�W���i�Z5b��Hp^?������l���f������Ar`��+��J!w$����c�YmykE�Z_���"R����;f�����j�z��R���0�ikb�������nG���R����kn�*������5[?q�G�3���X����=<Ds���|�y��i�ia��
W������(k������j����k��_��^
�����W���Y��uv�S|_M��<��5f!�k��cy��vFe��U��On{5u���z�p�W{�I���n����a����o�_�i��R�����lU�ex�5c/��"�/���7���7���X:�c�������Q��2	}��oJZ1a&<�*�I��-�!)l�
�n��b����`�7i���d�Q��0wG�pl�7�9��,o��D0)������}��'�����V*� N	v`�
=��5�<��B�VX���	��5#&~��f�Ch�xk�N������e�}���:MV}�������P����T6
��a��o��b�~���8f�"/������X������I�V��2����`��J���*R.������q	3�O�R<,!*�}�Bo���tA���(O�����o��X9�Gr��z�_��SLx�ID��v��+	4Z�r"'�!,�'e�k��Z���:���8���!l���v�o6�[�mf����Z�l/����>���D��(��O+;|���/��,�E/#\V�Z�����;	�n���Y��6��
#����L`������b�Xi@�4��{��Y���+�c#�m�|�9��TH��.��Y�$�*0Y���#����3�,���*�[�3�e�������N�(v�(6��@�v���"����,��ju���-�T�(���p�����8G�d�'c��S��_��s���(��=�@$�B������V\�Wm�W��8��dr^q��L"geF5�"�nT�wl������@��H�k
�d��f��G<��pE���5Z�BWfI?9	tC�/gi/�8d$����)�`��p�A�M����8����*H�'�z7}���SJ�htt��SY{������@��[y�v&���q�cHo���~I�g��'h��V�c����kzh,�-L�����`�����k�:����m������P}_C���������Yf!R$���A�R�`,��xsP�e���c��s���A���x�:u��,+)�g��Q�H��u�7��lM������G�j�/�����	|�� ��z�r����+���0������+���_#��_ gT��ZHG���D^-��p��p\�~���6�PE�U`���g�X�-����<��r���br���"��k��4*���C��X�9>�aP;����4b������\k�������K7r&%k�D����������8�c�U����y�+�#�n���2Ni���z�$�����7Z.o���:v��+"�����fvx2���dc���T1��4J��9�b��b�G��&�fF����2�2�Q���aI|b5W!�h��nt�2���q4%��9������������F����?����<��pVB��	������E�{vu�����0=��t��(*�9��;b!��zS���=���V�6+H���E��5a����*���ZO�o.��li��<7�<�p2��2]��w����zO�H���(Z#������,��)����/zC;E�QFJ�,�������+����O.�����1?zY��!@��!��ML��0��v���y���1���E���n�f���oJ��?�y�D>���0�T�_�@��=�$�l��N�3v�~���O��?����z{��e���h�O�`�]���,�X�X�u��<��y7��N}��+h�C��-���=�������$��y����{B,����������C������o��;�+�/�^&/�D���4���3X�f�|�h�('�\z@����F
�A�#�
^R���^S�[ow���h������H�c�z��g���`�Xz��{�J�+�T��.�C�&zKG�(����Ffi������aom�����(1�J�i�?�ttV,����VZ5�-���i50���Nrt�R=�����C���.|����\����.�%riY[<59��<��+�����O���_�3?��X�
�xI� 'vE���yU�
R�JcI�:���xb����������)F�5QfOnz��p8���������~�O
�"b��JI
c��Tx������z���8G'��������A5�SKF^����
Fz�5&k
]m�u3nK	��[�����p��`�un�z����L;H3jo�tGQQ�PH)i���r~W�b�F��#n�bFx�����9Es� �X"���x+�Q�m3�OX��kU'��e���y�5�"�E"�	�%AD�[���;z����B2�����X�E�W11���]Gm���.����8�������1�18�4^�k�;�mq��.��fq(�1s�8
P���4�sBX�\>6#�q����,[�&o���z�HY��vnE���B����;��b,Hz������C�s?��ht�F��������;J���k����$�5u�i�����1�n#��"�k�J���#�^������������5���������&�P3�G���yx#->�-��i��%��B[�Rr�����8�
���"cx�	���C���������Zj��1���8������aI�=o���N��bD�X�������,n���������0��z�;.�v[��,�J70`�.��*Y����
`e�_�S|t�W�1����G�U`��V����1��h�����G"+��pk�9�LVz��)?���}FZ'��Pp��(����%z_S
�)��#1�~(����s�}��r�|�����hJ
]r�C 9��H�3�/pc@/SF�	�
����;'���1vN%�<��3�	������ub,?��5��:�=���AD����y*��W{�b���Y#K�-mX�� �G��0@2�<
8���Z���a���'�B����k�:��
FG���|h������0I��B�����}/j�?��f���K��^_�{MR5k�)�j�3�rE^r�&���ny�7�*W]�F%�����D[��vH
]�����2%���]�M���u�
X�vOk�]��~��i���sssy��$�d���1��p�.��o��l�tQz�y�qq��s�����Nl�Vr��N����������4#���q����j�U�fZ6����v�`�������"��������������g�7|���nz��"z�
��k���7���(���8�0G���}zl��orj��<��Z����C�/���q�y���ZYD�
s�3�|o%f��S��*�Oz�=��K��V8��w�p9;�x��O�V�NV�|��v��������j�*U��V[m��_������v���w����4���!��44c:��H1�h������np������M������_z��������������A���cl��ttr��)����g9Z"	�������+��.�qE���O��+m�L���Y�?��?��P�����p�4�LE�i�T$���X�F�>�"flCs�\�C�8@��x��'}V:

[�&w��5Ax�J/���s������}1��I��	��[�]2���c�~�~�|qGo��q��p�=��O�<�L�i6�����W$�����-�A�e����dp�|t����M0��f�I\fZCm���X�"]0�L��D��W
���X�T[J�
��!2z���9����,�$'7�����)d����/�z�vu�1���0H���0�G�E�o
��!f���Z1����?�}�?P��~xQ\M-Nv�@�?�:�?c����"���&�u��~�T���bt��x7��<3�KN�9B�)�Rk�'�����Xu�jv�z.��;}H�$B��p��Ac��8���	Hse�����f���B���g�z	5��>�=�%$�{�~������7!1��������:B10��������`'���]�,>n����B��9��S�>���7U�����_%�Q���?5���������������4r�)�*����p�.��`0>:�b1�8��Q��G��#��g�K��J�p�H9�M�r���h0�$��0�fO��p��2�u��9��>��?���aO�K��9���{�7<u��|t�����z�����
����w�����Q��A6����Ai���u������s�0�3��_��?�����w@���u��p������g������3���I�@�)������sh�K���N>���{g���~�b�zS��>�i��#�p:�l]C�9���~i0�&�n5�f[����r�^��?�o^P�7R$7�k6>I�������4�8���<�4����5�{�Sj�:0Y��1k::���6���5�5%��`s����
|�������5�X���,�-j_���*0xS�U�l���e_^������
����S����4��K��z���t64�m����f�������LxeP�r����K���HJ���A��Qo��&�1%Xc�"I�P�����������0=�@���+[���m������+#>��-��v��{���uz�?�~8�Q�Ef9^�^8/�^pE�,�c5�Ym�CM�_+���/u��d���P��$��M�@J����
��T�{���L�c���������,k�`�����,�����]X����?���,�V�E`�#�6�	����6��r�/4��=:����I����hDud1�qP�K�0��e�����9��K�Xa0���)��:���b�$���j�O�4Pj�Z���S"��B����.7��p�|��0��s�����w�v��A�b����d��D�j%p��+B~��6���M]�+-rF���?��1w(��\v�$	k�dl���q�D�,L@�uj���pGZ�E�
u�����B{������w=[SYfo���	1�������BVXWI%�F�����#�ke������x��L���.����Og��S�IFOXMV�w�&���
G'�,S������n�d������\fo����������C�c��I�����;���L����������,��T�C[SU���p�!������i�l`A�dV)��������y�����y�{zL���RV*���������3�a=�(�7����Y��e���}4.�����n�Y���;8���������
+:y�}t�s^/\Ct����E!�5��$&�E�K*Qfmi\4������F��,��^b�`�c)�� ��I���`�b�~I��s�p;%N�Y[t8�2���i4���w�Frkk'�� QfG6�e�D�U���/�.��.�.�u��
�f����A���{ O���k�H4�^�?�`%���E#)�Q\`ka�]f����b��lca�����
l!��jU`k`�Vdkd(�U����U�l�Z�n���[��2[e]P������|KO�s��,��[:L��Tw�������:�;�T�x���d�?��I_�Z��I��6�]�F��[:U)���z�wt���6�������;�8����0�r��v���m�V�[�@���f��I�9��1�di���0]R~�^�|������g2��������Tj'Q��&��7MT_u����d��m7!�Lhz����� yn�$)#s��b8�n�2mRr-uQ	���l���M��b�B��w��wp>��0���fU����_�{�x��_-9iX���Dk�Nn���8�.Qx���I��w�EjB���/�����K�~�^�o�@��	}���9�%�5��<��6'�����/�����G����p<���b��>�,����-X� ��tR:K��L�t��c�k.Pt��j"�4�M�����Z�`.G C���������Ve
`xN���:�c��=����@�����S,��m@�>R�qL�s����y��2���5���x��KJ�4K��j>��|�y�Qv��Z�u�f��������I�
���}������S��y���Y�4+F�3��
�����4E���E��b�����q�AC�#
�A��_����v��5�y�f��v���������|���N�wg����cG�W�F�x{��}���B������P�:���X�/G-07��S/������s1#�K���[�?�H-��n�L���6��S��q�DT�}��p�n�(���5�{��j�]�T�j���~�q9����n��HZ�.�Dc0�s����[���>n{\���D����e�Zk�0�/R�aH�)e�tT*���^������'D&V K�wd0Z^D:�	�!��R0]`�!*��i(�d��t��:�t���r����h�������D����(k��CEAL�]����w�@�Z1����|YWH���q�^�{fbQa���G�V&��x��������R� ���
�g�L���Fu��k�Zz�J��/������r�|��ML\)",
������j�Q��c	������dT���1n���6\�0^D�pkK���O�^2}���n:*��c�,!�lPN��w��rja �H�f���+���#���rd/�#��%�;�|k��f���!<JhH���)��9@>�|J{�����5��9�,�	Q�x~�1���~��~%T>H��q��V�N��{�����N��G�808���8Y���4"�=�(�+.����52���0���5�G�%�^��nD%��+��D�����)���*���^���`D�����R���TP:����R�W�Q�5`2�����m�.�����ni�v#6�������%�����)2�Q��-���Y�7���"��mi��J�i�����q��_Y��Mc�1c�����g�O�V����h�i����h(�����q������M�SaR�s��u��?�5��Z���j�s@E�)�����:WS{��z�{����p��=�}�O�L:U��H�t�uD8��d��4��b�4Lw�W����E=��=Avb{�\e�U�+��+����$`�A��j���/�b-����S
��3�Sd&/:�1%1���Tlwv1�g�y�Uty��3�?�|���?��?���b+�d �d �d �d ��X�#�������.�R����K0
���|[�����|Pd�,���iG�j�2�R8�p:�{����@J�G��k,��8d���?����,e�p�R������_)�����kg:3=�f8��%���%A��[i����i�eA5L8��D��or�~�;���I���h��*&��b�<&����N�����co>&�v	�	Q��6�uC/�s�G�{#?��:_��(V���Q��M!�+�3�j�wl���-Nq�k���}����������&Yi�C�M�M������J�r��jgIU�!�f�9��r�yp���D��A�Zl�Gi��,PI���R�(���Be �����Y��N�[R�d�&,b^�T��ZRr
5O-����7�����CL@&R��� ���ui����=&�U�2k�	�!W��DQ�q����d�NJ�( ��V(9V�	�dU�E0W�D�!W%&�f�F%�p����-��X\k`$���U�hYQ��P����e�ND�L�m�Sv/����(�*n������-��4��&S���^��^�u�RK����t4��=�PMp/V�����)�N�_K'�WJ��B��b��?��
��V���������)v5������c�2��h�%�D��	E�.�����u���%��>�;lT�!t���W��t[����T�n�V����k))������;�1-$Q�C7RbJ�(�\�&*,^IE55KZjX�&��"�(�mVREY9�,&G�3���2���e%muR����iYH�=����&�����#�J����I��$��a���kP�����������>�J'�����k(E',o�&�8�H�H�`�����*0�|���;��t�\��=I)��Y�NXI-f�v�9Zd��yi`�u�R��s�szgxB�w�	�k��	��S��p�"x4�d�����L�U�R�T�!��l������+[���=���@�'/^��0v���1��eL�$\�,'�U���#{
}��Sd;3���	�	�1n��v�c��6���7F/�	�	H�����i�s
b��2�)���(��"V,�G^�����C)()�V�l�D��d�FJ�p2a8����IxU�����\gw9��^M�b>��V81������xW��v���+M��z�s�:����Ns��<��.4��?�������t��{�S���=����]��m�/l��?Cx��/-��b��o��8)�d}����'
,��[��&1�1�WWs�
������22�<z��;����8Z�MT�yJ��#&q�|d��x:�����|������g��[���	�9���$P[��	8h�&����� 1��l4�X��	��=M����M�{���~6������T��
&t �6���v�&�"�7Q��U)���h�����v�f���F/��K���(�H����C��u���y��<}^kZ�C����c��5&�8� ��9�������nU7���},���%����SL�������7]����9:����;��7����/1���rJm�w	:M���gZ�J}�&Z�
�g"j!�_�\w;i�*�*0�Zz3bC5}(�\
���Zr����X�YHp�&s�D�����U<�w~p
��R���c�`?���^�P��C2�{#��ypu�0��;,N�:��`6�X�qV�����������V�����o���8V���f���}�y��i����V�4?����
5f��ov�C��t��������"c�]�a,���:���Z.��C4�Y��vU��*\
��+�:.�YK�����4���\�oN�T�b�{�9�a��9;
u|J�����]3/������qT�{����yx�]L��i�p�>n���V�"��dB�-��v�GX�]�|��h	��e�����	�	�$-�Ku�g�vi/�yJ�B:������671]���@~k������f����xb�m|��_��R��L7yL�;��F4M�3���Gl�H����i��rr�~�xa�1<P&X���|��8�vb ������������7�2���t��h���(������!V�|U�z������'��I�l-A��R�3��w%gX�����P���!$��8���c��+����#��M����kuw��x;�����{H��?	��i��5:5p�N:-������:��7ZFd-Lw��X���$�
�t?a�es`k�����B���EiE��&�e�3��e�i�4������e�]���Bu	��F�������QX�0����b�&��8:{bB�a{;*�K!pU���[�Vu�?1z2G�7��SP���E��P���_����+/�*F���4��u����v�E�~��#%j������	R���"~�����������9�����Y�;_9���}��K�D��N*G�w�#��5�u�3>��'���:3a��2�P+5(�KG
���w�Vg�
�z��5���ry���=�2�����j������y�78���8i�%
d���-k�#M��N2p}u+�{<���D����z!�/k�6{=�W]����0n���������7~*@Z8�$�����F�4��e���^�:�Wd}��TP/Q����
���l�����jn�����
^�z��"�'��s���3�^�a�:��W.Bi�;�%p+�1CvZF�6���������7��#<��(wJ�/�;�v����u^;�������==r��������B�/����!�� ��^�DA<YQ��a����� ������-�����B4�F7�DJ��`�jv5/of�_����x�n��6���q�!g����T�?�����Hp�y�O"�P+��RWz��%&�Vk���|��5��������� Wf���o��[��G�h���e�\��M/Ct��~
���7���BJh���B'��o��\t�n���J6�O�����l'E���3�_��/���I��E�$����~��o�!D6���X`������X4��Xm�\���]P��	-.&��/��<b������o+t�����d�6�!b�rVo -�;������.�%�sx'bbro�V����U�������|K����E_8 ��o��g�-//a$�~��\Rb^�������#���8Z���h�i)����k��D������������}�����,�����>���r��S���5���Y�5#����|$1��K���������9���������7N�\-��(P�t^.��U�G��;��#��+������^M���p4P��8iB�(�91�������;�wF�����G�i��?G��UH��3���K��R�������(�D�}�PN�\���&�X�@���H�]���Yo0<�pz��P@���.N����,A��4��P��z���>O�����L��w
p�e�j,��q��s	c_ �����H����RM�!v�RK��5��RK����X+�B��e�$.���hd�-
����R��o���?bo���K=@��/���c`P?$�_�S�/���L���m�!�Q:��O�d�����j�db{g�����R8�C �G���D��p�E��x9�"P�2�m������2�b{��VO@��wtYx���Q2{-���I�N�6���� (ce��(�� ������3@A*��#~n0Cp�E��d�3��q&B���JH��$�IuZ�V����y�C�Op�$�`�:_�r��*��V��N�)���U������8�)���d�	Vt��������_����T�N���/9���Aj�z���JQ����P��*����%���x���X��[��/����y|����D��]�d�AIA�#��T��B5��j]�.B��==����8j�T+E�q#%�����������9���yW�>�A���#P-m���$i��&�*��Yy���|�M�=D�Lw]'9/q�����p���]�b�I�H��nf����Cx\_9c%��������[Ky�h���n�?Vb���{�����Y��w����f&���������>cPU���F��zD8+�D^�Z�3�gv�"V��7](���1V��cb��HNz���8-0{W��U�����u=�����U������]�����N���%z=k�H�m�e�������`@��H���u�m�xp��z��:C��v+Ua7�e�_:>�
�V��������|�=8v���������zF�)=^{	^B�,G�,������,g�v�3�A�-���U��(�5�yM�_�W9�@0�F3��D�;�:Z\A3bt�C������Xp	������1b"����������q��_4)�	�����b�	.�X�)���T1-;���>�Ef����A!3=g�yBc]���.9������z�'�"����a����*�h��FSX��/|x2Qe�'�w��5)�cd��6ZJ) z1�UW��L��V��J��`�v���O��:�%{RUhXh/��Zn:!GU5�u%�J���4�	�2`MH#t�nx�����{�|+��
�p�X�����;�q��/�*�3
'��������_~u�A�TZ�0���)6L�#QR����N	��Q�������Y�H1�x�Y�����J�,V�55m�q<:��36b��foD��j��J�����5����!!{[�;������D��_���a`�QS�����@$E@j]�2�X^�9�?P+���J���aZ��)�:v���ro7&A��0A
V�()�|�k�����t6�I^�h�b�1����`���|��d)-�@i��m�Z�h:W ��a-���	��6y%��&�f���i����������%=Y+��J��%���o���C�E����.f����zj1�t�9x2z!�/�~��#�&g�)~�l.g���S��h�=]��2����]������BK��Q~[��Z�L#Vc�E�0���X{��3Vu7���J���i~8�����IFly�-�Hn��i�V��N����qDrS����DYm��/���7��_��Z9��P�co�:xR�d��������r�6�%Y���S�#�c�pn�����"�?�����<��de���@�&��i����/�T���Ci��B�?���'=��d�&ZQ_���"������i|����l�����6����7!�����k����@e����FuG�W�NVg����G�������k�n=k�Y�DO~�}f�/2g�#��������������������*u�����E�O�j��t�B!����2B���4g����f� �{���8L��u�f�@���S./��_�!�����g��h|!��t�V)�E��#�:�3��&�������6�����:������/v^����X�-�2A� �](��������t9�����E�"XLP�9,�R���2E�E}En�}oh�
k
A�d�_��Av���t����������~O�+�HT�K�D����PB�������D#�x����r����]��l���!S!z�w	�����cv�V2�Wt���'9�=�U!����NL
�����c�s&f�HW��K���?���Qb7���U�D�x����S�����9��g�}.}o�U,������-�*���?��^�
D����]���u>�-�^�s:����!�0�����e��r�=����W�A�x2�S����_^f����H�����k7I��zJ#��/����9�\����m�s��B�L{�F���2����������/��1V����co��;���V��r��V����a���rX36��w���A�l��{z�.1$hN���7�LHrDu���B&c�\�5{?��PC���R�*4��f,���j�
�����!������w��4Q@��� I��q����a�+K�@b���)�9f���/������*���]H�o��Tnv{`$�@���8\�/y�~��D����`�MKg����;~&���.��$�	,�<�-P����)�L�����?�D���9��j}��|-a��X�1�()J�{�v�]9����dw�'����B^���z�Cb�����7;;���������gX�?�I�������~��Z�\7�NkKMV���������7����p��������/$�+��0�V�����e�xb��#tK��9���,��fa�f�i+W�J�j@PkB�z���~89�����W� ���h��F*������qMg��a�PWl��!i�~��~��_+!~c�p1�!�A0���������2��@|#h6��~VN�Vo����{S������0;W�j�f��|c^_`>���q�b��`��+&��m::�G~edna���2�/M�5�j2��	�K>1�B�q�.�P���h&&������V��?����$��^��2Q��zr�Q�x��%�d}N���g
(U�.#$'z�O/>v6	���m���]�E��n&�]���)F��O"��&�Sk����^�t
(�99���	efp�f�h0�����k��N2�������4�������3��+\�c���m-kh��C�"����?M����p������?�L^a�|�:��wL��'��u�Q�
�����"rh�L�(	�~</��)�*�O��EtJ�=�a4���+p3��^���;�&�tv����:���r�������/��a�K}q�_�glx��2���#W=�����8�3��N��#����Br��J#���}�,���
�����CC��^�X��q�(����"�����K�:&J���1�2O�:���y�����3����
�k�6	K��5�f
��x�W�����R��M�������D��R
	i���u	D��V��
���4�K��K]H (��Pvp=4(��)�������@��4�V��z�A���ab����������c]J��	1p�l���pN�����7�P�
J�p>�&�����9�0��w��|�ID�&�D8S.�+I��W �D6�"����\�0�a�`�zSL�D��M���[�e��]ihu��eb����SYY������
k++"P�b}eE��� V�`�
(�pn���5�}�5|�M�L��}��R���2���C@Z8���e����y���d����;7^�G}�t;W
T�\z��b�@j�x��M}8�|z)���sP<��blL��qvx����R]�:�����I,�p��Op��d�d�;&��>��z���~�8f�������EnE�2��/�/�%a�X�-bZ���*�}x9]�8H���VE��A�l��oO�o�TY,�@nT��p�8#6V�����t��[��G m�*}���pL���s���������dJ�����S;����qI�����,������M�k=�xg��������`�{�B��d�)����_n�?e�gU*�c�FE�|Fqu���v���=�a���,�k����Q(�S�������G����E��������J����{/�����������M� 3��~D���EJ<-({\mS}L�HX���y3T,��B_�N���� J2�65�9��Lh�-�	!����	�� )H���#����DuGa�u��������U���p[dt���w'{0N�Je��y��]�V������j����r$�Q
F�IHV��P�N��_��}�0����5�F�~�����P���b2��\�%�|-��\��������(�0����b����R0>��	=����,������2�w?��o����tG�����!�UNr
����)9����Q�h���X���	��/\��w� �9��3Zy��J�PnE���g'y��?�Y��!G�a.��,.������W�p�q�i��2>��+�+��7��p����6T4E�4_�-�z�%�R�L2�
I}=�����`:����_������PL�����g������1���~x�?�9������S*F����j���V�u��P|�M�x������G+�M���Z�$#�>����h:���f�1��R�k���6_�^���������)el�������b[d&������N~\-I%��P����?��3u<��w�t���;��<�R���l]��D�����[p��������=7�	��<���&�Kt��/*;�������>�_��H�O4��f)��C�|��]�aCO-�{��r���GB�B��n��A�b[Q���G(���2@xU'���%u�������@F�O�LS��\Q�ZK%TPfPw�4������c1���S'O-�+�/(�/PFb�IN;��b�t�P�cr���T�n��eg��e�:_��D;a!q��Xj�%����dI����@L�U��U��0�L_x,kO%�m3��Q�.B�RJ�M��d��N2yS:w�`�_Z��Z;�n5������/Y�nI��fdo���C��1�kP�1�����������m�vf�����%c���V���n����i��->tB��k�V�����V��U�o��+���cL��ab�I�*)��JX#'��a����?���lg��:��=�����g��=��[*��\+���M��fG�p"g=|g�=)�R�#cV��Nb�RB ��\%�oh���Pd+o��`Q�(�^�U����m�.a������,:���5G)hiA�\�*����D]+�mv�;6�L
#\J~g�EOiGm�cC�)�oJ6���^���	�l��t,8����XQ�y�c�l���mM>��l�:��(Bl�g1$���M��
��# V�_�?�)�Q����6:���O��/���{8�G���jQsr<���x�������2��[��P<����T����2-&�<����Fuc�ZG�l6���
��v@��y'3��g�4����s�f���u6UT��������iK<2��x��������I��kM��f?�3oz�����tX���E�D:���W)�<�_[�BWX���s��j������D��Y�%����W�������V�����Mz�fK�7�����������F%q`�S�������]�]P���^E�����*w_
i���^Y����N#m������������6KK�����. a:B��"��g��}����s����Cw~�n�zMo���3Q3�/U���0!N���z��E���G�]ye�)�
E������E���s�
��������xic�X�?l�oL���&a$.`��*�Uq��J�F��a��I�U��0$��'
�4�e��#J����c,0��B~�1���S�����M�$�kW
1mW6V[�cfn���Fmi���U4�G���V!�5T�s,v����q����<����8s~��J�a�xo�c�b��l?*��-�&���� {u��i��*��E��O�>�$���?��?�H�_�GL��v:	����WWqi�����#���
�h���^���
�������)j
��q_|<�;���x�{us$�/^�����o���8����v��om���
��5�R��ij��,I:g�����a���*e���z7��n�>���b��
������
��56u���(��\���;o�'�'���w� �u������!w�@@Lt�;��]��M����Y�����������CIAD�}��3:����4h��|t���NQp=���'�x�������j�S�h��r��>K���N�BmmA%�����m�Tk�n���^���Z���1l�K��*Uw3����o��/��+d��s�f619�u�3�|�� ��?`R=)�,������(���y`<���lK[�����zy�}>��mW��������(a�.6�@�d<Dg��~D2�&�?}��f�bF��8U�-��m9��Wf��7�}�����|o(���>c��8a
f��a�0=q�v-�����!s|���:H�C�"}&y-�X�q^�0�k��`����u���N������]�2�k���1�9&�*uM�U�^mS�L�8���4�n�<�:�z�����u�E%P?���8/��l���W�������4��F���0�qs�J��7e�$^��fQ��
K��/r������S"9���X��d��F��&]ij��6�U�[�W���BC������d�|�p�ul�-��0�N�z{Kj=��u9�,�}/���wM/"�=��#�����+����CB!yV��Z�k��~V�aE�������,*xu�X�6�(�����m�a�8:��,�{/,/��/�*���*r��
E[d�9���X��&"��0[����ib�_���DP���P����~aw1���,�����>��p�FL&D�S
accw��:n#+��VU r�����_���
�<�� ����UE���Q�4��}�J���\�u�@�*�J�5�/s3>L��0������IqrzP-O[�����Y��X�L�(��a�?r�f�3#��Q�!�+������\O����+�&@��m0���U��>��3P�7z\+�1�('c�`��T�c������p|�a�{�G�wv^�/2�2h����6����f�����������<�\j��.oD�se}�
�������_v��(D\/���	
����K���VB
SDa�j��&����� ��s������D:�3���Ms'��d9���	�rI^������i�g����G��4��KO�=~h�����K���������OA8w.��: 1��/����R�oS"�Xn
���J��,6��i��E8;kv�G{x�D��f���bs ��t1����ZB��������cq�1��X�F�����������9:�zN[������?%�T�8�x;��\��H�)p�
�|�M������d�������\��.��b��������v��� �Q/13�����~�kn
��=�>3�E��x�W�)������X����<��Q��:��n��:#E��=g�[���[�!d|#�<�sO$-�Z���9�w(��C8����#f������V�]���������i
�R)��Js�Jj�
��h+�Q�\���T�4�����G,���=�w����p�%+����g��O-�
�������*T[P��A �33���E�1U[]�I���V%���^r��w
[������N�>�j:��S�Q���EL�
��X	�`U�����X�<.���I��H�J�a8����O����"]w���������lJ�����T���&��
�M@�VE��j��89���1��C*�����-T�;�P��	q�������s�=����^�9��NIy�ej�V&�6*��h�]���2�c����H������y�
��?���}�>r�{���c�7��"���S�����Y���������;8'���*��>�A����������{tr���QK��������1�L�-������Y�C����d&4��r#����}��Pj+����m����������*5���F�GN�+#3�|J���)4����������=���#�_�jdF��`q>oN
aK�nd���D>�3�:z�j�@����7���%rbS
�0 	��,����Y���%�G������������o��W��;���P��z���sz�?�~8�;���w��z1�����G�@���'+�_�� ��i�8��bt�@1::���i-���d,&�1�M�+J��N�����0����`��b8����;�A\'OY���������U\'
o�1t����>uK9����������>��I8�������*�S\����~�V�,��|����:�b$���[pg�p�V�)g�{�nE����[%������9��[��{�S5Dk|�����n��9~bvQ�_�<%A��<����9�a�#B�L
�
���B�����[��k�P�5l��]q��L���������aiwz?���>�w�{���u<<����j{4&���G���3S�@�!��{�q�&�R�2���
���V��XFz�h@��Z��Rl��.���O�FQ�jP�Y �:�)�s�v,��,��bGV�M���z?���{o�Nz�*!]�m���yN���5��_����������1$Z�BeAp��T���:d	�*��a0��7N��
M�U�=e�B�f�*���Jb�8"�gI��p2>1&��	'KV\M���+�>�g���eg��[��p�j�f���3�������~�\���Wm�dr?��$�:�E�o%��c���bD9�J�W���F�1�%�f<\xW���"l8���,QI}CZ#c8���=��~�]��l2��~�@/r������
��4��/�c��Do���>���m����X�G�����"5.,�/���W�/\�\W.��V�j��LF���?�������<�Pd?� 7&+�,e����&����rZ4G�S�WJ�b��z����w<2
��������:�=��e��N��?��������r7��D5��p�2<�j2&���#n|�
�u#�K.^�T.^r�b��'@���4C��R��UYs��Kx)"��@	/9{������i=����|������A�����������0]w�fI�V��8�`~�&,�Xs�k�&���e���^W���);o�������+o2�_p+�n1���v �i@Z~[�S ��0�a0��]z��U�#���{��������K)����'��\���a��[ndl�+����}��g���� ���3�I�e�_���r��I�pJ�>��m�3|���;���$�S�����le�O��C�i$�� o?�~Z�kc��36�*	��%]82�m>�w��F���Hu����D�|W ]�4��T,��?���X�Q��7Z.������0�:������"S�����������Z��0��-_?���!�b8UL��w��_r�H�U�R����J:��&��"�jcp/��`��S���(��&;�t~a�yTJ����o��T�����]x���%���^�v�f�fD����
6gK����oW/�Q��cmP��L'�O�U��)�4\��?�wb'R�V���j���7�Q����f��f�Q��T^��^:_��e~)�*!��}z��������u&�%�u���PJ��?��u��g������F�|�.��y�f��Un���Oz{����o���l5�[��6�i�j����V��N+��
�<w�+���3�o�7*x�����$�mT���8B�
G�K��h�����%����_���`�M�>]��gN�#|������PT��+��uO��Y�U�<~k������vYn�&�}}8��N?RO�����k�\y�����VI���������q��{���u9�={Q�h)��uO�

��������F�M�V���N�����{��D�����V�"�_{�m��6	�jE
�\`����*�>Q�����Z�M� ��|W��
� Mb��aN+���VT@�����|��Z��@�+o�u�n�<;��a�������Rz1�o_��`Y���hF����<K������N�'o�����tq���3��'q<���2�B�d8B_{OH�)�|����9/�������//��.��k�o����a�}�hm.�h+WD#�cs�E[n4�V��F�=���4��[N&�zM����: _o�jc�t��~(i�a��4��3C��l��0����x������}�	a�O�9p�/����r6&���n�w������b�g�|�;Y���{�����X���P+�JS�Z��6C�Y��]�hU�k�`�t[X��W��k��Hzs��>����0���S�4g]��a������b�Q��F3���b*��7�Wb�� ��IB��"��po�N��F|%��+{���c[]e�R��Wf������k\xs[
����M�z�V?CI��u\�������z)i�L�{i���C�u��h$�5�k�"�|ivLk5����j;Vl2O���UX��:�zk�q����LLV5T.�v���g�VK�z�d��S#����t@�0�.�G�q�������@�b�\�ca�AwCV�g�*O�T�+b��vD*�A��wp�� �����i,��Bj����\\�������(��H�����V���������2�e ��b�*Qlubn��J��H��"���..&�u]r1����YC����<���9�J��o�h��^i`_�)���,�z����;�c���-�h	�u�G��]��%�`_�f�3=�b������~EC�f����:@�j6�hV��H�m_
0T��:�Z�Y���g#�G�T��1��J�_��LT�"�9]��I�����`\P��9�mb�2���JW�����+���2a��O����O��n��qk��l^�7w=W���^�����E���y�v��vkyT����z���K�-Yof\���I�nd'Y�$p#C��-�60�Z��'�de&%kG<���������}x��������b����G�)9�Sr����O��V$�/�L��o�W�'G��R��iV�N���=��wF(h�'��c�8y���(qT��%f"
j�8��k�:�0?'�7��O��bi��������;�Yy�&�Z$��%����Wl���"
#8Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#7)
Re: Command Triggers, patch v11

On 25 February 2012 12:00, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

D'oh, just as I sent some more queries...

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show

Any reason why?  I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

Just so it's easy to scan. If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom. It just looks random at the moment.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right?  So I'm not sure that's needed here.  By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Sure, that would be more consistent. You're right, it's not needed.
It just seemed odd that one of the statements lacked what both others
had.

Thanks

--
Thom

#9Thom Brown
thom@linux.com
In reply to: Thom Brown (#8)
Re: Command Triggers, patch v11

On 25 February 2012 12:07, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:00, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

D'oh, just as I sent some more queries...

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show

Any reason why?  I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

Just so it's easy to scan.  If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom.  It just looks random at the moment.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right?  So I'm not sure that's needed here.  By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Sure, that would be more consistent.  You're right, it's not needed.
It just seemed odd that one of the statements lacked what both others
had.

Yet another comment... (I should have really started looking at this
at an earlier stage)

It seems that if one were to enforce a naming convention for relations
as shown in the 2nd example for CREATE COMMAND TRIGGER, it could be
circumvented by someone using CREATE TABLE name AS...

test=# CREATE TABLE badname (id int, a int, b text);
ERROR: invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

--
Thom

#10Thom Brown
thom@linux.com
In reply to: Thom Brown (#9)
Re: Command Triggers, patch v11

On 25 February 2012 12:42, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:07, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:00, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

D'oh, just as I sent some more queries...

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show

Any reason why?  I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

Just so it's easy to scan.  If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom.  It just looks random at the moment.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right?  So I'm not sure that's needed here.  By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Sure, that would be more consistent.  You're right, it's not needed.
It just seemed odd that one of the statements lacked what both others
had.

Yet another comment... (I should have really started looking at this
at an earlier stage)

It seems that if one were to enforce a naming convention for relations
as shown in the 2nd example for CREATE COMMAND TRIGGER, it could be
circumvented by someone using CREATE TABLE name AS...

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

CREATE COMMAND TRIGGER doesn't output in pg_dump or pg_dumpall. I'd
expect ALTER COMMAND TRIGGER to output too for when individual
commands are disabled etc.

--
Thom

#11Thom Brown
thom@linux.com
In reply to: Thom Brown (#10)
Re: Command Triggers, patch v11

On 25 February 2012 13:15, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:42, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:07, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:00, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

D'oh, just as I sent some more queries...

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show

Any reason why?  I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

Just so it's easy to scan.  If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom.  It just looks random at the moment.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right?  So I'm not sure that's needed here.  By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Sure, that would be more consistent.  You're right, it's not needed.
It just seemed odd that one of the statements lacked what both others
had.

Yet another comment... (I should have really started looking at this
at an earlier stage)

It seems that if one were to enforce a naming convention for relations
as shown in the 2nd example for CREATE COMMAND TRIGGER, it could be
circumvented by someone using CREATE TABLE name AS...

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

CREATE COMMAND TRIGGER doesn't output in pg_dump or pg_dumpall.  I'd
expect ALTER COMMAND TRIGGER to output too for when individual
commands are disabled etc.

Just found another case where a table can be created without a command
trigger firing:

SELECT * INTO badname FROM goodname;

--
Thom

#12Thom Brown
thom@linux.com
In reply to: Thom Brown (#11)
Re: Command Triggers, patch v11

On 25 February 2012 13:28, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 13:15, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:42, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:07, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:00, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

D'oh, just as I sent some more queries...

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show

Any reason why?  I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

Just so it's easy to scan.  If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom.  It just looks random at the moment.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right?  So I'm not sure that's needed here.  By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Sure, that would be more consistent.  You're right, it's not needed.
It just seemed odd that one of the statements lacked what both others
had.

Yet another comment... (I should have really started looking at this
at an earlier stage)

It seems that if one were to enforce a naming convention for relations
as shown in the 2nd example for CREATE COMMAND TRIGGER, it could be
circumvented by someone using CREATE TABLE name AS...

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

CREATE COMMAND TRIGGER doesn't output in pg_dump or pg_dumpall.  I'd
expect ALTER COMMAND TRIGGER to output too for when individual
commands are disabled etc.

Just found another case where a table can be created without a command
trigger firing:

SELECT * INTO badname FROM goodname;

Right, hopefully this should be my last piece of list spam for the
time being. (apologies, I thought I'd just try it out at first, but
it's ended up being reviewed piecemeal)

On CREATE COMMAND TRIGGER page:

“The trigger will be associated with the specified command and will
execute the specified function function_name when that command is
run.”
should be:
“The trigger will be associated with the specified commands and will
execute the specified function function_name when those commands are
run.”

“A command trigger's function must return void, the only it can aborts
the execution of the command is by raising an exception.”
should be:
“A command trigger's function must return void. It can then only
abort the execution of the command by raising an exception.”

Remove:
“For a constraint trigger, this is also the name to use when modifying
the trigger's behavior using SET CONSTRAINTS.”

Remove:
“That leaves out the following list of non supported commands.”

s/exercize/exercise/

“that's the case for VACUUM, CLUSTER CREATE INDEX CONCURRENTLY, and
REINDEX DATABASE.”
should be:
“that's the case for VACUUM, CLUSTER, CREATE INDEX CONCURRENTLY, and
REINDEX DATABASE.”

I don’t understand this sentence:
“Triggers on ANY command support more commands than just this list,
and will only provide the command tag argument as NOT NULL.”

On ALTER COMMAND TRIGGER page:

“ALTER COMMAND TRIGGER name ON command SET enabled”
should be:
“ALTER COMMAND TRIGGER name ON command [, ... ] SET enabled”

On DROP COMMAND TRIGGER page:

There’s a mention of CASCADE and RESTRICT. I don’t know of any object
which could be dependant on a command trigger, so I don’t see what
these are for.

An oddity I’ve noticed is that you can add additional commands to an
existing command trigger, and you can also have them execute a
different function to the other commands referenced in the same
trigger.

--
Thom

#13Thom Brown
thom@linux.com
In reply to: Thom Brown (#12)
Re: Command Triggers, patch v11

On 25 February 2012 14:30, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 13:28, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 13:15, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:42, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:07, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:00, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

D'oh, just as I sent some more queries...

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show

Any reason why?  I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

Just so it's easy to scan.  If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom.  It just looks random at the moment.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right?  So I'm not sure that's needed here.  By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Sure, that would be more consistent.  You're right, it's not needed.
It just seemed odd that one of the statements lacked what both others
had.

Yet another comment... (I should have really started looking at this
at an earlier stage)

It seems that if one were to enforce a naming convention for relations
as shown in the 2nd example for CREATE COMMAND TRIGGER, it could be
circumvented by someone using CREATE TABLE name AS...

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

CREATE COMMAND TRIGGER doesn't output in pg_dump or pg_dumpall.  I'd
expect ALTER COMMAND TRIGGER to output too for when individual
commands are disabled etc.

Just found another case where a table can be created without a command
trigger firing:

SELECT * INTO badname FROM goodname;

Right, hopefully this should be my last piece of list spam for the
time being. (apologies, I thought I'd just try it out at first, but
it's ended up being reviewed piecemeal)

I was wrong.. a couple of corrections to my own response:

On CREATE COMMAND TRIGGER page:

“The trigger will be associated with the specified command and will
execute the specified function function_name when that command is
run.”
should be:
“The trigger will be associated with the specified commands and will
execute the specified function function_name when those commands are
run.”

Actually, perhaps "...when any of those commands..."

On ALTER COMMAND TRIGGER page:

“ALTER COMMAND TRIGGER name ON command SET enabled”
should be:
“ALTER COMMAND TRIGGER name ON command [, ... ] SET enabled”

This one is nonsense, so please ignore it.

--
Thom

#14Thom Brown
thom@linux.com
In reply to: Thom Brown (#13)
Re: Command Triggers, patch v11

On 25 February 2012 16:36, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 14:30, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 13:28, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 13:15, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:42, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:07, Thom Brown <thom@linux.com> wrote:

On 25 February 2012 12:00, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

D'oh, just as I sent some more queries...

Thom Brown <thom@linux.com> writes:

Is there any reason why the list of commands that command triggers can
be used with isn't in alphabetical order?  Also it appears to show

Any reason why?  I don't suppose it's really important one way or the
other, so I'm waiting on some more voices before working on it.

Just so it's easy to scan.  If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom.  It just looks random at the moment.

The ALTER COMMAND TRIGGER page also doesn't show which commands it can
be used against.  Perhaps, rather than repeat the list, there could be
a note to say that a list of valid commands can be found on the CREATE
COMMAND TRIGGER page?

Well you can only alter a command that you were successful in creating,
right?  So I'm not sure that's needed here.  By that count though, I
maybe should remove the supported command list from DROP COMMAND TRIGGER
reference page?

Sure, that would be more consistent.  You're right, it's not needed.
It just seemed odd that one of the statements lacked what both others
had.

Yet another comment... (I should have really started looking at this
at an earlier stage)

It seems that if one were to enforce a naming convention for relations
as shown in the 2nd example for CREATE COMMAND TRIGGER, it could be
circumvented by someone using CREATE TABLE name AS...

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

CREATE COMMAND TRIGGER doesn't output in pg_dump or pg_dumpall.  I'd
expect ALTER COMMAND TRIGGER to output too for when individual
commands are disabled etc.

Just found another case where a table can be created without a command
trigger firing:

SELECT * INTO badname FROM goodname;

Right, hopefully this should be my last piece of list spam for the
time being. (apologies, I thought I'd just try it out at first, but
it's ended up being reviewed piecemeal)

I was wrong.. a couple of corrections to my own response:

On CREATE COMMAND TRIGGER page:

“The trigger will be associated with the specified command and will
execute the specified function function_name when that command is
run.”
should be:
“The trigger will be associated with the specified commands and will
execute the specified function function_name when those commands are
run.”

Actually, perhaps "...when any of those commands..."

On ALTER COMMAND TRIGGER page:

“ALTER COMMAND TRIGGER name ON command SET enabled”
should be:
“ALTER COMMAND TRIGGER name ON command [, ... ] SET enabled”

This one is nonsense, so please ignore it.

Further testing reveals a problem with FTS configurations when using
the example function provided in the docs:

test=# CREATE TEXT SEARCH CONFIGURATION test (
PARSER = "default"
);
ERROR: invalid relation name:
test=# CREATE TEXT SEARCH CONFIGURATION fr_test (
PARSER = "default"
);
ERROR: invalid relation name:

The 2nd one should work as it matches the naming convention checked in
the function. The ALTER and DROP equivalents appear to be fine
though.

DROP CAST shares a similar issue too:

test=# DROP CAST (bigint as int4);
ERROR: invalid relation name: �

The odd thing about this one is that CREATE CAST shouldn't match on
name at all, but it creates a cast successfully, whereas DROP CAST
disagrees with the name.

Command triggers for CREATE TYPE don't work, but fine for ALTER TYPE
and DROP TYPE.

Also command triggers for DROP CONVERSION aren't working. A glance at
pg_cmdtrigger shows that the system views the command as "DROP
CONVERSION_P".

What is DROP ASSERTION? It's showing as a valid command for a command
trigger, but it's not documented.

I've noticed that ALTER <object> name OWNER TO role doesn't result in
any trigger being fired except for tables.

ALTER OPERATOR FAMILY .... RENAME TO ... doesn't fire command triggers.

ALTER OPERATOR CLASS with RENAME TO or OWNER TO doesn't fire command
triggers, but with SET SCHEMA it does.

And there's no command trigger available for ALTER VIEW.

I'll hold off on testing any further until a new patch is available.

--
Thom

#15Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#14)
Re: Command Triggers, patch v11

Thanks for your further testing!

Thom Brown <thom@linux.com> writes:

Further testing reveals a problem with FTS configurations when using
the example function provided in the docs:

Could you send me your tests so that I add them to the proper regression
test? I've been lazy on one or two object types and obviously that's
where I have to check some more.

Also command triggers for DROP CONVERSION aren't working. A glance at
pg_cmdtrigger shows that the system views the command as "DROP
CONVERSION_P".

That's easy to fix, that's a typo in gram.y. I'm not seeing other ones
like this though.

-			   | DROP CONVERSION_P					{ $$ = "DROP CONVERSION_P"; }
+			   | DROP CONVERSION_P					{ $$ = "DROP CONVERSION"; }

What is DROP ASSERTION? It's showing as a valid command for a command
trigger, but it's not documented.

It's a Not Implemented Feature for which we have the grammar support to
be able to fill a standard compliant checkbox, or something like that.
It could be better for me to remove explicit support for it in the
command triggers patch?

I've noticed that ALTER <object> name OWNER TO role doesn't result in
any trigger being fired except for tables.

ALTER OPERATOR FAMILY .... RENAME TO ... doesn't fire command triggers.

ALTER OPERATOR CLASS with RENAME TO or OWNER TO doesn't fire command
triggers, but with SET SCHEMA it does.

It seems I've forgotten to add some support here, that happens in
alter.c and is easy enough to check and complete, thanks for the
testing.

And there's no command trigger available for ALTER VIEW.

Will add.

I'll hold off on testing any further until a new patch is available.

That should happen soon. Ah, the joys of coding while kids are at home
thanks to school holidays. I can't count how many times I've been killed
by a captain and married to a princess while writing that patch, sorry
about those hiccups here.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#16Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#15)
Re: Command Triggers, patch v11

On 26 February 2012 14:12, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thanks for your further testing!

Thom Brown <thom@linux.com> writes:

Further testing reveals a problem with FTS configurations when using
the example function provided in the docs:

Could you send me your tests so that I add them to the proper regression
test?  I've been lazy on one or two object types and obviously that's
where I have to check some more.

Which tests? The FTS Config test was what I posted before. I haven't
gone to any great effort to set up tests for each command. I've just
been making them up as I go along.

What is DROP ASSERTION?  It's showing as a valid command for a command
trigger, but it's not documented.

It's a Not Implemented Feature for which we have the grammar support to
be able to fill a standard compliant checkbox, or something like that.
It could be better for me to remove explicit support for it in the
command triggers patch?

Well considering there are commands that exist which we don't allow
triggers on, it seems weird to support triggers on commands which
aren't implemented. DROP ASSERTION doesn't appear anywhere else in
the documentation, so I can't think of how supporting a trigger for it
could be useful.

I've noticed that ALTER <object> name OWNER TO role doesn't result in
any trigger being fired except for tables.

ALTER OPERATOR FAMILY .... RENAME TO ... doesn't fire command triggers.

ALTER OPERATOR CLASS with RENAME TO or OWNER TO doesn't fire command
triggers, but with SET SCHEMA it does.

It seems I've forgotten to add some support here, that happens in
alter.c and is easy enough to check and complete, thanks for the
testing.

So would the fix cover many cases at once?

I'll hold off on testing any further until a new patch is available.

That should happen soon. Ah, the joys of coding while kids are at home
thanks to school holidays. I can't count how many times I've been killed
by a captain and married to a princess while writing that patch, sorry
about those hiccups here.

Being killed by a captain does make things more difficult, yes.

--
Thom

#17Thom Brown
thom@linux.com
In reply to: Thom Brown (#16)
Re: Command Triggers, patch v11

On 26 February 2012 19:49, Thom Brown <thom@linux.com> wrote:

On 26 February 2012 14:12, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thanks for your further testing!

Thom Brown <thom@linux.com> writes:

Further testing reveals a problem with FTS configurations when using
the example function provided in the docs:

Could you send me your tests so that I add them to the proper regression
test?  I've been lazy on one or two object types and obviously that's
where I have to check some more.

Which tests?  The FTS Config test was what I posted before.  I haven't
gone to any great effort to set up tests for each command.  I've just
been making them up as I go along.

What is DROP ASSERTION?  It's showing as a valid command for a command
trigger, but it's not documented.

It's a Not Implemented Feature for which we have the grammar support to
be able to fill a standard compliant checkbox, or something like that.
It could be better for me to remove explicit support for it in the
command triggers patch?

Well considering there are commands that exist which we don't allow
triggers on, it seems weird to support triggers on commands which
aren't implemented.  DROP ASSERTION doesn't appear anywhere else in
the documentation, so I can't think of how supporting a trigger for it
could be useful.

I've noticed that ALTER <object> name OWNER TO role doesn't result in
any trigger being fired except for tables.

ALTER OPERATOR FAMILY .... RENAME TO ... doesn't fire command triggers.

ALTER OPERATOR CLASS with RENAME TO or OWNER TO doesn't fire command
triggers, but with SET SCHEMA it does.

It seems I've forgotten to add some support here, that happens in
alter.c and is easy enough to check and complete, thanks for the
testing.

So would the fix cover many cases at once?

I'll hold off on testing any further until a new patch is available.

That should happen soon. Ah, the joys of coding while kids are at home
thanks to school holidays. I can't count how many times I've been killed
by a captain and married to a princess while writing that patch, sorry
about those hiccups here.

Being killed by a captain does make things more difficult, yes.

I've got a question regarding the function signatures required for
command triggers, and apologies if it's already been discussed to
death (I didn't see all the original conversations around this).
These differ from regular trigger functions which don't require any
arguments, and instead use special variables. Why aren't we doing the
same for command triggers? So instead of having the parameters
tg_when, cmd_tag, objectid, schemaname and objectname, using pl/pgsql
as an example, we'd have the variables TG_WHEN (already exists), TG_OP
(already exists and equivalent to cmd_tag), TG_RELID (already exists,
although maybe not directly equivalent), TG_REL_SCHEMA (doesn't exist
but would replace schemaname) and TG_RELNAME (this is actually
deprecated but could be re-used for this purpose).

Advantages of implementing it like this is that there's consistency in
the trigger system, it's easier as no function parameters required,
and any future options you may wish to add won't break functions from
previous versions, meaning more room for adding stuff later on.

Disadvantages are that there's more maintenance overhead for
supporting multiple languages using special variables.

--
Thom

#18Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#9)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

You won't believe it: CTAS is not implemented as a DDL. Andres did
some work about that and sent a patch that received positive reviews by
both Tom and Robert, once that's in I can easily add support for the
command.

Thanks Andres :)
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#19Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#11)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

SELECT * INTO badname FROM goodname;

Again, see Andres' patch about that.

--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#20Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#18)
Re: Command Triggers, patch v11

On 27 February 2012 19:19, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thom Brown <thom@linux.com> writes:

test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

You won't believe it:  CTAS is not implemented as a DDL.  Andres did
some work about that and sent a patch that received positive reviews by
both Tom and Robert, once that's in I can easily add support for the
command.

Thanks Andres :)

I don't see it anywhere in the commitfest. Has it been properly submitted?

--
Thom

#21Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#17)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

I've got a question regarding the function signatures required for
command triggers, and apologies if it's already been discussed to
death (I didn't see all the original conversations around this).
These differ from regular trigger functions which don't require any
arguments, and instead use special variables. Why aren't we doing the
same for command triggers? So instead of having the parameters

Basically so that we don't have to special code support for each and
every language out there.

Disadvantages are that there's more maintenance overhead for
supporting multiple languages using special variables.

Lots of, so I've been told, enough of it for not taking this choice
seriously. I'll admit I didn't personally looked at what it would
entail implementation wise.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#22Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#6)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

CREATE COMMAND TRIGGER test_cmd_trg
BEFORE CREATE SCHEMA,
CREATE OPERATOR,
CREATE COLLATION,
CREATE CAST
EXECUTE PROCEDURE my_func();

I couldn't drop it completely unless I specified all of those commands. Why?

Because I couldn't find a nice enough way to implement that given the
shared infrastructure Robert and Kaigai did put into place to handle
dropping of objects. It could be that I didn't look hard enough, I'll
be happy to get back that feature.

Incidentally, I've noticed the DROP COMMAND TRIGGER has a mistake in the syntax.

Thanks, fix will be in the next version.

The documentation also needs to cover the pg_cmdtrigger catalogue as
it's not mentioned anywhere.

That too, working on it now, adding forgotten forms you reported and
more, adding regression tests, fixing weird cases, getting there :)

I'm enjoying playing with this feature though btw. :)

Thanks :)
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#23Andres Freund
andres@anarazel.de
In reply to: Thom Brown (#20)
Re: Command Triggers, patch v11

On Monday, February 27, 2012 08:30:31 PM Thom Brown wrote:

On 27 February 2012 19:19, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thom Brown <thom@linux.com> writes:

test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

You won't believe it: CTAS is not implemented as a DDL. Andres did
some work about that and sent a patch that received positive reviews by
both Tom and Robert, once that's in I can easily add support for the
command.

I actually don't think anybody actually reviewed the patch so far. Tom and I
discussed the implementation strategy beforehand a bit though.

Thanks Andres :)

Youre welcome. Thanks for your awesome work that actually made it necessary ;)

I don't see it anywhere in the commitfest. Has it been properly submitted?

I actually always viewed it as a part of the Dim's patch which is why I didn't
submit it as a separate patch. Maybe that was a mistake...

http://archives.postgresql.org/message-
id/201112112346.07611.andres@anarazel.de contains the latest revision.

Andres

#24Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#21)
Re: Command Triggers, patch v11

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Thom Brown <thom@linux.com> writes:

I've got a question regarding the function signatures required for
command triggers, and apologies if it's already been discussed to
death (I didn't see all the original conversations around this).
These differ from regular trigger functions which don't require any
arguments, and instead use special variables. Why aren't we doing the
same for command triggers? So instead of having the parameters

Basically so that we don't have to special code support for each and
every language out there.

FWIW, I agree with Thom on this. If we do it as you suggest, I
confidently predict that it will be less than a year before we seriously
regret it. Given all the discussion around this, it's borderline insane
to believe that the set of parameters to be passed to command triggers
is nailed down and won't need to change in the future.

As for special coding of support, it hardly seems onerous when every
language that has triggers at all has got some provision for the
existing trigger parameters. A bit of copying and pasting should get
the job done.

regards, tom lane

#25Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#24)
Re: Command Triggers, patch v11

Tom Lane <tgl@sss.pgh.pa.us> writes:

FWIW, I agree with Thom on this. If we do it as you suggest, I
confidently predict that it will be less than a year before we seriously
regret it. Given all the discussion around this, it's borderline insane
to believe that the set of parameters to be passed to command triggers
is nailed down and won't need to change in the future.

I agree with the analysis…

As for special coding of support, it hardly seems onerous when every
language that has triggers at all has got some provision for the
existing trigger parameters. A bit of copying and pasting should get
the job done.

But had been (too easily) convinced not to take that route. You changed
my mind already, I'll see about changing the code too tomorrow (a cold
is having me out of steam for tonight).

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#26Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#23)
1 attachment(s)
Re: Command Triggers, patch v11

On Monday, February 27, 2012 08:37:24 PM Andres Freund wrote:

On Monday, February 27, 2012 08:30:31 PM Thom Brown wrote:

On 27 February 2012 19:19, Dimitri Fontaine <dimitri@2ndquadrant.fr>

wrote:

Thom Brown <thom@linux.com> writes:

test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

You won't believe it: CTAS is not implemented as a DDL. Andres did
some work about that and sent a patch that received positive reviews by
both Tom and Robert, once that's in I can easily add support for the
command.

I actually don't think anybody actually reviewed the patch so far. Tom and
I discussed the implementation strategy beforehand a bit though.

Thanks Andres :)

Youre welcome. Thanks for your awesome work that actually made it necessary
;)

I don't see it anywhere in the commitfest. Has it been properly
submitted?

I actually always viewed it as a part of the Dim's patch which is why I
didn't submit it as a separate patch. Maybe that was a mistake...

http://archives.postgresql.org/message-
id/201112112346.07611.andres@anarazel.de contains the latest revision.

I refreshed the patch so it works again on current HEAD. Basically some
trivial fixes and dfd26f9c5f371437f243249025863ea9911aacaa. The latter doesn't
seem necessary to me after the changes, so I simply ditched it. Am I missing
something?

I noticed no new things I dislike about the patch besides what I voiced last
time round:

I attached the - from my side - final version of the patch. I dislike two
things about it:
* code duplication due to error handling. Before making the error message
for various illegal SELECT INTOs the patch actually shrank the code size...
If anybody has a good idea to avoid duplicating that loop around SelectStmt-

ops I would be happy.

* new executor flags to define whether oids should be returned

It would be great if somebody could take a look.

Andres

Attachments:

0001-Transform-CREATE-TABLE-AS-SELECT-INTO-into-a-utility.patchtext/x-patch; charset=UTF-8; name=0001-Transform-CREATE-TABLE-AS-SELECT-INTO-into-a-utility.patchDownload
From 10a3e5c26c4196cdb8ce4ba588c19872d2f1b246 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 28 Feb 2012 00:19:50 +0100
Subject: [PATCH] Transform CREATE TABLE AS/SELECT INTO into a utility
 statement

This basically includes a revert of d8fb1f9adbddd1eef123d66a89a9fc0ecd96f60b
which doesn't seem to be necessary anymore now CTAS is a proper utility
statement in any incarnation.
---
 src/backend/commands/copy.c                |   20 +-
 src/backend/commands/prepare.c             |   69 +----
 src/backend/commands/tablecmds.c           |  335 +++++++++++++++++++++
 src/backend/commands/view.c                |   20 +-
 src/backend/executor/execMain.c            |  441 ++--------------------------
 src/backend/executor/execUtils.c           |    2 -
 src/backend/executor/functions.c           |    4 +-
 src/backend/executor/spi.c                 |   10 +-
 src/backend/nodes/copyfuncs.c              |   18 +-
 src/backend/nodes/equalfuncs.c             |   16 +-
 src/backend/nodes/outfuncs.c               |    4 +-
 src/backend/nodes/readfuncs.c              |    1 -
 src/backend/optimizer/plan/planner.c       |    1 -
 src/backend/optimizer/plan/subselect.c     |    1 -
 src/backend/optimizer/prep/prepjointree.c  |    6 +-
 src/backend/optimizer/util/clauses.c       |    4 +-
 src/backend/parser/analyze.c               |  113 +++++---
 src/backend/parser/gram.y                  |   28 ++-
 src/backend/parser/parse_clause.c          |   21 +-
 src/backend/parser/parse_cte.c             |   21 +-
 src/backend/parser/parse_expr.c            |   21 +-
 src/backend/parser/parser.c                |   38 +++
 src/backend/rewrite/rewriteDefine.c        |    3 +-
 src/backend/tcop/pquery.c                  |   13 +-
 src/backend/tcop/utility.c                 |   46 ++--
 src/include/access/htup.h                  |    2 +-
 src/include/commands/prepare.h             |    3 +
 src/include/commands/tablecmds.h           |    4 +
 src/include/executor/executor.h            |    5 +
 src/include/nodes/execnodes.h              |    9 +-
 src/include/nodes/nodes.h                  |    1 +
 src/include/nodes/parsenodes.h             |   20 +-
 src/include/nodes/plannodes.h              |    2 -
 src/pl/plpgsql/src/pl_exec.c               |   27 +--
 src/test/regress/expected/select_into.out  |   19 ++
 src/test/regress/expected/transactions.out |    2 +-
 src/test/regress/sql/select_into.sql       |   15 +
 37 files changed, 720 insertions(+), 645 deletions(-)

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 110480f..a3c5b4b 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1190,6 +1190,20 @@ BeginCopy(bool is_from,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("COPY (SELECT) WITH OIDS is not supported")));
 
+
+		/* Query mustn't use INTO, either */
+		if(IsA(raw_query, SelectStmt))
+		{
+			SelectStmt* first = (SelectStmt*)raw_query;
+			while (first && first->op != SETOP_NONE)
+				first = first->larg;
+
+			if (first->intoClause)
+				ereport(ERROR,
+				        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				         errmsg("COPY (SELECT INTO) is not supported")));
+		}
+
 		/*
 		 * Run parse analysis and rewrite.	Note this also acquires sufficient
 		 * locks on the source table(s).
@@ -1211,12 +1225,6 @@ BeginCopy(bool is_from,
 		Assert(query->commandType == CMD_SELECT);
 		Assert(query->utilityStmt == NULL);
 
-		/* Query mustn't use INTO, either */
-		if (query->intoClause)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("COPY (SELECT INTO) is not supported")));
-
 		/* plan the query */
 		plan = planner(query, 0, NULL);
 
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 4883abe..86e8cf9 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -43,8 +43,6 @@
 static HTAB *prepared_queries = NULL;
 
 static void InitQueryHashTable(void);
-static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
-			   const char *queryString, EState *estate);
 static Datum build_regtype_array(Oid *param_types, int num_params);
 
 /*
@@ -177,6 +175,9 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
  * EXECUTE, which we might need for error reporting while processing the
  * parameter expressions.  The query_string that we copy from the plan
  * source is that of the original PREPARE.
+ *
+ * If you add additional things here you should check whether
+ * CreateTableAs also needs them.
  */
 void
 ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
@@ -222,51 +223,9 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
 	query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
 									   entry->plansource->query_string);
 
-	/*
-	 * For CREATE TABLE / AS EXECUTE, we must make a copy of the stored query
-	 * so that we can modify its destination (yech, but this has always been
-	 * ugly).  For regular EXECUTE we can just use the cached query, since the
-	 * executor is read-only.
-	 */
-	if (stmt->into)
-	{
-		MemoryContext oldContext;
-		PlannedStmt *pstmt;
-
-		/* Replan if needed, and increment plan refcount transiently */
-		cplan = GetCachedPlan(entry->plansource, paramLI, true);
-
-		/* Copy plan into portal's context, and modify */
-		oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
-		plan_list = copyObject(cplan->stmt_list);
-
-		if (list_length(plan_list) != 1)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("prepared statement is not a SELECT")));
-		pstmt = (PlannedStmt *) linitial(plan_list);
-		if (!IsA(pstmt, PlannedStmt) ||
-			pstmt->commandType != CMD_SELECT ||
-			pstmt->utilityStmt != NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("prepared statement is not a SELECT")));
-		pstmt->intoClause = copyObject(stmt->into);
-
-		MemoryContextSwitchTo(oldContext);
-
-		/* We no longer need the cached plan refcount ... */
-		ReleaseCachedPlan(cplan, true);
-		/* ... and we don't want the portal to depend on it, either */
-		cplan = NULL;
-	}
-	else
-	{
-		/* Replan if needed, and increment plan refcount for portal */
-		cplan = GetCachedPlan(entry->plansource, paramLI, false);
-		plan_list = cplan->stmt_list;
-	}
+	/* Replan if needed, and increment plan refcount for portal */
+	cplan = GetCachedPlan(entry->plansource, paramLI, false);
+	plan_list = cplan->stmt_list;
 
 	PortalDefineQuery(portal,
 					  NULL,
@@ -302,7 +261,7 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
  * CreateQueryDesc(), which allows the executor to make use of the parameters
  * during query execution.
  */
-static ParamListInfo
+ParamListInfo
 EvaluateParams(PreparedStatement *pstmt, List *params,
 			   const char *queryString, EState *estate)
 {
@@ -666,20 +625,6 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
 
 		if (IsA(pstmt, PlannedStmt))
 		{
-			if (execstmt->into)
-			{
-				if (pstmt->commandType != CMD_SELECT ||
-					pstmt->utilityStmt != NULL)
-					ereport(ERROR,
-							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-							 errmsg("prepared statement is not a SELECT")));
-
-				/* Copy the stmt so we can modify it */
-				pstmt = copyObject(pstmt);
-
-				pstmt->intoClause = execstmt->into;
-			}
-
 			ExplainOnePlan(pstmt, es, query_string, paramLI);
 		}
 		else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cd4490a..9a67cf5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -44,6 +44,7 @@
 #include "commands/cluster.h"
 #include "commands/comment.h"
 #include "commands/defrem.h"
+#include "commands/prepare.h"
 #include "commands/sequence.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
@@ -68,6 +69,7 @@
 #include "parser/parser.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
+#include "tcop/tcopprot.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/lock.h"
@@ -195,6 +197,18 @@ struct dropmsgstrings
 	const char *drophint_msg;
 };
 
+/*
+ * Support structure for CTAS
+ */
+typedef struct
+{
+	DestReceiver pub;			/* publicly-known function pointers */
+	EState	   *estate;			/* EState we are working with */
+	Relation	rel;			/* Relation to write to */
+	int			hi_options;		/* heap_insert performance options */
+	BulkInsertState bistate;	/* bulk insert state */
+} DR_intorel;
+
 static const struct dropmsgstrings dropmsgstringarray[] = {
 	{RELKIND_RELATION,
 		ERRCODE_UNDEFINED_TABLE,
@@ -671,6 +685,235 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	return relationId;
 }
 
+void
+CreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
+              ParamListInfo params, DestReceiver *dest){
+	IntoClause *into = stmt->into;
+	Query *query;
+	List *rewritten;
+	PlannedStmt *plan;
+	QueryDesc *queryDesc;
+	Relation intoRelationDesc;
+	CreateStmt *create;
+	int natt;
+	Oid intoRelationId;
+	ListCell *lc;
+	int eflags = 0;
+	EState *param_estate = NULL;
+	CachedPlan *cplan = NULL;
+
+	Assert(IsA(stmt->query, Query));
+	query = (Query*)stmt->query;
+
+	/*
+	 * Construct plans for the supported
+	 */
+	if(query->commandType == CMD_SELECT){
+		rewritten = QueryRewrite((Query *) copyObject(stmt->query));
+
+		if(!rewritten)
+			elog(ERROR, "CREATE TABLE AS/SELECT INTO query rewrote to nothing");
+
+		if(list_length(rewritten) != 1)
+			elog(ERROR, "CREATE TABLE AS/SELECT INTO query rewrote to more than one query");
+
+		plan = pg_plan_query(lfirst(list_head(rewritten)), 0, params);
+	}
+	else if(query->commandType == CMD_UTILITY &&
+	        IsA(query->utilityStmt, ExecuteStmt)){
+		/*
+		 * We cannot easily share more infrastructure with prepare.c's
+		 * ExecuteQuery because teaching it to return a started
+		 * executor would amount to about the same amount of
+		 * duplication as doing it here.
+		 *
+		 */
+		ExecuteStmt *stmt;
+		PreparedStatement *entry;
+		List *plan_list;
+		ParamListInfo paramLI = NULL;
+
+		Assert(IsA(query->utilityStmt, ExecuteStmt));
+		stmt = (ExecuteStmt*)(query->utilityStmt);
+
+		entry = FetchPreparedStatement(stmt->name, true);
+
+		/* Shouldn't find a non-fixed-result cached plan */
+		if (!entry->plansource->fixed_result)
+			elog(ERROR, "EXECUTE does not support variable-result cached plans");
+
+		/* Evaluate parameters, if any */
+		if (entry->plansource->num_params > 0)
+		{
+			param_estate = CreateExecutorState();
+			param_estate->es_param_list_info = params;
+			paramLI = EvaluateParams(entry, stmt->params,
+			                         queryString, param_estate);
+
+		}
+
+		cplan = GetCachedPlan(entry->plansource, paramLI, true);
+		plan_list = cplan->stmt_list;
+
+		if (list_length(plan_list) != 1)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("prepared statement is not a SELECT")));
+
+		plan = (PlannedStmt *) linitial(plan_list);
+		if (!IsA(plan, PlannedStmt) ||
+			plan->commandType != CMD_SELECT ||
+			plan->utilityStmt != NULL)
+			ereport(ERROR,
+			        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+			         errmsg("prepared statement is not a SELECT")));
+
+		queryString = pstrdup(entry->plansource->query_string);
+	}
+	else{
+		elog(ERROR, "unsupported command for CREATE TABLE AS");
+		abort();//silence compiler
+	}
+
+
+	/*
+	 * Use a snapshot with an updated command ID to ensure this query sees
+	 * results of any previously executed queries.
+	 */
+	PushCopiedSnapshot(GetActiveSnapshot());
+	UpdateActiveSnapshotCommandId();
+
+	queryDesc = CreateQueryDesc(plan, queryString,
+								GetActiveSnapshot(), InvalidSnapshot,
+								CreateDestReceiver(DestIntoRel), params,
+	                            false);
+
+	/*
+	 * We need to tell the executor whether it has to produce oids or
+	 * not because it doesn't have enough information to do so itself
+	 * as were not telling it that its inserting into a table (because
+	 * that would be too complicated for now)
+	 */
+	if(interpretOidsOption(into->options))
+		eflags |= EXEC_FLAG_FORCE_OIDS_ON;
+	else
+		eflags |= EXEC_FLAG_FORCE_OIDS_OFF;
+
+	/*
+	 * call ExecutorStart to prepare the plan for execution. Only
+	 * after this we have enough information to actually create a
+	 * target relation
+	 */
+	ExecutorStart(queryDesc, eflags);
+
+	/*
+	 * create the target relation
+	 */
+	create = makeNode(CreateStmt);
+	create->relation = into->rel;
+	create->options = into->options;
+	create->oncommit = into->onCommit;
+	create->tablespacename = into->tableSpaceName;
+
+	/*
+	 * If a column name list was specified in CREATE TABLE AS,
+	 * override the column names derived from the query.  (Too few
+	 * column names are OK, too many are not.)
+	 */
+	if (list_length(into->colNames) > queryDesc->tupDesc->natts)
+		ereport(ERROR,
+		        (errcode(ERRCODE_SYNTAX_ERROR),
+		         errmsg("CREATE TABLE AS specifies too many column names")));
+	lc = list_head(into->colNames);
+	for(natt = 0; natt < queryDesc->tupDesc->natts; natt++){
+		ColumnDef *col = makeNode(ColumnDef);
+		TypeName *type = makeNode(TypeName);
+
+		Form_pg_attribute attribute = queryDesc->tupDesc->attrs[natt];
+
+		if(!lc)
+			col->colname = NameStr(attribute->attname);
+		else{
+			col->colname = strVal(lfirst(lc));
+			lc = lnext(lc);
+		}
+
+		col->collOid = attribute->attcollation;
+
+		type->typeOid = attribute->atttypid;
+		type->typemod = attribute->atttypmod;
+		col->typeName= type;
+
+		/*
+		 * We check the column type here because collOid=InvalidOid
+		 * denotes an invalid oid for a collatable type in
+		 * queryDesc->tupDesc but GetColumnDefCollation - which will
+		 * be called by DefineRelation - defers the default collation
+		 * out of that...
+		 * XXX: A better solution would be welcome.
+		 */
+		CheckAttributeType(col->colname, col->typeName->typeOid, col->collOid,
+		                   NULL, false);
+		create->tableElts = lappend(create->tableElts, col);
+	}
+
+	/*
+	 * Actually create the target table
+	 */
+	intoRelationId = DefineRelation(create, RELKIND_RELATION, InvalidOid);
+	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
+
+
+	if(!into->skipData){
+		DR_intorel *myState = (DR_intorel *) queryDesc->dest;
+		/* setup the target of the DestIntoRel receiver */
+		myState->estate = queryDesc->estate;
+		myState->rel = intoRelationDesc;
+
+		/* run the plan */
+		ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+	}
+
+	/*
+	 * Check INSERT permission on the constructed table.
+	 *
+	 * XXX: It would arguable make sense to do this check only if
+	 * into->skipData is true.
+	 */
+	{
+		RangeTblEntry *rte = makeNode(RangeTblEntry);
+		rte->rtekind = RTE_RELATION;
+		rte->relid = intoRelationId;
+		rte->relkind = RELKIND_RELATION;
+		rte->requiredPerms = ACL_INSERT;
+
+		for (natt = 1; natt <= intoRelationDesc->rd_att->natts; natt++)
+			rte->modifiedCols = bms_add_member(rte->modifiedCols,
+			                                   natt - FirstLowInvalidHeapAttributeNumber);
+
+		ExecCheckRTPerms(list_make1(rte), true);
+	}
+
+	/* run cleanup too */
+	ExecutorFinish(queryDesc);
+	ExecutorEnd(queryDesc);
+
+	/* close rel, but keep lock until commit */
+	heap_close(intoRelationDesc, NoLock);
+
+	FreeQueryDesc(queryDesc);
+
+	if(cplan){
+		ReleaseCachedPlan(cplan, true);
+
+		if (param_estate)
+			FreeExecutorState(param_estate);
+	}
+
+	PopActiveSnapshot();
+}
+
 /*
  * Emit the right error or warning message for a "DROP" command issued on a
  * non-existent relation
@@ -10238,3 +10481,95 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
 
 	ReleaseSysCache(tuple);
 }
+
+/*
+ * Support for SELECT INTO (aka CREATE TABLE AS)
+ *
+ * We implement SELECT INTO by diverting SELECT's normal output with
+ * a specialized DestReceiver type.
+ */
+
+/*
+ * intorel_startup --- executor startup
+ */
+static void
+intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+	myState->bistate = GetBulkInsertState();
+	myState->hi_options = HEAP_INSERT_SKIP_FSM;
+	if (!XLogIsNeeded())
+		myState->hi_options |= HEAP_INSERT_SKIP_WAL;
+
+}
+
+/*
+ * intorel_receive --- receive one tuple
+ */
+static void
+intorel_receive(TupleTableSlot *slot, DestReceiver *self)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+	HeapTuple	tuple;
+
+	/*
+	 * get the heap tuple out of the tuple table slot, making sure we have a
+	 * writable copy
+	 */
+	tuple = ExecMaterializeSlot(slot);
+
+	/*
+	 * force assignment of new OID (see comments in ExecInsert)
+	 */
+	if (myState->rel->rd_rel->relhasoids)
+		HeapTupleSetOid(tuple, InvalidOid);
+
+	heap_insert(myState->rel,
+				tuple,
+				myState->estate->es_output_cid,
+				myState->hi_options,
+				myState->bistate);
+
+	/* We know this is a newly created relation, so there are no indexes */
+}
+
+/*
+ * intorel_shutdown --- executor end
+ */
+static void
+intorel_shutdown(DestReceiver *self)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+
+	FreeBulkInsertState(myState->bistate);
+	if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
+		heap_sync(myState->rel);
+}
+
+/*
+ * intorel_destroy --- release DestReceiver object
+ */
+static void
+intorel_destroy(DestReceiver *self)
+{
+	pfree(self);
+}
+
+/*
+ * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
+ *
+ */
+DestReceiver *
+CreateIntoRelDestReceiver(void)
+{
+	DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
+
+	self->pub.receiveSlot = intorel_receive;
+	self->pub.rStartup = intorel_startup;
+	self->pub.rShutdown = intorel_shutdown;
+	self->pub.rDestroy = intorel_destroy;
+	self->pub.mydest = DestIntoRel;
+	/* private fields will be set on startup */
+
+	return (DestReceiver *) self;
+}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 99fb7db..93e523a 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -428,6 +428,22 @@ DefineView(ViewStmt *stmt, const char *queryString)
 	RangeVar   *view;
 
 	/*
+	 * We check for SELECT INTO before parse analysis because that
+	 * would complain about the SELECT INTO without specific enough detail.
+	 */
+	if(IsA(stmt->query, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)stmt->query;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			         errmsg("views must not contain SELECT INTO")));
+	}
+
+	/*
 	 * Run parse analysis to convert the raw parse tree to a Query.  Note this
 	 * also acquires sufficient locks on the source table(s).
 	 *
@@ -449,10 +465,6 @@ DefineView(ViewStmt *stmt, const char *queryString)
 	 * DefineQueryRewrite(), but that function will complain about a bogus ON
 	 * SELECT rule, and we'd rather the message complain about a view.
 	 */
-	if (viewParse->intoClause != NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("views must not contain SELECT INTO")));
 	if (viewParse->hasModifyingCTE)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 36dcc8e..1979325 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -90,12 +90,6 @@ static char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
 										   int maxfieldlen);
 static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
 				  Plan *planTree);
-static void OpenIntoRel(QueryDesc *queryDesc);
-static void CloseIntoRel(QueryDesc *queryDesc);
-static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
-static void intorel_receive(TupleTableSlot *slot, DestReceiver *self);
-static void intorel_shutdown(DestReceiver *self);
-static void intorel_destroy(DestReceiver *self);
 
 /* end of local decls */
 
@@ -174,11 +168,10 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
 		case CMD_SELECT:
 
 			/*
-			 * SELECT INTO, SELECT FOR UPDATE/SHARE and modifying CTEs need to
+			 * SELECT FOR UPDATE/SHARE and modifying CTEs need to
 			 * mark tuples
 			 */
-			if (queryDesc->plannedstmt->intoClause != NULL ||
-				queryDesc->plannedstmt->rowMarks != NIL ||
+			if (queryDesc->plannedstmt->rowMarks != NIL ||
 				queryDesc->plannedstmt->hasModifyingCTE)
 				estate->es_output_cid = GetCurrentCommandId(true);
 
@@ -212,6 +205,13 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
 	estate->es_top_eflags = eflags;
 	estate->es_instrument = queryDesc->instrument_options;
 
+	if(eflags & EXEC_FLAG_FORCE_OIDS_ON)
+		estate->es_force_oids = ESTATE_FORCE_OIDS_ON;
+	else if(eflags & EXEC_FLAG_FORCE_OIDS_OFF)
+		estate->es_force_oids = ESTATE_FORCE_OIDS_OFF;
+	else
+		estate->es_force_oids = ESTATE_FORCE_OIDS_DEFAULT;
+
 	/*
 	 * Initialize the plan state tree
 	 */
@@ -310,13 +310,6 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 		(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
 
 	/*
-	 * if it's CREATE TABLE AS ... WITH NO DATA, skip plan execution
-	 */
-	if (estate->es_select_into &&
-		queryDesc->plannedstmt->intoClause->skipData)
-		direction = NoMovementScanDirection;
-
-	/*
 	 * run plan
 	 */
 	if (!ScanDirectionIsNoMovement(direction))
@@ -451,12 +444,6 @@ standard_ExecutorEnd(QueryDesc *queryDesc)
 
 	ExecEndPlan(queryDesc->planstate, estate);
 
-	/*
-	 * Close the SELECT INTO relation if any
-	 */
-	if (estate->es_select_into)
-		CloseIntoRel(queryDesc);
-
 	/* do away with our snapshots */
 	UnregisterSnapshot(estate->es_snapshot);
 	UnregisterSnapshot(estate->es_crosscheck_snapshot);
@@ -706,15 +693,6 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 {
 	ListCell   *l;
 
-	/*
-	 * CREATE TABLE AS or SELECT INTO?
-	 *
-	 * XXX should we allow this if the destination is temp?  Considering that
-	 * it would still require catalog changes, probably not.
-	 */
-	if (plannedstmt->intoClause != NULL)
-		PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
-
 	/* Fail if write permissions are requested on any non-temp table */
 	foreach(l, plannedstmt->rtable)
 	{
@@ -864,18 +842,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	}
 
 	/*
-	 * Detect whether we're doing SELECT INTO.  If so, set the es_into_oids
-	 * flag appropriately so that the plan tree will be initialized with the
-	 * correct tuple descriptors.  (Other SELECT INTO stuff comes later.)
-	 */
-	estate->es_select_into = false;
-	if (operation == CMD_SELECT && plannedstmt->intoClause != NULL)
-	{
-		estate->es_select_into = true;
-		estate->es_into_oids = interpretOidsOption(plannedstmt->intoClause->options);
-	}
-
-	/*
 	 * Initialize the executor's tuple table to empty.
 	 */
 	estate->es_tupleTable = NIL;
@@ -926,9 +892,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	planstate = ExecInitNode(plan, estate, eflags);
 
 	/*
-	 * Get the tuple descriptor describing the type of tuples to return. (this
-	 * is especially important if we are creating a relation with "SELECT
-	 * INTO")
+	 * Get the tuple descriptor describing the type of tuples to return.
 	 */
 	tupType = ExecGetResultType(planstate);
 
@@ -968,16 +932,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 
 	queryDesc->tupDesc = tupType;
 	queryDesc->planstate = planstate;
-
-	/*
-	 * If doing SELECT INTO, initialize the "into" relation.  We must wait
-	 * till now so we have the "clean" result tuple type to create the new
-	 * table from.
-	 *
-	 * If EXPLAIN, skip creating the "into" relation.
-	 */
-	if (estate->es_select_into && !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
-		OpenIntoRel(queryDesc);
 }
 
 /*
@@ -1230,7 +1184,7 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
 /*
  *		ExecContextForcesOids
  *
- * This is pretty grotty: when doing INSERT, UPDATE, or SELECT INTO,
+ * This is pretty grotty: when doing INSERT or UPDATE
  * we need to ensure that result tuples have space for an OID iff they are
  * going to be stored into a relation that has OIDs.  In other contexts
  * we are free to choose whether to leave space for OIDs in result tuples
@@ -1255,15 +1209,25 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
  * the ModifyTable node, so ModifyTable has to set es_result_relation_info
  * while initializing each subplan.
  *
- * SELECT INTO is even uglier, because we don't have the INTO relation's
- * descriptor available when this code runs; we have to look aside at a
- * flag set by InitPlan().
+ * SELECT INTO is even uglier, because we don't have the INTO
+ * relation's descriptor available - because its only defined after
+ * the query started - when this code runs; we have to look aside at
+ * flags which is passed to ExecutorStart via eflags.
  */
 bool
 ExecContextForcesOids(PlanState *planstate, bool *hasoids)
 {
 	ResultRelInfo *ri = planstate->state->es_result_relation_info;
 
+	if(planstate->state->es_force_oids == ESTATE_FORCE_OIDS_ON){
+		*hasoids = true;
+		return true;
+	}
+	else if(planstate->state->es_force_oids == ESTATE_FORCE_OIDS_OFF){
+		*hasoids = false;
+		return true;
+	}
+
 	if (ri != NULL)
 	{
 		Relation	rel = ri->ri_RelationDesc;
@@ -1275,12 +1239,6 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids)
 		}
 	}
 
-	if (planstate->state->es_select_into)
-	{
-		*hasoids = planstate->state->es_into_oids;
-		return true;
-	}
-
 	return false;
 }
 
@@ -2290,8 +2248,7 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
 	estate->es_rowMarks = parentestate->es_rowMarks;
 	estate->es_top_eflags = parentestate->es_top_eflags;
 	estate->es_instrument = parentestate->es_instrument;
-	estate->es_select_into = parentestate->es_select_into;
-	estate->es_into_oids = parentestate->es_into_oids;
+
 	/* es_auxmodifytables must NOT be copied */
 
 	/*
@@ -2423,351 +2380,3 @@ EvalPlanQualEnd(EPQState *epqstate)
 	epqstate->planstate = NULL;
 	epqstate->origslot = NULL;
 }
-
-
-/*
- * Support for SELECT INTO (a/k/a CREATE TABLE AS)
- *
- * We implement SELECT INTO by diverting SELECT's normal output with
- * a specialized DestReceiver type.
- */
-
-typedef struct
-{
-	DestReceiver pub;			/* publicly-known function pointers */
-	EState	   *estate;			/* EState we are working with */
-	DestReceiver *origdest;		/* QueryDesc's original receiver */
-	Relation	rel;			/* Relation to write to */
-	int			hi_options;		/* heap_insert performance options */
-	BulkInsertState bistate;	/* bulk insert state */
-} DR_intorel;
-
-/*
- * OpenIntoRel --- actually create the SELECT INTO target relation
- *
- * This also replaces QueryDesc->dest with the special DestReceiver for
- * SELECT INTO.  We assume that the correct result tuple type has already
- * been placed in queryDesc->tupDesc.
- */
-static void
-OpenIntoRel(QueryDesc *queryDesc)
-{
-	IntoClause *into = queryDesc->plannedstmt->intoClause;
-	EState	   *estate = queryDesc->estate;
-	TupleDesc	intoTupDesc = queryDesc->tupDesc;
-	Relation	intoRelationDesc;
-	char	   *intoName;
-	Oid			namespaceId;
-	Oid			tablespaceId;
-	Datum		reloptions;
-	Oid			intoRelationId;
-	DR_intorel *myState;
-	RangeTblEntry  *rte;
-	AttrNumber		attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
-
-	Assert(into);
-
-	/*
-	 * XXX This code needs to be kept in sync with DefineRelation(). Maybe we
-	 * should try to use that function instead.
-	 */
-
-	/*
-	 * Check consistency of arguments
-	 */
-	if (into->onCommit != ONCOMMIT_NOOP
-		&& into->rel->relpersistence != RELPERSISTENCE_TEMP)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-				 errmsg("ON COMMIT can only be used on temporary tables")));
-
-	{
-		AclResult aclresult;
-		int i;
-
-		for (i = 0; i < intoTupDesc->natts; i++)
-		{
-			Oid atttypid = intoTupDesc->attrs[i]->atttypid;
-
-			aclresult = pg_type_aclcheck(atttypid, GetUserId(), ACL_USAGE);
-			if (aclresult != ACLCHECK_OK)
-				aclcheck_error(aclresult, ACL_KIND_TYPE,
-							   format_type_be(atttypid));
-		}
-	}
-
-	/*
-	 * If a column name list was specified in CREATE TABLE AS, override the
-	 * column names derived from the query.  (Too few column names are OK, too
-	 * many are not.)  It would probably be all right to scribble directly on
-	 * the query's result tupdesc, but let's be safe and make a copy.
-	 */
-	if (into->colNames)
-	{
-		ListCell   *lc;
-
-		intoTupDesc = CreateTupleDescCopy(intoTupDesc);
-		attnum = 1;
-		foreach(lc, into->colNames)
-		{
-			char	   *colname = strVal(lfirst(lc));
-
-			if (attnum > intoTupDesc->natts)
-				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("CREATE TABLE AS specifies too many column names")));
-			namestrcpy(&(intoTupDesc->attrs[attnum - 1]->attname), colname);
-			attnum++;
-		}
-	}
-
-	/*
-	 * Find namespace to create in, check its permissions, lock it against
-	 * concurrent drop, and mark into->rel as RELPERSISTENCE_TEMP if the
-	 * selected namespace is temporary.
-	 */
-	intoName = into->rel->relname;
-	namespaceId = RangeVarGetAndCheckCreationNamespace(into->rel, NoLock,
-													   NULL);
-
-	/*
-	 * Security check: disallow creating temp tables from security-restricted
-	 * code.  This is needed because calling code might not expect untrusted
-	 * tables to appear in pg_temp at the front of its search path.
-	 */
-	if (into->rel->relpersistence == RELPERSISTENCE_TEMP
-		&& InSecurityRestrictedOperation())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("cannot create temporary table within security-restricted operation")));
-
-	/*
-	 * Select tablespace to use.  If not specified, use default tablespace
-	 * (which may in turn default to database's default).
-	 */
-	if (into->tableSpaceName)
-	{
-		tablespaceId = get_tablespace_oid(into->tableSpaceName, false);
-	}
-	else
-	{
-		tablespaceId = GetDefaultTablespace(into->rel->relpersistence);
-		/* note InvalidOid is OK in this case */
-	}
-
-	/* Check permissions except when using the database's default space */
-	if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
-	{
-		AclResult	aclresult;
-
-		aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
-										   ACL_CREATE);
-
-		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
-						   get_tablespace_name(tablespaceId));
-	}
-
-	/* Parse and validate any reloptions */
-	reloptions = transformRelOptions((Datum) 0,
-									 into->options,
-									 NULL,
-									 validnsps,
-									 true,
-									 false);
-	(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
-
-	/* Now we can actually create the new relation */
-	intoRelationId = heap_create_with_catalog(intoName,
-											  namespaceId,
-											  tablespaceId,
-											  InvalidOid,
-											  InvalidOid,
-											  InvalidOid,
-											  GetUserId(),
-											  intoTupDesc,
-											  NIL,
-											  RELKIND_RELATION,
-											  into->rel->relpersistence,
-											  false,
-											  false,
-											  true,
-											  0,
-											  into->onCommit,
-											  reloptions,
-											  true,
-											  allowSystemTableMods);
-	Assert(intoRelationId != InvalidOid);
-
-	/*
-	 * Advance command counter so that the newly-created relation's catalog
-	 * tuples will be visible to heap_open.
-	 */
-	CommandCounterIncrement();
-
-	/*
-	 * If necessary, create a TOAST table for the INTO relation. Note that
-	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
-	 * the TOAST table will be visible for insertion.
-	 */
-	reloptions = transformRelOptions((Datum) 0,
-									 into->options,
-									 "toast",
-									 validnsps,
-									 true,
-									 false);
-
-	(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
-
-	AlterTableCreateToastTable(intoRelationId, reloptions);
-
-	/*
-	 * And open the constructed table for writing.
-	 */
-	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
-
-	/*
-	 * Check INSERT permission on the constructed table.
-	 */
-	rte = makeNode(RangeTblEntry);
-	rte->rtekind = RTE_RELATION;
-	rte->relid = intoRelationId;
-	rte->relkind = RELKIND_RELATION;
-	rte->requiredPerms = ACL_INSERT;
-
-	for (attnum = 1; attnum <= intoTupDesc->natts; attnum++)
-		rte->modifiedCols = bms_add_member(rte->modifiedCols,
-				attnum - FirstLowInvalidHeapAttributeNumber);
-
-	ExecCheckRTPerms(list_make1(rte), true);
-
-	/*
-	 * Now replace the query's DestReceiver with one for SELECT INTO
-	 */
-	myState = (DR_intorel *) CreateDestReceiver(DestIntoRel);
-	Assert(myState->pub.mydest == DestIntoRel);
-	myState->estate = estate;
-	myState->origdest = queryDesc->dest;
-	myState->rel = intoRelationDesc;
-
-	queryDesc->dest = (DestReceiver *) myState;
-
-	/*
-	 * We can skip WAL-logging the insertions, unless PITR or streaming
-	 * replication is in use. We can skip the FSM in any case.
-	 */
-	myState->hi_options = HEAP_INSERT_SKIP_FSM |
-		(XLogIsNeeded() ? 0 : HEAP_INSERT_SKIP_WAL);
-	myState->bistate = GetBulkInsertState();
-
-	/* Not using WAL requires smgr_targblock be initially invalid */
-	Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
-}
-
-/*
- * CloseIntoRel --- clean up SELECT INTO at ExecutorEnd time
- */
-static void
-CloseIntoRel(QueryDesc *queryDesc)
-{
-	DR_intorel *myState = (DR_intorel *) queryDesc->dest;
-
-	/*
-	 * OpenIntoRel might never have gotten called, and we also want to guard
-	 * against double destruction.
-	 */
-	if (myState && myState->pub.mydest == DestIntoRel)
-	{
-		FreeBulkInsertState(myState->bistate);
-
-		/* If we skipped using WAL, must heap_sync before commit */
-		if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
-			heap_sync(myState->rel);
-
-		/* close rel, but keep lock until commit */
-		heap_close(myState->rel, NoLock);
-
-		/* restore the receiver belonging to executor's caller */
-		queryDesc->dest = myState->origdest;
-
-		/* might as well invoke my destructor */
-		intorel_destroy((DestReceiver *) myState);
-	}
-}
-
-/*
- * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
- */
-DestReceiver *
-CreateIntoRelDestReceiver(void)
-{
-	DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
-
-	self->pub.receiveSlot = intorel_receive;
-	self->pub.rStartup = intorel_startup;
-	self->pub.rShutdown = intorel_shutdown;
-	self->pub.rDestroy = intorel_destroy;
-	self->pub.mydest = DestIntoRel;
-
-	/* private fields will be set by OpenIntoRel */
-
-	return (DestReceiver *) self;
-}
-
-/*
- * intorel_startup --- executor startup
- */
-static void
-intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
-{
-	/* no-op */
-}
-
-/*
- * intorel_receive --- receive one tuple
- */
-static void
-intorel_receive(TupleTableSlot *slot, DestReceiver *self)
-{
-	DR_intorel *myState = (DR_intorel *) self;
-	HeapTuple	tuple;
-
-	/*
-	 * get the heap tuple out of the tuple table slot, making sure we have a
-	 * writable copy
-	 */
-	tuple = ExecMaterializeSlot(slot);
-
-	/*
-	 * force assignment of new OID (see comments in ExecInsert)
-	 */
-	if (myState->rel->rd_rel->relhasoids)
-		HeapTupleSetOid(tuple, InvalidOid);
-
-	heap_insert(myState->rel,
-				tuple,
-				myState->estate->es_output_cid,
-				myState->hi_options,
-				myState->bistate);
-
-	/* We know this is a newly created relation, so there are no indexes */
-}
-
-/*
- * intorel_shutdown --- executor end
- */
-static void
-intorel_shutdown(DestReceiver *self)
-{
-	/* no-op */
-}
-
-/*
- * intorel_destroy --- release DestReceiver object
- */
-static void
-intorel_destroy(DestReceiver *self)
-{
-	pfree(self);
-}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 6db42e7..40cd5ce 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -137,8 +137,6 @@ CreateExecutorState(void)
 
 	estate->es_top_eflags = 0;
 	estate->es_instrument = 0;
-	estate->es_select_into = false;
-	estate->es_into_oids = false;
 	estate->es_finished = false;
 
 	estate->es_exprcontexts = NIL;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 61f4622..ae8d374 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -535,7 +535,6 @@ init_execution_state(List *queryTree_list,
 
 			if (ps->commandType == CMD_SELECT &&
 				ps->utilityStmt == NULL &&
-				ps->intoClause == NULL &&
 				!ps->hasModifyingCTE)
 				fcache->lazyEval = lasttages->lazyEval = true;
 		}
@@ -1493,8 +1492,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
 	 */
 	if (parse &&
 		parse->commandType == CMD_SELECT &&
-		parse->utilityStmt == NULL &&
-		parse->intoClause == NULL)
+		parse->utilityStmt == NULL)
 	{
 		tlist_ptr = &parse->targetList;
 		tlist = parse->targetList;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 81f284c..ce6abcf 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1917,7 +1917,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 				if (_SPI_current->tuptable)
 					_SPI_current->processed = _SPI_current->tuptable->alloced -
 						_SPI_current->tuptable->free;
-				res = SPI_OK_UTILITY;
+				if(nodeTag(stmt) == T_CreateTableAsStmt &&
+				   ((CreateTableAsStmt*)stmt)->is_select_into)
+					res = SPI_OK_SELINTO;
+				else
+					res = SPI_OK_UTILITY;
 			}
 
 			/*
@@ -2042,9 +2046,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
 	{
 		case CMD_SELECT:
 			Assert(queryDesc->plannedstmt->utilityStmt == NULL);
-			if (queryDesc->plannedstmt->intoClause)		/* select into table? */
-				res = SPI_OK_SELINTO;
-			else if (queryDesc->dest->mydest != DestSPI)
+			if (queryDesc->dest->mydest != DestSPI)
 			{
 				/* Don't return SPI_OK_SELECT if we're discarding result */
 				res = SPI_OK_UTILITY;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7fec4db..e4f1815 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -85,7 +85,6 @@ _copyPlannedStmt(const PlannedStmt *from)
 	COPY_NODE_FIELD(rtable);
 	COPY_NODE_FIELD(resultRelations);
 	COPY_NODE_FIELD(utilityStmt);
-	COPY_NODE_FIELD(intoClause);
 	COPY_NODE_FIELD(subplans);
 	COPY_BITMAPSET_FIELD(rewindPlanIDs);
 	COPY_NODE_FIELD(rowMarks);
@@ -2419,7 +2418,6 @@ _copyQuery(const Query *from)
 	COPY_SCALAR_FIELD(canSetTag);
 	COPY_NODE_FIELD(utilityStmt);
 	COPY_SCALAR_FIELD(resultRelation);
-	COPY_NODE_FIELD(intoClause);
 	COPY_SCALAR_FIELD(hasAggs);
 	COPY_SCALAR_FIELD(hasWindowFuncs);
 	COPY_SCALAR_FIELD(hasSubLinks);
@@ -3207,6 +3205,18 @@ _copyExplainStmt(const ExplainStmt *from)
 	return newnode;
 }
 
+static CreateTableAsStmt *
+_copyCreateTableAsStmt(CreateTableAsStmt *from)
+{
+	CreateTableAsStmt *newnode = makeNode(CreateTableAsStmt);
+
+	COPY_NODE_FIELD(query);
+	COPY_NODE_FIELD(into);
+	COPY_SCALAR_FIELD(is_select_into);
+
+	return newnode;
+}
+
 static CreateSeqStmt *
 _copyCreateSeqStmt(const CreateSeqStmt *from)
 {
@@ -3615,7 +3625,6 @@ _copyExecuteStmt(const ExecuteStmt *from)
 	ExecuteStmt *newnode = makeNode(ExecuteStmt);
 
 	COPY_STRING_FIELD(name);
-	COPY_NODE_FIELD(into);
 	COPY_NODE_FIELD(params);
 
 	return newnode;
@@ -4250,6 +4259,9 @@ copyObject(const void *from)
 		case T_ExplainStmt:
 			retval = _copyExplainStmt(from);
 			break;
+		case T_CreateTableAsStmt:
+			retval = _copyCreateTableAsStmt(from);
+			break;
 		case T_CreateSeqStmt:
 			retval = _copyCreateSeqStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d2a79eb..4ac895e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -900,7 +900,6 @@ _equalQuery(const Query *a, const Query *b)
 	COMPARE_SCALAR_FIELD(canSetTag);
 	COMPARE_NODE_FIELD(utilityStmt);
 	COMPARE_SCALAR_FIELD(resultRelation);
-	COMPARE_NODE_FIELD(intoClause);
 	COMPARE_SCALAR_FIELD(hasAggs);
 	COMPARE_SCALAR_FIELD(hasWindowFuncs);
 	COMPARE_SCALAR_FIELD(hasSubLinks);
@@ -1564,6 +1563,17 @@ _equalExplainStmt(const ExplainStmt *a, const ExplainStmt *b)
 	return true;
 }
 
+
+static bool
+_equalCreateTableAsStmt(CreateTableAsStmt *a, CreateTableAsStmt *b)
+{
+	COMPARE_NODE_FIELD(query);
+	COMPARE_NODE_FIELD(into);
+	COMPARE_SCALAR_FIELD(is_select_into);
+
+	return true;
+}
+
 static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
@@ -1908,7 +1918,6 @@ static bool
 _equalExecuteStmt(const ExecuteStmt *a, const ExecuteStmt *b)
 {
 	COMPARE_STRING_FIELD(name);
-	COMPARE_NODE_FIELD(into);
 	COMPARE_NODE_FIELD(params);
 
 	return true;
@@ -2793,6 +2802,9 @@ equal(const void *a, const void *b)
 		case T_ExplainStmt:
 			retval = _equalExplainStmt(a, b);
 			break;
+		case T_CreateTableAsStmt:
+			retval = _equalCreateTableAsStmt(a, b);
+			break;
 		case T_CreateSeqStmt:
 			retval = _equalCreateSeqStmt(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 25a215e..9cb8592 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -248,7 +248,6 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
 	WRITE_NODE_FIELD(rtable);
 	WRITE_NODE_FIELD(resultRelations);
 	WRITE_NODE_FIELD(utilityStmt);
-	WRITE_NODE_FIELD(intoClause);
 	WRITE_NODE_FIELD(subplans);
 	WRITE_BITMAPSET_FIELD(rewindPlanIDs);
 	WRITE_NODE_FIELD(rowMarks);
@@ -2187,7 +2186,6 @@ _outQuery(StringInfo str, const Query *node)
 		appendStringInfo(str, " :utilityStmt <>");
 
 	WRITE_INT_FIELD(resultRelation);
-	WRITE_NODE_FIELD(intoClause);
 	WRITE_BOOL_FIELD(hasAggs);
 	WRITE_BOOL_FIELD(hasWindowFuncs);
 	WRITE_BOOL_FIELD(hasSubLinks);
@@ -2805,7 +2803,7 @@ _outNode(StringInfo str, const void *obj)
 			case T_RangeVar:
 				_outRangeVar(str, obj);
 				break;
-			case T_IntoClause:
+			case T_IntoClause:/*XXX: This could be removed but dim would need to add it right back*/
 				_outIntoClause(str, obj);
 				break;
 			case T_Var:
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index b9258ad..9b57956 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -198,7 +198,6 @@ _readQuery(void)
 	READ_BOOL_FIELD(canSetTag);
 	READ_NODE_FIELD(utilityStmt);
 	READ_INT_FIELD(resultRelation);
-	READ_NODE_FIELD(intoClause);
 	READ_BOOL_FIELD(hasAggs);
 	READ_BOOL_FIELD(hasWindowFuncs);
 	READ_BOOL_FIELD(hasSubLinks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8bbe977..6b0541b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -233,7 +233,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	result->rtable = glob->finalrtable;
 	result->resultRelations = glob->resultRelations;
 	result->utilityStmt = parse->utilityStmt;
-	result->intoClause = parse->intoClause;
 	result->subplans = glob->subplans;
 	result->rewindPlanIDs = glob->rewindPlanIDs;
 	result->rowMarks = glob->finalrowmarks;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 40a420a..8006787 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1399,7 +1399,6 @@ simplify_EXISTS_query(Query *query)
 	 * are complex.
 	 */
 	if (query->commandType != CMD_SELECT ||
-		query->intoClause ||
 		query->setOperations ||
 		query->hasAggs ||
 		query->hasWindowFuncs ||
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 59a5268..0304c9d 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1129,8 +1129,7 @@ is_simple_subquery(Query *subquery)
 	 */
 	if (!IsA(subquery, Query) ||
 		subquery->commandType != CMD_SELECT ||
-		subquery->utilityStmt != NULL ||
-		subquery->intoClause != NULL)
+		subquery->utilityStmt != NULL)
 		elog(ERROR, "subquery is bogus");
 
 	/*
@@ -1216,8 +1215,7 @@ is_simple_union_all(Query *subquery)
 	/* Let's just make sure it's a valid subselect ... */
 	if (!IsA(subquery, Query) ||
 		subquery->commandType != CMD_SELECT ||
-		subquery->utilityStmt != NULL ||
-		subquery->intoClause != NULL)
+		subquery->utilityStmt != NULL)
 		elog(ERROR, "subquery is bogus");
 
 	/* Is it a set-operation query at all? */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index cd3da46..7d40e3e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4168,7 +4168,6 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 	if (!IsA(querytree, Query) ||
 		querytree->commandType != CMD_SELECT ||
 		querytree->utilityStmt ||
-		querytree->intoClause ||
 		querytree->hasAggs ||
 		querytree->hasWindowFuncs ||
 		querytree->hasSubLinks ||
@@ -4682,8 +4681,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 	 */
 	if (!IsA(querytree, Query) ||
 		querytree->commandType != CMD_SELECT ||
-		querytree->utilityStmt ||
-		querytree->intoClause)
+		querytree->utilityStmt)
 		goto fail;
 
 	/*
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index b187b03..182d6f7 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -62,6 +62,8 @@ static Query *transformDeclareCursorStmt(ParseState *pstate,
 						   DeclareCursorStmt *stmt);
 static Query *transformExplainStmt(ParseState *pstate,
 					 ExplainStmt *stmt);
+static Query *transformCreateTableAsStmt(ParseState *pstate,
+					 CreateTableAsStmt *stmt);
 static void transformLockingClause(ParseState *pstate, Query *qry,
 					   LockingClause *lc, bool pushedDown);
 
@@ -202,6 +204,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
 										  (ExplainStmt *) parseTree);
 			break;
 
+		case T_CreateTableAsStmt:
+			result = transformCreateTableAsStmt(pstate,
+			                           (CreateTableAsStmt *) parseTree);
+			break;
+
 		default:
 
 			/*
@@ -257,6 +264,7 @@ analyze_requires_snapshot(Node *parseTree)
 			result = true;
 			break;
 
+		case T_CreateTableAsStmt:
 		case T_ExplainStmt:
 			/* yes, because we must analyze the contained statement */
 			result = true;
@@ -455,21 +463,29 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 		sub_pstate->p_relnamespace = sub_relnamespace;
 		sub_pstate->p_varnamespace = sub_varnamespace;
 
+		/* The grammar should have produced a SELECT, but it might have INTO */
+		if(IsA(stmt->selectStmt, SelectStmt))
+		{
+			SelectStmt* first = (SelectStmt*)stmt->selectStmt;
+			while (first && first->op != SETOP_NONE)
+				first = first->larg;
+
+			if (first->intoClause)
+				ereport(ERROR,
+				        (errcode(ERRCODE_SYNTAX_ERROR),
+				         errmsg("INSERT ... SELECT cannot specify INTO"),
+				         parser_errposition(pstate,
+				                            exprLocation((Node *)first->intoClause))));
+		}
+
 		selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
 
 		free_parsestate(sub_pstate);
 
-		/* The grammar should have produced a SELECT, but it might have INTO */
 		if (!IsA(selectQuery, Query) ||
 			selectQuery->commandType != CMD_SELECT ||
 			selectQuery->utilityStmt != NULL)
 			elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
-		if (selectQuery->intoClause)
-			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("INSERT ... SELECT cannot specify INTO"),
-					 parser_errposition(pstate,
-						   exprLocation((Node *) selectQuery->intoClause))));
 
 		/*
 		 * Make the source be a subquery in the INSERT's rangetable, and add
@@ -876,6 +892,18 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	Node	   *qual;
 	ListCell   *l;
 
+	/*
+	 * Validity-check whether we got called from somewhere where
+	 * ... INTO was not allowed
+	 */
+	if (stmt->intoClause)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"),
+				 parser_errposition(pstate,
+				                    exprLocation((Node *) stmt->intoClause))));
+
+
 	qry->commandType = CMD_SELECT;
 
 	/* process the WITH clause independently of all else */
@@ -963,8 +991,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 												   pstate->p_windowdefs,
 												   &qry->targetList);
 
-	/* SELECT INTO/CREATE TABLE AS spec is just passed through */
-	qry->intoClause = stmt->intoClause;
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
@@ -1185,9 +1211,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			 errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
 
-	/* CREATE TABLE AS spec is just passed through */
-	qry->intoClause = stmt->intoClause;
-
 	/*
 	 * There mustn't have been any table references in the expressions, else
 	 * strange things would happen, like Cartesian products of those tables
@@ -1253,7 +1276,6 @@ static Query *
 transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
-	SelectStmt *leftmostSelect;
 	int			leftmostRTI;
 	Query	   *leftmostQuery;
 	SetOperationStmt *sostmt;
@@ -1286,20 +1308,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	}
 
 	/*
-	 * Find leftmost leaf SelectStmt; extract the one-time-only items from it
-	 * and from the top-level node.
-	 */
-	leftmostSelect = stmt->larg;
-	while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
-		leftmostSelect = leftmostSelect->larg;
-	Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
-		   leftmostSelect->larg == NULL);
-	qry->intoClause = leftmostSelect->intoClause;
-
-	/* clear this to prevent complaints in transformSetOperationTree() */
-	leftmostSelect->intoClause = NULL;
-
-	/*
 	 * These are not one-time, exactly, but we want to process them here and
 	 * not let transformSetOperationTree() see them --- else it'll just
 	 * recurse right back here!
@@ -1330,7 +1338,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->setOperations = (Node *) sostmt;
 
 	/*
-	 * Re-find leftmost SELECT (now it's a sub-query in rangetable)
+	 * Find leftmost SELECT (it's a sub-query in rangetable)
 	 */
 	node = sostmt->larg;
 	while (node && IsA(node, SetOperationStmt))
@@ -2099,6 +2107,25 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
 				 errmsg("cannot specify both SCROLL and NO SCROLL")));
 
+	/*
+	 * We must explicitly disallow DECLARE CURSOR ... SELECT INTO We
+	 * have to do this before transformStmt() as that will holler if
+	 * it ever finds a intoClause
+	 */
+	if(IsA(stmt->query, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)stmt->query;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+			         errmsg("DECLARE CURSOR cannot specify INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	result = transformStmt(pstate, stmt->query);
 
 	/* Grammar should not have allowed anything but SELECT */
@@ -2107,14 +2134,6 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 		result->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR");
 
-	/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
-	if (result->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-				 errmsg("DECLARE CURSOR cannot specify INTO"),
-				 parser_errposition(pstate,
-								exprLocation((Node *) result->intoClause))));
-
 	/*
 	 * We also disallow data-modifying WITH in a cursor.  (This could be
 	 * allowed, but the semantics of when the updates occur might be
@@ -2183,6 +2202,28 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
 
 
 /*
+ * transformCreateTableAsStmt -
+ *	transform an CREATE TABLE AS/SELECT ... INTO Statement
+ *
+ */
+static Query *
+transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
+{
+	Query	   *result;
+
+	/* transform contained query */
+	stmt->query = (Node *) transformStmt(pstate, stmt->query);
+
+	/* represent the command as a utility Query */
+	result = makeNode(Query);
+	result->commandType = CMD_UTILITY;
+	result->utilityStmt = (Node *) stmt;
+
+	return result;
+}
+
+
+/*
  * Check for features that are not supported together with FOR UPDATE/SHARE.
  *
  * exported so planner can check again after rewriting, query pullup, etc
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d1ce2ab..2208594 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3052,23 +3052,28 @@ ExistingIndex:   USING INDEX index_name				{ $$ = $3; }
 CreateAsStmt:
 		CREATE OptTemp TABLE create_as_target AS SelectStmt opt_with_data
 				{
+                    SelectStmt *n;
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+                    ctas->into = $4;
+                    ctas->query = $6;
+                    ctas->is_select_into = false;
+                    $4->skipData = !($7);
+					$4->rel->relpersistence = $2;
+					$4->skipData = !($7);
+                    $$ = (Node*)ctas;
+
 					/*
 					 * When the SelectStmt is a set-operation tree, we must
 					 * stuff the INTO information into the leftmost component
 					 * Select, because that's where analyze.c will expect
 					 * to find it.
 					 */
-					SelectStmt *n = findLeftmostSelect((SelectStmt *) $6);
+					n = findLeftmostSelect((SelectStmt *) $6);
 					if (n->intoClause != NULL)
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("CREATE TABLE AS cannot specify INTO"),
 								 parser_errposition(exprLocation((Node *) n->intoClause))));
-					n->intoClause = $4;
-					/* cram additional flags into the IntoClause */
-					$4->rel->relpersistence = $2;
-					$4->skipData = !($7);
-					$$ = $6;
 				}
 		;
 
@@ -8275,20 +8280,25 @@ ExecuteStmt: EXECUTE name execute_param_clause
 					ExecuteStmt *n = makeNode(ExecuteStmt);
 					n->name = $2;
 					n->params = $3;
-					n->into = NULL;
 					$$ = (Node *) n;
 				}
 			| CREATE OptTemp TABLE create_as_target AS
 				EXECUTE name execute_param_clause opt_with_data
 				{
 					ExecuteStmt *n = makeNode(ExecuteStmt);
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+
 					n->name = $7;
 					n->params = $8;
-					n->into = $4;
+
 					/* cram additional flags into the IntoClause */
 					$4->rel->relpersistence = $2;
 					$4->skipData = !($9);
-					$$ = (Node *) n;
+
+                    ctas->into = $4;
+                    ctas->query = (Node*)n;
+                    ctas->is_select_into = false;
+					$$ = (Node *) ctas;
 				}
 		;
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 7f4da92..52f449f 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -481,6 +481,21 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	if (r->alias == NULL)
 		elog(ERROR, "subquery in FROM must have an alias");
 
+
+	if(IsA(r->subquery, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)r->subquery;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery in FROM cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *)first->intoClause))));
+	}
+
 	/*
 	 * Analyze and transform the subquery.
 	 */
@@ -495,12 +510,6 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 		query->commandType != CMD_SELECT ||
 		query->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in subquery in FROM");
-	if (query->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery in FROM cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) query->intoClause))));
 
 	/*
 	 * The subquery cannot make use of any variables from FROM items created
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index c0c6240..4ea8ee6 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -241,6 +241,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
 	/* Analysis not done already */
 	Assert(!IsA(cte->ctequery, Query));
 
+	if(IsA(cte->ctequery, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)cte->ctequery;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery in WITH cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	query = parse_sub_analyze(cte->ctequery, pstate, cte, false);
 	cte->ctequery = (Node *) query;
 
@@ -253,13 +267,6 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
 	if (query->utilityStmt != NULL)
 		elog(ERROR, "unexpected utility statement in WITH");
 
-	if (query->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery in WITH cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) query->intoClause))));
-
 	/*
 	 * We disallow data-modifying WITH except at the top level of a query,
 	 * because it's not clear when such a modification should be executed.
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d22d8a1..d4d9121 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1398,6 +1398,21 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		return result;
 
 	pstate->p_hasSubLinks = true;
+
+	if(IsA(sublink->subselect, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)sublink->subselect;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
 
 	/*
@@ -1408,12 +1423,6 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		qtree->commandType != CMD_SELECT ||
 		qtree->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in SubLink");
-	if (qtree->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) qtree->intoClause))));
 
 	sublink->subselect = (Node *) qtree;
 
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index f9ec2b2..854925c 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -37,6 +37,7 @@ raw_parser(const char *str)
 	core_yyscan_t yyscanner;
 	base_yy_extra_type yyextra;
 	int			yyresult;
+	ListCell *c;
 
 	/* initialize the flex scanner */
 	yyscanner = scanner_init(str, &yyextra.core_yy_extra,
@@ -57,6 +58,43 @@ raw_parser(const char *str)
 	if (yyresult)				/* error */
 		return NIL;
 
+	/*
+	 * Some things are rather hard to properly diagnose in grammar
+	 * without complicating/duplicating it too much. So we do some
+	 * postprocessing here.
+	 */
+	foreach(c, yyextra.parsetree){
+		switch(nodeTag(lfirst(c))){
+			/*
+			 * The grammar currently doesn't disambiguate between
+			 * SELECT and SELECT ... INTO. Do that now.
+			 */
+			case T_SelectStmt:
+			{
+				SelectStmt* s = (SelectStmt*)lfirst(c);
+				SelectStmt* first = s;
+				while (first && first->op != SETOP_NONE)
+					first = first->larg;
+				Assert(first && IsA(first, SelectStmt) && first->larg == NULL);
+				if(first->intoClause){
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+					ctas->into = first->intoClause;
+					ctas->query = (Node*)s;
+					ctas->is_select_into = true;
+					/*
+					 * this way everyone can complain if this is set
+					 * without further checks because it shall never
+					 * be set but here.
+					 */
+					first->intoClause = NULL;
+					lfirst(c) = ctas;
+					break;
+				}
+			}
+			default:
+				break;
+		}
+	}
 	return yyextra.parsetree;
 }
 
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 8c87ac5..24199e8 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -323,8 +323,7 @@ DefineQueryRewrite(char *rulename,
 		query = (Query *) linitial(action);
 		if (!is_instead ||
 			query->commandType != CMD_SELECT ||
-			query->utilityStmt != NULL ||
-			query->intoClause != NULL)
+			query->utilityStmt != NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("rules on SELECT must have action INSTEAD SELECT")));
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 42a0fb0..7c50284 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -260,8 +260,7 @@ ChoosePortalStrategy(List *stmts)
 			if (query->canSetTag)
 			{
 				if (query->commandType == CMD_SELECT &&
-					query->utilityStmt == NULL &&
-					query->intoClause == NULL)
+					query->utilityStmt == NULL)
 				{
 					if (query->hasModifyingCTE)
 						return PORTAL_ONE_MOD_WITH;
@@ -285,8 +284,7 @@ ChoosePortalStrategy(List *stmts)
 			if (pstmt->canSetTag)
 			{
 				if (pstmt->commandType == CMD_SELECT &&
-					pstmt->utilityStmt == NULL &&
-					pstmt->intoClause == NULL)
+					pstmt->utilityStmt == NULL)
 				{
 					if (pstmt->hasModifyingCTE)
 						return PORTAL_ONE_MOD_WITH;
@@ -395,8 +393,7 @@ FetchStatementTargetList(Node *stmt)
 		else
 		{
 			if (query->commandType == CMD_SELECT &&
-				query->utilityStmt == NULL &&
-				query->intoClause == NULL)
+				query->utilityStmt == NULL)
 				return query->targetList;
 			if (query->returningList)
 				return query->returningList;
@@ -408,8 +405,7 @@ FetchStatementTargetList(Node *stmt)
 		PlannedStmt *pstmt = (PlannedStmt *) stmt;
 
 		if (pstmt->commandType == CMD_SELECT &&
-			pstmt->utilityStmt == NULL &&
-			pstmt->intoClause == NULL)
+			pstmt->utilityStmt == NULL)
 			return pstmt->planTree->targetlist;
 		if (pstmt->hasReturning)
 			return pstmt->planTree->targetlist;
@@ -430,7 +426,6 @@ FetchStatementTargetList(Node *stmt)
 		ExecuteStmt *estmt = (ExecuteStmt *) stmt;
 		PreparedStatement *entry;
 
-		Assert(!estmt->into);
 		entry = FetchPreparedStatement(estmt->name, true);
 		return FetchPreparedStatementTargetList(entry);
 	}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 5b81c0b..35ccdb3 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -127,9 +127,7 @@ CommandIsReadOnly(Node *parsetree)
 		switch (stmt->commandType)
 		{
 			case CMD_SELECT:
-				if (stmt->intoClause != NULL)
-					return false;		/* SELECT INTO */
-				else if (stmt->rowMarks != NIL)
+				if (stmt->rowMarks != NIL)
 					return false;		/* SELECT FOR UPDATE/SHARE */
 				else if (stmt->hasModifyingCTE)
 					return false;		/* data-modifying CTE */
@@ -198,6 +196,7 @@ check_xact_readonly(Node *parsetree)
 		case T_CreateSchemaStmt:
 		case T_CreateSeqStmt:
 		case T_CreateStmt:
+		case T_CreateTableAsStmt:
 		case T_CreateTableSpaceStmt:
 		case T_CreateTrigStmt:
 		case T_CompositeTypeStmt:
@@ -1036,6 +1035,10 @@ standard_ProcessUtility(Node *parsetree,
 			ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
 			break;
 
+		case T_CreateTableAsStmt:
+			CreateTableAs((CreateTableAsStmt *) parsetree, queryString, params, dest);
+			break;
+
 		case T_VariableSetStmt:
 			ExecSetVariableStmt((VariableSetStmt *) parsetree);
 			break;
@@ -1230,8 +1233,6 @@ UtilityReturnsTuples(Node *parsetree)
 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
 				PreparedStatement *entry;
 
-				if (stmt->into)
-					return false;
 				entry = FetchPreparedStatement(stmt->name, false);
 				if (!entry)
 					return false;		/* not our business to raise error */
@@ -1282,8 +1283,6 @@ UtilityTupleDescriptor(Node *parsetree)
 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
 				PreparedStatement *entry;
 
-				if (stmt->into)
-					return NULL;
 				entry = FetchPreparedStatement(stmt->name, false);
 				if (!entry)
 					return NULL;	/* not our business to raise error */
@@ -1318,8 +1317,7 @@ QueryReturnsTuples(Query *parsetree)
 	{
 		case CMD_SELECT:
 			/* returns tuples ... unless it's DECLARE CURSOR or SELECT INTO */
-			if (parsetree->utilityStmt == NULL &&
-				parsetree->intoClause == NULL)
+			if (parsetree->utilityStmt == NULL)
 				return true;
 			break;
 		case CMD_INSERT:
@@ -1907,6 +1905,13 @@ CreateCommandTag(Node *parsetree)
 			tag = "EXPLAIN";
 			break;
 
+		case T_CreateTableAsStmt:
+			if (((CreateTableAsStmt*)parsetree)->is_select_into)
+				tag = "SELECT INTO";
+			else
+				tag = "CREATE TABLE AS";
+			break;
+
 		case T_VariableSetStmt:
 			switch (((VariableSetStmt *) parsetree)->kind)
 			{
@@ -2060,8 +2065,6 @@ CreateCommandTag(Node *parsetree)
 							Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
 							tag = "DECLARE CURSOR";
 						}
-						else if (stmt->intoClause != NULL)
-							tag = "SELECT INTO";
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
@@ -2110,8 +2113,6 @@ CreateCommandTag(Node *parsetree)
 							Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
 							tag = "DECLARE CURSOR";
 						}
-						else if (stmt->intoClause != NULL)
-							tag = "SELECT INTO";
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
@@ -2178,10 +2179,7 @@ GetCommandLogLevel(Node *parsetree)
 			break;
 
 		case T_SelectStmt:
-			if (((SelectStmt *) parsetree)->intoClause)
-				lev = LOGSTMT_DDL;		/* CREATE AS, SELECT INTO */
-			else
-				lev = LOGSTMT_ALL;
+			lev = LOGSTMT_ALL;
 			break;
 
 			/* utility statements --- same whether raw or cooked */
@@ -2429,6 +2427,10 @@ GetCommandLogLevel(Node *parsetree)
 			}
 			break;
 
+		case T_CreateTableAsStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_VariableSetStmt:
 			lev = LOGSTMT_ALL;
 			break;
@@ -2529,10 +2531,7 @@ GetCommandLogLevel(Node *parsetree)
 				switch (stmt->commandType)
 				{
 					case CMD_SELECT:
-						if (stmt->intoClause != NULL)
-							lev = LOGSTMT_DDL;	/* CREATE AS, SELECT INTO */
-						else
-							lev = LOGSTMT_ALL;	/* SELECT or DECLARE CURSOR */
+						lev = LOGSTMT_ALL;
 						break;
 
 					case CMD_UPDATE:
@@ -2558,10 +2557,7 @@ GetCommandLogLevel(Node *parsetree)
 				switch (stmt->commandType)
 				{
 					case CMD_SELECT:
-						if (stmt->intoClause != NULL)
-							lev = LOGSTMT_DDL;	/* CREATE AS, SELECT INTO */
-						else
-							lev = LOGSTMT_ALL;	/* SELECT or DECLARE CURSOR */
+						lev = LOGSTMT_ALL;
 						break;
 
 					case CMD_UPDATE:
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6a3778d..abbc152 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -311,7 +311,7 @@ do { \
 
 #define HeapTupleHeaderSetOid(tup, oid) \
 do { \
-	Assert((tup)->t_infomask & HEAP_HASOID); \
+	if(!((tup)->t_infomask & HEAP_HASOID)) elog(ERROR, "HEAP_HASOID"); \
 	*((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \
 } while (0)
 
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 472a357..6b545dc 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -54,4 +54,7 @@ extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
 
 void		DropAllPreparedStatements(void);
 
+extern ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
+                                    const char *queryString, EState *estate);
+
 #endif   /* PREPARE_H */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 03f397d..212a51d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -15,6 +15,7 @@
 #define TABLECMDS_H
 
 #include "access/htup.h"
+#include "executor/executor.h"
 #include "nodes/parsenodes.h"
 #include "storage/lock.h"
 #include "utils/relcache.h"
@@ -22,6 +23,9 @@
 
 extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
 
+extern void CreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
+			 ParamListInfo params, DestReceiver *dest);
+
 extern void RemoveRelations(DropStmt *drop);
 
 extern Oid	AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 7f27669..47bd1ee 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -49,12 +49,17 @@
  * AfterTriggerBeginQuery/AfterTriggerEndQuery.  This does not necessarily
  * mean that the plan can't queue any AFTER triggers; just that the caller
  * is responsible for there being a trigger context for them to be queued in.
+ *
+ * FORCE_OIDS tells the executor to build a tupleDesc that includes
+ * the oid column. This is used from tablecmd.c's CreateTableAs.
  */
 #define EXEC_FLAG_EXPLAIN_ONLY	0x0001	/* EXPLAIN, no ANALYZE */
 #define EXEC_FLAG_REWIND		0x0002	/* need efficient rescan */
 #define EXEC_FLAG_BACKWARD		0x0004	/* need backward scan */
 #define EXEC_FLAG_MARK			0x0008	/* need mark/restore */
 #define EXEC_FLAG_SKIP_TRIGGERS 0x0010	/* skip AfterTrigger calls */
+#define EXEC_FLAG_FORCE_OIDS_ON 0x0020	/* force oids in returned tuples */
+#define EXEC_FLAG_FORCE_OIDS_OFF 0x0040	/* force oids in returned tuples */
 
 
 /*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5207102..d268fb9 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -322,6 +322,12 @@ typedef struct ResultRelInfo
 	ProjectionInfo *ri_projectReturning;
 } ResultRelInfo;
 
+typedef enum {
+	ESTATE_FORCE_OIDS_DEFAULT,
+	ESTATE_FORCE_OIDS_ON,
+	ESTATE_FORCE_OIDS_OFF
+} EStateForceOids;
+
 /* ----------------
  *	  EState information
  *
@@ -371,8 +377,7 @@ typedef struct EState
 
 	int			es_top_eflags;	/* eflags passed to ExecutorStart */
 	int			es_instrument;	/* OR of InstrumentOption flags */
-	bool		es_select_into; /* true if doing SELECT INTO */
-	bool		es_into_oids;	/* true to generate OIDs in SELECT INTO */
+	EStateForceOids        es_force_oids;  /* used by CreateTableAs */
 	bool		es_finished;	/* true when ExecutorFinish is done */
 
 	List	   *es_exprcontexts;	/* List of ExprContexts within EState */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 0e7d184..f4d9ce2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -304,6 +304,7 @@ typedef enum NodeTag
 	T_DropdbStmt,
 	T_VacuumStmt,
 	T_ExplainStmt,
+	T_CreateTableAsStmt,
 	T_CreateSeqStmt,
 	T_AlterSeqStmt,
 	T_VariableSetStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ab55639..5c2797c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -111,8 +111,6 @@ typedef struct Query
 	int			resultRelation; /* rtable index of target relation for
 								 * INSERT/UPDATE/DELETE; 0 for SELECT */
 
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
-
 	bool		hasAggs;		/* has aggregates in tlist or havingQual */
 	bool		hasWindowFuncs; /* has window functions in tlist */
 	bool		hasSubLinks;	/* has subquery SubLink */
@@ -1009,7 +1007,7 @@ typedef struct SelectStmt
 	 */
 	List	   *distinctClause; /* NULL, list of DISTINCT ON exprs, or
 								 * lcons(NIL,NIL) for all (SELECT DISTINCT) */
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
+	IntoClause *intoClause;		/* target for SELECT INTO */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	List	   *fromClause;		/* the FROM clause */
 	Node	   *whereClause;	/* WHERE qualification */
@@ -2384,7 +2382,7 @@ typedef struct VacuumStmt
  *		Explain Statement
  *
  * The "query" field is either a raw parse tree (SelectStmt, InsertStmt, etc)
- * or a Query node if parse analysis has been done.  Note that rewriting and
+ * or a Query node if parse analysis has been done. Note that rewriting and
  * planning of the query are always postponed until execution of EXPLAIN.
  * ----------------------
  */
@@ -2396,6 +2394,19 @@ typedef struct ExplainStmt
 } ExplainStmt;
 
 /* ----------------------
+ * analyzing, rewriting and planning are handled as in explain
+ * statements (see comment above) only that query can only be a
+ * SelectStmt and not some other type.
+ */
+typedef struct CreateTableAsStmt
+{
+	NodeTag		type;
+	IntoClause  *into;
+	Node        *query;			/* the query (see comments above) */
+	bool        is_select_into; /* plpgsql wants to disambiguate */
+} CreateTableAsStmt;
+
+/* ----------------------
  * Checkpoint Statement
  * ----------------------
  */
@@ -2509,7 +2520,6 @@ typedef struct ExecuteStmt
 {
 	NodeTag		type;
 	char	   *name;			/* The name of the plan to execute */
-	IntoClause *into;			/* Optional table to store results in */
 	List	   *params;			/* Values to assign to parameters */
 } ExecuteStmt;
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7d90b91..b67d988 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -54,8 +54,6 @@ typedef struct PlannedStmt
 
 	Node	   *utilityStmt;	/* non-null if this is DECLARE CURSOR */
 
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
-
 	List	   *subplans;		/* Plan trees for SubPlan expressions */
 
 	Bitmapset  *rewindPlanIDs;	/* indices of subplans that require REWIND */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 0b126a3..231210a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3286,29 +3286,18 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
 			break;
 
 		case SPI_OK_SELINTO:
-
 			/*
 			 * We want to disallow SELECT INTO for now, because its behavior
 			 * is not consistent with SELECT INTO in a normal plpgsql context.
 			 * (We need to reimplement EXECUTE to parse the string as a
 			 * plpgsql command, not just feed it to SPI_execute.) However,
-			 * CREATE AS should be allowed ... and since it produces the same
-			 * parsetree as SELECT INTO, there's no way to tell the difference
-			 * except to look at the source text.  Wotta kluge!
+			 * CREATE AS should be allowed ...
 			 */
-			{
-				char	   *ptr;
-
-				for (ptr = querystr; *ptr; ptr++)
-					if (!scanner_isspace(*ptr))
-						break;
-				if (*ptr == 'S' || *ptr == 's')
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("EXECUTE of SELECT ... INTO is not implemented"),
-							 errhint("You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.")));
-				break;
-			}
+			ereport(ERROR,
+			        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			         errmsg("EXECUTE of SELECT ... INTO is not implemented"),
+			         errhint("You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.")));
+			break;
 
 			/* Some SPI errors deserve specific error messages */
 		case SPI_ERROR_COPY:
@@ -5759,7 +5748,7 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
 	 */
 	if (!IsA(query, Query))
 		return;
-	if (query->commandType != CMD_SELECT || query->intoClause)
+	if (query->commandType != CMD_SELECT)
 		return;
 	if (query->rtable != NIL)
 		return;
@@ -5833,7 +5822,7 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
 	 */
 	if (!IsA(stmt, PlannedStmt))
 		return;
-	if (stmt->commandType != CMD_SELECT || stmt->intoClause)
+	if (stmt->commandType != CMD_SELECT)
 		return;
 	plan = stmt->planTree;
 	if (!IsA(plan, Result))
diff --git a/src/test/regress/expected/select_into.out b/src/test/regress/expected/select_into.out
index c8327f6..d124382 100644
--- a/src/test/regress/expected/select_into.out
+++ b/src/test/regress/expected/select_into.out
@@ -44,6 +44,25 @@ CREATE TABLE selinto_schema.tmp3 (a,b,c)
 	   AS SELECT oid,relname,relacl FROM pg_class
 	   WHERE relname like '%c%';	-- OK
 RESET SESSION AUTHORIZATION;
+--
+-- disallowed uses of SELECT ... INTO. All should fail
+--
+DECLARE foo CURSOR FOR SELECT 1 INTO b;
+ERROR:  DECLARE CURSOR cannot specify INTO
+LINE 1: DECLARE foo CURSOR FOR SELECT 1 INTO b;
+                                             ^
+COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blub';
+ERROR:  COPY (SELECT INTO) is not supported
+SELECT * FROM (SELECT 1 INTO f) bar;
+ERROR:  subquery in FROM cannot have SELECT INTO
+LINE 1: SELECT * FROM (SELECT 1 INTO f) bar;
+                                     ^
+CREATE VIEW foo AS SELECT 1 INTO b;
+ERROR:  views must not contain SELECT INTO
+INSERT INTO b SELECT 1 INTO f;
+ERROR:  INSERT ... SELECT cannot specify INTO
+LINE 1: INSERT INTO b SELECT 1 INTO f;
+                                    ^
 DROP SCHEMA selinto_schema CASCADE;
 NOTICE:  drop cascades to 3 other objects
 DETAIL:  drop cascades to table selinto_schema.tmp1
diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out
index e9d3908..6981692 100644
--- a/src/test/regress/expected/transactions.out
+++ b/src/test/regress/expected/transactions.out
@@ -139,7 +139,7 @@ SELECT * FROM writetest, temptest; -- ok
 (0 rows)
 
 CREATE TABLE test AS SELECT * FROM writetest; -- fail
-ERROR:  cannot execute SELECT INTO in a read-only transaction
+ERROR:  cannot execute CREATE TABLE AS in a read-only transaction
 START TRANSACTION READ WRITE;
 DROP TABLE writetest; -- ok
 COMMIT;
diff --git a/src/test/regress/sql/select_into.sql b/src/test/regress/sql/select_into.sql
index 09d210b..0c07f76 100644
--- a/src/test/regress/sql/select_into.sql
+++ b/src/test/regress/sql/select_into.sql
@@ -50,6 +50,21 @@ CREATE TABLE selinto_schema.tmp3 (a,b,c)
 	   WHERE relname like '%c%';	-- OK
 RESET SESSION AUTHORIZATION;
 
+
+--
+-- disallowed uses of SELECT ... INTO. All should fail
+--
+DECLARE foo CURSOR FOR SELECT 1 INTO b;
+
+COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blub';
+
+SELECT * FROM (SELECT 1 INTO f) bar;
+
+CREATE VIEW foo AS SELECT 1 INTO b;
+
+INSERT INTO b SELECT 1 INTO f;
+
+
 DROP SCHEMA selinto_schema CASCADE;
 DROP USER selinto_user;
 
-- 
1.7.6.409.ge7a85.dirty

#27Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#26)
Re: Command Triggers, patch v11

Andres Freund <andres@anarazel.de> writes:

I refreshed the patch so it works again on current HEAD. Basically some
trivial fixes and dfd26f9c5f371437f243249025863ea9911aacaa. The latter doesn't
seem necessary to me after the changes, so I simply ditched it. Am I missing
something?

No, that was only needed because execMain.c was overriding somebody
else's tuple receiver. If you're putting the right receiver into the
QueryDesc to start with, it shouldn't be necessary.

I'm confused by this though:

This basically includes a revert of d8fb1f9adbddd1eef123d66a89a9fc0ecd96f60b

I don't find that commit ID anywhere.

regards, tom lane

#28anarazel@anarazel.de
andres@anarazel.de
In reply to: Tom Lane (#27)
Re: Command Triggers, patch v11

Tom Lane <tgl@sss.pgh.pa.us> schrieb:

Andres Freund <andres@anarazel.de> writes:

I refreshed the patch so it works again on current HEAD. Basically

some

trivial fixes and dfd26f9c5f371437f243249025863ea9911aacaa. The

latter doesn't

seem necessary to me after the changes, so I simply ditched it. Am I

missing

something?

No, that was only needed because execMain.c was overriding somebody
else's tuple receiver. If you're putting the right receiver into the
QueryDesc to start with, it shouldn't be necessary.

I'm confused by this though:

This basically includes a revert of

d8fb1f9adbddd1eef123d66a89a9fc0ecd96f60b

I don't find that commit ID anywhere.

That should have been the aforementioned commit. Must have screwed up the copy&paste buffer. Sorry for that.

Andres

Please excuse the brevity and formatting - I am writing this on my mobile phone.

#29Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#22)
Re: Command Triggers, patch v11

On 27 February 2012 19:37, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thom Brown <thom@linux.com> writes:

CREATE COMMAND TRIGGER test_cmd_trg
BEFORE CREATE SCHEMA,
  CREATE OPERATOR,
  CREATE COLLATION,
  CREATE CAST
EXECUTE PROCEDURE my_func();

I couldn't drop it completely unless I specified all of those commands.  Why?

Because I couldn't find a nice enough way to implement that given the
shared infrastructure Robert and Kaigai did put into place to handle
dropping of objects.  It could be that I didn't look hard enough, I'll
be happy to get back that feature.

Well the problem is that you can add commands to a trigger en masse,
but you can only remove them one at a time. Couldn't we at least
allow the removal of multiple commands at the same time? The docs you
wrote suggest you can do this, but you can't.

--
Thom

#30Thom Brown
thom@linux.com
In reply to: Thom Brown (#29)
Re: Command Triggers, patch v11

On 28 February 2012 11:43, Thom Brown <thom@linux.com> wrote:

On 27 February 2012 19:37, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thom Brown <thom@linux.com> writes:

CREATE COMMAND TRIGGER test_cmd_trg
BEFORE CREATE SCHEMA,
  CREATE OPERATOR,
  CREATE COLLATION,
  CREATE CAST
EXECUTE PROCEDURE my_func();

I couldn't drop it completely unless I specified all of those commands.  Why?

Because I couldn't find a nice enough way to implement that given the
shared infrastructure Robert and Kaigai did put into place to handle
dropping of objects.  It could be that I didn't look hard enough, I'll
be happy to get back that feature.

Well the problem is that you can add commands to a trigger en masse,
but you can only remove them one at a time.  Couldn't we at least
allow the removal of multiple commands at the same time?  The docs you
wrote suggest you can do this, but you can't.

Also note that as of commit 66f0cf7da8eeaeca4b9894bfafd61789b514af4a
(Remove useless const qualifier) the patch no longer applies due to
changes to src/backend/commands/typecmds.c.

--
Thom

#31Tom Lane
tgl@sss.pgh.pa.us
In reply to: Thom Brown (#29)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

Well the problem is that you can add commands to a trigger en masse,
but you can only remove them one at a time. Couldn't we at least
allow the removal of multiple commands at the same time? The docs you
wrote suggest you can do this, but you can't.

This seems over-complicated. Triggers on tables do not have alterable
properties, why should command triggers? I vote for

CREATE COMMAND TRIGGER name ... properties ...;

DROP COMMAND TRIGGER name;

full stop. If you want to run the same trigger function on some more
commands, add another trigger name.

regards, tom lane

#32Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Tom Lane (#31)
Re: Command Triggers, patch v11

Tom Lane <tgl@sss.pgh.pa.us> wrote:

This seems over-complicated. Triggers on tables do not have
alterable properties, why should command triggers? I vote for

CREATE COMMAND TRIGGER name ... properties ...;

DROP COMMAND TRIGGER name;

full stop. If you want to run the same trigger function on some
more commands, add another trigger name.

+1

-Kevin

#33Thom Brown
thom@linux.com
In reply to: Tom Lane (#31)
Re: Command Triggers, patch v11

On 28 February 2012 15:03, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Thom Brown <thom@linux.com> writes:

Well the problem is that you can add commands to a trigger en masse,
but you can only remove them one at a time.  Couldn't we at least
allow the removal of multiple commands at the same time?  The docs you
wrote suggest you can do this, but you can't.

This seems over-complicated.  Triggers on tables do not have alterable
properties, why should command triggers?  I vote for

       CREATE COMMAND TRIGGER name ... properties ...;

       DROP COMMAND TRIGGER name;

full stop.  If you want to run the same trigger function on some more
commands, add another trigger name.

This would make more sense, particularly since dropping a command
trigger, as it stands, doesn't necessarily drop the trigger.

--
Thom

#34Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#31)
1 attachment(s)
Re: Command Triggers, patch v11

Hi,

Please find attached v13 of the command trigger patch, fixing most of
known items and rebased against master. Two important items remain to be
done, but I figured I should keep you posted in the meantime.

Tom Lane <tgl@sss.pgh.pa.us> writes:

This seems over-complicated. Triggers on tables do not have alterable
properties, why should command triggers? I vote for

CREATE COMMAND TRIGGER name ... properties ...;

DROP COMMAND TRIGGER name;

full stop. If you want to run the same trigger function on some more
commands, add another trigger name.

I reworked ALTER COMMAND TRIGGER and DROP COMMAND TRIGGER in the
attached, and the catalog too so that the command trigger's name is now
unique there. I added separate index on the command name.

Thom Brown <thom@linux.com> writes:

Just so it's easy to scan. If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom. It just looks random at the moment.

I did M-x sort-lines in the documentation. Still have to add entries
for the new catalog though.

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

I think Andres should add an entry for his patch on the commitfest. Is
it ok?

CREATE COMMAND TRIGGER doesn't output in pg_dump or pg_dumpall. I'd
expect ALTER COMMAND TRIGGER to output too for when individual
commands are disabled etc.

To be done in next patch's version, sorry about that.

Tom Lane <tgl@sss.pgh.pa.us> writes:

FWIW, I agree with Thom on this. If we do it as you suggest, I
confidently predict that it will be less than a year before we seriously
regret it. Given all the discussion around this, it's borderline insane
to believe that the set of parameters to be passed to command triggers
is nailed down and won't need to change in the future.

That too will need to wait for the next revision, it's just about
finding enough cycles, which is definitely happening very soon.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

command-trigger.v13.patch.gzapplication/octet-streamDownload
�/HQO�=iW�����Wtx�`&bf�0�������$�����R��"K�'7��U��nI��Y����P/��uuUuI�������co7M��i������� ����ooo���A��|&���C�O�Og!�(	�4#����;
)��$�����#AD�� %~�P/���[@� ���:!�_9�A{pM�Q�d���_��9�`]��cJ����L:qc
v
]��hr3C7��f�'���o+��S7�I0���2�u3�����&���B�-��r3w���������s�����2	�@V�Vi>���	�N;�����O�G�����~x	A�������C�n�U�K���o�D]O�����]�(j�*M�P4U��X�*|��+��KtEL��n�3�g}����k����o��7/+��'��4
K�Z�8�P��������4m�Z���J!��R	A�eKG�����D�T]�S��@v}z�����g���U�=�H����w�u��~-o���\�i�����)�2&+���FQ!@�
�����
�mwN�yq~nw[�^����1���S������YHO�,"�1�x��'��0��'��w�u�^��D|�t�h��J��=E"RW
c��j�Id#wJ�����
K0e�j�,OfqJO�@��6�N� 
�V�M��o6YW�h��� ������G��F�[���&�B���4u��"te7_N���O�Q<K�T q,���*����G���B7M�m\�=��� EH�sB���v��M`p���;|s�P��M;����&�Ip��FdD	pD���Cl��k��8�P��}�7�z�e���Ec���44AH�A�
�.C��S
�������
�[�\C��SV������p��������}�c|W�h�%���xWmT��@L��4Rv�aR2SI�,�?q�D������r��w�������Ls��0�r�������������$1?.� �
;��$@a��$[�
��m	�����'�0�`0y��`�@�go�Y���5Wp�[Z�x�L���X��@��;���0�n��$iu�,�����M������8,����T
D�����4E�g�;��IR$�� �0=�?������'���Q�?����xB�~�e��.\���ih�T���3t'����d�� v��:�}~f����� CN7$mS:����&\#>c�|tI����AaWCm��=��]���fWy��'qg�=�8��g���C��z�����PD�?���W_��]��Gz��`����A�;�=�����h{����^K��S��i^�z��������+V���,r���-N�g��)���$�Q���:����Y�9����N��/�Fk���n�M����/D�����el�S���tz�Z�!z������{ve��#/�=���6�f�����N��v����7�w�m����X�������v��=��{�>��U���j�yv�z���~ye�{��_v���l��4]Xr�hw�DG�s��yb��uBn=�?(���D5w�u�J��?���@�y��F�B�]���v���l6��J����jhV��P-���*�gh�ls~�r���H]	E[
����fXEk��X�_�D9�PE�X��h�SF��c��I��z�ud��2���*���"�VC
YKY	Y�4�uy�S�X��|���z���xF��vf��t�KS9�l(o1�M�i��Z*��R�����UFJV�-��7�7���k���k������r�������:_����E�_�0bd*��4��X�>C��a�����������z�8�&	�e�`���
��g������/Y���c#�\���) `M�he��Hub��S���d��4��~�	q�����,*��w�]��cZ��Ih�'Q�^���H%Xl�8
!��f��:e�|-oKP��9I� ��Vy���J�� �b��R�+I7Np�L�?%S~[���$7�{�5��t�@`�$��:��x;8� ��>Kz'�TA��������(mDF��u�X�T���b���n����-wC���L}7�y�`H�A+\�0>�`=l%BNc����T)R��x��Ba4d�����N�?�Y��@v����w,����y���U�
�AN�A�x��s����1]�K:afq�������z8
"���(.v����]�
C��J�S�>��OO!���X�yl4C�S�;���f`���1)t/��tf71����\=�U^D�x�o�>�Efa��4fFm�z�;
h�o68^B����
�9�T��=B�u�O+H-�G�����e��JRf[�x���i�������~Mg��T�C�af�����P������'>lF�m	�S����y��H�i��C��Bw&;x5�0 >��y!���$Hn�l=0-@a����:�x����	��# ��R��!���Zx��NI)������0�b�n���CeBK��p�O?�U��������!Z��&�y��9�A�Z�X���IX����k�<������.�O-c�����:/��]��:�7m��-K[GA��|����jn0�S�.{��N-�[���\4���Z���$�A��dw�2��i97-X��4�7hIi��g��[R�P��x3��L�6�H�FG����	���)��|^���x�<N��%���2��YRD5Yp�T!�Pah/�SC"�n��*�Q
��muoa���K/t�.0U�2�������^��	�?F<m/E
���d��
���?K�����6��z8
�L���������t4 OK4��_�$�����8��`���fA���C	�����-��n���X�x���odI��H��Fw6�F���V+��	�	),���E��y|�$��
�7��x�=�">Kk��6
����%�T"Zt��6Q)�wMM�2t��_D��e��"~<D��Y�x�h��n%�C�n�g1��|C��1(�s�f=E/���n����������Q1h�M6b<���P���g����M�Fl*pp�m�Xas�z����q�O����R��1
�y @*`�nJXo�"qM�m�)$K�T�>z>�9E3�`��j0�����[;���r��J��Yk�����B@%��?,�rdVOW�,���T�|(����X��T��D�j�r�������8!t[o�y�o�U�c+tW���`4������u���,�9�E�����CX��q����U*�V���`�<��4�Sp&>��m9��w@�I�h]���8~5-�6@���8�.n��x.��k���E��e�������i�m���������cvq�	����B��n���x�\��}v��;
��g�	����3�B
Fr�n�����\2$7�T�f�q����Mx���iN����={����n��s��VkH��}�e�-\�/���`-K^���_'�������4�c��	�.V�Sqn����]jO�?IjP�;�C�v�(�=M@��Y��Aw��%�Mw4��@[~g���p��0��y�TQ� �s���*
!G���Q�f��*��5��w[�����o�j��F&����*O�14
���W^X��(�,*B����������|z�,.�M\��/S�(��c
k�n��k^��4\^�������z�'�����vo@~}z�����W���p���\����
�i���/����~P[N���[~[�N��cRwyL���H���W��V�2����#�G���������/]�w>���n��,�C�Q����:�����v~n�}��/��x�v�0{��v��������:����wF�W�d�E��L��xy�`��a!���r__�������/Hc���I�{��$��>(�JF���8���p��}u��g-��������|����8�����Hudb�G��d\
�r��z��,n|���v��X=���:f�Ic�e~�JX���*�>+a��>��x�)��1��BAhAc���������W#���TK\��H����%��`F��S����K#=�8X�=W94�����
	
�0�S����z���{z\���u�D}+���n4�|���MQP�<�^�*�
||�V�0B�K��I���
���fN-O��6��p���pU^������Z��^X�{o��h�������o�<�t��2!�����/�	-�����jj^X�/4������tj�z�i�meJX�IG��$�`�|�*-Q������+#��W��AA=<�u�)b�R���w��c����G���x�b�~���%:��Hi���]�o�r��Ya5K0�Ch/���R���%)L��d����q�wqG�����o&�9�^�u�M��!�����rk�B5�}��/K��V�����$�%om�(�.y1)M���
h��D�,8NX�\8�x����� �*�&H����-jUX�*!����;	��S�?��z�^sbJP�.M���G�P��\��F��X���8�����]����N\=�vG����p�D���a�V�1�����w����y��gi�xj�n[��������Z�j��,�Kc��J�~<��n�7��~���DP"��_���j�V#^'�C��p�eU$WQ�[�H0�����+INE.��Ki���t�x�{��R��Y�����u(v��Epa������~��'������30T��;�gCX��-�Rya�S������l2t�,s�sC��xy�?�����?^�fqOP<�� ��,�������_`�=m�:�Y7�����s��Z<Z*=�������#&�I�=!@KG�=�E�P��}����(����*����� ��5�j�{��;2��U���g��r/M���M�R��!c���x2����Fn��v�{S��J�#>������$�j��#�P�~�Gb��0;dC���oc�A��\>�A�wkB^C}��c�����M�Q������b���u�f)��tX����1{����S�U�%���%�RC������G����C�8��E��?ze��e{��/���D�f����rn�<�`oF�
���MN�����1Mz����[k��	:on����.���V}�6
_*����$��\��i9�v����892�U^����9�?����X���R�?�,2��d���Tft����2eY�K������{����[4��!��)���	MS������	����ot's���p�����5V�[��;���cw7Q��m_�6m�����|
�,�3�b����9�S/�G4x��y��k�`_~{wB3��&������G������N�q����5���Em��^'�Ks~�jF��	��f�63$�������r������*qA�:~/^"�W/Wh��Y�/�_k��54���P����F��s�F/Io����_U�<�^M�I
����;����e�L6����C�e7�8���`Mo�zQ�N@�=�����{�[�q;
��7��_-�?Bim8��Q��n�n�6����w��p�ft2��tx�n9?��:��'�V7��g���S�p
GtD����[�>����d$�����������e���"���\>��]�������c��JI����u�
���%W�p�7�^��g�Q�I5�4�I�Y,�T�J�:�(p&�FH< ����9s�A��9`(��o���]p�A\���'^6�l�v�zc��{G�9p�+�gu��h7���T������OK�i[�HeQt���m��7W��*IN���1�:��NN\�
��~���0N���uN3<�r�#�q�*��b�7�(�^<����t�@m����AQ��|/�K�	+.%��Z�������� �v �oi{�r[��8$� �o��-L�>'����m~9��y����?��y��zO��f@���2fpQp5����L����:8E\�K�,{I���e�%�1$��.�oB�%�4m�{�
��c�[p�7��Px#O�0?���3Ynk
p�u�������y�F�(���|
��LL���}�o���vt#�QN&/3�?��$SC�V43y����+�A���93�G�TWW�^%���\�]s��sb���'q�18��[�JE�C������	0	2����!J�W;��4h�X���Kr����p3PiJ���������-�3Z;A���N��3M�0��;JZa��xM4��0��bH{]�|��ziK��Z9f(��bU�s��w��n�e(:��n���������N�j���;@�N
��W:!Z -�Y�a����s17�5�I6#�i$�4�5��u4t��r��������u	iS��{OA�bF��W�m��iVZ"g���r5��u�
��0)=���Lbs5
�TZ��522Q/�����4;h�1���Q�(���
�V���a� ��v����US<3����������N����S�?1�f�>����(��>6�u	r�6d�L��rI,�>�-F?���O�3F�(���Z���
��
Eb
gD��r�aBq�<��E���0�:q�V��t6���q''1������
�����X2�����-�����I9���3��M2A��q�g%��Rb���w2<��w�������Z���9[a���8TA�����F�����g�&�nXde�Mn�J[�M7�Ce*f������rB���(*8Q������Q�`0��w���_�vV	���@�ua������}1_	 ����������W�&���Y���g�T�/�g�.�gE.��ftLn���
v���[��W���~��- v�K�i�n�l�nCH���
��z�o���@���/�J��-i[#�FF �k����j��W�G�4�2�������b��/��e�+7�j����5��l$3����8u��7����|]���;I����_���������xN�w��������6��H}6�L��&��z�[��5>��Fm������4����� U):x4]���1�C$���_Z#�s�/�\���_��zO��@76q(��"���
me�H,��<lMq��Dt	x��I�8��z�C��(�.��(���d��'�4��G��w�P�a.��^X������!�{����
N[c�#Mp��9Y�
�#Ww�+tr�Y&��xH<?)�ARgFjr=����@$P8�P6��2]�O�8W�9�����8��4R{!�$~�~�,��w��ap�=����x�)���1�Bq!�J���xs�Qy���7���EWQV96�]�oL����R����������I1�����������X�PN�����$�'COe�5,�����r�b�����(`��t}�?d�J|W&[#��
��lj� �h��z�M
�o�K��,g!�h8Be@��yRAcvSX��d.*�	b4_�'�*�4�c��	���-�:*�����Rn`�KC�D��D��&�b4
Q q��s�}��nc��g�:kN�����L����J��/�;��OB+P�)T+��}�!�����TH�T�
��z>
[���K�@���_XW	�n)�r8��
�wy��jf�P������d&����=��d�1]B0����fY��n�p1J�V����+�
���
7�AK8c0m��7�5xx5�����][��Wx��f�� 
�������s�����1�g�����,X�6��'��|[D)��'����t�)�=��U��e
�`4e�D�J��u��=<���x(v��gn�%a��2,��M3,v��g���e����m�����,mt���/����N|AO6�S{����1\�/o(�90��}�1H{q�ga+F�D��3�������hP����aK�S������G�b|�\�������q8���m���Q��\�������������iJ�D��lin�@*�X�|�e�����nZs��
3�+d~�s���D����>���"V��j9��������R�&~{u{�~)�-A�)��i|�y��]��%r������_w ���[��q������@�"h����*����NfK�o|�%�������&��R v[4��@�$�8�]���U�Z
���W�����f�w��t^�$T��J�l��J�6�.=WG����?�{�p��{���[���b�����i.M9�
nMp[��x����$M����+�|oz��Gw���������(b����X�]�S�W��$B�J��W�Z�Mk�Z�!y�
ON�5�����d^�it���mv�J1<bO���pF=7�
`[s���Q�I�
�r���W�����M���td������&�3������pq-����� �I.c
�<��WR3H��LP?-Bu�L�y��j~���p[��������I1�l���$��O$y�������mIL_2� \���*�Qz}�|�wL)���1g���������":�o���S,��
]f����X�]��h��F��64Z-��;�C�����j�_��������xD/X�$�~?+H�%������o������;����=�T�U��_����DI�&���y���z�G�`��+�������w�[����'�O1�Ax�O�����8��������F���Ze�1�xB[����J�j%����-<*��a�{P�����+�o�	@Vh�$��H���5���f�~��F�x���+� 2�nI�����'[hk\S��I�_C�D�67[M�����p�4��})E�$��������.���z��y�����]���,a�y������(���%oD�R�]������,)[���4dn�[3��?��_��\�:����5�U��.���l��~�bE�@�E��Q���������1���l_
�J@��2�1
K`1�����[�$�WUar��������.�����R����n���.hcqD8%�7E�{���@�C�/1c.%=*��r1������c�>����oI��nO����9����B��6�v��^�P�zK�W8�8f`'45������9�J����+���B����*L�3��u�8W��������yB�3V�w��A��o��)�6`�7�k����i����������dGh�V����;R�O�H����[��G�D��)b�1������]���e�]�>Z��bq�Z�����|f�6N6��W�3��r<Y���X	�3��-nup���?���Ny&�p�39�Wef��)�KS�p\�������_���?B�+�������w2N�q�)�8}N��CH����Cy�g�H�@�?��b1>�<����7�mJI�h����
eM��ZPh�x���O)���$������X���_[#�rC�]�"��� t�1YM]����E`��^g-6���z(=f��3����R�kR��7���D�����\`G,���+�I+ay���sD-�����M�_��=����|��*qF3D��������0���F��>��X[���� ��p�B�4��8B8�%�#��X^��v�2��������/���K�������sC������7�5�>�H��m�4Y�j��W+UKO�����\�h��[w#\Z�J{��}%%	t��t���X?dB�#�B�F~�]��(Q��b�+��K���I�����s�m�*���!�������|Q{EQ�#�Ca�e�Z���YQ���[�
�}�)*K�2�g��	�pWX�
�c�jk�[j]�t����T�)�e2������|X�*CreX���+���%|����C�d(9��	����������H�I�ah���f��>�B�<��9W3%�y�t�]��U���������U#;��L����mL�O����i[L�$�q�	r,�!����]f���"�1����.��,b���?�`�N>`&*�H���a�c���t�����:i��5S*���>��.i��2�������!��1~>O�es�u
U<��6e��V�P^,��)���f]
ktH�F7�t,wP�z_���:u��
1?����zt#XI1a�L^�
����_�1��u�%��V\�t���Rt�Pt~�c�|��]n������$�����B6���6��l��-D�1�����%����i��>v��s"�j��-�4��v���{5�3c�j��g��5����Kdk|s��f��#}�����O�zDBn����oS�k�����]cq������0�e���%;���}�}k�����E��(���u���w�]��7�w�'?'zC�/v:�y�D��F��]1�����dc'�Yx���g������#�W����-:����v�wB|����u�5
Hs�@:b�����*L�]��m7��p]���Rn���&w����������"�I���)Y�U)�i�"��w��@��A�{z�a4I'�+
�R�Ca3I���xU�:���bY�a<�����hp���D�G,��d�!tZkY����s����i�Z&�Z-v M��u�Oi/~>K���m�;������f�w��
�mq�����=����*:��8����4'�k��G�0�3Dz��q	_����i�"&RIj���<Kyw*U\��"��� �����l.��A���h����m��@F*-=�����a4{�����wn�{2��\l?WL�(>�)�I�0�|d�-Y@���c�9���}�T����_-�4�[��G�~�g[��VDhk��v���p;!H�k�,��wL��SU<�Dm����*:�&WNv��i�;���jm�zSW������`��@����`	\%9��b��h��!���b���M��u*u������_���a���������a��p���O�y[���Z���=�P^����\�5�?�9�{a����fU�4���X%V�����E#��7���D>�q�X=�&�H%�h2���3����U�PZ��yX#*^]~-�(t��L�fS���0������G�l����pa�6��79m�<�����2��{J��&O���x��{�MkU�j�a8m�;���6��Um	=�����{�zn�������u���%Hj�,l�y��N��f��F3���� ����W�(.:�N�\|0
m����;~��03���*9��{$V��)b��cQ�����H�#�_�w���v�l��SP6�L��"��My�?`��`4~�?^�:�]y��Y�TK�	"eT����
)�� )����\�T�t�z��Md7�������txo�U����&���)o&Y�0��+�v�WQ��JT�Yz#�O�n��Wr��uh�C��h�	�9����
�>�`��Va:���Re�c�%�x5�G]���{�����k��p}��+�:��c������yS���o�E5����j��K4���6�w._��f�T��J�����i��n	�KJY���`����5ED��wD
�B0�'��"������P-�$����Tr�s�F���|���zO����FY�7O���:�-���Wv����]��w1F�wk��BF�l�K5�zQVh���t�����~wr�&�9���fc�����|����4�9Y� [�#>,�G4��D�v�Vk4�L`).�%%)M����p��}�;�l��h_a�
!�;�L��m���@��"
���]��?e�Z�fQ��#n �`�va{��v�#��omT�~�����(OqNYb�� �^2��Ro�_��l7��H�r�Q<l�>��:|Ps�9�
Fw%����]P8�����l�����vi�����+����������aQC\V��L�����p�\���VIaTIx������"�[L�����,��E��N�L]:��j.r�L��h~~������B
��,��=���Z��(�I��h��j���k���AV.�
h��,������0��q.���.%�`y��2��r�@�?F��� #����������FZ�Z1h[-��6�>?����������_��7�;���J�P�c.JT���VO���J�dRU~x�~���dUT��F�4Zr�TD8���V���y��/��Ql��3A���^@���1��,'�x2Fc8����M�H���u�eWw���wz7�t���Fb�.�Bs��nv}���*���ny(Y����m����q������`�`��p�rR����.|�1������B�N=�����������?kl��g��3���:1O�$�'&"��&N�9�]�
Db�Z��Z����0�M����
���l�iF�uI��)��Z2y��S?��M�_���f��r�o'�ap-,����O����1��Da8�"m�����5�:����Z�����n=h
d�n����A���";�Z8E=N���]�7�)5lw4
�8�xV�U�@�i�4-f��eK�?P�����L{�2�~�4���X\�6_���1����� �2F��9  _��e�"�*��n���"4a�/��p`F���]�>�E������m��Qo�����x�Z��[d��[~����������N����<�C��'�g�c�*����*^�} ��tn�2X<���dXt6�s� �BU�*Q(���kU��
v]�,�p.��.�����#u������~AFU>�W���� �al���w2�?;;�
.<|?������N,2~����������h�\��1��'��
%s�V�a����<�2�n��A�����g$����� ��bV�'^������.a�M*m���F�v�y������`tU��Q��t���`w�;������
��zn�:��%�c�����X�&��dJg�$T�n��>fbNCa�����d3A"&]2������mVC=L����8{:"��B�L�v��E��2Hk��Y��s�����6�b�7�*<k����W������)��j��N�W�[2Q�K�l����7pk:�r���E8���w���{z�� |/^��������g�����h~��,���{�rD!����,� �#�=����������z��o�>�q�T�i=,U��++S���sA�U@��+���m2��x���r��?(2��f�+w��t2&���V��d����V��FSa��i"�g�^?�)��M����&]8��[
�Qe�|je�O�aF�������f��5����o�[���6�b�*��~�5�r�_a���^S�.x0�o���Q}^�qW9i�y�Jy�� ���7ptE�&�������^�1�LQr���`�5���wL��;f^��($�b��&D��_W �"���P�x�8�i86��1��P��i����P��c6_r�L�62~������W����
E��0�F�v�r��+��������DuB��KX�%W��gCW��=�P�I<�n�����4z�(�����vC�?<S�P����<S}���7\�x�`a������t������7p!��0<;?�������/�{%Y5�k����(�%���t���F��|��ZO��#�����A���E1�Et�H�c���
��M�/�7� �>������y���!�w���N��S'
9}�d�D�-����(6	[���w�pZOU�g���'��9�k-j��wj���=v��h�����8�n�9��"������B"g�t��4�)�������D�k����C�����
k3�!�;���]��������x�j��^H���B�O�>����>�BI��;[��Q!�94�����`��z���Vq��$�a�t����
�L�-�t��*r�_Ky0��x|�����y�'t:�;��_�;d�=��?f���%��\��z�xB��X��h1��F�S�T�T�+����[C
d>w�Cq��'� �����4��j��Y3��RU�������2	������P��4�z;X]R��\�>��H�������[X�����������_�o}��w�;y����i��]�d_O���^a�*����R�����\].��������������~]����9�V�Y��_�������%��_(��p\����i��|Jt���\�����aG�+�IB�K��?/z������eo�������^��_���{������I�Sy��H�_�����ds(�S��"���H���\�E�3���\�y���T�{�����I8���0Y�;�"���f2����n�O�v��,z�#S{(F����a����#�"��S�](�b59�/��d4Y�0�����3[D(��^X���U�����$�N�5�[/a�lE��yC���FP;3��Gh#X�0|N��g��nh`��VK� j9�E
��"|2����B%Y7��s$3dey�;�^ V�e�hEGsr%�����!!OD@^�������$�%Mx�ipp���k�
.S�����H��F��4<�I���P�]O(��lWU%�n���q1��E �E�}�TtC����%.V�	D�<�Y(���d��H:��T�P����=���i� ����iDe��\�4:�}�J���L�Z��pG�$�DP�`����ms7��1����-aXo 0�0�-�Y��c,�g�!�w��l
K	�����"Bl�%M((Z3x�\�l�^�j�c����w����yK��=�;���??��gm�<�z^����3��yrO���'����--���@���B
4�CFM ^c�\f�6�v/����]
*�5�<(�_J�s�@B�!�l�h�����9-���L�HEP�6q@��-'��-@
�
M��hH=e��Kl����gJ�{���|�(o$m�p�?��tA����E�H�R����=w*_
*�G8~���.Tv}���'\�������=?=>}��g�	���Wj~<��dZz~q�R�0����������\����?�����F|�u����o����L#�c���h(}�{o�O���������=�S���\/��p�����=�4I��� 	Nk�X����*!s'�4q���M��<4�\�v8����I�roW����^��i(�eKd;��F����c��_
�4K"���Oa�
��c���?�������=�[SP�kG����F�l���C���4�����HM�J������.zh)D>��^����{Ivb���\gR.A�E��cv���c��r�5��
Y�c��i`���<E���M��9��z���Zy�vE:�Q�F>���ZO4#!�I�4����J����v��v��,�
Oi5M����YR���JC���z��&�H!
�WQ��&��h���b������_�	����y�������a7y�8�\�H��f����=Y���>��O_3,�n\)@u�A���}8A�=�:�S���r�n���Z���A���VS�=������!��d��}��7�c��B���5���n��������$q��/#R�q����g��I��t�4�e(e��G��e�R�P���Wv�=�����F�d��"����y�|��]*|Q��A�dq��ku���Y�]�3(�(8����e���m�v�����K�CI
��YA��`D�4��aV�����z��� �Z��dN�OE�6�p���-���sD
g��~�(�m�?Z�Y��M�6�zn���e���)v����`3*�=����^��>��N�+'p����G�8��!|�xls_�,`���������LA
|��p}����1�Tm�r�G���A|)$AO.��� �4:M�E��I�w!)�������0�.P9����k�!e	Ys=H�d�@,?7v������D{�tZ�r��%*��gZ�T,��ue�3�K���.�S���]X��>	��#�a~Jr{pa��7
��cr*r�]�xn���'-�EoS�Gb!m,T>���;E���F���4�VBE�4A��0��O��_�0��K�����7�(eGZs_�t~\��	��E�1b~Dg��	)A��5��1&���4�l�|��Q�L�C�5%�8�\��$���1��\�YW����G6VFxT�so�%��yI���_�	QMj
�L��R'$BA]a��1v�T>�#�������|o�Rt!����L/M���-$U,��K���_��o������@'������X@���8^�:Z'-
#w@_M�$��oHg$����^����Uww7 sgQ,�S#�y���]�J��t��M���
�**N�l�J�w�������
;�����������>�.
B�y�C�:������7�
0��Ph�/���IO>��X7��#��������-1
���=�0�L`�����]��2���R:fkP��~�r�������3=�Q�1F����Q�������$f�5 ����$�H
G/��&�O���K��&����k�H���l�3)g��29�3�c�S�?_�z�L������z�^+�X����&3�����xs{�8�]E{W�	��}���\��(��H�?����f���_�V�_j$|��&\M���D-�B]*�l�+}��\��k�N��K* GC���8�-���W�6���zj��e�y����W0����x���,eg���Z��W���btw+�}����;�&-���~�����i5�1�)y3zW�I�:�VO�B��5[��I���A���|q����,]:�#E�X8]�mk���� He
DiA����@�4���TAA����"Bc����]�]b�VX�3~�����*�W }vm�u@����a�M��?\FCJ�&�zt�,
�O�
�U�s��������qD���&�t3����jJ����Kr:-��me��j~����3n�J�b�K2KJ�kOQw���I&t������S'ppo3Hi����vB���<3W��j��Rz��75����&{�zb�d{	Pq���������vb
V�����!��CI��t����G��������g������j+@6#�?�ax��x&�?�q���_z���wp��x
�a�~��f��e������2RR9����&BY.���%P���~y/�����
z�����O��y�lA��q�*)y�5�'R�8�yPb�z0��������x�i�`�x(�	\|I��WxA�~67c������
S�g|	�����n^M���Mh+s;e�d�-�d����i)4Nl�y�~]���1Z[��r$���j]-�����,���M'Oc�F�M�&Gx=_��2�����0�9Ry
�T[�/�i��x�z/*��xLu�$���bw�{kG�	8	J9�S[�#}��cV�����8�-"Ycnwv��X�P�q�Hie�Q]����15F�<���
�
G5��B���2�K�)�.�	X��� �~m��M�������f�.H�/�_	���[��1%5��h:�-o�����+IW��g�%�%C��\�s@��
s�T-t�j	u%��@�Iw)�T�5���m���<�B�}�i |����t ����9���(��e�]�XCaC�?E[H,"j��]��H�<���-����
�H���W*��hN���T��H����[���L5���X�U���0���s�e�G������2����H��)�P�`j�s�r�~!�����,�vpp@�"[�oc�������w{�D���|����H�Nm:��=�i9�����w�m���<v��1�%�������vB%Ow�,�d���2�L�.������a:�� @ ���p����a�3-����h�����R4d�\r�a�<���!��s�Q���8���}������������[�
��4{��+��*Vk�����d�Z�M��L��k��a��x�&��#�*�%h�/B��d�^�Cn��d,y�-�0��i���Jb�>bf��2����]�n3dLc�F�����Ly>,�h3��C�h��N�/�%�}\`V�Po��{}�����f�u"������	�������f���0��}����[9;��+A�p!�`I��k���K�B$��5��|���=S�������*G����,r��n�U���Y�
f24��?0S��8,�:����_�T������xM-�DSg^��_m��{�Se�w'J��b��um�W�frv�;�t���^^V+;��Ix�)�3=p��U���y�(�����Q�P��]�e|����m��'�w���X�����:�W�F�3�?����!T�(����8��"X��%I2"�7�&T'�k�8�|D��Ei,�F�'�QL�<��	�����������+
��z�
�z.����U`�u���7EX��n�d��&Z���,��/01	,"���0/7�\��3@Y�k�(p�p��1>=j���p��7���"�i��G�������(�4��"~����������>�\�}Y]�Rk��f����x�`=����"�	tEf��+4��S���~�~QX"
���T�}�����4���`
l������bC�B�E0�y��~�*j�l��{{�h����Z�������������#r�"}:�����mLw�����D@@I���i'�1&z:M�x��x��x��p���+�7���B�C�������D�@�\��}L��k���y"~������&1�������`ph��B��k�tA9U02]�Jb���Kt�\�6��}�����M�(��F1�
�O����BV�n�/�*$i�`E!�e����<�A����[�3�qmNy�Q5F@shv�����p�������f��b"y��_mT�Z�O<k�&�N��a5���m7,K#* �I	<)�*�^�q����������$����U]���X���g���v8�F3���B���j����T?*�F��J>�)�p���k�[�{����D����8��K�J
�P�fT{��gJ�9�����X����N�l�?�����Z�������������,����r�k�����u/�`�����>^��fAC{��x�k�,}�q���D��O�*4�����-�h�^��}�6<��?���+���5�V)?6�"<f������*��,�C�/C3[3��N2n�4��YW�����p�������k��F�O;���)$�����0s�R�
��~T�`h�J�a�������u�9�/�S
�!w��n���RW��m���7�N��D���s���Vs2���DtI%c&�'���S-E�^����2I %��~�-���S��n4�
�:5M�F��������p���'Nt��'[������&[�
^*T�������1�4f����iu�"��
\%�R���f�C����\D��i��}�K��?��}_��|��Z�Q�vSMm3uQ;����s�t,�_{��x����p������_o�q���Uip��-��2���f.I$`�R��c&a��Z~K������dH�S&�3;[BWd���R��z���0e��?J�����v�Q�b'�Y���tY�.�Tr��8���kcQ�`m�����^�e���ppq~|�6Y@D��]J��t+��@z����Z�^tE
pv���o�����k�V�|��
���IH�@�M�
2]��}�s;*XVx-m�SB��H~ B��]�x��U���i�E]�8oA���k��
Z�K�(�'T/�L�w�K5`>@�)�A���);���P�b'�$|<�DX���y~��k��D��qx��Na��iA��7v�0(\�G��z�������e��z��W��&�#��P+#�%Y�%S�e��e�J���H�<���-���)�K�-�IN� ��'���@
����3�Ic�0����uFM���}&����{�g�{��fW����^�L;�{�{�n���,D&Z�E����"f|nn�N�����Q`�p�p"d��WZ�m�-2����������G�%gl�{�<�i�P��������x�y>"EO�g�$�8{��]����+~���4Q��	�����B]�Q��E��+L���[��o �0�W�~�Z�]D�|��:#k��]���j���.�A�l�{�Z#i-3�x�e�=m����(Sm��Z�������s����8]-�����q�8���qbK��z��?�g�ky�U�5d7+�$�B�!��D�z��.�o���k�jA��b37��be>��Hb5���8��y��T�r�{,l��;�3�c�mO���/�</
c���������i=�9=��4���zA���E���t��SO������s�p�w�T{C����at�/�(a
�h���F`	�m\�k�����Y�hg��L���nqw���A��
j'���WN����o�L����������n���X@���HM�)��x-����d>J���W�G$X,�����u*���W��0Y��c'�`4u��\M���,�v��bJ`8
qV��V���j"�����/���2�tZ���2>�v�L���;:�������b����K�����iq�n�u
���	���xb�eh�����K�*l�j~�n�6��wD?�>/I�������O�3RM���[_�)�����1Z9�S����-�^�N��l������c�������:����>
���TZ� q��6���-51�,�xE�b��#����S�i#%,b4	}H�Q���Q�3_`)j�.��79�F��O������d��o;Kgy���\c��0i]����Z������WXg���w1� ��rQm�������L�o����-���aL��O�H&��V�O�f�G��,;�{X��R:N����F8����2�V�p�|�����V�a���?��*�F{������r�%u��������Pl���j�C�	l�)1���id�J��:\�^����C��=�S�a&���K����E��n�� �����RH
��|����e�L����3�{��(�$�/��/�n���u��B��V*
�Rm]+4�+���Z�O�m�Y6zcid�.����Z*�����!+��x)��bh�L����	�')�!�i���6���{L!�Y��@�/�X�L/-�������4�1�(����IL1��^����TB�4����
.��4�:��qIt)�u�'����)7V9�B`Q"��������e�n`�	����CK��t}�U9Q��)��Ni�j7��Rj�J���7��{}E�a��Ib_`���}�J"�5�~.����dP���/���
��6+�PVl��6<|w$*���6��5<���h�*��)1%R�+�(��8d��%N��B�\
��s"����l&�%s����
/����j�kP�\�Ex�"���^�d�3�eJ��8�.����9�3�S��;�_��B��}���{M��v���q�)&D���h�������&K�7�YD�N�_�U��9�<���DRV���� �\�|&
H�O�������dQ��K?��a�� �s�����.C���AIOWko��XE�~~18���y���@���]o/z��`&�]`&o���?��h���S�<��?=t�}�����9`��C��8���}�}���I��h`����|���!0R�oO��]]��O�����������3q�A-���8 tp����L�Y��:
����������H�@�/�L�_�������P����0���%���>+����P,Z�"Cj+	�-�5�����Z0*s����N��r��x���K��)�������OOK�����g�M!6�`j���������a#�Pu��"��M�����=V,
��]x�T�N����K_U7|q0��'���n��Y�g��o-.����JQ�i_{��� ����:��������U�M=���J#���p��us�����\@7C,���39^Fs��DWjY`���c.����\-�/���ZL��L'��Z�i���7[1R�����U���$�������36��%����U�hj��<� �{���#?�3|a3���/�D�Oy���G������Qt���������p���������=;p�����X��v�l��:�
 �n2)"��7��e��w��2[�s����a��n	dW���-��TmpL����s+
���q���f��t�F����T�W$CiRL�]������ ��K����(O7�l�
�Ht��@�<pom�^������sTkm���mc����De�9V�����u��nPo�EIM����h���j�fS������v�%��
��nQk�g�YX6E�	�fm^R��#����:p��e���r�c��a�����2x�~
����4��TO4m�rA�,�����0i�BG��
�z����C�Qo`��V���
x�,��T,��`=i�������'��(�\���w-I_���6�����V�a�`"�JP��{���(��Zo��(�#�D-���C���^����R��d�z5s��C��QC5n��
w�`�N��1� ��e��eA��D_�����Jgb���5_Yn��ZiU�n3��c���������
�5CI�.t�W�k��q6Q��^ty���&n8���jJ3���X�+��u)QM�������Zh9%,�a��P~2���{���*#U6��
�X���Ji"�xci�`����(�m;,@������l�K�g�L�W6(g��6���nO^g�f%�U��[v����j|�k6�H��I���uT�:�;�6X��[������d���,��-���@��p�o��%�x;����v2p���|�g
ntM�����%]v���r%I�L/]8�'�f;D�L3��K��l�:n���@�0��s[�&��QEZ��,��w�!M���E�;��(!��5�n���~�����x��������F��M�y�zu2�k��SN����Q��[�s��m
kf���2�>��%�{xr�}����i�b����wn��xs���(C����&2���~:�����O�COz���B�,J%�%Y-��w����ZC���[���]�h�
�&��}�>5���n��wlecK>MP�G���r�����/W��m�����!�.:pu:[X��`cZ������t5^||�����p�V���J��Hj1,�\�h ����D��]�ts�&��tn �:)�"k�C�Xr3�������I�m����.�����D���@��Ij$qQ����(��d���T�F+=�P��+tCp��-��(������k2�}nq8vM�qIj��o�7X��hf��H�^i���>&"5k~��T�����S�0�<*��P{$�V�<<����h����t���t?W}>�����	.���&~��u3���A��p��@�m��~����
S\�u�Z��
Ag�������������Q���i�h�>���x�w����:�������2��Z���/����34
/��_�X��f����U:�D�Z���9������v1
O�yZ��F3x:���MT��1�h���E���p���@�(1K����q�aH�-Dv����E��Z�e�G��y&67_���z���?���l�&Lr�.��W*T�%���3�h��������isK��*Es�RK����b�7K���p1�"�����z�T��h;�D�h��$]�fh�n����_+�iL���Z�6�9.�]C�:��\*�_nr��Dv���;U�3���Nv�:�z�������19s�-XU'�m�m5�M�Q�>��z��l�s��
��r����������<�(��4�^���Zk7<�����u�������%�)Q������~�axT�����>���x�;^��t\��2��:=����]�h�+~������l7L�)$��������D	/�7�R����"Z��10Q�o�>�V�,�jct<8G��aR8�B��9q������������V�lky�	�G]
���I"���������8�w��?7�;p;^��8?w���[FG�B�h1���n�0u����pa?T_|B������U>z@��e�]�w�	ElL8V�u�/�=�^� :�]�I)���OK�n��8\���O1���q"��|&���-1�J,�n�/��g{�'+�L�xR�W�g"Y���;5�!��a�jWf�m"R��i��v�����Y��,#�H�vF�b�����}��=uJ�%��v�������R.��J���F�SUS
M������
��G)�s�?�sOn���/�X��X&?�Y��h�i��XW�l$e�DDb�.%��>�>�����Yp1����L�B����m�����pgT �ig*sJ�G�l/��_�1s�G�����`������\�XT�n�!����a@��g�����5+����3�'~/�'�a?9�����J��$k:,� Y��y�4�%=�����8w�i�Q��Q�YL�����Gv[*��������Y|�1�eLuK
Ui nt�N����>[Hs0O�|>@���x
����{�������y"���E��(�,e�qOz?%3���[��\V
<�v�������Z��1#�����]rQy� ^
��3V�R��P��\����M��_�v;�*<��r�}{�M�>������w�]v��^�Z��3�@�����b��J2I��)��jNE��1����������z�(��}6�J��''Q�3�T�4���w3�k�1S��9�TC��VVQ,���f������v�Q�����#P��"�,�#��-f�g��=�7�������J��!�C7��Y����&�����x<J��D�'�������'�Q���P���(�$�,A.'Uk����j'm|7�0�Y�k�K�RY���� +&��>�*��>�����-��9���2B�a�t�����e����
������\�V�Sw��
[��wz��� 7�����	���j�W�f�o���;�@jN5k^.@��U�-5rBW,�U��Cj�f��
�\B�Ak����&9��t��.'�a�d���������!�2�y���E�DT{m�H���,�K��i0�^�a�������rG+������
�@9�e)h�O��Dr4��;���\p�wS�%U��G����{_�g&3n���\4�_d���� �X)F��@�	T\ �������
�	O���ON.���"�d�Xe��&���-��X�a��R�w�\��q�����	>��E�,���	'4�����]�b����d����*�k��5@��,Rz4Z/u&'��p�����
�djW��G�� �LP�O���x���z���z$����&��(]1����=b>�x�Dm���B�E| z�Xl!�k�P����Ex�@��&�O��8�C��`G�\1�nu����l@a~�������N�21������3�J�Z�8�Z�g6U=��:���	�O���+DdO
�%���77m�6Dw�%������n�����Y	�e.�O��u�f��Di_��J��d�g���4J�����Pk)��de�,�rQ-'���[���)WA���]i���^i���_Yl
V���a9D�K�1��=#Wn�f����(�0z��Y�B(��Y��Yew����a��$O������������I�����I�Y:�,�,�������8pu�U9�J���5h���cn(N�y;����������Z����
.��������{�3�����5~����wS��Q���x����P�"����d�:w�y*=�'��B��.��������%e����$��s"j���6�<�i�E�
2��������So��������x�$Z�p����f�Z�}�SP'Rh9=��@c&5�@���������*O��9��&��dP*h���[�F�������;v����@GPd�*�X�(��MsGX�wj������02'a�`�GV��9��gz����)�7��d'��`�=��A,F7����
*����a��H�=[R�bj�0$
h�
��C��}�f2K@!��� ~�x�=ip��N2��j<��(���9��{���ZE�\�����=��~=��f��Z
���4W+�����zT�
t�k&.��%�u�;Y�<?�K����/�<����D���M�� �6���a(�Y���j�G�#��-[�w�F),M=umMS��F��a����r��z���E�3�!V����,j
�����IV3����9k��q�Z�Bd�P�L���Qy<.���tK�-$\<����e����_i��������-����-�+�s�g�$����"�����W�yD��\'#� {�h����[����yW$U1�t���Z�q�('2��}���l�����Q�#�{��\��g���� Ol�NKW�����
J��[����jZ�.�}%h��<i�*�Lb�P?��W����R�P�����w��~��&���ZL���&��K`�|���Ei)y���1Yc�d(��4=@�b���!z��Z��DG}���
%�i��6-vr6�U�L1�S��T5m������B�:�B���-�"��rF�ooj�:�FZ�%nCC����������$��9D�����w�'W��6 D�Et��,���Xe���~=��w�> c���Wc��e�7p���e�%,l� �6 H;���gxnV����L�}(����u�d��,��	2��w
6��v�kR�(����c��t���xs!@%;%N]l�����^?�?L�C���O]M��F��K���e����&�����)�D��:]Zc�SO��������F��7d��"[B�@q#ym�� �9?��������������}�
9g���������\�|7���.�����t�38�!�T1e�����0��0;�.��@*����uu�7��63%K�����<�HCDoAL%�r�N'����T�!R%?�_����4�������M�M���E7�{y���@7eP�R�,�����&H���`/b���\����_��4�
�Y+o���z�o6����T"����R�����f���
X�\�eP�b(S��T�8����}��2���
"�~�I�"U���T�~��!���:~��U���gS��M������N�x�@
Ym����9"U�Z�c�h�`����n���\�YtE���	W��������H9&����A�����wF\��1��p���"o��p����FFFP�C������J�o��t���1iK����y#!�Q��ix����w
g�����hr5����$����������-����|c<V�5�bl��+ ���{� Q��������<O�B�E�N���o�.Q�W���b�J \�GC���K��?SS��o��S���hF)�4S�TA�_�53	�5�/jc�
�.����4^	o��R�2
�/z��]7�����lTJ�@3��������I�X�t��v�Q��J��u�`b�/��m�z�	��cC�I�~�;�
S������uYx�>��)G)�e���1<���\�����B&>X3T:�_k���w�M	�ok��h$���'��E�}�n���v�e)��h�����sbc�VA_�8����t	���,����8���2���1d�W2X���f��)	�(��+
yxo�+�����l�����K�V����.|�1��%�`c�x��hWR�"��u�����8�-�0�el��Kw��`�1�
�X��������
RqX>-`����D���������T�+�,�j"�A�����Hf3�_���=��r��T�����/����o��6�n�:"LJO�������S#�����c���m7��q�q����t�23}��M�x��Z���
S00&`�+i��th^BxK��ad=���M6���$;���)����@k`��r;!��������T��z�����wx����*����Y|�~������A��hU�~��9�j5�UOX���dML�#��]e�+�A�����Yv�V���j�".�LL���	�d2�59r1���J�n����k=�%�<{�]�Zx�wX�p2����Ew^���0aT�j����uz�}h���� ��t��:����������z2w�yN���M�
��d��BT����o��N/@�8�������.G%�U����`�f������2y��z��d@�F�^8[.�.��n���P2�$�W�m����b*�C��r~%�G��[�M������k^�'������C('����=4����|<-+w����+U��Z��1�88��o(���n/����V��z�5+I���"epGp�G�#�@"A�����I�U�5�WK/�\��m����������ps��8�P���,yF
�J P�r���n��||�!'��(;�}Gc�
�>���7�!+9P��^	r�]�E,	=���4fH�E�\�ll���1�GS�!�\�����&dJ���O�3��	{�[����(e	E���]8�btk���|U��-��=��t�s�8O���k��O���M��uw<v�HRW���Y���3��=������m�yq������f ���_i�jf���-����re�k��L�"d��Q�Ji���6�L��L���[��l;vpkX�1��U/��O3I!s�����scZ�����������w�����D�Wj�9K8�]�����|6WJ��BxkU@�aR@����s��*������4R�geQ��t�����U��`�I���x��D,���O�����FQ�"���mN�o��/���Q��^�&YU>z+�������f���z��.�����s��q",q�Y���<e1-���-�>���z�-�r�E;;��cS�b���g�z�m����R��-t�Z��@NK��N	�G4�g���S���RE%�}���I��lTe�F��S��O�G�MV��_���_�&p���5)'�]��r���B�r���j�1�B�iW ���h�P�'�G�l?'��d�:8SD���w����+���2Q��	���hcw�5w����IA���+�V��o|���\��^�`�D�N����,��>�N�\���r�X��4'�.[S�x���MjmoCLA�8��"���/R�<�&��z
���v��^��F���w:(�������E:��oIE��|4D?a�T�u*���CP��2*��$_S�;�k�M����6D�&t���R�S���&�\h0�'<������Vz���78��������n�Z@�s�q���Z�b:����6#5�n�������x�!hw�2�CA���-h��fD��Si��g���������r���l&|
?�:�SKf,���MJ���������*+�^Q��K�<t���:���F�K��Kq�
k�����V��.����D:P��������;x\��S��X����4pD�������:��B������D�����1�����:�������:`M*����6�8���G��$`&�L�D�l���J��k�������j�Q�h���)D�G�f�MC�v���VZ�J�����-�X��������D�H��
��j���
 ����i��j�r}?K
P�R�V����D� Q��8* �����
b�c)���j6�&��!��t1X��p�����t���[�#V����)�=�&�S������Vg���,�5r�v��]nIJ���t(�@�7�V��v9�����>�v��S�T�|���L�,sy"���R���<�Op
C�x�����r���*�l�'[��J���|���a�t�P��O��Lf0�q�=_-'��U|�lup��'�t�i�Ary�?h�]`�'���^9-LRER}�I�y%��Hd,��f����]O���BW|~������a����gO��3b�'^���\������xB��/n������S�M}Vx����U�~��.
5�,�s�5q(T#���3���^N<��E�oC�M����W��':
f���u���"
��L��� ��m5��G��mo��X1����^�JuC;��� ��_�w�k�<��Jv�DqUd��X��C�������|����z��5*�����v�����d���x�.���X]X|7#:%�\��*��U����;����j
����w�	'Z��D���;����V3k����V���YMD���+Op���	>�����2�T���M�~��I���B�;ds�� X]H^��^id��m6��^���j2��[��T\.��r3E��4��HV����Ti�Z�����W�~��I�$��`��j&�@��24k<Q��J�����:Cl-�y�+l3uF��n��0����A��M��W(���_o�h�\����.�f��������G�6�ii�ii����H�Rq�CAJ�C�I�fQ���,������[U��jg�G�~;������u&�����p)��1oq��N�z��,h��Q�L��u�^�h�D��/���qx��b5ZZ����|:�\.��=iG���5W�
�2����;;���`
6���q4��6�yYF=|�
��,�X�?��lF�8c�
,vj`�d6EM�N�BO?SjA=�2�eh%��3������C9�p)�gN��F+7O��j%aF��BY�	��?qh|#
m�[����o���M_��yG��"
��/�m�"O&��r�"���Nd�4;�.
���8���
3v�!��g|�	�\�:�T�-�T�.	��Yu|��PT�M��2�-ny�E���7����C1i�t�Y� ����-Q��F�,7'?��&i{��0�Z��!sk�a�Y�c")�'�S�A}�}��sR2�$a��^�)+����F�a�}�g��o0t&Jm.���G���F�j�".e&��O���W�5�R�
����{��}�m/+Y��|��"/hV�o��&].����K�����x����7�:��]V�8g�9S�;Uh��<s�OV�P�%4�ILV���f�,���	�t�N��Uj����:ulZ���%U���PU����3�>?�H�mT�C~�X�'��D������G�)y�*Z����I�kv�z+��y��Fi���vi�"��g�+['�Zq���uQ]�����o����Of ��H�r���qL�E�q:�,��/m�J��-�|����8���'�������h6��c[@���r�oV���D^������������a��STy)
�$6f~;1d~�A����Z3B��u���&7=Mw;���}�:�`�Lu��f��Lu
�sU)�.U�:+�"��gM������)O��{@��7+-�Y{����&��s��|vO��@�;U��9V yJ��z�{s|�;������t~Qtav�h���M��s�!!��K��}!i		��4��o*����h|��`��b��2���f�C�P�kK�c�+�p�m2�v���l�M��s[D����~V����l��@�~�n�E���]W����N����Bq�u�$on4��������mf��L�P��+w}�-���f�����A!�*7�V�b�Z��P�H�2BG��C�Tx%�PK�]'����p�mmJ������u���^�'�I�v+��p�?tq�,�<:���SU�;��#����Xg~��8?��s(F(F;�x:�����;	�u���
�-�u;���rF`|Z�e�s�-f����q|����DA�WTrz2�����{����������FN�+���F6_�?3�KO4����`|;��Pj�����
�����*D�ZG*����?�����-��E�_����i��9����3m����_W�������?Ma����p%���x�D��p�+���^[ M�h��x��X
�{_&��&����n�(��_)�4���Qd.�[-=��u�v��#pdA �`�yX�G���-�[�Y|`��DyN����W��tI7�G����%�e���sG�a���u��dV]�����@K�v��G4�{����N����Qn�%�,�tX��������8�\�=��6�z�Z�,b�����-�	��{�@��yq�~��)������R�����n���"�a���=����_�����q��W0�����J�7,�Z�f�d����'��`:��ml�����<;��������?g3k����Y����L��)#?vk����1 T:����6 �pB;��QB��U(gcW��U[)�TM����/I\$�����Z����j��8:���69���c����_���h�]U��U"f��Zz�N������0��'����V1:Y����U��g�x|Fx���
�G�Q�n��F,$�tc��[�Y�
��)�cJ���-"&N������$m}`��������}�@�����j�d���}��~��%�dW��W�eE�<P���oNfm�f��]���2��j\�v�4c�
�`<�^t��zFH��7\rq����#������	'��,b74��c�$4Y|���p7������k�-�t�~�������m�(x�5R(n����k�������:Q���a<�V�d����%�z����2JP��|�e����Z��D��E�t'�2Z�����uj��:�f���w����h�
�
��}�;�6uw��\M���,�V~;5��5W*
�Rm����R�*���DA�[�	��_���'������wC�?���������J#�~�r�����'ER�m8C�*�>#���d���j5=��|t^t�n��9�!���>|��w�;9�����Cd�N�o�����48Z,������u/����t����~�8�U��0d�	����Z���o�]zHa8=�\�d��=L�0�,t1���>zc���%�,���xxy�9�J���UT����������;�$Y�2���%�V�����j9���&Qv�_R�'WCX����^C�u�9���_��.��r#�S�����Oi�)�LM�`�b��W��I|�A2 ��!'�E���&�$�Z����h��)y��_����`�=F �e���q@t��\{*ax��\-�J�*0:���r��JsEF0����yf�$���� ��
�^�l]�.�(�!��Ro�u#.V�����$_�Hv��@)����#�]�f��g&r5�
PDNc���s���2��#�%���I���HwR�mI��R������Z�v�o��{,�4�S)�XS�&����I�A��d���+Le�H��iTq8����aP��t�a6*�^e�6{��F���)"�l�_0������{~1<<?���wxq�?-)B�.X|�n�����by_�\]eaJ�I���4ju�Q��P'X��@-�DJ���S��!�ZK����Y���rEC���8�Z"���l�P�~��Y�p(d����(����m[����y���C��.��j�HJ%|+�v9GDd�����'��`0���u��V�f�&H�$.a�#��1�%�N����F��;���3}�,q������l�n���\h#�/O�	�����0����zyS����0�w\}�
�9z%��{s8���4�..���v��6p��%����0A$�v`������i�W�-�"yx'NC7��u����n��t���`2��
{t$�����V�H��H���{�vwD>a����	cdJ�W�\%��%���F&�La�>hHQ���G��\!H�`i�;~[j����]��Y3��d7�H�)HM��bv�Ce����4��~#���Sug������,G���_&/_�����1o2[q�3+�'�[���RA��!+��8�$o�����9	��-�� `���������9���"���_��AB����,{�@��3�:��1SjeX��b��K|������LN,�V�vu,�����������)z�jl�N���O��ex|�D��W�(&��(�}�M���y���<����O!>�,?�Ly*q���c�]����;�z)�� �4{���+�����V/�%^�%�G2���v{��F�U"�H��y�7,P,G7>�V�nc@����H������%����GQ��_� �������`x��.�5	a:"�}eX��4����p1����j�V��q<��+X.K�u���Tv���w'�W4�W���q��WHN���r��i�]��g����F�+B��"A�6�!I�f�vn�������@�O�j:���B�Ozr6�E�U�����:���g��)�vf��R�����?248����(�+2���7��p�����?$�&K�����{���f��{�����`K��+�e��[U��{�z��������p��2�+��e�ij����E�����$&N$�l��H��V�lm�P�NG��9��?��k��5����G����8K�;4�"75�,7����f`�F�B����!q}BoH�h+�v�Wp���aO���c��������;iR�^zS���<V<
+�!����<J�56B
��13�v����gN{O�swG�i�9�:M0��W�@���4���i�yk���-4���r�8Rk"}���������;4�C�t��
��pF$�"��3�	�
�B���$h{�s���r�8�U��F��[�@�	�X+.A�0������G=�-�����	f�G��
�'#L����C��=��C��#�b0���#�{�h:�=T�pg��&��$���7RBEh���� �F���S<��m�3o5��p�5:�6/\x����E���g	�9���mn6
?�S�'&s��+W�c�����x�qo�F\���e(V�d9FMU���M�z���>B���4S��-VV68���+X����IAk�{����S�{L�D�g��
�6��-�7�Q��o���TS���h�x|�1�pL��xvy_�
�a����]v���,c�q�a2��~l<���K#$<;�����<��g����
�G��L��q�V1����f����C ��f���Z;Yo�����w5y��uj��N��o���-������q|�3}��Q7�}���=:����������(aNZ�,�U$k�-C<�����9�J&���gR�+l"�
�U!X�~���qLg�{t�R8��X�Cx/e��">�c��7�����B.\l�[�i���:_m�m���9�����"�w �1�4��Zb�PBJ9��3AR)���`���E����P��bH	M�$������>j�T�'cU�3���}���P�SmX���~y^�
:�F��4�)R��35b�]2����N�\������oFu�l-I��
�O�^��,�#�����+����]h(�$�#2��i�Z[[��O/�p�����b`F�*)N�f�� \����KJ��L�7m�������t��*���9x�Q
�#����1��k<wu%O���j�����}��><�R�L~���^�e	U,o�(�f���tB�Vc	� ������C�~,wVWW����
��A��$�BO�z�����������1!?iF��Z2C?��A�*�)UDG�v"��q��8���*'�nU�p����p���T��:tT$��f�����d��P-
��9���>�k���,�����nW�nvlI�X��e�"�n���j�>���h��V����c@�Zt��G�����������.$��Qa�<��
�\�1P~@*]�6�8Qe9�^��� ���*>�c�7�\G������S�
\�t*�9E����j'K�YK;Y�#��z���&eRf8��8��r	S���l�,k��_k���\�F&��|�$�\�=�}���	��8������E=�"4`YX;G�(����������w'��?�\QX���\�iT�~C��A����3�^-���O��+U�3�[0�ejW~��<�V����0y1��Z>����'�)s��B{�	�TF����W2��� ���6d(T������w��Z
�r��2f���CI�2��6�s �;�|%�%�S4��#���s9
B3���KExe�i�A�O�U#���R��
$
�b?�[���f�*E���\	�f.�#�A�So����c}Fv]����v���^m�u#���Gf�[^��}k�1���zKF��X�3���.B���k�����v���+IA�+�S���#���$�o���>����8��
%����lL��kSI��������V�����aY���[:����`��y���� nD�>����k��w���n$�����L�2���4�S�S��9W�m�R��KH�He�kq/���mPZ��0�/I�8��g�1�����D8#*&��s��	7:�����]W�>q��xy�Pi=1�^�Zk����{������,d��\P��:�R�����,��7��(���h��1��p��mL������Q/��52J��i��/2�(e��n)M����QFDy�.m����Ia�����$K:�H��	�$=��t)h����J��Y/ju�����|g>Q��N����P��~P�|dL�z�(S��k�0�~KK����g�b��&"
�Bc�u})�Y��|�N�+�0
Q���l���s��Y�NC%������k*��������7�u��������y�Jsr���L���0�=�A�IB�O�H4�hbG�E����a�sy{4�Nl���eb��I����_m�uz�����`��r{q�$c!�R��a�{�������}��&�O5�������3e�'���o3����m�6
��9:a��uQMa4��0!i�;��
���V���+[�I����U#�5/!���������1�"0��Z{3g��1��*
>����3���h��^�,!~�
��U�����y��Z����Zm3h��z�XL-���������]���tG{?���s�$�qD��o�z��X���J���q'���Mb�4W�IQj��,'"�`����D��
��N�����g�DlL�6�Z�����0n����_D�-���d��G�v��G5�i�s�``�w^d���8��/���K7��;[�8;Z�
��up���/��>>���^���r���)'m'/8s���k >w�6NX�y����V���+�]�v['�$e�^m&�yO?��GF��6������Ph~����x2Z�$��qK���<>���le��1%_����%wM�$X�T
$Ej���9�K��Q���j+_)��	���B�v�K�����-����U���]�k������*@�������:@d����>�����~���h��F{k(,�<���k�
c������w&
71��H���u�q8Pz�����'������le�V	l�9�
� g]?&��	N��{Y���f��l��o{u���/h�*~�l�d���E���&$aK-�:|�L��cSL���k�)�O|���4�y���i�
���PZF��%f��p����r�QV}CRW�
f�vbmC���[���g����!����l��m�RED��E��Na���N��w�V���P|&����U��-����&��$]pZ���2���`��E��tb�/��u1j�\�����l�[�?�Vk}�C�%,��jj���%'�Z��v��X�v�����<%B�[\:��,WJ��!w��R�qCY��R>�n0$�]]|��J��S�P�@X�Cv��2$���`�K*:\��.7�v���y9UI���y��O�i/������6d�-��hwj~�H����n��f�����x�
��F�����F'����x�1G9y)9��[K���5�T�3A�,������{��\��[c?T�&��-�U��E��q!Rs���F�H_f�"����M��P�L���?�F�\U���U,���[/z��N�a�BI���}�,4�)sV��������n���1KM���r�
K�K�A�Rma���Z� �f�������]%x�c�r��2v�����m����8��e����E�����Ri���BkC�U��Z�R8����v�E;�+�V��c�o�5���.Ze�y�����o�2����mo��2_�
U�j�f�:�|��hO��������e�~&��?�Y)���U���'<�lS�c��**�]���#<B� ]���G%
%�l�;��J��d��H���~�;�<�\����U�7SY��R3-O
k|Is��@�K���J�>�vf]�E��@T����\�8%~������&����"����>���9~���f����u�]]]�'��sN�r�����+�j%_����K�P`
�-�50&����������T1cPF��t<�
�?�� n��>�������h���gmS'U���p���`Ldn�~�Ar������.ld���]������o��E�G��B�I�Z�����{MGk��m[��
zm���'�M�!�I��Y
s���������@��f
p�����b@u���}�]K�J%�/?}���S�l���F�����'=p�-�r���%M?�*��I����S��n�����8�Z������A���e��~�U�N��� #)+��b�R�0~����Sq@���@���V�������;�K��h���<��<����yl���af��S/�J����k���y�����oV���;����*��v%�����{�?�1���
��%��v��'�q��1��6k���y���[{�}]��,OT#4�D��v����_�W��o������`p����U��z+K���L�mLO��&�t�]0/�A���T-��5� vz�#
;)gH����u/��D)������B7�Ck���T=��,?�=��)"R�����'�������Ax]� nqY�DP���C�����`���n �x�Hy���.���g�T'�;{�E4�n��P#�L��D=d��s��rL��n�m<E�`�9�v�ll�gx�`��r2-���i�u+���;�H��Y��������x�m�]�~=���>��]H�V��B�$#�r��7��������a�F�W��a��<5a�fw�m ��Y��`�t�/�4���f���~�@1�����,X�?OM���������20e�%H*qx�����^m��z)����Z��YqM:��`�-�=���e�^?a�5�mj�m?�����t��D������hz��_��(��J��9f���>�����&�w�j��0�X������q<���Ft�)��_���`��6����f��H��Q4�+|�5	
���~,���y�O[�k�o������	��v�(�Un�������h����?R1�U��������HpG�|��U����|��c�O���
x�1\,�)�mt��#��d@���T!k$�*�/+8W�p���eZ�w�N_����N��wZ�
��gW�(HGB�{���������u�I+;����������/��'v��A��C��7�Y���)<�zw��Cn��J�g���	��[f��^��h���jp�
������8�u�]3��V���8��f@�Q��)�-�7[�
��i�i�b����9����1� &�t��r41iL]�������
��W������v}�U����m��v�C
�{��U�>����I%�h?�����\`qrdm�v�����,�(D'k��v�B�N��J���l�#�
:[�~���+�
��)2�L��d�
�EX�w\��
x�Z�{�Zo�@��s��w��U�_Ut��Y����[���yT��A���TY���HU����w��A���B�H�R��nW����{1y�������H������w��K�&=���?������;y]����z�;dx(^�L�E����������i4B"�?>�
!���#k���yNT�k
HQ������h9Y8#MS�B�j��i���4�"�7���]�+�	�P+8��Q_�g>�3 l\G��#P�B��FD����iK�������~����o��`��(�,Y��5@�������WH���Q��f����B`���t��L�1�Zn��j�,���,Wy��(�v	�T1F�\�x�-�����_�!�E�6�Fr�l����j���5�53���*�A'o&3���I��<�i\V�xS��5*r�X|^������	YF����������[,%1?1�����g�����d���B�F��D�&��sj�ah���?�D�k�y�Z��y� ��U���&���������w�{�}rA�j���dAd�L+6�V�2G3�W����;�.~f��
���:��J�(����ke(�8�v7q��|���!�B��[&S|��	�h:���{�Y�*��M{�pq;��d5�i,��a�xN�o������i���\�5nf�!��u�y����[�V��z�p
e��c��o����(Z�X@�W���=�V�XR_Q��X���}V`����4���j��W�	�����M0������>�������C��@�����4nF�N��S\���F�S�u{�=�7��j��j��q��*�2W:HT;���-��V���Z�����R�c�^�6�v.Q!Fo�c��]1�[��fN%Z"�E�q�tN��6@�6��cV�m�����]D��i�����~Z�[m6r�� g��"��hnx�X}��T���Ta@~�]x|��I���������m�m��s�ld��tkxLj[Ej[uP�m����)����!��A��l�O���y=�0�]��/^0���/&��(�"�\�$+����h�HT����������:��Z��#o��m�d�����$������5���*��0F���X`�?L�I����g��������!����������l�f{��z,yUf�Ex��#tI\��C�����.����`���W�l��?�Z�^��`Yk�R[�)�	z
���C8��K������=_�Pb���=��5f��7����Ty%���]�%{�?��Y��|$��!�}��Y ����n���R�|�g�/]9n����x�wv�3�2�qM��V�����
������!V� ��=9>B��T�G��C�L������X,�	�Y�8�}�%=&�mTa��ZZ�9��(���f���
�w����|�]�����K��1��Q��g�_��{k�����`+�9��MZ��4M6���V���l&@#�~��gOCqEl�|k�N���d��n�}���eMPX}��t�������lH�e��l��P�;�}�����<�fJEgdS����g
�_���;���z;Z�s�*��_�H������+qf������p����P/����
��==�m$o�o�E8"(�
}�T:����MY�$����j���'Wh}e�������u�	�j���tL��?puF��C��6����l���!<����#����]y�;��#�q���q:cK�06��,3�_��YJ7_F$��j5����;	�n���9�8��
+����L`�����y�3R�8i@�5�d�Q�J���8�@�
&o�������_I���\�U`�����;/�gFY�9/F+U�����o�#8�RG:
�X�"5\����O��xvoe�T�(%�Iv�R��T�&���.�c�M����LafO�E~��o�E���SJNwF�"H���m	2�}��V��@��mp��-���t�L"w*�:[�j$=�!�nT�wl������@���H�k
�d��f��G<��qCr�������s���H	ts������Q�x�Pz���Ps3������[�+CX(�ri4A����+�@�:P�F�cZ�>��k�(�[�����K�3�m����Kv��Kb�}+�?A{�n3�0~��`�C;�`�l���\���*�7�l]�����O7��fg(Ojh���e���k�,�I�c�$F��d4YR`�%sP�p��]`�����9�T:0��N��5�J��	'n�(Rq#c]�
�#[�H�������^4M��,�A*�.��l������~w��J�0<_[����Y'4�]�"����Q�F����c�r7���{��xruoE�.�;������M�A��	����G��%`�7���{�S�,3Y�FD�R�Q����7Z�,�����h�^c#��+L�m�=�;!Y������l���=E����jkt�Z�,^Kg�<Wbf��*\dmS���F�$��Uv���h����_��8q�����?aJ�SK��O6�?1�KN��m����k��^,;�X�Q0���2wa�fY���(�H�a�<be5W�
��h
�Q�w_S@=�1�\J/8�
#�v���������E0��<�2+M��h~�Y�(zWS�������K���8�����'�~���"�q\������qi}EgB#zV�9�[�� e?�~6�>���t�sj=�8�t8�������K[��N63���U������i!����H�r2��
!���j6���(X��7�e�e�D�2~��a�b���D�pzu���r��e��&�
%��Hoc:����w�`,��L�4��&��v7��`1	f���������^�-+�H��4)1!(�q�<��NFs�����]��������;T�������0��;!��R!�+���Gy��~h/����W��?�h�[������}�%���`e\��X��7	���{��uP	U�����o�Ex��,�W�����P����|M��j>��'�U9������u�u0��p���?�@�
o�$V
��)����c�O�82�#�����������b�
�*DX��J�+�[��.J��f�KO�(�P��Ffi������Qo������*��J�i;�?�thv�,�I��m����a����\~�`+�$�@�/������������7"����y<9��p����j.�t�S�c�����Ef�������6��p$.c�����$t���"�������YJ���D���|<�b���*�DS�����(�T��lyw4�Nz�f�s������B����h�RR�X�D+�����m��������w~�{�r$tQM`��A;����*��d]���:�v����c���f�_q�����o�z�����$����n����h[(��X��j�5a=JyO���dT�c"�-��.pF*��1�D��	�F��7����b�����j�VuR��ht���}����2��4�� "�-����ud~���;0��e86�R�U�$��������?/��u�<13�������Y�]//�d([�`2����Z��c����'���m��{,l!���S �6<K�b9�I��R�Xo�G��n�����3�L����X)������)�>�Rw�f��������
�R�T5xG�Z����Fm��'�{M�i�ef�;?��������Ho�~0��+-!G�B�5f/s�2H�q�
�����=�,�n"�gx�C���Et+]?�����*M��$VV��^J���:�����1������g��#H;����)��Vj�A�yx
���q���$����^.��O�����@&L�������|?�E���C}�
��z�	�.�v;��|#�����U��T�F6�,`l+��u8C�s� ��?g'��xx&����(�9�D����c��B+&��uX�P���Y���9�7�9Y�P�CJ�y8��j+����R�?@b��P��w��;@���o�����]4t�]���/"	�8��X����BMNd��(�~�{�M����sz��A��1N�����3&��i,?����[��6�}���F"<�G7=����}`1�dI���o@�o��T��eI�3@2�<	8��G�RF�C(���X��i}�Vk@{��a����}?|�"d��a��{�ze��Kx�AH�f��5��.M
(z}��5I����������y�Y�&L��g�����U����F������hK����B} ����KY�{n��e;Hux]���Z�.���w�=�����>�BM����YW8j�����W�@�Uz�y�qq��s��Y���Nl���R�)u��f�]�^m7j>�#"�hF9S��O���v�����6�Q�!.B�2N��v[��F@G�P7��;��E�P&_G��6�o`l��l��Q8���p���M��s�h���G�K�����������[1�8� Y
O����q�Ze5z�s�����Nb��8E���Q��W�7N���h��h�|��V��i����m%���J��U��������RE�i��6}�P
��8����/�{�??����'`25�\�@C3&�#/����=������o�.��JK�$�:Ko�3;��/������t�z�m�`��������O������h�y������q�����*(+i�����v�����oY�?��?�A(���(��U�C�"(�2�r�`���U����9��v��e��B<��C���<K�2��=+���]T	�;M��� <"�����L�@��>������uB!�~WD��'��x���oY
Dw�f|�_��!��A��.�cBN�3�4_���U��A���A�e��I��mL��(;�������	n'�{c����ef|h,T<K3��F�����BZ�JA�z���Lu�����L��G�c�7�����O��irr�Kz��<�nW�9
�R���n1��#��oR�BY��H����m�h���c��S�������a�fjq��!���YE�����7Ivh�������	�����E0���_rj�1RL��Z#��!�rc�	����i��\��CJ$��3��`�|����������g��I3���I!���3���� J'��_B��G
����sqy���f�8��`�gO���y82~b�������?���]�,�����1E��GJ|$~�g!=���7U����]\'�����pfM��g�T8�^M��/�.��8=��*����h���(0X�`�\p���i
oOMO��O��6|3<�����<�l������n�+�tzH%�=����}�;���G=�_z@�.����I����Y���#���z����d��.���w��{���_ i
��I�C�)�r��~��78��`''�#��������?��������8�:-���G)��������������e7���C���?��pO��1.�:>}��?��c�2�q�=#8H�����������B��N�7�P��`�==����u|����l��s��]M��o������/���R�W�7�U�����)������b�h�u�[(A�+i��0YCa�5�%{����M�c�[m����i�����������O��~�����$[�^XL�z*���S��r/���[�zzy����
��K���o��LFS.,�o�M��2����u�W�e��u8����������6�������P�x�x��F�O�+$ij��:88np�>E��P�R���_�/��]��+w��+#>��y	�D�R��w��9;��������"s/J/��/���s:��w��Og_�Y*�����[/H�/��@����Bj��EG����B���Q���
�����m������l�a��� W)���@��#�O�M������[@w����w4�F���%��C��#j#�y�ldZ��J�{�6�#��������������5��>��zI#^��@2@D�1����~����B=q��(^^/��_N�C�O�s 9�o��v�TE|���2f%�+�4��9/������
�&��*���K��'�����o{�O�;��o����R�sI����7�������z�1K@����z��L���]�
l�O���3�IFO�L6rw�&�����oS2_�;8>=���	
z�@�TO�;-���D�\�h���������O�o�����_���K��}�u����T�s�����e��������{g���-����HgG�t~��?���[�����������lT�CK����j��3�=sx������_�����Rr�����E��N|��3>�9(���l��s�����M�����y�p�M���n\���&1	~�����_ZWT���+�_Z7T����H����uK��;�'��{�Y���unYWS�U���
�S������Ix���K��'�� �����p���"}:�
������;7�9o�$���E\}�HNw��������0�
qu��A�]�] :W���o��X�G�#��!�s]���g�}0H��H�p}a�k�7��������W��/i�b%y�P����6^�>���U��/�;:�i���u�l��'��f�u�1����j�{�=�N}���C��0}�S�eyGg���0��Sc7S`�r��5m�p}�����.i�A��j�qES��:���.�e�q}�}�:^�v��_]��0trr�+�g6?b���XSr|���0qQ�}����{z��d��CH�pj�4��S�%��������DS�4����=Jv��T3��K�+�R���'�e4_���POM*\��$�g��-��"��>R�Rh�����/���4[�\a���(�?�_�%�_��}k9���V|�W����z	�����l��5r<��a���V+X2�e=��e�aH���h��<����#dwm��\�R�{i���I�>�,���Xd�o�����_�l������k
g���O��F�����x�1u�wlTZ}� ��A�tz��K��;����_}��+�h�|��m�k!�J���m(��^��u,�1y}OyO5;����$
d��m��l�`	��}���tQJ�)��f�����l��	�&	�`��W��zU3���R����	���I�C���	~ `�O	�bp�*��N^����j�BC���Y>����<K�d��Nt�U��S���W�Fzxw.B��1-����P�"���Kt ��,1�*F����x��l}�K1#������Uf��H-r�m������fb�T{i�n9�<J�6����O|�[C�N����L��J��V�������
��s�n�~���Q�~���%`n����M�ZM�������/���0���j�i����D��kf��J8�U���^���K�b��&V �$�������t�{C���l	_��`��E�:j
N�YA���Z�
��q�S�]���F�}"o�v�o�
`%�]�G>B�v�f�@.B1���|9��K��
�:�
��-��!�MMb�%��N�;�-����b.#�\���,U�&��5�e�Q�l�����L\�X'���������t0q�rY�4����"��z6`|��2?cT<�M����o%�u���N���E�`'��*��*���gFb��lT$������,���~�;}����0]al4sbc����^���z��0���%�;�}k�9����5�!<J�����)������|J������
���0/<N�
05: ~�1���~�z1���*�$N��Q�+
A���=���F�������pkL�)x�F�e$ ��I���}�D���Nx�&��k.�.�K�fxI����srg*�
��	z��0�"�.�����~����&�-�u;(����7 ����8�M���fG��~%��/T��������L�R�u����}
E$���"i
8���"�""3����J��9>0R�������:T��_Yo�|��1c��W��c������j4�
��sg|(�]��L�n�8��9:m�S����9�2���*�3�4��:�;�f@E�)����q��&�3�[���hB,20�����G�-�����f|M�x��:"�A%/2B��6q���;�k������j}N��� 7�=d3������X'�fb�"���V�z��e�'1\��-'�	{��L%�JP9�B�j�c>�b���R)��k��yO�Ffg$L�I�LF&@��?|���O����c�����?��r,[� �����bC�c>+�t6���S��(��V�`@��=�L��8����1�&�����S�IJ,����p�[��S*$|�5��o��}.B�V���o�F�@�"���W�W����n��7���z0�����+��qk��u�����t��j
��^O�e5���p�2��1I���[/��&x�TyG�^���d�:�V���k�b<L(6��0ko+�Q�]=���voN`��W�0��=�p�E)�6�f[��<���k�J�<�T�0�������o�+�0�������:RZ���"^k�7DQ��c���h�Jr����	OV���(�(��)��'�(�C��DX�6�Rxx1}-��qB|m_9�7	+I}������d�dd{���j�F]��=�i�GB�3�DIm~�;�nJS���������i9�����K����'c����3��z���i4�R��TS�E�Xz��!���V����O�K��.�Yp��`]�l�'F�@�U%�i�\��������<��<��Ei'QhU�F���o5�~�]��<��t��2���%�W(��������R��M�>��8m�6�*��pe�=��/]�Y�=��UFf�h'��2�;�*��VyS������~�R7]�������/si��Z����@�=�*M\8�.�������0�4DT �
�J�oU+�@�Z�;���f!v�(MF5��%����Jq�h��Z��$InB�>n�2���$�L�h~��*���d19����b���1�����F�&Em
����D:���V}�s����V��j�)�z�'������6n@��3�$f�|6��Q,��/�{���%���- l�NJ�J��-�+�W\�L���O��{G���)i��&)���M����b5W/Z��(+@�.@[�R6Z-�2�=���3������a�y*��T"�O�  ��B�d2�n����3B1))tq9%g9�KW����J����@��/���a��3b�����i�<]M�����#{
}�0zf73Ra�������Q�cD>,m?��o�^t���1��3����)x��f9�d�Tj����#Y��y?vO��D����[���b���9����D�<6G��u�w~�?�������z6�&��yo���q�x?����+�j�������u�|��\h"��M��Ov�i�Q�w�E��=��_���}����t|U�l��?C���/���b��g��8��d}���ZB37JET|�7����^_/�k���Bn�edue�?�w��G�q����*�T�rBBL.����w=��P��z��|�@r��y���"c�N�l1	�9�h����$�+Ht���&�M��������t.���4�7���/d�Y��LK���bB�2�b��(��zb+���K�Y�r��V���\wC4��[I��/.��<!5�`:�I�7��P����t�y�i=
��hJOM�7���P�����t��#7~�U��GN�!�W'ie�j���|IiS�����$_��3m47����B�z�7���)��m�D��[.��O�����m��[�F�B��N��w���U*5`4*��fh79s(�U
��wN����{*�m��aO���=_���#�o@�-M��h~��'�g�S��7�>(�h��|�����%���:���&���j2g��u5	����'�c����N�V=��c�Uhul��'�����j����UuZ�h'8��(�Yi��[�P�d���E�����\��1�.��K��di���X�`%�������h�*�J5����+��N;Qj�c�\���A���G�V��b�{�;����%�,s|��1���^��n�Q���t���8.�����"�.���,Zz�7	�h���
g�%�x2aX��`��!��>Z����G��i����`
{B%�J�R}��j]��i������ �6PZon��Z�!���	W��=�_����L�zb�=~�:��R�j�����wxi�h;�g��J�V�@6���5=��\������� �L��*��u���@&�;������k��� �o4����+��3�!��e��h�N�3)<G_e����K�H�������������%��TIK�t��o�#$��8���c!�}TR��w�6��[�uw�:p��r����=&������4
c��Z����0�Mez��z�I�i��V�����I��W������`������"Sz��_ZQ���`�`:-Xr��.�]��$�wY�����������o��nv�HL���Y�`v����s�,��	9����9�>����*����I���J9[��vO@��~:����[{������^�U�:�47�u������ac���������qx��"}]X�O������1���{���D��!C{� �p|�����X_� �*���`��?��} �lP��i�$�,�7��HG$��Z���'�qEW�]����e���B��^�<������hb�Y��L�s~��<��\��zt���(��~i����F�,����\_��Z�^��R��D����F(�/g�;w;���:~������N:	bT�
����s��f�#\���9
���LfE#�Y�"_���������h��n'^�������)cxn�j��R9��������������r/����*�+2�p���.�p�9#���v������������WT�{%�/������{����{�?=��{����y�k��/(<���_����#
�A^�#j��2�_���G��(g�%����#rq=�����jS���>���_����������.X��~��q�����O{�l�m*w"��@rq$8����kL����h�����	����S�h5
�!�~�����`�����A��i����W������
:�u����f��xva �����-�<�:*�_�GJ����q_�Q����u�3x�_����IEC�[���_��_��UI�7��I�$��>������l�Aw���)��c����� �j���?B�tA��2'���b�����@��f�[z'"]���lw2RC��\[H���N�$�y�%�q%�D�H,1��qT�s��/d�����#{���p_��&*��g�p���o��VWW0���
?��_�)1/v�v�p��r�_����Q��q���Z_j9���D��z�������N{m<3��g��<6��>���rT�Nf��k�c��;�p�9���b$1���H+Q��Ju�z[|�	,���L�?�u��r�Hd������%��e�}�|�;!�(������f3	$O��3@��������i�]�9���,������?>������#~�������8#����R����}n��~��;F���z��N)e�*���?#������������	����2m%tq��f	�To����xI�����oQ�4*�p��]%�jU��i���}��:n<����	���b'�����kj8Z����0������*{W�YH4r�/�A!�4��������bo����f�7��_&?#��"Q?�����lg�x|`lK������_�P~����7���vOqw����.e�:�}<.}��0cn@�x����A
8����!]|�*�Sl��T#��	�����
��*{!%��"������/�i,,���W�}�R�B�0n�D���UQ��Y�`��2����b���M�/)T���1$_d%jUZ5�\�G����I.�~�}E����\mT68�����W��R����Y��]J��`aE�i�m,S��U�+�L�X��z�3_�Rk�>�+UMr��t��t��VK�Pq �4���j%w2�v\��S<`�3��%����uvl��e�w=q���,��W�@�Kbu�MH��m`$���(��R�[0S"����y�h�q������\�C?�*P���C�Ad���@_������%�� ����yHr`�������s�K�K�����r8����}�b�K�S[�n������}HB��+g����������
�<u������_{������������-p-q1��G��s��@S�����J<��`y^�F�7T�gv�"6$�7[*����L��cb��H^z���X*p��0�/�'�2�7������?`{�jlI���w���/y�(U�XCK�"z6�O�k/��_���%����k!�RB����P�5���5���]Wi����B1���~����=����?�vO|�����w��b����2�Z�A�_���H��'���P������C&�(��A�s��(�5iSM~������X�kM��b�r-��3bL�BX���j��:w~��Ct%,Tch"�z�����W��=q��O���	�����b�	�l�z�Jd������`����h���%����:�J�{���������	��eDe�v���<��h���p�[� X)o����z�T��v����T�&r"����L����8F�)W���]�B{|3�'{RMhX�^�E��@VhHUK���PI�ssU�4�U��z�����;~/��e�a-���0��sg;~��E�q��t�	��b�u����o8����0J`�"���� 1o*��+�S���j�UD������F��P�n<����i��J�~�(kj��}�l�����w�Q��[r��0�����Z���F��Vm���*'j�B;�q�W�>�#\�0;�br&tIe�Z_�/WW|��T�
��RiB����;l1�^����������k��D��`5����I9�H���x�g�L��D�S�l�9/��NO�ez����&��n%/����:2V� l��p9A^�_����d�~�dz�5�c:��G��t�,c�~�b�������
���)���o����uv��p�^FFn*�%���^H/K�/�uh;qF��k�r8|�Mj�4@/��KB���hk.��_�O'���+������FL�g�=��'d-�����U���"���/'x���O8��t����;�`�!�[�>E�[�1�v��n�M1�����UD�p��$�k�nk�r-����1�2��<��sj�8���~���K#I^�@�2-�D�����/���h����8\��ojc14"��`�:����v#����3R���+[�l�4��/a2J�g2�|m�V�-W�j�����?�{n�r���=�{���_���?1?!�����p����@3����Vs��7�N6g}������1�����c�z�,%�����,��|�_d��G��������������������r�������'k5N3������@���m8��y��)�X3_�d�
S��')�aJ��;?>������P�/}���SgP���To4�P��b���t��{�ss��[�|�rV|�r(�B� ]Gv����� �=X����r�4��������9����3<�F�x��,�h���S���*E�E{Enn�`h�-gA�d:_��Ev�����_R>S=�)�!��~O�k�<T�K;�g��P��������A���;���R���[�-�l����S!z�w	�����cv�V:�Wt�����������|'����bT�1���93R�+����vN����dnU��� �yc��j�����`y}�2���J�+!���m�Ty���6�e���@���z�5 ��=�|��$��x�F(
.<���S����_��/g��-O���#(�@�n���r����^��q��$������qv�$,��4"���R���������_���`<'^$�����NA'�I<���8x����0c���+�;<������Nn3'.��~�QI9<�hPA-�kD���;�z��w��3v!Ep�=��eB�#�������6�����[��z�;��B�
��U��EK���M
f�{}����������\x�a��@d���\t�d$!K�-�����P,	�i�n�W3�
�~o��'_X��R��l�uA���RU���k������
�s)��;��}��`V:����s����M%�G�8uH`���u>�7����,��iYz��c_��1;[p�������%�l��C������o���w9Yr$��N�����a�����������h���>���^��r%������96�GpR�u5�������������R�U&�m��������X�����]T[���5��0������U����:�������9����a�V�l=m�FN�Q
jM(_�z@��{����y�cx����r��&
Y��H�{�V���,�[z+\��p�����������������76��13��%m�=��/��	�A���y������qY�S���L�������Q�Vs�~�	e�D\Y�d��'�u��G`�9-������:kf��-�l�
�Y����	��F����YV R�l]���{�	q�H%2D"�N����Vh�&�Z�j���&��Fr�@��[X�����|�8#9���:���p�^l�w��^���7W�a���&g�*,�eqdt�t��l	�3�>il�!,,�^����= ����l��+vA�G|�&FB\�.���@�,��0�$3�o�o�����T�����1��
���
��[��G
�����a�HNH���\������W�-%����)<�b���`�p��&��G6{4B@�KT��@��'w�J��a4����^i��*����r.��(�{z����4����0�A4G/F��"Z��uU��;����R��&�X���C`Q���Mcf{k�����/z	�^�90�$�Su�����r������c����8��EtK���Ci_Gh�p�-��":������4�c����jM����s���g_�3�F����Z<�d#�M���
@
�gC�/������l)��;��3?��������{e�C����I�A�4��p��f��l��!��0��<�B�AEc�m���� ���n|�!�
�W�o"}���W��(�J�+&fr5��T#3������6�`��y�3 ���5.��X���zj�g��
*Z,����Dy��<�`5=�A��D�$����#�)���$UkE"�{�W�Y�]�FR�`3-�^"�s�=
��wY_��4�6��*1C)@�������k�"�B���
�������K^�j0�S���n&���d��(�����U��(h@	q7F��������A����y��;����m��Gb����_zW���\-1{�#;x�,�^��A��{�
��b�M��p������Jd$%�������,��n��p��df��;&���������A
����`�^|�"�!Q������y,��]]l��?���m34�U����+���iR&H��J�FU~{W�3b�8l)��f2��"��@>�V���p6����i>���1�=�B�9�����6��S�w<.����$�5�� 9{h�!am�r��?�����!�')EO��"��[��W�Z���:=��cY�E�|Fq��-G{�K�IZ���8�kY��Q(�(
���V��L�����E1�����w��Z����� ���j
Mu�Zy����eF�ia!��m.R�i)@�u��������jd>��0h�����BDM��4�NB�#&kZtvB��)�B�/H
R;��9�Y0U�Qvj�g��E���@k��0�-2:��"��=X'FUH��
��U�m���_^�
XIv�j�i�hF��'!�d�S@i@���l��"�a:-�)k�n�<E'���@���;�e^�5�|-��^������?gq�>�O]]i��$P��d|�S=f3�/�@��{@��#A����@0�L���h�s�a,9tH����%�������d��$�.�,6Ykc�Z9��GW���=s�0���b��y��Z7�+��I������A6�m�Qa�L�.��c�|����4}X��G��b�Lt��!�W������&�
���/
���n�K�)� 0�vo$k�"�o'�!,�zF�������pP��bB�WP��>;�$&�o<���w�?��]���!�oaCR�b"Q1�1'N��e�ZA�]��g�	[����������W��M���<n����|\[���t�M�ZcV�%�t7�t]:�xv!�.<�i&�.�g�qvK��R��m�ZXe:f+{u�q�"����KtT_��a�������yWJ�x@����c���?�	;3Kg��XT�K��{!|'[p������_�4x\M�S�.Lt��6������A�_��X�O�x��e)]D�|�\���Fvxm�z����hY�
��1���MbT��&>�NB=����U���2c������������.��>�U��s�*��G����<�F��k83�=�&B�]�����T�E��n!i�i?���,��%��X�"�kz���]x-�%�������7*��o<�
:a����:����/s|�v��LV���}e?���{�>5v[�oR�����B7�d�(�nR�������6D7�Mn;W��1<g~��R�l�=r�U�����&YD�=�!��R�:*���
��'�aB��4Y�C��	E��E�������������?VR���Fwg�[~�,�����NX�L��Z�Oj���x��h����Q���T������zS@�K8Z'�53�����?����\���u��{�8����"����;zA��~��F�1e���"$g=|9�=)EV�#kV��N�+�|Hl��4=�V(�=�C�(Cm� �*��f��m��0p��7�JC��%-�k�Q
�[:�:�����"6��Ia��]�����R������Y1�Y�Q[����j���%����^���	��d3��9p0��5�������� M;��.�������u���d���%nk,O�X9�k|)���\V;�j��F��\���p|��+|���qM=u�T9���R�K���%i�[&����"k��e;��`���e1�Q>/�5���&�k��pYJ>���;�; H��8Y$���y�&�Z��kN�l�����zo�{�,V�x�#�h��3l(q&#A6R�UM-��by���q7������v�~��s
�%�
����I�4�c�0��S�:n.�<��=<����q�-��U�����c��
�I�#��M`)���f���C�����hTU9�m��[�L��xZH*Z����})dY���u��u��H�a1=~#�%�g1����S����hw�O���(��S�"�-2�P���p��=t�����,88[u&Zf��
&&�����7Qdoi~4�e)��M1oh?p*��I-���s�
�>��%�gN{���������[p3�I�8���~1�Z��h0LyU)��jE�A��IA`�������*�K����O\�c�������t���/�Z%����������['[�Q[�j����ei�e�Uu�h�������D�x�1���Sg����VA�z�=��ph�Q���xy?U.#��{/L�TW9��E��O���`��)�� R�WJ�i6�N'�����*����:�r�uW\!���������z���V9eFE�{��e���-~6��^������������}\��nuG��<����h��F��i�!�^�3HqC����v2�7�����to�s����	�W��e���k�%l���R�D� 'g&;����gqi�il������5N����T�:�0�Xw�`��7IE��s��y�Sx^���#1�y�<O]�O�K�)'��v��F~-vL��Ir(�m��;�3~��[��.�0X�n�Y���j���\�!��+�?d5���1�S	�D���N��o��6%��8�8X�o	�T���u/j��	zU���5l�I��-�v;G
���o��/��'O�����v>���M�3��6�Av�p��zR`Y����B��p�?����iKX����tXi���:�|�c��?%V�m����Q�����@�dH@�3b��"9	�����}����/st�"��Q�\�z�����K�������,���=?�34����B$��&�����F�TGj�
�RK��t���9]����s��|��x1��Q�e�C����KC��Wt%�S��H���������q��|����h�����t�d*a�v;�������#�>�8�)�@D-N��[��g.�n����
�&����	m%o�H�@4�=6�7�"�@�c�������F'�� ~o�]��������������l.�o�p��D������s&�O���3a6�6lBU��"'��;�p��^�����[ey��U�J:���U�O��WJ~�7k�ni�����7��q]��L3\J���_�k��� �z�o8�!�l�e�?N@$�X��4��R�h�<TUW��s,Sy���Jk>,v@��H�r
�'0���/��������f���X�0�n�������������0U�]rx*�=MXf����S�_d��X�)��x=~����aQ�8��Z� �H��C����O�`�s��j5�����}m���D����,�^�Z�O'C�g��/H�����$���+g�`^Y���{��v*���^�O�&0,~�v��CB9H�����h O�Yl�YI�Q��~w�s'tbe����j�}	�GL�[o�
G��A������`uIj{Y�VT�R�b�7�R�I����j�\��&R8�0[���+�^�3����s����;x��7���b*��yO�!�7�����$�0������ob�;~#+��NU U�����_��v
�<�������E�|���I0�mw�e��2�����T+��|��	(f���J�D�I�x2��Y'=I���6�%K�cS����Q��I�����S=���l:>=�ze���&��e�9>^
�&@������A���}��g�
nt��y&H�����L*�������M�}r�uk�z'����^�/2-+N�M4~����fZ��1�"L-N;��
���lu+��?������{�.�P��*xd�Bqm�t\��`!�68m` ���k�}��0���1O��`����tiog�������Gs���t5�����M���1��x��D��9��[�u�cT�s�9���h�6)������Y�XU�do�PL����&�8����#����
�<	=�8��n#!L��p������R��N�����,�g������YQKQ��k�9E~{�s�H�9�t����65[��.�c}��~���Z�$>�����%X�����E���(+��[OH�����r�.�B�2��6���'�F61���j��Z
Y���)��)xm�a�ki�n7���+�������e|�n��\z�n���/E[��^k�d�o�t����V�b�Z��MKrVN�g�wZ,:e�*s����!�w":�5�������.�����������T�,�
�u��1�?���VS����Z���*�5+�U*��c��GQ�gp��S�����(�:b��7��T[e.4�gF��h&��x��GN`��Zu�-h�6 �G��������+��X�~d�3r:$��{}r��s[�������!0i&�D6�!V_�<��~���
��f9	S���}\�w��")���,o+v.Ku�~�;����}��w�|>�0�:u��.So�`l4@�u`l�*?+c��Z���spH���C�1���W�����w��7�s��{>�y���w�?�
��)s�gj�U&��9n�r��T*.��w���r��i����;���}{�{��������������=�K�=���`	���
�����O����F�Xau;��.�������*�Rm�p�����N�'�������E��Kp�V�U+��O���&���x�4k:���J{��T���������w��#i��Kx�������"w��H(���c���������J��V)3�6�<��h�T����b�v[���-Vi[�X��F�^��.'��cY�
������n����Rg�����|���@����0������`z�3��T�:U!M�!K��,���yt�w�wI�Q`�
��������!��^xC��	���������v ��<�OrH���Vj4\��/a�\@AP�����<�zG���':j����i��_x��)�6k`���^�T�>��rR����:��I������\��dI���g�5���"����
�A���5�*{�oO��8�$�|�|^sO�:�-����G����#������9����g����P9��I�6��n����E����K>LR#�Vn,O�b����{�r$�K��mo�j���)�����L��)O5��3���=�v�/�[���G=�B|�����G���&s���*�x���<%A��<�����X��#B���T��D�� ~b���*��|G���\�+~[�Vgo�?�~��~Tz>��i��� u��'��a�:�v��d�}{h��f��E�����������#���@)�������_!���e��O��+��r��%>�;S��X5(��Du�1R+��b�T�e��;�Z:�o�����O���������eH���W��+��X��#� /�/��q��(����i�G
\���
?��Y��^

�p2��a0N��:�F�O��q�*F�L�tD��+�=q��'������lB}'L�_���7t��2��f�V�\�W���a���^�b��.�pl c|��Nl���W��.��'���+.�����f��W�3,��wx	�B_�
��������Z?`��t��Fq�D#��!���c�t�����u����_����'3�5
f/��d��:�z���
#K�.ZM��%,�����a=��0������{�s�b1��&q���	���O�r���/��K�QIU���?��_�����z�G��8���w@�%�9���p�d���x�h�qJ��{������F=$4�O9��!A�m�OX�[?}~;$/k��?J/b��\��y���J�����+����	�t#(��B?~8�e�r����0&C�������So�]<����1k6�|g�Ih>�B,*�W��]��|*�\�x���l�/����m��gz����a��������w��*�|b�����V�kswC��:���������Z�h�<^(�$�	.?XF��T=�c�-Iy�16#=��1��������7�'y8V�tx��^�D0XN[������3����5���-~�>�������k�U����{�Cx�_p�2f65�{w��T�;^pu��
g��>�	'�!<xN:V1
a�������@���}���W&9��]������W������������+�Z���9������+L�95���'�Q1Pmk-�&��D
�������#
Lq8	u�d�dl,�s?�~*4�����H^0K<[G�nlW
����F�@e�:��b�L�$W���$��������>���0T���N}V�9�O���e��g�]��2X0��7��[>C��oC�L4SlY�Te���F�	�����
Gv��Y����n5+Mf�}�� �[) ��<NK�8��\Q@������Z(
@����C���%
�!#�jV�F����Zt�����\?ag���9��x-���'"�
�C���g���r?�x�s��.�>���- �	Oh�e�V�����:���+��u��@�+���k�����|��h��{��L���,'�\��g%�*��{��
+���"� F����������lI�y���Z��J �������>-y01_M_`���z'�W�Xo-������iJ�F�����4
�mT��H�
���,�7�`4Z����v��/���&�`�����|;��E3��^��������*�c�Co=����e0�Bhf�x�����h���$���	m�t�3*�5�O�M)w�B��xt��N��]��aS��7�/���rSXKUK��i�B;j��K���"���I\l7sW`���� =�����c��%�8���U��cEu?�8M����#�1�kCd��<�E�]�6�������1+m.t��f�8X�Yr;/W��x,W1r��\z1�^���/f���4��q�����<`��e�&���{q��W��^<l�3v����$V�p�,��
B@z=�P�)������������^�
;��k����B����{���q3�=�BW�XZa�z�rF�Cw�b���u���b���_|P2dj�
N��a;�@�9[��x����Y����m���%�Y^K��j>&v])R��5��*�sh�m��
�YS�<�Q�5[��t����Wj���{�H�
���K��]A�s��gL�XE��a�t�f��	������iK��,�i{.��F��_/�A�{~�=�'�o���?/��e~)�)�Z1��������>\FC�O��?5���Ei��x/!?\{}=�������`�xO,��]�{w��M�����������!�I�M6��p���n0����~avSy���-t�R�����;�	���i\��`b��_W�E��'W0���e�9Y�"_�rh,�U)F�����n!eE\��I�m���c�(������i�����=#�:^@]�7����E����VY *���ZH3U�������e�p�0@E��-�]���(�B�����X�sN���f��n_��}�� 6�Z�_�iE�A��n{k;�nx��j�����+�\�D�9KE_����/�5���`�b��.�qQt����b�M����(���`�J�Vl���$�XT��bxb����-�N9fNEwP@���C��)*`]A�WA��wD��A��wx��T��3�L�46UI.��;X-o&c������xM<D��kL��z��_����f�x-S�d�	(��H��_i��	�XNn��?���ir]W�S�|�{�n!^Da#X�>����R�QC�5������`�<�7�h��@���`�d@5{��|���Q��7����G�l�BU���^L���q�����x5
�P��'�U�)����o�$�����
t������t)�L���i�tL�j4��&�d�ty����q�E�X$���0$4��
C�4*�	C���w6)�@��)���zw"�{�F����$�M�MHJ+��_����{��4�����O���D��4�f����P%I�$U�V�$�}S+I�$���J*j%��B1I��D�� D|�U��@������7"�a��P�^J���������W����yx0����[ ?���BP�?!�����^�o���Q�o�m)�'��\���5)%y���Y��Wb�r�#��:���D��$�R�
�F6�%��DH(~���qB�)�[�Zs��@(H�&��dj������s��O���5�a�2�%3�r��ma�-�:<��������1���������%�2}}H<%S��v7\+�����b���]���5���
#35Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#34)
Re: Command Triggers, patch v11

On 2 March 2012 22:32, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Please find attached v13 of the command trigger patch, fixing most of
known items and rebased against master. Two important items remain to be
done, but I figured I should keep you posted in the meantime.

Thanks Dimitri. I'll give it a spin this weekend.

Tom Lane <tgl@sss.pgh.pa.us> writes:

This seems over-complicated.  Triggers on tables do not have alterable
properties, why should command triggers?  I vote for

      CREATE COMMAND TRIGGER name ... properties ...;

      DROP COMMAND TRIGGER name;

full stop.  If you want to run the same trigger function on some more
commands, add another trigger name.

I reworked ALTER COMMAND TRIGGER and DROP COMMAND TRIGGER in the
attached, and the catalog too so that the command trigger's name is now
unique there.  I added separate index on the command name.

I see you now only allow a single command to be attached to a trigger
at creation time. I was actually fine with how it worked before, just
not with the DROP COMMAND. So, CREATE COMMAND TRIGGER could add a
trigger against several commands, but DROP COMMAND TRIGGER would drop
the whole lot as a single trigger rather than having the ability to
drop separate commands. This is how regular triggers work, in that
you can attach to several types of SQL statement, but you have to drop
the trigger as a whole rather than dropping individual statements.

Tom, could you clarify your suggestion for this? By "properties", do
you mean possibly several commands?

Thom Brown <thom@linux.com> writes:

Just so it's easy to scan.  If someone is looking for CREATE CAST,
they'd kind of expect it near the drop of the CREATE list, but it's
actually toward the bottom.  It just looks random at the moment.

I did M-x sort-lines in the documentation.  Still have to add entries
for the new catalog though.

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

I think Andres should add an entry for his patch on the commitfest.  Is
it ok?

I'll try Andres' patch this weekend while I test yours.

Tom Lane <tgl@sss.pgh.pa.us> writes:

FWIW, I agree with Thom on this.  If we do it as you suggest, I
confidently predict that it will be less than a year before we seriously
regret it.  Given all the discussion around this, it's borderline insane
to believe that the set of parameters to be passed to command triggers
is nailed down and won't need to change in the future.

That too will need to wait for the next revision, it's just about
finding enough cycles, which is definitely happening very soon.

Could you give your thoughts on the design?

--
Thom

#36Thom Brown
thom@linux.com
In reply to: Thom Brown (#35)
Re: Command Triggers, patch v11

On 2 March 2012 23:33, Thom Brown <thom@linux.com> wrote:

On 2 March 2012 22:32, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

I think Andres should add an entry for his patch on the commitfest.  Is
it ok?

I'll try Andres' patch this weekend while I test yours.

Actually no matter which patch I apply first, they cause the other to
fail to apply.

--
Thom

#37Robert Haas
robertmhaas@gmail.com
In reply to: Kevin Grittner (#32)
Re: Command Triggers, patch v11

On Tue, Feb 28, 2012 at 10:09 AM, Kevin Grittner
<Kevin.Grittner@wicourts.gov> wrote:

Tom Lane <tgl@sss.pgh.pa.us> wrote:

This seems over-complicated.  Triggers on tables do not have
alterable properties, why should command triggers?  I vote for

      CREATE COMMAND TRIGGER name ... properties ...;

      DROP COMMAND TRIGGER name;

full stop.  If you want to run the same trigger function on some
more commands, add another trigger name.

+1

+1. I suggested the same thing a while back.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#38anarazel@anarazel.de
andres@anarazel.de
In reply to: Dimitri Fontaine (#1)
Re: Command Triggers, patch v11

"anarazel@anarazel.de" <andres@anarazel.de> schrieb:

Thom Brown <thom@linux.com> schrieb:

On 2 March 2012 23:33, Thom Brown <thom@linux.com> wrote:

On 2 March 2012 22:32, Dimitri Fontaine <dimitri@2ndquadrant.fr>

wrote:

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a,

''::text b;

SELECT 1

This doesn't even get picked up by ANY COMMAND.

I think Andres should add an entry for his patch on the commitfest.

 Is

it ok?

I'll try Andres' patch this weekend while I test yours.

Actually no matter which patch I apply first, they cause the other to
fail to apply.

I will try to fix that on the plain tomorrow (to NYC) but I am not yet
sure when/where I will have internet access again.

One more try: To the list.

Andres

Please excuse the brevity and formatting - I am writing this on my mobile phone.

#39Thom Brown
thom@linux.com
In reply to: Thom Brown (#36)
Re: Command Triggers, patch v11

On 3 March 2012 00:08, Thom Brown <thom@linux.com> wrote:

On 2 March 2012 23:33, Thom Brown <thom@linux.com> wrote:

On 2 March 2012 22:32, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

test=# CREATE TABLE badname (id int, a int, b text);
ERROR:  invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

I think Andres should add an entry for his patch on the commitfest.  Is
it ok?

I'll try Andres' patch this weekend while I test yours.

Actually no matter which patch I apply first, they cause the other to
fail to apply.

And having tried building it, it appears to fail.

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 alter.o alter.c -MMD -MP
-MF .deps/alter.Po
alter.c: In function ‘ExecRenameStmt’:
alter.c:69:4: warning: passing argument 1 of ‘RenameCmdTrigger’ from
incompatible pointer type [enabled by default]
../../../src/include/commands/cmdtrigger.h:45:13: note: expected
‘const char *’ but argument is of type ‘struct List *’
alter.c:69:4: error: too many arguments to function ‘RenameCmdTrigger’
../../../src/include/commands/cmdtrigger.h:45:13: note: declared here

You've changed the signature of RenameCmdTrigger but still pass in the
old arguments in alter.c. If I fix this locally, I then get:

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 copyfuncs.o copyfuncs.c
-MMD -MP -MF .deps/copyfuncs.Po
copyfuncs.c: In function ‘_copyAlterCmdTrigStmt’:
copyfuncs.c:3479:2: error: ‘AlterCmdTrigStmt’ has no member named ‘command’
copyfuncs.c:3479:2: error: ‘AlterCmdTrigStmt’ has no member named ‘command’
copyfuncs.c:3479:2: error: ‘AlterCmdTrigStmt’ has no member named ‘command’

There's an erroneous "COPY_STRING_FIELD(command)" in there, as well as
in equalfuncs.c. After removing both those instances it builds.

Are you sure you don't have any uncommitted changes?

--
Thom

#40Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#37)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

      CREATE COMMAND TRIGGER name ... properties ...;
      DROP COMMAND TRIGGER name;

full stop.  If you want to run the same trigger function on some
more commands, add another trigger name.

+1

+1. I suggested the same thing a while back.

Yeah, I know, I just wanted to hear from more people before ditching out
a part of the work I did, and Thom was balancing in the opposite
direction.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#41Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#40)
Re: Command Triggers, patch v11

On 3 March 2012 13:45, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

      CREATE COMMAND TRIGGER name ... properties ...;
      DROP COMMAND TRIGGER name;

full stop.  If you want to run the same trigger function on some
more commands, add another trigger name.

+1

+1.  I suggested the same thing a while back.

Yeah, I know, I just wanted to hear from more people before ditching out
a part of the work I did, and Thom was balancing in the opposite
direction.

I was? I agreed with Tom's comment, but I did query your
interpretation of it with regards to the CREATE COMMAND TRIGGER
statement. It seems you removed the ability to create a command
trigger against multiple commands, but I don't think that was the
problem. It was the DROP COMMAND TRIGGER statement that garnered
comment, as it makes more sense to drop the entire trigger than
individual commands for that trigger. Initially I had proposed a way
to drop all commands on a trigger at once as an additional option, but
just dropping it completely or not at all is preferable.

But if there is agreement to have multiple commands on a command
trigger, I'm wondering whether we should have an OR separator rather
than a comma? The reason is that regular triggers define a list of
statements this way. Personally I prefer the comma syntax, but my
concern (not a strong concern) is for lack of consistency.

--
Thom

#42Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#39)
1 attachment(s)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

And having tried building it, it appears to fail.

Sorry about that, my compiler here was happy building the source (and I
had been doing make clean install along the way) and make installcheck
passed, here.

Now fixed on my github's branch, including docs.

I'll send an updated patch revision later, hopefully including pg_dump
support fixtures (well, adaptation to the new way of doing things IIUC)
and maybe with some trigger arguments rework done.

I understand that you're not blocked until that new version is out,
right? You could use either the incremental patch attached for
convenience.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

fix-rename-cmdtrigger.patchtext/x-patchDownload
commit 301d8e58b6bfe89f35d82ad7a5216891f56c5d48
Author: Dimitri Fontaine <dim@tapoueh.org>
Date:   Sat Mar 3 15:18:59 2012 +0100

    Fix RenameCmdTrigger(), per review.

diff --git a/doc/src/sgml/ref/alter_command_trigger.sgml b/doc/src/sgml/ref/alter_command_trigger.sgml
index dd903e7..48b536c 100644
--- a/doc/src/sgml/ref/alter_command_trigger.sgml
+++ b/doc/src/sgml/ref/alter_command_trigger.sgml
@@ -22,6 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 ALTER COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> SET <replaceable class="parameter">enabled</replaceable>
+ALTER COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">newname</replaceable>
 
 <phrase>where <replaceable class="parameter">enabled</replaceable> can be one of:</phrase>
 
@@ -62,10 +63,10 @@ ALTER COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> SET <rep
    </varlistentry>
 
    <varlistentry>
-    <term><replaceable class="PARAMETER">command</replaceable></term>
+    <term><replaceable class="PARAMETER">newname</replaceable></term>
     <listitem>
      <para>
-      The command tag on which this trigger acts.
+      The new name of the command trigger.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4d07642..b447306 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -66,7 +66,7 @@ ExecRenameStmt(RenameStmt *stmt)
 			break;
 
 		case OBJECT_CMDTRIGGER:
-			RenameCmdTrigger(stmt->object, stmt->subname, stmt->newname);
+			RenameCmdTrigger(stmt->object, stmt->newname);
 			break;
 
 		case OBJECT_DATABASE:
diff --git a/src/backend/commands/cmdtrigger.c b/src/backend/commands/cmdtrigger.c
index e8d294d..ef9794f 100644
--- a/src/backend/commands/cmdtrigger.c
+++ b/src/backend/commands/cmdtrigger.c
@@ -294,13 +294,17 @@ AlterCmdTrigger(AlterCmdTrigStmt *stmt)
  * Rename command trigger
  */
 void
-RenameCmdTrigger(const char *trigname, const char *newname)
+RenameCmdTrigger(List *name, const char *newname)
 {
 	SysScanDesc tgscan;
 	ScanKeyData skey[1];
 	HeapTuple	tup;
 	Relation	rel;
 	Form_pg_cmdtrigger cmdForm;
+	char *trigname;
+
+	Assert(list_length(name) == 1);
+	trigname = strVal((Value *)linitial(name));
 
 	CheckCmdTriggerPrivileges();
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index defcdd1..6a20a6c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3463,7 +3463,6 @@ _copyCreateCmdTrigStmt(const CreateCmdTrigStmt *from)
 {
 	CreateCmdTrigStmt *newnode = makeNode(CreateCmdTrigStmt);
 
-	COPY_NODE_FIELD(command);
 	COPY_STRING_FIELD(trigname);
 	COPY_SCALAR_FIELD(timing);
 	COPY_NODE_FIELD(funcname);
@@ -3476,7 +3475,6 @@ _copyAlterCmdTrigStmt(const AlterCmdTrigStmt *from)
 {
 	AlterCmdTrigStmt *newnode = makeNode(AlterCmdTrigStmt);
 
-	COPY_STRING_FIELD(command);
 	COPY_STRING_FIELD(trigname);
 	COPY_STRING_FIELD(tgenabled);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 1ad31f0..137075a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1780,7 +1780,6 @@ _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 static bool
 _equalCreateCmdTrigStmt(const CreateCmdTrigStmt *a, const CreateCmdTrigStmt *b)
 {
-	COMPARE_NODE_FIELD(command);
 	COMPARE_STRING_FIELD(trigname);
 	COMPARE_SCALAR_FIELD(timing);
 	COMPARE_NODE_FIELD(funcname);
@@ -1791,7 +1790,6 @@ _equalCreateCmdTrigStmt(const CreateCmdTrigStmt *a, const CreateCmdTrigStmt *b)
 static bool
 _equalAlterCmdTrigStmt(const AlterCmdTrigStmt *a, const AlterCmdTrigStmt *b)
 {
-	COMPARE_STRING_FIELD(command);
 	COMPARE_STRING_FIELD(trigname);
 	COMPARE_STRING_FIELD(tgenabled);
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e5a0d34..e9872d3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -6931,13 +6931,12 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
-			| ALTER TRIGGER name ON COMMAND trigger_command RENAME TO name
+			| ALTER COMMAND TRIGGER name RENAME TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
 					n->renameType = OBJECT_CMDTRIGGER;
-					n->object  = list_make1(makeString($6));
-					n->subname = $3;
-					n->newname = $9;
+					n->object  = list_make1(makeString($4));
+					n->newname = $7;
 					$$ = (Node *)n;
 				}
 			| ALTER ROLE RoleId RENAME TO RoleId
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3c75cf3..69d6479 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -168,7 +168,6 @@ call_before_cmdtriggers(Node *parsetree, CommandContext cmd)
 		case T_AlterOwnerStmt:
 		case T_AlterSeqStmt:
 		case T_AlterTableStmt:
-		case T_RenameStmt:
 		case T_CommentStmt:
 		case T_DefineStmt:
 		case T_CreateCastStmt:
@@ -224,8 +223,14 @@ call_before_cmdtriggers(Node *parsetree, CommandContext cmd)
 				ExecBeforeCommandTriggers(cmd);
 			return;
 
+		case T_RenameStmt:
+			if (((RenameStmt *) parsetree)->renameType != OBJECT_CMDTRIGGER)
+				if (CommandFiresTriggers(cmd))
+					ExecBeforeCommandTriggers(cmd);
+			return;
+
 		case T_DropStmt:
-			if (((DropStmt *) cmd->parsetree)->removeType != OBJECT_CMDTRIGGER)
+			if (((DropStmt *) parsetree)->removeType != OBJECT_CMDTRIGGER)
 				if (CommandFiresTriggers(cmd))
 					ExecBeforeCommandTriggers(cmd);
 			return;
@@ -251,7 +256,6 @@ call_after_cmdtriggers(Node *parsetree, CommandContext cmd)
 		case T_AlterOwnerStmt:
 		case T_AlterSeqStmt:
 		case T_AlterTableStmt:
-		case T_RenameStmt:
 		case T_CommentStmt:
 		case T_DefineStmt:
 		case T_CreateCastStmt:
@@ -310,8 +314,14 @@ call_after_cmdtriggers(Node *parsetree, CommandContext cmd)
 					ExecAfterCommandTriggers(cmd);
 			return;
 
+		case T_RenameStmt:
+			if (((RenameStmt *) parsetree)->renameType != OBJECT_CMDTRIGGER)
+				if (CommandFiresTriggers(cmd))
+					ExecAfterCommandTriggers(cmd);
+			return;
+
 		case T_DropStmt:
-			if (((DropStmt *) cmd->parsetree)->removeType != OBJECT_CMDTRIGGER)
+			if (((DropStmt *) parsetree)->removeType != OBJECT_CMDTRIGGER)
 				if (CommandFiresAfterTriggers(cmd))
 					ExecAfterCommandTriggers(cmd);
 			return;
diff --git a/src/include/commands/cmdtrigger.h b/src/include/commands/cmdtrigger.h
index 01a6ca3..fae2bfc 100644
--- a/src/include/commands/cmdtrigger.h
+++ b/src/include/commands/cmdtrigger.h
@@ -42,7 +42,7 @@ extern void CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString);
 extern void RemoveCmdTriggerById(Oid ctrigOid);
 extern Oid	get_cmdtrigger_oid(const char *trigname, bool missing_ok);
 extern void AlterCmdTrigger(AlterCmdTrigStmt *stmt);
-extern void RenameCmdTrigger(const char *trigname, const char *newname);
+extern void RenameCmdTrigger(List *name, const char *newname);
 
 extern void InitCommandContext(CommandContext cmd, const Node *stmt, bool list_triggers);
 extern bool ListCommandTriggers(CommandContext cmd);
diff --git a/src/test/regress/expected/cmdtriggers.out b/src/test/regress/expected/cmdtriggers.out
index 8cb1f78..85a8d38 100644
--- a/src/test/regress/expected/cmdtriggers.out
+++ b/src/test/regress/expected/cmdtriggers.out
@@ -21,9 +21,10 @@ begin
 end;
 $$;
 create command trigger snitch_before before any command execute procedure any_snitch();
-create command trigger snitch_after  after  any command execute procedure any_snitch();
+create command trigger snitch_after_ after  any command execute procedure any_snitch();
 alter command trigger snitch_before set disable;
 alter command trigger snitch_before set enable;
+alter command trigger snitch_after_ rename to snitch_after;
 create command trigger snitch_create_table after create table execute procedure snitch();
 create command trigger snitch_alter_table after alter table execute procedure snitch();
 create command trigger snitch_drop_table after drop table execute procedure snitch();
diff --git a/src/test/regress/sql/cmdtriggers.sql b/src/test/regress/sql/cmdtriggers.sql
index 215da78..60d3be0 100644
--- a/src/test/regress/sql/cmdtriggers.sql
+++ b/src/test/regress/sql/cmdtriggers.sql
@@ -23,10 +23,11 @@ end;
 $$;
 
 create command trigger snitch_before before any command execute procedure any_snitch();
-create command trigger snitch_after  after  any command execute procedure any_snitch();
+create command trigger snitch_after_ after  any command execute procedure any_snitch();
 
 alter command trigger snitch_before set disable;
 alter command trigger snitch_before set enable;
+alter command trigger snitch_after_ rename to snitch_after;
 
 create command trigger snitch_create_table after create table execute procedure snitch();
 create command trigger snitch_alter_table after alter table execute procedure snitch();
#43Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#41)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

problem. It was the DROP COMMAND TRIGGER statement that garnered
comment, as it makes more sense to drop the entire trigger than
individual commands for that trigger.

What you're saying here is that a single command could have more than
one command attached to it, and what I understand Tom, Robert and Kevin
are saying is that any given command trigger should only be attached to
a single command.

If we wanted to be more consistent we would need to have a way to attach
the same trigger to both BEFORE and AFTER the command, as of now you
need two triggers here.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#44Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#42)
Re: Command Triggers, patch v11

On 3 March 2012 14:26, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thom Brown <thom@linux.com> writes:

And having tried building it, it appears to fail.

Sorry about that, my compiler here was happy building the source (and I
had been doing make clean install along the way) and make installcheck
passed, here.

Now fixed on my github's branch, including docs.

I'll send an updated patch revision later, hopefully including pg_dump
support fixtures (well, adaptation to the new way of doing things IIUC)
and maybe with some trigger arguments rework done.

I understand that you're not blocked until that new version is out,
right? You could use either the incremental patch attached for
convenience.

Thanks for the extra patch. Do you see any functional changes between
now and your next patch? If so, it doesn't make sense for me to test
anything yet.

--
Thom

#45Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#43)
Re: Command Triggers, patch v11

On 3 March 2012 14:34, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Thom Brown <thom@linux.com> writes:

problem.  It was the DROP COMMAND TRIGGER statement that garnered
comment, as it makes more sense to drop the entire trigger than
individual commands for that trigger.

What you're saying here is that a single command could have more than
one command attached to it, and what I understand Tom, Robert and Kevin
are saying is that any given command trigger should only be attached to
a single command.

I hadn't interpreted it that way, but then that could just be my
misinterpretation. I'm still of the opinion that a single command
trigger should be able to attach to multiple commands, just not
amendable once the trigger has been created. If that's not how others
saw it, I'm outnumbered.

So my vote was for:

CREATE COMMAND TRIGGER name { BEFORE | AFTER } command [, ... ]
EXECUTE PROCEDURE function_name ()

ALTER COMMAND TRIGGER name SET enabled

DROP COMMAND TRIGGER [ IF EXISTS ] name [ CASCADE | RESTRICT ]

The only difference shown above compared to the current implementation
in your patch is [, ... ] after command in the CREATE COMMAND TRIGGER
statement.

My argument for this is that you may wish to implement the same
trigger for tables, views and foreign tables rather than create 3
separate triggers.

If we wanted to be more consistent we would need to have a way to attach
the same trigger to both BEFORE and AFTER the command, as of now you
need two triggers here.

I'm not sure I understand. Attaching a trigger to both BEFORE and
AFTER isn't possible for regular triggers so I don't see why that
would introduce consistency. Could you explain?

--
Thom

#46Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Thom Brown (#45)
Re: Command Triggers, patch v11

Thom Brown wrote:
Dimitri Fontaine wrote:

Thom Brown writes:

problem. It was the DROP COMMAND TRIGGER statement that garnered
comment, as it makes more sense to drop the entire trigger than
individual commands for that trigger.

What you're saying here is that a single command could have more
than one command attached to it, and what I understand Tom, Robert
and Kevin are saying is that any given command trigger should only
be attached to a single command.

I hadn't interpreted it that way

Nor had I.

I'm still of the opinion that a single command trigger should be

able to attach to multiple commands, just not

amendable once the trigger has been created.

That was my understanding of what we were discussing, too.

So my vote was for:

CREATE COMMAND TRIGGER name { BEFORE | AFTER } command [, ... ]
EXECUTE PROCEDURE function_name ()

ALTER COMMAND TRIGGER name SET enabled

DROP COMMAND TRIGGER [ IF EXISTS ] name [ CASCADE | RESTRICT ]

Same here.

My argument for this is that you may wish to implement the same
trigger for tables, views and foreign tables rather than create 3
separate triggers.

Right. What I thought I was agreeing with was the notion that you
should need to specify more than the trigger name to drop the
trigger. Rather like how you can create a trigger AFTER INSERT OR
UPDATE OR DELETE, but you don't need to specify all those events to
drop the trigger -- just the name will do.

-Kevin

#47Thom Brown
thom@linux.com
In reply to: Kevin Grittner (#46)
Re: Command Triggers, patch v11

On 3 March 2012 16:12, Kevin Grittner <Kevin.Grittner@wicourts.gov> wrote:

Thom Brown  wrote:
Dimitri Fontaine  wrote:

Thom Brown  writes:

problem. It was the DROP COMMAND TRIGGER statement that garnered
comment, as it makes more sense to drop the entire trigger than
individual commands for that trigger.

What you're saying here is that a single command could have more
than one command attached to it, and what I understand Tom, Robert
and Kevin are saying is that any given command trigger should only
be attached to a single command.

I hadn't interpreted it that way

Nor had I.

I'm still of the opinion that a single command trigger should be

able to attach to multiple commands, just not

amendable once the trigger has been created.

That was my understanding of what we were discussing, too.

So my vote was for:

CREATE COMMAND TRIGGER name { BEFORE | AFTER } command [, ... ]
EXECUTE PROCEDURE function_name ()

ALTER COMMAND TRIGGER name SET enabled

DROP COMMAND TRIGGER [ IF EXISTS ] name [ CASCADE | RESTRICT ]

Same here.

My argument for this is that you may wish to implement the same
trigger for tables, views and foreign tables rather than create 3
separate triggers.

Right.  What I thought I was agreeing with was the notion that you
should need to specify more than the trigger name to drop the
trigger.  Rather like how you can create a trigger AFTER INSERT OR
UPDATE OR DELETE, but you don't need to specify all those events to
drop the trigger -- just the name will do.

Don't you mean "shouldn't need to specify more than the trigger name"?

--
Thom

#48Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Thom Brown (#47)
Re: Command Triggers, patch v11

Thom Brown wrote:

Don't you mean "shouldn't need to specify more than the trigger
name"?

You are right, that's what I meant to say.

-Kevin

#49Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Kevin Grittner (#46)
Re: Command Triggers, patch v11

"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:

Right. What I thought I was agreeing with was the notion that you
should need to specify more than the trigger name to drop the
trigger. Rather like how you can create a trigger AFTER INSERT OR
UPDATE OR DELETE, but you don't need to specify all those events to
drop the trigger -- just the name will do.

The parallel between INSERT/UPDATE/DELETE and the trigger's command is
not working well enough, because in the data trigger case we're managing
a single catalog entry with a single command, and in the command trigger
case, in my model at least, we would be managing several catalog entries
per command.

To take an example:

CREATE COMMAND TRIGGER foo AFTER create table, create view;
DROP COMMAND TRIGGER foo;

The first command would create two catalog entries, and the second one
would delete the same two entries. It used to work this way in the
patch, then when merging with the new remove object infrastructure I
lost that ability. From the beginning Robert has been saying he didn't
want that behavior, and Tom is now saying the same, IIUC.

So we're back to one command, one catalog entry.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#50Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#49)
Re: Command Triggers, patch v11

On 3 March 2012 19:25, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:

Right.  What I thought I was agreeing with was the notion that you
should need to specify more than the trigger name to drop the
trigger.  Rather like how you can create a trigger AFTER INSERT OR
UPDATE OR DELETE, but you don't need to specify all those events to
drop the trigger -- just the name will do.

The parallel between INSERT/UPDATE/DELETE and the trigger's command is
not working well enough, because in the data trigger case we're managing
a single catalog entry with a single command, and in the command trigger
case, in my model at least, we would be managing several catalog entries
per command.

To take an example:

 CREATE COMMAND TRIGGER foo AFTER create table, create view;
 DROP COMMAND TRIGGER foo;

The first command would create two catalog entries, and the second one
would delete the same two entries.  It used to work this way in the
patch, then when merging with the new remove object infrastructure I
lost that ability.  From the beginning Robert has been saying he didn't
want that behavior, and Tom is now saying the same, IIUC.

So we're back to one command, one catalog entry.

That sucks. I'm surprised there's no provision for overriding it on a
command-by-command basis.

I would suggest an array instead, but that sounds costly from a
look-up perspective.

--
Thom

#51Andres Freund
andres@anarazel.de
In reply to: Thom Brown (#36)
2 attachment(s)
Re: Command Triggers, patch v11

Hi,

On Saturday, March 03, 2012 01:08:33 AM Thom Brown wrote:

On 2 March 2012 23:33, Thom Brown <thom@linux.com> wrote:

On 2 March 2012 22:32, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

test=# CREATE TABLE badname (id int, a int, b text);
ERROR: invalid relation name: badname
test=# CREATE TABLE badname AS SELECT 1::int id, 1::int a, ''::text b;
SELECT 1

This doesn't even get picked up by ANY COMMAND.

I think Andres should add an entry for his patch on the commitfest. Is
it ok?

I'll try Andres' patch this weekend while I test yours.

Actually no matter which patch I apply first, they cause the other to
fail to apply.

Ok, I rebased my patch ontop of dim's current HEAD. There was only one trivial
conflict in tablecmds.h. I had written the patch independently of the command
triggers stuff because I though, and still do, that would make applying it
easier.

Attached are two versions of the patch, one based on command triggers and one
without. Both pass regression tests for me.

Andres

Attachments:

ctas-01.patchtext/x-patch; charset=UTF-8; name=ctas-01.patchDownload
From a7259034845e8d3e0f2b9cd93121553580d64715 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 4 Mar 2012 16:45:01 +0100
Subject: [PATCH] Transform CREATE TABLE AS/SELECT INTO into a utility
 statement

---
 src/backend/commands/copy.c                |   20 +-
 src/backend/commands/prepare.c             |   69 +----
 src/backend/commands/tablecmds.c           |  335 +++++++++++++++++++++
 src/backend/commands/view.c                |   20 +-
 src/backend/executor/execMain.c            |  441 ++--------------------------
 src/backend/executor/execUtils.c           |    2 -
 src/backend/executor/functions.c           |    4 +-
 src/backend/executor/spi.c                 |   10 +-
 src/backend/nodes/copyfuncs.c              |   18 +-
 src/backend/nodes/equalfuncs.c             |   16 +-
 src/backend/nodes/outfuncs.c               |    4 +-
 src/backend/nodes/readfuncs.c              |    1 -
 src/backend/optimizer/plan/planner.c       |    1 -
 src/backend/optimizer/plan/subselect.c     |    1 -
 src/backend/optimizer/prep/prepjointree.c  |    6 +-
 src/backend/optimizer/util/clauses.c       |    4 +-
 src/backend/parser/analyze.c               |  113 +++++---
 src/backend/parser/gram.y                  |   28 ++-
 src/backend/parser/parse_clause.c          |   21 +-
 src/backend/parser/parse_cte.c             |   21 +-
 src/backend/parser/parse_expr.c            |   21 +-
 src/backend/parser/parser.c                |   38 +++
 src/backend/rewrite/rewriteDefine.c        |    3 +-
 src/backend/tcop/pquery.c                  |   13 +-
 src/backend/tcop/utility.c                 |   46 ++--
 src/include/access/htup.h                  |    2 +-
 src/include/commands/prepare.h             |    3 +
 src/include/commands/tablecmds.h           |    4 +
 src/include/executor/executor.h            |    5 +
 src/include/nodes/execnodes.h              |    9 +-
 src/include/nodes/nodes.h                  |    1 +
 src/include/nodes/parsenodes.h             |   20 +-
 src/include/nodes/plannodes.h              |    2 -
 src/pl/plpgsql/src/pl_exec.c               |   27 +--
 src/test/regress/expected/select_into.out  |   19 ++
 src/test/regress/expected/transactions.out |    2 +-
 src/test/regress/sql/select_into.sql       |   15 +
 37 files changed, 720 insertions(+), 645 deletions(-)

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 110480f..a3c5b4b 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1190,6 +1190,20 @@ BeginCopy(bool is_from,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("COPY (SELECT) WITH OIDS is not supported")));
 
+
+		/* Query mustn't use INTO, either */
+		if(IsA(raw_query, SelectStmt))
+		{
+			SelectStmt* first = (SelectStmt*)raw_query;
+			while (first && first->op != SETOP_NONE)
+				first = first->larg;
+
+			if (first->intoClause)
+				ereport(ERROR,
+				        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				         errmsg("COPY (SELECT INTO) is not supported")));
+		}
+
 		/*
 		 * Run parse analysis and rewrite.	Note this also acquires sufficient
 		 * locks on the source table(s).
@@ -1211,12 +1225,6 @@ BeginCopy(bool is_from,
 		Assert(query->commandType == CMD_SELECT);
 		Assert(query->utilityStmt == NULL);
 
-		/* Query mustn't use INTO, either */
-		if (query->intoClause)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("COPY (SELECT INTO) is not supported")));
-
 		/* plan the query */
 		plan = planner(query, 0, NULL);
 
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 4883abe..86e8cf9 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -43,8 +43,6 @@
 static HTAB *prepared_queries = NULL;
 
 static void InitQueryHashTable(void);
-static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
-			   const char *queryString, EState *estate);
 static Datum build_regtype_array(Oid *param_types, int num_params);
 
 /*
@@ -177,6 +175,9 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
  * EXECUTE, which we might need for error reporting while processing the
  * parameter expressions.  The query_string that we copy from the plan
  * source is that of the original PREPARE.
+ *
+ * If you add additional things here you should check whether
+ * CreateTableAs also needs them.
  */
 void
 ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
@@ -222,51 +223,9 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
 	query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
 									   entry->plansource->query_string);
 
-	/*
-	 * For CREATE TABLE / AS EXECUTE, we must make a copy of the stored query
-	 * so that we can modify its destination (yech, but this has always been
-	 * ugly).  For regular EXECUTE we can just use the cached query, since the
-	 * executor is read-only.
-	 */
-	if (stmt->into)
-	{
-		MemoryContext oldContext;
-		PlannedStmt *pstmt;
-
-		/* Replan if needed, and increment plan refcount transiently */
-		cplan = GetCachedPlan(entry->plansource, paramLI, true);
-
-		/* Copy plan into portal's context, and modify */
-		oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
-		plan_list = copyObject(cplan->stmt_list);
-
-		if (list_length(plan_list) != 1)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("prepared statement is not a SELECT")));
-		pstmt = (PlannedStmt *) linitial(plan_list);
-		if (!IsA(pstmt, PlannedStmt) ||
-			pstmt->commandType != CMD_SELECT ||
-			pstmt->utilityStmt != NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("prepared statement is not a SELECT")));
-		pstmt->intoClause = copyObject(stmt->into);
-
-		MemoryContextSwitchTo(oldContext);
-
-		/* We no longer need the cached plan refcount ... */
-		ReleaseCachedPlan(cplan, true);
-		/* ... and we don't want the portal to depend on it, either */
-		cplan = NULL;
-	}
-	else
-	{
-		/* Replan if needed, and increment plan refcount for portal */
-		cplan = GetCachedPlan(entry->plansource, paramLI, false);
-		plan_list = cplan->stmt_list;
-	}
+	/* Replan if needed, and increment plan refcount for portal */
+	cplan = GetCachedPlan(entry->plansource, paramLI, false);
+	plan_list = cplan->stmt_list;
 
 	PortalDefineQuery(portal,
 					  NULL,
@@ -302,7 +261,7 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
  * CreateQueryDesc(), which allows the executor to make use of the parameters
  * during query execution.
  */
-static ParamListInfo
+ParamListInfo
 EvaluateParams(PreparedStatement *pstmt, List *params,
 			   const char *queryString, EState *estate)
 {
@@ -666,20 +625,6 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
 
 		if (IsA(pstmt, PlannedStmt))
 		{
-			if (execstmt->into)
-			{
-				if (pstmt->commandType != CMD_SELECT ||
-					pstmt->utilityStmt != NULL)
-					ereport(ERROR,
-							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-							 errmsg("prepared statement is not a SELECT")));
-
-				/* Copy the stmt so we can modify it */
-				pstmt = copyObject(pstmt);
-
-				pstmt->intoClause = execstmt->into;
-			}
-
 			ExplainOnePlan(pstmt, es, query_string, paramLI);
 		}
 		else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cd4490a..9a67cf5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -44,6 +44,7 @@
 #include "commands/cluster.h"
 #include "commands/comment.h"
 #include "commands/defrem.h"
+#include "commands/prepare.h"
 #include "commands/sequence.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
@@ -68,6 +69,7 @@
 #include "parser/parser.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
+#include "tcop/tcopprot.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/lock.h"
@@ -195,6 +197,18 @@ struct dropmsgstrings
 	const char *drophint_msg;
 };
 
+/*
+ * Support structure for CTAS
+ */
+typedef struct
+{
+	DestReceiver pub;			/* publicly-known function pointers */
+	EState	   *estate;			/* EState we are working with */
+	Relation	rel;			/* Relation to write to */
+	int			hi_options;		/* heap_insert performance options */
+	BulkInsertState bistate;	/* bulk insert state */
+} DR_intorel;
+
 static const struct dropmsgstrings dropmsgstringarray[] = {
 	{RELKIND_RELATION,
 		ERRCODE_UNDEFINED_TABLE,
@@ -671,6 +685,235 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	return relationId;
 }
 
+void
+CreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
+              ParamListInfo params, DestReceiver *dest){
+	IntoClause *into = stmt->into;
+	Query *query;
+	List *rewritten;
+	PlannedStmt *plan;
+	QueryDesc *queryDesc;
+	Relation intoRelationDesc;
+	CreateStmt *create;
+	int natt;
+	Oid intoRelationId;
+	ListCell *lc;
+	int eflags = 0;
+	EState *param_estate = NULL;
+	CachedPlan *cplan = NULL;
+
+	Assert(IsA(stmt->query, Query));
+	query = (Query*)stmt->query;
+
+	/*
+	 * Construct plans for the supported
+	 */
+	if(query->commandType == CMD_SELECT){
+		rewritten = QueryRewrite((Query *) copyObject(stmt->query));
+
+		if(!rewritten)
+			elog(ERROR, "CREATE TABLE AS/SELECT INTO query rewrote to nothing");
+
+		if(list_length(rewritten) != 1)
+			elog(ERROR, "CREATE TABLE AS/SELECT INTO query rewrote to more than one query");
+
+		plan = pg_plan_query(lfirst(list_head(rewritten)), 0, params);
+	}
+	else if(query->commandType == CMD_UTILITY &&
+	        IsA(query->utilityStmt, ExecuteStmt)){
+		/*
+		 * We cannot easily share more infrastructure with prepare.c's
+		 * ExecuteQuery because teaching it to return a started
+		 * executor would amount to about the same amount of
+		 * duplication as doing it here.
+		 *
+		 */
+		ExecuteStmt *stmt;
+		PreparedStatement *entry;
+		List *plan_list;
+		ParamListInfo paramLI = NULL;
+
+		Assert(IsA(query->utilityStmt, ExecuteStmt));
+		stmt = (ExecuteStmt*)(query->utilityStmt);
+
+		entry = FetchPreparedStatement(stmt->name, true);
+
+		/* Shouldn't find a non-fixed-result cached plan */
+		if (!entry->plansource->fixed_result)
+			elog(ERROR, "EXECUTE does not support variable-result cached plans");
+
+		/* Evaluate parameters, if any */
+		if (entry->plansource->num_params > 0)
+		{
+			param_estate = CreateExecutorState();
+			param_estate->es_param_list_info = params;
+			paramLI = EvaluateParams(entry, stmt->params,
+			                         queryString, param_estate);
+
+		}
+
+		cplan = GetCachedPlan(entry->plansource, paramLI, true);
+		plan_list = cplan->stmt_list;
+
+		if (list_length(plan_list) != 1)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("prepared statement is not a SELECT")));
+
+		plan = (PlannedStmt *) linitial(plan_list);
+		if (!IsA(plan, PlannedStmt) ||
+			plan->commandType != CMD_SELECT ||
+			plan->utilityStmt != NULL)
+			ereport(ERROR,
+			        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+			         errmsg("prepared statement is not a SELECT")));
+
+		queryString = pstrdup(entry->plansource->query_string);
+	}
+	else{
+		elog(ERROR, "unsupported command for CREATE TABLE AS");
+		abort();//silence compiler
+	}
+
+
+	/*
+	 * Use a snapshot with an updated command ID to ensure this query sees
+	 * results of any previously executed queries.
+	 */
+	PushCopiedSnapshot(GetActiveSnapshot());
+	UpdateActiveSnapshotCommandId();
+
+	queryDesc = CreateQueryDesc(plan, queryString,
+								GetActiveSnapshot(), InvalidSnapshot,
+								CreateDestReceiver(DestIntoRel), params,
+	                            false);
+
+	/*
+	 * We need to tell the executor whether it has to produce oids or
+	 * not because it doesn't have enough information to do so itself
+	 * as were not telling it that its inserting into a table (because
+	 * that would be too complicated for now)
+	 */
+	if(interpretOidsOption(into->options))
+		eflags |= EXEC_FLAG_FORCE_OIDS_ON;
+	else
+		eflags |= EXEC_FLAG_FORCE_OIDS_OFF;
+
+	/*
+	 * call ExecutorStart to prepare the plan for execution. Only
+	 * after this we have enough information to actually create a
+	 * target relation
+	 */
+	ExecutorStart(queryDesc, eflags);
+
+	/*
+	 * create the target relation
+	 */
+	create = makeNode(CreateStmt);
+	create->relation = into->rel;
+	create->options = into->options;
+	create->oncommit = into->onCommit;
+	create->tablespacename = into->tableSpaceName;
+
+	/*
+	 * If a column name list was specified in CREATE TABLE AS,
+	 * override the column names derived from the query.  (Too few
+	 * column names are OK, too many are not.)
+	 */
+	if (list_length(into->colNames) > queryDesc->tupDesc->natts)
+		ereport(ERROR,
+		        (errcode(ERRCODE_SYNTAX_ERROR),
+		         errmsg("CREATE TABLE AS specifies too many column names")));
+	lc = list_head(into->colNames);
+	for(natt = 0; natt < queryDesc->tupDesc->natts; natt++){
+		ColumnDef *col = makeNode(ColumnDef);
+		TypeName *type = makeNode(TypeName);
+
+		Form_pg_attribute attribute = queryDesc->tupDesc->attrs[natt];
+
+		if(!lc)
+			col->colname = NameStr(attribute->attname);
+		else{
+			col->colname = strVal(lfirst(lc));
+			lc = lnext(lc);
+		}
+
+		col->collOid = attribute->attcollation;
+
+		type->typeOid = attribute->atttypid;
+		type->typemod = attribute->atttypmod;
+		col->typeName= type;
+
+		/*
+		 * We check the column type here because collOid=InvalidOid
+		 * denotes an invalid oid for a collatable type in
+		 * queryDesc->tupDesc but GetColumnDefCollation - which will
+		 * be called by DefineRelation - defers the default collation
+		 * out of that...
+		 * XXX: A better solution would be welcome.
+		 */
+		CheckAttributeType(col->colname, col->typeName->typeOid, col->collOid,
+		                   NULL, false);
+		create->tableElts = lappend(create->tableElts, col);
+	}
+
+	/*
+	 * Actually create the target table
+	 */
+	intoRelationId = DefineRelation(create, RELKIND_RELATION, InvalidOid);
+	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
+
+
+	if(!into->skipData){
+		DR_intorel *myState = (DR_intorel *) queryDesc->dest;
+		/* setup the target of the DestIntoRel receiver */
+		myState->estate = queryDesc->estate;
+		myState->rel = intoRelationDesc;
+
+		/* run the plan */
+		ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+	}
+
+	/*
+	 * Check INSERT permission on the constructed table.
+	 *
+	 * XXX: It would arguable make sense to do this check only if
+	 * into->skipData is true.
+	 */
+	{
+		RangeTblEntry *rte = makeNode(RangeTblEntry);
+		rte->rtekind = RTE_RELATION;
+		rte->relid = intoRelationId;
+		rte->relkind = RELKIND_RELATION;
+		rte->requiredPerms = ACL_INSERT;
+
+		for (natt = 1; natt <= intoRelationDesc->rd_att->natts; natt++)
+			rte->modifiedCols = bms_add_member(rte->modifiedCols,
+			                                   natt - FirstLowInvalidHeapAttributeNumber);
+
+		ExecCheckRTPerms(list_make1(rte), true);
+	}
+
+	/* run cleanup too */
+	ExecutorFinish(queryDesc);
+	ExecutorEnd(queryDesc);
+
+	/* close rel, but keep lock until commit */
+	heap_close(intoRelationDesc, NoLock);
+
+	FreeQueryDesc(queryDesc);
+
+	if(cplan){
+		ReleaseCachedPlan(cplan, true);
+
+		if (param_estate)
+			FreeExecutorState(param_estate);
+	}
+
+	PopActiveSnapshot();
+}
+
 /*
  * Emit the right error or warning message for a "DROP" command issued on a
  * non-existent relation
@@ -10238,3 +10481,95 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
 
 	ReleaseSysCache(tuple);
 }
+
+/*
+ * Support for SELECT INTO (aka CREATE TABLE AS)
+ *
+ * We implement SELECT INTO by diverting SELECT's normal output with
+ * a specialized DestReceiver type.
+ */
+
+/*
+ * intorel_startup --- executor startup
+ */
+static void
+intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+	myState->bistate = GetBulkInsertState();
+	myState->hi_options = HEAP_INSERT_SKIP_FSM;
+	if (!XLogIsNeeded())
+		myState->hi_options |= HEAP_INSERT_SKIP_WAL;
+
+}
+
+/*
+ * intorel_receive --- receive one tuple
+ */
+static void
+intorel_receive(TupleTableSlot *slot, DestReceiver *self)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+	HeapTuple	tuple;
+
+	/*
+	 * get the heap tuple out of the tuple table slot, making sure we have a
+	 * writable copy
+	 */
+	tuple = ExecMaterializeSlot(slot);
+
+	/*
+	 * force assignment of new OID (see comments in ExecInsert)
+	 */
+	if (myState->rel->rd_rel->relhasoids)
+		HeapTupleSetOid(tuple, InvalidOid);
+
+	heap_insert(myState->rel,
+				tuple,
+				myState->estate->es_output_cid,
+				myState->hi_options,
+				myState->bistate);
+
+	/* We know this is a newly created relation, so there are no indexes */
+}
+
+/*
+ * intorel_shutdown --- executor end
+ */
+static void
+intorel_shutdown(DestReceiver *self)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+
+	FreeBulkInsertState(myState->bistate);
+	if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
+		heap_sync(myState->rel);
+}
+
+/*
+ * intorel_destroy --- release DestReceiver object
+ */
+static void
+intorel_destroy(DestReceiver *self)
+{
+	pfree(self);
+}
+
+/*
+ * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
+ *
+ */
+DestReceiver *
+CreateIntoRelDestReceiver(void)
+{
+	DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
+
+	self->pub.receiveSlot = intorel_receive;
+	self->pub.rStartup = intorel_startup;
+	self->pub.rShutdown = intorel_shutdown;
+	self->pub.rDestroy = intorel_destroy;
+	self->pub.mydest = DestIntoRel;
+	/* private fields will be set on startup */
+
+	return (DestReceiver *) self;
+}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 99fb7db..93e523a 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -428,6 +428,22 @@ DefineView(ViewStmt *stmt, const char *queryString)
 	RangeVar   *view;
 
 	/*
+	 * We check for SELECT INTO before parse analysis because that
+	 * would complain about the SELECT INTO without specific enough detail.
+	 */
+	if(IsA(stmt->query, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)stmt->query;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			         errmsg("views must not contain SELECT INTO")));
+	}
+
+	/*
 	 * Run parse analysis to convert the raw parse tree to a Query.  Note this
 	 * also acquires sufficient locks on the source table(s).
 	 *
@@ -449,10 +465,6 @@ DefineView(ViewStmt *stmt, const char *queryString)
 	 * DefineQueryRewrite(), but that function will complain about a bogus ON
 	 * SELECT rule, and we'd rather the message complain about a view.
 	 */
-	if (viewParse->intoClause != NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("views must not contain SELECT INTO")));
 	if (viewParse->hasModifyingCTE)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 36dcc8e..1979325 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -90,12 +90,6 @@ static char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
 										   int maxfieldlen);
 static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
 				  Plan *planTree);
-static void OpenIntoRel(QueryDesc *queryDesc);
-static void CloseIntoRel(QueryDesc *queryDesc);
-static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
-static void intorel_receive(TupleTableSlot *slot, DestReceiver *self);
-static void intorel_shutdown(DestReceiver *self);
-static void intorel_destroy(DestReceiver *self);
 
 /* end of local decls */
 
@@ -174,11 +168,10 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
 		case CMD_SELECT:
 
 			/*
-			 * SELECT INTO, SELECT FOR UPDATE/SHARE and modifying CTEs need to
+			 * SELECT FOR UPDATE/SHARE and modifying CTEs need to
 			 * mark tuples
 			 */
-			if (queryDesc->plannedstmt->intoClause != NULL ||
-				queryDesc->plannedstmt->rowMarks != NIL ||
+			if (queryDesc->plannedstmt->rowMarks != NIL ||
 				queryDesc->plannedstmt->hasModifyingCTE)
 				estate->es_output_cid = GetCurrentCommandId(true);
 
@@ -212,6 +205,13 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
 	estate->es_top_eflags = eflags;
 	estate->es_instrument = queryDesc->instrument_options;
 
+	if(eflags & EXEC_FLAG_FORCE_OIDS_ON)
+		estate->es_force_oids = ESTATE_FORCE_OIDS_ON;
+	else if(eflags & EXEC_FLAG_FORCE_OIDS_OFF)
+		estate->es_force_oids = ESTATE_FORCE_OIDS_OFF;
+	else
+		estate->es_force_oids = ESTATE_FORCE_OIDS_DEFAULT;
+
 	/*
 	 * Initialize the plan state tree
 	 */
@@ -310,13 +310,6 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 		(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
 
 	/*
-	 * if it's CREATE TABLE AS ... WITH NO DATA, skip plan execution
-	 */
-	if (estate->es_select_into &&
-		queryDesc->plannedstmt->intoClause->skipData)
-		direction = NoMovementScanDirection;
-
-	/*
 	 * run plan
 	 */
 	if (!ScanDirectionIsNoMovement(direction))
@@ -451,12 +444,6 @@ standard_ExecutorEnd(QueryDesc *queryDesc)
 
 	ExecEndPlan(queryDesc->planstate, estate);
 
-	/*
-	 * Close the SELECT INTO relation if any
-	 */
-	if (estate->es_select_into)
-		CloseIntoRel(queryDesc);
-
 	/* do away with our snapshots */
 	UnregisterSnapshot(estate->es_snapshot);
 	UnregisterSnapshot(estate->es_crosscheck_snapshot);
@@ -706,15 +693,6 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 {
 	ListCell   *l;
 
-	/*
-	 * CREATE TABLE AS or SELECT INTO?
-	 *
-	 * XXX should we allow this if the destination is temp?  Considering that
-	 * it would still require catalog changes, probably not.
-	 */
-	if (plannedstmt->intoClause != NULL)
-		PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
-
 	/* Fail if write permissions are requested on any non-temp table */
 	foreach(l, plannedstmt->rtable)
 	{
@@ -864,18 +842,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	}
 
 	/*
-	 * Detect whether we're doing SELECT INTO.  If so, set the es_into_oids
-	 * flag appropriately so that the plan tree will be initialized with the
-	 * correct tuple descriptors.  (Other SELECT INTO stuff comes later.)
-	 */
-	estate->es_select_into = false;
-	if (operation == CMD_SELECT && plannedstmt->intoClause != NULL)
-	{
-		estate->es_select_into = true;
-		estate->es_into_oids = interpretOidsOption(plannedstmt->intoClause->options);
-	}
-
-	/*
 	 * Initialize the executor's tuple table to empty.
 	 */
 	estate->es_tupleTable = NIL;
@@ -926,9 +892,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	planstate = ExecInitNode(plan, estate, eflags);
 
 	/*
-	 * Get the tuple descriptor describing the type of tuples to return. (this
-	 * is especially important if we are creating a relation with "SELECT
-	 * INTO")
+	 * Get the tuple descriptor describing the type of tuples to return.
 	 */
 	tupType = ExecGetResultType(planstate);
 
@@ -968,16 +932,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 
 	queryDesc->tupDesc = tupType;
 	queryDesc->planstate = planstate;
-
-	/*
-	 * If doing SELECT INTO, initialize the "into" relation.  We must wait
-	 * till now so we have the "clean" result tuple type to create the new
-	 * table from.
-	 *
-	 * If EXPLAIN, skip creating the "into" relation.
-	 */
-	if (estate->es_select_into && !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
-		OpenIntoRel(queryDesc);
 }
 
 /*
@@ -1230,7 +1184,7 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
 /*
  *		ExecContextForcesOids
  *
- * This is pretty grotty: when doing INSERT, UPDATE, or SELECT INTO,
+ * This is pretty grotty: when doing INSERT or UPDATE
  * we need to ensure that result tuples have space for an OID iff they are
  * going to be stored into a relation that has OIDs.  In other contexts
  * we are free to choose whether to leave space for OIDs in result tuples
@@ -1255,15 +1209,25 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
  * the ModifyTable node, so ModifyTable has to set es_result_relation_info
  * while initializing each subplan.
  *
- * SELECT INTO is even uglier, because we don't have the INTO relation's
- * descriptor available when this code runs; we have to look aside at a
- * flag set by InitPlan().
+ * SELECT INTO is even uglier, because we don't have the INTO
+ * relation's descriptor available - because its only defined after
+ * the query started - when this code runs; we have to look aside at
+ * flags which is passed to ExecutorStart via eflags.
  */
 bool
 ExecContextForcesOids(PlanState *planstate, bool *hasoids)
 {
 	ResultRelInfo *ri = planstate->state->es_result_relation_info;
 
+	if(planstate->state->es_force_oids == ESTATE_FORCE_OIDS_ON){
+		*hasoids = true;
+		return true;
+	}
+	else if(planstate->state->es_force_oids == ESTATE_FORCE_OIDS_OFF){
+		*hasoids = false;
+		return true;
+	}
+
 	if (ri != NULL)
 	{
 		Relation	rel = ri->ri_RelationDesc;
@@ -1275,12 +1239,6 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids)
 		}
 	}
 
-	if (planstate->state->es_select_into)
-	{
-		*hasoids = planstate->state->es_into_oids;
-		return true;
-	}
-
 	return false;
 }
 
@@ -2290,8 +2248,7 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
 	estate->es_rowMarks = parentestate->es_rowMarks;
 	estate->es_top_eflags = parentestate->es_top_eflags;
 	estate->es_instrument = parentestate->es_instrument;
-	estate->es_select_into = parentestate->es_select_into;
-	estate->es_into_oids = parentestate->es_into_oids;
+
 	/* es_auxmodifytables must NOT be copied */
 
 	/*
@@ -2423,351 +2380,3 @@ EvalPlanQualEnd(EPQState *epqstate)
 	epqstate->planstate = NULL;
 	epqstate->origslot = NULL;
 }
-
-
-/*
- * Support for SELECT INTO (a/k/a CREATE TABLE AS)
- *
- * We implement SELECT INTO by diverting SELECT's normal output with
- * a specialized DestReceiver type.
- */
-
-typedef struct
-{
-	DestReceiver pub;			/* publicly-known function pointers */
-	EState	   *estate;			/* EState we are working with */
-	DestReceiver *origdest;		/* QueryDesc's original receiver */
-	Relation	rel;			/* Relation to write to */
-	int			hi_options;		/* heap_insert performance options */
-	BulkInsertState bistate;	/* bulk insert state */
-} DR_intorel;
-
-/*
- * OpenIntoRel --- actually create the SELECT INTO target relation
- *
- * This also replaces QueryDesc->dest with the special DestReceiver for
- * SELECT INTO.  We assume that the correct result tuple type has already
- * been placed in queryDesc->tupDesc.
- */
-static void
-OpenIntoRel(QueryDesc *queryDesc)
-{
-	IntoClause *into = queryDesc->plannedstmt->intoClause;
-	EState	   *estate = queryDesc->estate;
-	TupleDesc	intoTupDesc = queryDesc->tupDesc;
-	Relation	intoRelationDesc;
-	char	   *intoName;
-	Oid			namespaceId;
-	Oid			tablespaceId;
-	Datum		reloptions;
-	Oid			intoRelationId;
-	DR_intorel *myState;
-	RangeTblEntry  *rte;
-	AttrNumber		attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
-
-	Assert(into);
-
-	/*
-	 * XXX This code needs to be kept in sync with DefineRelation(). Maybe we
-	 * should try to use that function instead.
-	 */
-
-	/*
-	 * Check consistency of arguments
-	 */
-	if (into->onCommit != ONCOMMIT_NOOP
-		&& into->rel->relpersistence != RELPERSISTENCE_TEMP)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-				 errmsg("ON COMMIT can only be used on temporary tables")));
-
-	{
-		AclResult aclresult;
-		int i;
-
-		for (i = 0; i < intoTupDesc->natts; i++)
-		{
-			Oid atttypid = intoTupDesc->attrs[i]->atttypid;
-
-			aclresult = pg_type_aclcheck(atttypid, GetUserId(), ACL_USAGE);
-			if (aclresult != ACLCHECK_OK)
-				aclcheck_error(aclresult, ACL_KIND_TYPE,
-							   format_type_be(atttypid));
-		}
-	}
-
-	/*
-	 * If a column name list was specified in CREATE TABLE AS, override the
-	 * column names derived from the query.  (Too few column names are OK, too
-	 * many are not.)  It would probably be all right to scribble directly on
-	 * the query's result tupdesc, but let's be safe and make a copy.
-	 */
-	if (into->colNames)
-	{
-		ListCell   *lc;
-
-		intoTupDesc = CreateTupleDescCopy(intoTupDesc);
-		attnum = 1;
-		foreach(lc, into->colNames)
-		{
-			char	   *colname = strVal(lfirst(lc));
-
-			if (attnum > intoTupDesc->natts)
-				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("CREATE TABLE AS specifies too many column names")));
-			namestrcpy(&(intoTupDesc->attrs[attnum - 1]->attname), colname);
-			attnum++;
-		}
-	}
-
-	/*
-	 * Find namespace to create in, check its permissions, lock it against
-	 * concurrent drop, and mark into->rel as RELPERSISTENCE_TEMP if the
-	 * selected namespace is temporary.
-	 */
-	intoName = into->rel->relname;
-	namespaceId = RangeVarGetAndCheckCreationNamespace(into->rel, NoLock,
-													   NULL);
-
-	/*
-	 * Security check: disallow creating temp tables from security-restricted
-	 * code.  This is needed because calling code might not expect untrusted
-	 * tables to appear in pg_temp at the front of its search path.
-	 */
-	if (into->rel->relpersistence == RELPERSISTENCE_TEMP
-		&& InSecurityRestrictedOperation())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("cannot create temporary table within security-restricted operation")));
-
-	/*
-	 * Select tablespace to use.  If not specified, use default tablespace
-	 * (which may in turn default to database's default).
-	 */
-	if (into->tableSpaceName)
-	{
-		tablespaceId = get_tablespace_oid(into->tableSpaceName, false);
-	}
-	else
-	{
-		tablespaceId = GetDefaultTablespace(into->rel->relpersistence);
-		/* note InvalidOid is OK in this case */
-	}
-
-	/* Check permissions except when using the database's default space */
-	if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
-	{
-		AclResult	aclresult;
-
-		aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
-										   ACL_CREATE);
-
-		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
-						   get_tablespace_name(tablespaceId));
-	}
-
-	/* Parse and validate any reloptions */
-	reloptions = transformRelOptions((Datum) 0,
-									 into->options,
-									 NULL,
-									 validnsps,
-									 true,
-									 false);
-	(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
-
-	/* Now we can actually create the new relation */
-	intoRelationId = heap_create_with_catalog(intoName,
-											  namespaceId,
-											  tablespaceId,
-											  InvalidOid,
-											  InvalidOid,
-											  InvalidOid,
-											  GetUserId(),
-											  intoTupDesc,
-											  NIL,
-											  RELKIND_RELATION,
-											  into->rel->relpersistence,
-											  false,
-											  false,
-											  true,
-											  0,
-											  into->onCommit,
-											  reloptions,
-											  true,
-											  allowSystemTableMods);
-	Assert(intoRelationId != InvalidOid);
-
-	/*
-	 * Advance command counter so that the newly-created relation's catalog
-	 * tuples will be visible to heap_open.
-	 */
-	CommandCounterIncrement();
-
-	/*
-	 * If necessary, create a TOAST table for the INTO relation. Note that
-	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
-	 * the TOAST table will be visible for insertion.
-	 */
-	reloptions = transformRelOptions((Datum) 0,
-									 into->options,
-									 "toast",
-									 validnsps,
-									 true,
-									 false);
-
-	(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
-
-	AlterTableCreateToastTable(intoRelationId, reloptions);
-
-	/*
-	 * And open the constructed table for writing.
-	 */
-	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
-
-	/*
-	 * Check INSERT permission on the constructed table.
-	 */
-	rte = makeNode(RangeTblEntry);
-	rte->rtekind = RTE_RELATION;
-	rte->relid = intoRelationId;
-	rte->relkind = RELKIND_RELATION;
-	rte->requiredPerms = ACL_INSERT;
-
-	for (attnum = 1; attnum <= intoTupDesc->natts; attnum++)
-		rte->modifiedCols = bms_add_member(rte->modifiedCols,
-				attnum - FirstLowInvalidHeapAttributeNumber);
-
-	ExecCheckRTPerms(list_make1(rte), true);
-
-	/*
-	 * Now replace the query's DestReceiver with one for SELECT INTO
-	 */
-	myState = (DR_intorel *) CreateDestReceiver(DestIntoRel);
-	Assert(myState->pub.mydest == DestIntoRel);
-	myState->estate = estate;
-	myState->origdest = queryDesc->dest;
-	myState->rel = intoRelationDesc;
-
-	queryDesc->dest = (DestReceiver *) myState;
-
-	/*
-	 * We can skip WAL-logging the insertions, unless PITR or streaming
-	 * replication is in use. We can skip the FSM in any case.
-	 */
-	myState->hi_options = HEAP_INSERT_SKIP_FSM |
-		(XLogIsNeeded() ? 0 : HEAP_INSERT_SKIP_WAL);
-	myState->bistate = GetBulkInsertState();
-
-	/* Not using WAL requires smgr_targblock be initially invalid */
-	Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
-}
-
-/*
- * CloseIntoRel --- clean up SELECT INTO at ExecutorEnd time
- */
-static void
-CloseIntoRel(QueryDesc *queryDesc)
-{
-	DR_intorel *myState = (DR_intorel *) queryDesc->dest;
-
-	/*
-	 * OpenIntoRel might never have gotten called, and we also want to guard
-	 * against double destruction.
-	 */
-	if (myState && myState->pub.mydest == DestIntoRel)
-	{
-		FreeBulkInsertState(myState->bistate);
-
-		/* If we skipped using WAL, must heap_sync before commit */
-		if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
-			heap_sync(myState->rel);
-
-		/* close rel, but keep lock until commit */
-		heap_close(myState->rel, NoLock);
-
-		/* restore the receiver belonging to executor's caller */
-		queryDesc->dest = myState->origdest;
-
-		/* might as well invoke my destructor */
-		intorel_destroy((DestReceiver *) myState);
-	}
-}
-
-/*
- * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
- */
-DestReceiver *
-CreateIntoRelDestReceiver(void)
-{
-	DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
-
-	self->pub.receiveSlot = intorel_receive;
-	self->pub.rStartup = intorel_startup;
-	self->pub.rShutdown = intorel_shutdown;
-	self->pub.rDestroy = intorel_destroy;
-	self->pub.mydest = DestIntoRel;
-
-	/* private fields will be set by OpenIntoRel */
-
-	return (DestReceiver *) self;
-}
-
-/*
- * intorel_startup --- executor startup
- */
-static void
-intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
-{
-	/* no-op */
-}
-
-/*
- * intorel_receive --- receive one tuple
- */
-static void
-intorel_receive(TupleTableSlot *slot, DestReceiver *self)
-{
-	DR_intorel *myState = (DR_intorel *) self;
-	HeapTuple	tuple;
-
-	/*
-	 * get the heap tuple out of the tuple table slot, making sure we have a
-	 * writable copy
-	 */
-	tuple = ExecMaterializeSlot(slot);
-
-	/*
-	 * force assignment of new OID (see comments in ExecInsert)
-	 */
-	if (myState->rel->rd_rel->relhasoids)
-		HeapTupleSetOid(tuple, InvalidOid);
-
-	heap_insert(myState->rel,
-				tuple,
-				myState->estate->es_output_cid,
-				myState->hi_options,
-				myState->bistate);
-
-	/* We know this is a newly created relation, so there are no indexes */
-}
-
-/*
- * intorel_shutdown --- executor end
- */
-static void
-intorel_shutdown(DestReceiver *self)
-{
-	/* no-op */
-}
-
-/*
- * intorel_destroy --- release DestReceiver object
- */
-static void
-intorel_destroy(DestReceiver *self)
-{
-	pfree(self);
-}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 6db42e7..40cd5ce 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -137,8 +137,6 @@ CreateExecutorState(void)
 
 	estate->es_top_eflags = 0;
 	estate->es_instrument = 0;
-	estate->es_select_into = false;
-	estate->es_into_oids = false;
 	estate->es_finished = false;
 
 	estate->es_exprcontexts = NIL;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 61f4622..ae8d374 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -535,7 +535,6 @@ init_execution_state(List *queryTree_list,
 
 			if (ps->commandType == CMD_SELECT &&
 				ps->utilityStmt == NULL &&
-				ps->intoClause == NULL &&
 				!ps->hasModifyingCTE)
 				fcache->lazyEval = lasttages->lazyEval = true;
 		}
@@ -1493,8 +1492,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
 	 */
 	if (parse &&
 		parse->commandType == CMD_SELECT &&
-		parse->utilityStmt == NULL &&
-		parse->intoClause == NULL)
+		parse->utilityStmt == NULL)
 	{
 		tlist_ptr = &parse->targetList;
 		tlist = parse->targetList;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 81f284c..ce6abcf 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1917,7 +1917,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 				if (_SPI_current->tuptable)
 					_SPI_current->processed = _SPI_current->tuptable->alloced -
 						_SPI_current->tuptable->free;
-				res = SPI_OK_UTILITY;
+				if(nodeTag(stmt) == T_CreateTableAsStmt &&
+				   ((CreateTableAsStmt*)stmt)->is_select_into)
+					res = SPI_OK_SELINTO;
+				else
+					res = SPI_OK_UTILITY;
 			}
 
 			/*
@@ -2042,9 +2046,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
 	{
 		case CMD_SELECT:
 			Assert(queryDesc->plannedstmt->utilityStmt == NULL);
-			if (queryDesc->plannedstmt->intoClause)		/* select into table? */
-				res = SPI_OK_SELINTO;
-			else if (queryDesc->dest->mydest != DestSPI)
+			if (queryDesc->dest->mydest != DestSPI)
 			{
 				/* Don't return SPI_OK_SELECT if we're discarding result */
 				res = SPI_OK_UTILITY;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7fec4db..e4f1815 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -85,7 +85,6 @@ _copyPlannedStmt(const PlannedStmt *from)
 	COPY_NODE_FIELD(rtable);
 	COPY_NODE_FIELD(resultRelations);
 	COPY_NODE_FIELD(utilityStmt);
-	COPY_NODE_FIELD(intoClause);
 	COPY_NODE_FIELD(subplans);
 	COPY_BITMAPSET_FIELD(rewindPlanIDs);
 	COPY_NODE_FIELD(rowMarks);
@@ -2419,7 +2418,6 @@ _copyQuery(const Query *from)
 	COPY_SCALAR_FIELD(canSetTag);
 	COPY_NODE_FIELD(utilityStmt);
 	COPY_SCALAR_FIELD(resultRelation);
-	COPY_NODE_FIELD(intoClause);
 	COPY_SCALAR_FIELD(hasAggs);
 	COPY_SCALAR_FIELD(hasWindowFuncs);
 	COPY_SCALAR_FIELD(hasSubLinks);
@@ -3207,6 +3205,18 @@ _copyExplainStmt(const ExplainStmt *from)
 	return newnode;
 }
 
+static CreateTableAsStmt *
+_copyCreateTableAsStmt(CreateTableAsStmt *from)
+{
+	CreateTableAsStmt *newnode = makeNode(CreateTableAsStmt);
+
+	COPY_NODE_FIELD(query);
+	COPY_NODE_FIELD(into);
+	COPY_SCALAR_FIELD(is_select_into);
+
+	return newnode;
+}
+
 static CreateSeqStmt *
 _copyCreateSeqStmt(const CreateSeqStmt *from)
 {
@@ -3615,7 +3625,6 @@ _copyExecuteStmt(const ExecuteStmt *from)
 	ExecuteStmt *newnode = makeNode(ExecuteStmt);
 
 	COPY_STRING_FIELD(name);
-	COPY_NODE_FIELD(into);
 	COPY_NODE_FIELD(params);
 
 	return newnode;
@@ -4250,6 +4259,9 @@ copyObject(const void *from)
 		case T_ExplainStmt:
 			retval = _copyExplainStmt(from);
 			break;
+		case T_CreateTableAsStmt:
+			retval = _copyCreateTableAsStmt(from);
+			break;
 		case T_CreateSeqStmt:
 			retval = _copyCreateSeqStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d2a79eb..4ac895e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -900,7 +900,6 @@ _equalQuery(const Query *a, const Query *b)
 	COMPARE_SCALAR_FIELD(canSetTag);
 	COMPARE_NODE_FIELD(utilityStmt);
 	COMPARE_SCALAR_FIELD(resultRelation);
-	COMPARE_NODE_FIELD(intoClause);
 	COMPARE_SCALAR_FIELD(hasAggs);
 	COMPARE_SCALAR_FIELD(hasWindowFuncs);
 	COMPARE_SCALAR_FIELD(hasSubLinks);
@@ -1564,6 +1563,17 @@ _equalExplainStmt(const ExplainStmt *a, const ExplainStmt *b)
 	return true;
 }
 
+
+static bool
+_equalCreateTableAsStmt(CreateTableAsStmt *a, CreateTableAsStmt *b)
+{
+	COMPARE_NODE_FIELD(query);
+	COMPARE_NODE_FIELD(into);
+	COMPARE_SCALAR_FIELD(is_select_into);
+
+	return true;
+}
+
 static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
@@ -1908,7 +1918,6 @@ static bool
 _equalExecuteStmt(const ExecuteStmt *a, const ExecuteStmt *b)
 {
 	COMPARE_STRING_FIELD(name);
-	COMPARE_NODE_FIELD(into);
 	COMPARE_NODE_FIELD(params);
 
 	return true;
@@ -2793,6 +2802,9 @@ equal(const void *a, const void *b)
 		case T_ExplainStmt:
 			retval = _equalExplainStmt(a, b);
 			break;
+		case T_CreateTableAsStmt:
+			retval = _equalCreateTableAsStmt(a, b);
+			break;
 		case T_CreateSeqStmt:
 			retval = _equalCreateSeqStmt(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 25a215e..9cb8592 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -248,7 +248,6 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
 	WRITE_NODE_FIELD(rtable);
 	WRITE_NODE_FIELD(resultRelations);
 	WRITE_NODE_FIELD(utilityStmt);
-	WRITE_NODE_FIELD(intoClause);
 	WRITE_NODE_FIELD(subplans);
 	WRITE_BITMAPSET_FIELD(rewindPlanIDs);
 	WRITE_NODE_FIELD(rowMarks);
@@ -2187,7 +2186,6 @@ _outQuery(StringInfo str, const Query *node)
 		appendStringInfo(str, " :utilityStmt <>");
 
 	WRITE_INT_FIELD(resultRelation);
-	WRITE_NODE_FIELD(intoClause);
 	WRITE_BOOL_FIELD(hasAggs);
 	WRITE_BOOL_FIELD(hasWindowFuncs);
 	WRITE_BOOL_FIELD(hasSubLinks);
@@ -2805,7 +2803,7 @@ _outNode(StringInfo str, const void *obj)
 			case T_RangeVar:
 				_outRangeVar(str, obj);
 				break;
-			case T_IntoClause:
+			case T_IntoClause:/*XXX: This could be removed but dim would need to add it right back*/
 				_outIntoClause(str, obj);
 				break;
 			case T_Var:
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index b9258ad..9b57956 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -198,7 +198,6 @@ _readQuery(void)
 	READ_BOOL_FIELD(canSetTag);
 	READ_NODE_FIELD(utilityStmt);
 	READ_INT_FIELD(resultRelation);
-	READ_NODE_FIELD(intoClause);
 	READ_BOOL_FIELD(hasAggs);
 	READ_BOOL_FIELD(hasWindowFuncs);
 	READ_BOOL_FIELD(hasSubLinks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8bbe977..6b0541b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -233,7 +233,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	result->rtable = glob->finalrtable;
 	result->resultRelations = glob->resultRelations;
 	result->utilityStmt = parse->utilityStmt;
-	result->intoClause = parse->intoClause;
 	result->subplans = glob->subplans;
 	result->rewindPlanIDs = glob->rewindPlanIDs;
 	result->rowMarks = glob->finalrowmarks;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 40a420a..8006787 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1399,7 +1399,6 @@ simplify_EXISTS_query(Query *query)
 	 * are complex.
 	 */
 	if (query->commandType != CMD_SELECT ||
-		query->intoClause ||
 		query->setOperations ||
 		query->hasAggs ||
 		query->hasWindowFuncs ||
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 59a5268..0304c9d 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1129,8 +1129,7 @@ is_simple_subquery(Query *subquery)
 	 */
 	if (!IsA(subquery, Query) ||
 		subquery->commandType != CMD_SELECT ||
-		subquery->utilityStmt != NULL ||
-		subquery->intoClause != NULL)
+		subquery->utilityStmt != NULL)
 		elog(ERROR, "subquery is bogus");
 
 	/*
@@ -1216,8 +1215,7 @@ is_simple_union_all(Query *subquery)
 	/* Let's just make sure it's a valid subselect ... */
 	if (!IsA(subquery, Query) ||
 		subquery->commandType != CMD_SELECT ||
-		subquery->utilityStmt != NULL ||
-		subquery->intoClause != NULL)
+		subquery->utilityStmt != NULL)
 		elog(ERROR, "subquery is bogus");
 
 	/* Is it a set-operation query at all? */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index cd3da46..7d40e3e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4168,7 +4168,6 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 	if (!IsA(querytree, Query) ||
 		querytree->commandType != CMD_SELECT ||
 		querytree->utilityStmt ||
-		querytree->intoClause ||
 		querytree->hasAggs ||
 		querytree->hasWindowFuncs ||
 		querytree->hasSubLinks ||
@@ -4682,8 +4681,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 	 */
 	if (!IsA(querytree, Query) ||
 		querytree->commandType != CMD_SELECT ||
-		querytree->utilityStmt ||
-		querytree->intoClause)
+		querytree->utilityStmt)
 		goto fail;
 
 	/*
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index b187b03..182d6f7 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -62,6 +62,8 @@ static Query *transformDeclareCursorStmt(ParseState *pstate,
 						   DeclareCursorStmt *stmt);
 static Query *transformExplainStmt(ParseState *pstate,
 					 ExplainStmt *stmt);
+static Query *transformCreateTableAsStmt(ParseState *pstate,
+					 CreateTableAsStmt *stmt);
 static void transformLockingClause(ParseState *pstate, Query *qry,
 					   LockingClause *lc, bool pushedDown);
 
@@ -202,6 +204,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
 										  (ExplainStmt *) parseTree);
 			break;
 
+		case T_CreateTableAsStmt:
+			result = transformCreateTableAsStmt(pstate,
+			                           (CreateTableAsStmt *) parseTree);
+			break;
+
 		default:
 
 			/*
@@ -257,6 +264,7 @@ analyze_requires_snapshot(Node *parseTree)
 			result = true;
 			break;
 
+		case T_CreateTableAsStmt:
 		case T_ExplainStmt:
 			/* yes, because we must analyze the contained statement */
 			result = true;
@@ -455,21 +463,29 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 		sub_pstate->p_relnamespace = sub_relnamespace;
 		sub_pstate->p_varnamespace = sub_varnamespace;
 
+		/* The grammar should have produced a SELECT, but it might have INTO */
+		if(IsA(stmt->selectStmt, SelectStmt))
+		{
+			SelectStmt* first = (SelectStmt*)stmt->selectStmt;
+			while (first && first->op != SETOP_NONE)
+				first = first->larg;
+
+			if (first->intoClause)
+				ereport(ERROR,
+				        (errcode(ERRCODE_SYNTAX_ERROR),
+				         errmsg("INSERT ... SELECT cannot specify INTO"),
+				         parser_errposition(pstate,
+				                            exprLocation((Node *)first->intoClause))));
+		}
+
 		selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
 
 		free_parsestate(sub_pstate);
 
-		/* The grammar should have produced a SELECT, but it might have INTO */
 		if (!IsA(selectQuery, Query) ||
 			selectQuery->commandType != CMD_SELECT ||
 			selectQuery->utilityStmt != NULL)
 			elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
-		if (selectQuery->intoClause)
-			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("INSERT ... SELECT cannot specify INTO"),
-					 parser_errposition(pstate,
-						   exprLocation((Node *) selectQuery->intoClause))));
 
 		/*
 		 * Make the source be a subquery in the INSERT's rangetable, and add
@@ -876,6 +892,18 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	Node	   *qual;
 	ListCell   *l;
 
+	/*
+	 * Validity-check whether we got called from somewhere where
+	 * ... INTO was not allowed
+	 */
+	if (stmt->intoClause)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"),
+				 parser_errposition(pstate,
+				                    exprLocation((Node *) stmt->intoClause))));
+
+
 	qry->commandType = CMD_SELECT;
 
 	/* process the WITH clause independently of all else */
@@ -963,8 +991,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 												   pstate->p_windowdefs,
 												   &qry->targetList);
 
-	/* SELECT INTO/CREATE TABLE AS spec is just passed through */
-	qry->intoClause = stmt->intoClause;
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
@@ -1185,9 +1211,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			 errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
 
-	/* CREATE TABLE AS spec is just passed through */
-	qry->intoClause = stmt->intoClause;
-
 	/*
 	 * There mustn't have been any table references in the expressions, else
 	 * strange things would happen, like Cartesian products of those tables
@@ -1253,7 +1276,6 @@ static Query *
 transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
-	SelectStmt *leftmostSelect;
 	int			leftmostRTI;
 	Query	   *leftmostQuery;
 	SetOperationStmt *sostmt;
@@ -1286,20 +1308,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	}
 
 	/*
-	 * Find leftmost leaf SelectStmt; extract the one-time-only items from it
-	 * and from the top-level node.
-	 */
-	leftmostSelect = stmt->larg;
-	while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
-		leftmostSelect = leftmostSelect->larg;
-	Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
-		   leftmostSelect->larg == NULL);
-	qry->intoClause = leftmostSelect->intoClause;
-
-	/* clear this to prevent complaints in transformSetOperationTree() */
-	leftmostSelect->intoClause = NULL;
-
-	/*
 	 * These are not one-time, exactly, but we want to process them here and
 	 * not let transformSetOperationTree() see them --- else it'll just
 	 * recurse right back here!
@@ -1330,7 +1338,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->setOperations = (Node *) sostmt;
 
 	/*
-	 * Re-find leftmost SELECT (now it's a sub-query in rangetable)
+	 * Find leftmost SELECT (it's a sub-query in rangetable)
 	 */
 	node = sostmt->larg;
 	while (node && IsA(node, SetOperationStmt))
@@ -2099,6 +2107,25 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
 				 errmsg("cannot specify both SCROLL and NO SCROLL")));
 
+	/*
+	 * We must explicitly disallow DECLARE CURSOR ... SELECT INTO We
+	 * have to do this before transformStmt() as that will holler if
+	 * it ever finds a intoClause
+	 */
+	if(IsA(stmt->query, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)stmt->query;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+			         errmsg("DECLARE CURSOR cannot specify INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	result = transformStmt(pstate, stmt->query);
 
 	/* Grammar should not have allowed anything but SELECT */
@@ -2107,14 +2134,6 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 		result->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR");
 
-	/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
-	if (result->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-				 errmsg("DECLARE CURSOR cannot specify INTO"),
-				 parser_errposition(pstate,
-								exprLocation((Node *) result->intoClause))));
-
 	/*
 	 * We also disallow data-modifying WITH in a cursor.  (This could be
 	 * allowed, but the semantics of when the updates occur might be
@@ -2183,6 +2202,28 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
 
 
 /*
+ * transformCreateTableAsStmt -
+ *	transform an CREATE TABLE AS/SELECT ... INTO Statement
+ *
+ */
+static Query *
+transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
+{
+	Query	   *result;
+
+	/* transform contained query */
+	stmt->query = (Node *) transformStmt(pstate, stmt->query);
+
+	/* represent the command as a utility Query */
+	result = makeNode(Query);
+	result->commandType = CMD_UTILITY;
+	result->utilityStmt = (Node *) stmt;
+
+	return result;
+}
+
+
+/*
  * Check for features that are not supported together with FOR UPDATE/SHARE.
  *
  * exported so planner can check again after rewriting, query pullup, etc
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9aea2cd..962074c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3052,23 +3052,28 @@ ExistingIndex:   USING INDEX index_name				{ $$ = $3; }
 CreateAsStmt:
 		CREATE OptTemp TABLE create_as_target AS SelectStmt opt_with_data
 				{
+                    SelectStmt *n;
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+                    ctas->into = $4;
+                    ctas->query = $6;
+                    ctas->is_select_into = false;
+                    $4->skipData = !($7);
+					$4->rel->relpersistence = $2;
+					$4->skipData = !($7);
+                    $$ = (Node*)ctas;
+
 					/*
 					 * When the SelectStmt is a set-operation tree, we must
 					 * stuff the INTO information into the leftmost component
 					 * Select, because that's where analyze.c will expect
 					 * to find it.
 					 */
-					SelectStmt *n = findLeftmostSelect((SelectStmt *) $6);
+					n = findLeftmostSelect((SelectStmt *) $6);
 					if (n->intoClause != NULL)
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("CREATE TABLE AS cannot specify INTO"),
 								 parser_errposition(exprLocation((Node *) n->intoClause))));
-					n->intoClause = $4;
-					/* cram additional flags into the IntoClause */
-					$4->rel->relpersistence = $2;
-					$4->skipData = !($7);
-					$$ = $6;
 				}
 		;
 
@@ -8275,20 +8280,25 @@ ExecuteStmt: EXECUTE name execute_param_clause
 					ExecuteStmt *n = makeNode(ExecuteStmt);
 					n->name = $2;
 					n->params = $3;
-					n->into = NULL;
 					$$ = (Node *) n;
 				}
 			| CREATE OptTemp TABLE create_as_target AS
 				EXECUTE name execute_param_clause opt_with_data
 				{
 					ExecuteStmt *n = makeNode(ExecuteStmt);
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+
 					n->name = $7;
 					n->params = $8;
-					n->into = $4;
+
 					/* cram additional flags into the IntoClause */
 					$4->rel->relpersistence = $2;
 					$4->skipData = !($9);
-					$$ = (Node *) n;
+
+                    ctas->into = $4;
+                    ctas->query = (Node*)n;
+                    ctas->is_select_into = false;
+					$$ = (Node *) ctas;
 				}
 		;
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 7f4da92..52f449f 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -481,6 +481,21 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	if (r->alias == NULL)
 		elog(ERROR, "subquery in FROM must have an alias");
 
+
+	if(IsA(r->subquery, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)r->subquery;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery in FROM cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *)first->intoClause))));
+	}
+
 	/*
 	 * Analyze and transform the subquery.
 	 */
@@ -495,12 +510,6 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 		query->commandType != CMD_SELECT ||
 		query->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in subquery in FROM");
-	if (query->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery in FROM cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) query->intoClause))));
 
 	/*
 	 * The subquery cannot make use of any variables from FROM items created
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index c0c6240..4ea8ee6 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -241,6 +241,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
 	/* Analysis not done already */
 	Assert(!IsA(cte->ctequery, Query));
 
+	if(IsA(cte->ctequery, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)cte->ctequery;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery in WITH cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	query = parse_sub_analyze(cte->ctequery, pstate, cte, false);
 	cte->ctequery = (Node *) query;
 
@@ -253,13 +267,6 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
 	if (query->utilityStmt != NULL)
 		elog(ERROR, "unexpected utility statement in WITH");
 
-	if (query->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery in WITH cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) query->intoClause))));
-
 	/*
 	 * We disallow data-modifying WITH except at the top level of a query,
 	 * because it's not clear when such a modification should be executed.
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d22d8a1..d4d9121 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1398,6 +1398,21 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		return result;
 
 	pstate->p_hasSubLinks = true;
+
+	if(IsA(sublink->subselect, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)sublink->subselect;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
 
 	/*
@@ -1408,12 +1423,6 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		qtree->commandType != CMD_SELECT ||
 		qtree->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in SubLink");
-	if (qtree->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) qtree->intoClause))));
 
 	sublink->subselect = (Node *) qtree;
 
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index f9ec2b2..854925c 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -37,6 +37,7 @@ raw_parser(const char *str)
 	core_yyscan_t yyscanner;
 	base_yy_extra_type yyextra;
 	int			yyresult;
+	ListCell *c;
 
 	/* initialize the flex scanner */
 	yyscanner = scanner_init(str, &yyextra.core_yy_extra,
@@ -57,6 +58,43 @@ raw_parser(const char *str)
 	if (yyresult)				/* error */
 		return NIL;
 
+	/*
+	 * Some things are rather hard to properly diagnose in grammar
+	 * without complicating/duplicating it too much. So we do some
+	 * postprocessing here.
+	 */
+	foreach(c, yyextra.parsetree){
+		switch(nodeTag(lfirst(c))){
+			/*
+			 * The grammar currently doesn't disambiguate between
+			 * SELECT and SELECT ... INTO. Do that now.
+			 */
+			case T_SelectStmt:
+			{
+				SelectStmt* s = (SelectStmt*)lfirst(c);
+				SelectStmt* first = s;
+				while (first && first->op != SETOP_NONE)
+					first = first->larg;
+				Assert(first && IsA(first, SelectStmt) && first->larg == NULL);
+				if(first->intoClause){
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+					ctas->into = first->intoClause;
+					ctas->query = (Node*)s;
+					ctas->is_select_into = true;
+					/*
+					 * this way everyone can complain if this is set
+					 * without further checks because it shall never
+					 * be set but here.
+					 */
+					first->intoClause = NULL;
+					lfirst(c) = ctas;
+					break;
+				}
+			}
+			default:
+				break;
+		}
+	}
 	return yyextra.parsetree;
 }
 
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 8c87ac5..24199e8 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -323,8 +323,7 @@ DefineQueryRewrite(char *rulename,
 		query = (Query *) linitial(action);
 		if (!is_instead ||
 			query->commandType != CMD_SELECT ||
-			query->utilityStmt != NULL ||
-			query->intoClause != NULL)
+			query->utilityStmt != NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("rules on SELECT must have action INSTEAD SELECT")));
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 42a0fb0..7c50284 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -260,8 +260,7 @@ ChoosePortalStrategy(List *stmts)
 			if (query->canSetTag)
 			{
 				if (query->commandType == CMD_SELECT &&
-					query->utilityStmt == NULL &&
-					query->intoClause == NULL)
+					query->utilityStmt == NULL)
 				{
 					if (query->hasModifyingCTE)
 						return PORTAL_ONE_MOD_WITH;
@@ -285,8 +284,7 @@ ChoosePortalStrategy(List *stmts)
 			if (pstmt->canSetTag)
 			{
 				if (pstmt->commandType == CMD_SELECT &&
-					pstmt->utilityStmt == NULL &&
-					pstmt->intoClause == NULL)
+					pstmt->utilityStmt == NULL)
 				{
 					if (pstmt->hasModifyingCTE)
 						return PORTAL_ONE_MOD_WITH;
@@ -395,8 +393,7 @@ FetchStatementTargetList(Node *stmt)
 		else
 		{
 			if (query->commandType == CMD_SELECT &&
-				query->utilityStmt == NULL &&
-				query->intoClause == NULL)
+				query->utilityStmt == NULL)
 				return query->targetList;
 			if (query->returningList)
 				return query->returningList;
@@ -408,8 +405,7 @@ FetchStatementTargetList(Node *stmt)
 		PlannedStmt *pstmt = (PlannedStmt *) stmt;
 
 		if (pstmt->commandType == CMD_SELECT &&
-			pstmt->utilityStmt == NULL &&
-			pstmt->intoClause == NULL)
+			pstmt->utilityStmt == NULL)
 			return pstmt->planTree->targetlist;
 		if (pstmt->hasReturning)
 			return pstmt->planTree->targetlist;
@@ -430,7 +426,6 @@ FetchStatementTargetList(Node *stmt)
 		ExecuteStmt *estmt = (ExecuteStmt *) stmt;
 		PreparedStatement *entry;
 
-		Assert(!estmt->into);
 		entry = FetchPreparedStatement(estmt->name, true);
 		return FetchPreparedStatementTargetList(entry);
 	}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 5b81c0b..35ccdb3 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -127,9 +127,7 @@ CommandIsReadOnly(Node *parsetree)
 		switch (stmt->commandType)
 		{
 			case CMD_SELECT:
-				if (stmt->intoClause != NULL)
-					return false;		/* SELECT INTO */
-				else if (stmt->rowMarks != NIL)
+				if (stmt->rowMarks != NIL)
 					return false;		/* SELECT FOR UPDATE/SHARE */
 				else if (stmt->hasModifyingCTE)
 					return false;		/* data-modifying CTE */
@@ -198,6 +196,7 @@ check_xact_readonly(Node *parsetree)
 		case T_CreateSchemaStmt:
 		case T_CreateSeqStmt:
 		case T_CreateStmt:
+		case T_CreateTableAsStmt:
 		case T_CreateTableSpaceStmt:
 		case T_CreateTrigStmt:
 		case T_CompositeTypeStmt:
@@ -1036,6 +1035,10 @@ standard_ProcessUtility(Node *parsetree,
 			ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
 			break;
 
+		case T_CreateTableAsStmt:
+			CreateTableAs((CreateTableAsStmt *) parsetree, queryString, params, dest);
+			break;
+
 		case T_VariableSetStmt:
 			ExecSetVariableStmt((VariableSetStmt *) parsetree);
 			break;
@@ -1230,8 +1233,6 @@ UtilityReturnsTuples(Node *parsetree)
 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
 				PreparedStatement *entry;
 
-				if (stmt->into)
-					return false;
 				entry = FetchPreparedStatement(stmt->name, false);
 				if (!entry)
 					return false;		/* not our business to raise error */
@@ -1282,8 +1283,6 @@ UtilityTupleDescriptor(Node *parsetree)
 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
 				PreparedStatement *entry;
 
-				if (stmt->into)
-					return NULL;
 				entry = FetchPreparedStatement(stmt->name, false);
 				if (!entry)
 					return NULL;	/* not our business to raise error */
@@ -1318,8 +1317,7 @@ QueryReturnsTuples(Query *parsetree)
 	{
 		case CMD_SELECT:
 			/* returns tuples ... unless it's DECLARE CURSOR or SELECT INTO */
-			if (parsetree->utilityStmt == NULL &&
-				parsetree->intoClause == NULL)
+			if (parsetree->utilityStmt == NULL)
 				return true;
 			break;
 		case CMD_INSERT:
@@ -1907,6 +1905,13 @@ CreateCommandTag(Node *parsetree)
 			tag = "EXPLAIN";
 			break;
 
+		case T_CreateTableAsStmt:
+			if (((CreateTableAsStmt*)parsetree)->is_select_into)
+				tag = "SELECT INTO";
+			else
+				tag = "CREATE TABLE AS";
+			break;
+
 		case T_VariableSetStmt:
 			switch (((VariableSetStmt *) parsetree)->kind)
 			{
@@ -2060,8 +2065,6 @@ CreateCommandTag(Node *parsetree)
 							Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
 							tag = "DECLARE CURSOR";
 						}
-						else if (stmt->intoClause != NULL)
-							tag = "SELECT INTO";
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
@@ -2110,8 +2113,6 @@ CreateCommandTag(Node *parsetree)
 							Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
 							tag = "DECLARE CURSOR";
 						}
-						else if (stmt->intoClause != NULL)
-							tag = "SELECT INTO";
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
@@ -2178,10 +2179,7 @@ GetCommandLogLevel(Node *parsetree)
 			break;
 
 		case T_SelectStmt:
-			if (((SelectStmt *) parsetree)->intoClause)
-				lev = LOGSTMT_DDL;		/* CREATE AS, SELECT INTO */
-			else
-				lev = LOGSTMT_ALL;
+			lev = LOGSTMT_ALL;
 			break;
 
 			/* utility statements --- same whether raw or cooked */
@@ -2429,6 +2427,10 @@ GetCommandLogLevel(Node *parsetree)
 			}
 			break;
 
+		case T_CreateTableAsStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_VariableSetStmt:
 			lev = LOGSTMT_ALL;
 			break;
@@ -2529,10 +2531,7 @@ GetCommandLogLevel(Node *parsetree)
 				switch (stmt->commandType)
 				{
 					case CMD_SELECT:
-						if (stmt->intoClause != NULL)
-							lev = LOGSTMT_DDL;	/* CREATE AS, SELECT INTO */
-						else
-							lev = LOGSTMT_ALL;	/* SELECT or DECLARE CURSOR */
+						lev = LOGSTMT_ALL;
 						break;
 
 					case CMD_UPDATE:
@@ -2558,10 +2557,7 @@ GetCommandLogLevel(Node *parsetree)
 				switch (stmt->commandType)
 				{
 					case CMD_SELECT:
-						if (stmt->intoClause != NULL)
-							lev = LOGSTMT_DDL;	/* CREATE AS, SELECT INTO */
-						else
-							lev = LOGSTMT_ALL;	/* SELECT or DECLARE CURSOR */
+						lev = LOGSTMT_ALL;
 						break;
 
 					case CMD_UPDATE:
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6a3778d..abbc152 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -311,7 +311,7 @@ do { \
 
 #define HeapTupleHeaderSetOid(tup, oid) \
 do { \
-	Assert((tup)->t_infomask & HEAP_HASOID); \
+	if(!((tup)->t_infomask & HEAP_HASOID)) elog(ERROR, "HEAP_HASOID"); \
 	*((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \
 } while (0)
 
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 472a357..6b545dc 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -54,4 +54,7 @@ extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
 
 void		DropAllPreparedStatements(void);
 
+extern ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
+                                    const char *queryString, EState *estate);
+
 #endif   /* PREPARE_H */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 03f397d..212a51d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -15,6 +15,7 @@
 #define TABLECMDS_H
 
 #include "access/htup.h"
+#include "executor/executor.h"
 #include "nodes/parsenodes.h"
 #include "storage/lock.h"
 #include "utils/relcache.h"
@@ -22,6 +23,9 @@
 
 extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
 
+extern void CreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
+			 ParamListInfo params, DestReceiver *dest);
+
 extern void RemoveRelations(DropStmt *drop);
 
 extern Oid	AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 7f27669..47bd1ee 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -49,12 +49,17 @@
  * AfterTriggerBeginQuery/AfterTriggerEndQuery.  This does not necessarily
  * mean that the plan can't queue any AFTER triggers; just that the caller
  * is responsible for there being a trigger context for them to be queued in.
+ *
+ * FORCE_OIDS tells the executor to build a tupleDesc that includes
+ * the oid column. This is used from tablecmd.c's CreateTableAs.
  */
 #define EXEC_FLAG_EXPLAIN_ONLY	0x0001	/* EXPLAIN, no ANALYZE */
 #define EXEC_FLAG_REWIND		0x0002	/* need efficient rescan */
 #define EXEC_FLAG_BACKWARD		0x0004	/* need backward scan */
 #define EXEC_FLAG_MARK			0x0008	/* need mark/restore */
 #define EXEC_FLAG_SKIP_TRIGGERS 0x0010	/* skip AfterTrigger calls */
+#define EXEC_FLAG_FORCE_OIDS_ON 0x0020	/* force oids in returned tuples */
+#define EXEC_FLAG_FORCE_OIDS_OFF 0x0040	/* force oids in returned tuples */
 
 
 /*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5207102..d268fb9 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -322,6 +322,12 @@ typedef struct ResultRelInfo
 	ProjectionInfo *ri_projectReturning;
 } ResultRelInfo;
 
+typedef enum {
+	ESTATE_FORCE_OIDS_DEFAULT,
+	ESTATE_FORCE_OIDS_ON,
+	ESTATE_FORCE_OIDS_OFF
+} EStateForceOids;
+
 /* ----------------
  *	  EState information
  *
@@ -371,8 +377,7 @@ typedef struct EState
 
 	int			es_top_eflags;	/* eflags passed to ExecutorStart */
 	int			es_instrument;	/* OR of InstrumentOption flags */
-	bool		es_select_into; /* true if doing SELECT INTO */
-	bool		es_into_oids;	/* true to generate OIDs in SELECT INTO */
+	EStateForceOids        es_force_oids;  /* used by CreateTableAs */
 	bool		es_finished;	/* true when ExecutorFinish is done */
 
 	List	   *es_exprcontexts;	/* List of ExprContexts within EState */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 0e7d184..f4d9ce2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -304,6 +304,7 @@ typedef enum NodeTag
 	T_DropdbStmt,
 	T_VacuumStmt,
 	T_ExplainStmt,
+	T_CreateTableAsStmt,
 	T_CreateSeqStmt,
 	T_AlterSeqStmt,
 	T_VariableSetStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ab55639..5c2797c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -111,8 +111,6 @@ typedef struct Query
 	int			resultRelation; /* rtable index of target relation for
 								 * INSERT/UPDATE/DELETE; 0 for SELECT */
 
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
-
 	bool		hasAggs;		/* has aggregates in tlist or havingQual */
 	bool		hasWindowFuncs; /* has window functions in tlist */
 	bool		hasSubLinks;	/* has subquery SubLink */
@@ -1009,7 +1007,7 @@ typedef struct SelectStmt
 	 */
 	List	   *distinctClause; /* NULL, list of DISTINCT ON exprs, or
 								 * lcons(NIL,NIL) for all (SELECT DISTINCT) */
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
+	IntoClause *intoClause;		/* target for SELECT INTO */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	List	   *fromClause;		/* the FROM clause */
 	Node	   *whereClause;	/* WHERE qualification */
@@ -2384,7 +2382,7 @@ typedef struct VacuumStmt
  *		Explain Statement
  *
  * The "query" field is either a raw parse tree (SelectStmt, InsertStmt, etc)
- * or a Query node if parse analysis has been done.  Note that rewriting and
+ * or a Query node if parse analysis has been done. Note that rewriting and
  * planning of the query are always postponed until execution of EXPLAIN.
  * ----------------------
  */
@@ -2396,6 +2394,19 @@ typedef struct ExplainStmt
 } ExplainStmt;
 
 /* ----------------------
+ * analyzing, rewriting and planning are handled as in explain
+ * statements (see comment above) only that query can only be a
+ * SelectStmt and not some other type.
+ */
+typedef struct CreateTableAsStmt
+{
+	NodeTag		type;
+	IntoClause  *into;
+	Node        *query;			/* the query (see comments above) */
+	bool        is_select_into; /* plpgsql wants to disambiguate */
+} CreateTableAsStmt;
+
+/* ----------------------
  * Checkpoint Statement
  * ----------------------
  */
@@ -2509,7 +2520,6 @@ typedef struct ExecuteStmt
 {
 	NodeTag		type;
 	char	   *name;			/* The name of the plan to execute */
-	IntoClause *into;			/* Optional table to store results in */
 	List	   *params;			/* Values to assign to parameters */
 } ExecuteStmt;
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7d90b91..b67d988 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -54,8 +54,6 @@ typedef struct PlannedStmt
 
 	Node	   *utilityStmt;	/* non-null if this is DECLARE CURSOR */
 
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
-
 	List	   *subplans;		/* Plan trees for SubPlan expressions */
 
 	Bitmapset  *rewindPlanIDs;	/* indices of subplans that require REWIND */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 0b126a3..231210a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3286,29 +3286,18 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
 			break;
 
 		case SPI_OK_SELINTO:
-
 			/*
 			 * We want to disallow SELECT INTO for now, because its behavior
 			 * is not consistent with SELECT INTO in a normal plpgsql context.
 			 * (We need to reimplement EXECUTE to parse the string as a
 			 * plpgsql command, not just feed it to SPI_execute.) However,
-			 * CREATE AS should be allowed ... and since it produces the same
-			 * parsetree as SELECT INTO, there's no way to tell the difference
-			 * except to look at the source text.  Wotta kluge!
+			 * CREATE AS should be allowed ...
 			 */
-			{
-				char	   *ptr;
-
-				for (ptr = querystr; *ptr; ptr++)
-					if (!scanner_isspace(*ptr))
-						break;
-				if (*ptr == 'S' || *ptr == 's')
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("EXECUTE of SELECT ... INTO is not implemented"),
-							 errhint("You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.")));
-				break;
-			}
+			ereport(ERROR,
+			        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			         errmsg("EXECUTE of SELECT ... INTO is not implemented"),
+			         errhint("You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.")));
+			break;
 
 			/* Some SPI errors deserve specific error messages */
 		case SPI_ERROR_COPY:
@@ -5759,7 +5748,7 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
 	 */
 	if (!IsA(query, Query))
 		return;
-	if (query->commandType != CMD_SELECT || query->intoClause)
+	if (query->commandType != CMD_SELECT)
 		return;
 	if (query->rtable != NIL)
 		return;
@@ -5833,7 +5822,7 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
 	 */
 	if (!IsA(stmt, PlannedStmt))
 		return;
-	if (stmt->commandType != CMD_SELECT || stmt->intoClause)
+	if (stmt->commandType != CMD_SELECT)
 		return;
 	plan = stmt->planTree;
 	if (!IsA(plan, Result))
diff --git a/src/test/regress/expected/select_into.out b/src/test/regress/expected/select_into.out
index c8327f6..d124382 100644
--- a/src/test/regress/expected/select_into.out
+++ b/src/test/regress/expected/select_into.out
@@ -44,6 +44,25 @@ CREATE TABLE selinto_schema.tmp3 (a,b,c)
 	   AS SELECT oid,relname,relacl FROM pg_class
 	   WHERE relname like '%c%';	-- OK
 RESET SESSION AUTHORIZATION;
+--
+-- disallowed uses of SELECT ... INTO. All should fail
+--
+DECLARE foo CURSOR FOR SELECT 1 INTO b;
+ERROR:  DECLARE CURSOR cannot specify INTO
+LINE 1: DECLARE foo CURSOR FOR SELECT 1 INTO b;
+                                             ^
+COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blub';
+ERROR:  COPY (SELECT INTO) is not supported
+SELECT * FROM (SELECT 1 INTO f) bar;
+ERROR:  subquery in FROM cannot have SELECT INTO
+LINE 1: SELECT * FROM (SELECT 1 INTO f) bar;
+                                     ^
+CREATE VIEW foo AS SELECT 1 INTO b;
+ERROR:  views must not contain SELECT INTO
+INSERT INTO b SELECT 1 INTO f;
+ERROR:  INSERT ... SELECT cannot specify INTO
+LINE 1: INSERT INTO b SELECT 1 INTO f;
+                                    ^
 DROP SCHEMA selinto_schema CASCADE;
 NOTICE:  drop cascades to 3 other objects
 DETAIL:  drop cascades to table selinto_schema.tmp1
diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out
index e9d3908..6981692 100644
--- a/src/test/regress/expected/transactions.out
+++ b/src/test/regress/expected/transactions.out
@@ -139,7 +139,7 @@ SELECT * FROM writetest, temptest; -- ok
 (0 rows)
 
 CREATE TABLE test AS SELECT * FROM writetest; -- fail
-ERROR:  cannot execute SELECT INTO in a read-only transaction
+ERROR:  cannot execute CREATE TABLE AS in a read-only transaction
 START TRANSACTION READ WRITE;
 DROP TABLE writetest; -- ok
 COMMIT;
diff --git a/src/test/regress/sql/select_into.sql b/src/test/regress/sql/select_into.sql
index 09d210b..0c07f76 100644
--- a/src/test/regress/sql/select_into.sql
+++ b/src/test/regress/sql/select_into.sql
@@ -50,6 +50,21 @@ CREATE TABLE selinto_schema.tmp3 (a,b,c)
 	   WHERE relname like '%c%';	-- OK
 RESET SESSION AUTHORIZATION;
 
+
+--
+-- disallowed uses of SELECT ... INTO. All should fail
+--
+DECLARE foo CURSOR FOR SELECT 1 INTO b;
+
+COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blub';
+
+SELECT * FROM (SELECT 1 INTO f) bar;
+
+CREATE VIEW foo AS SELECT 1 INTO b;
+
+INSERT INTO b SELECT 1 INTO f;
+
+
 DROP SCHEMA selinto_schema CASCADE;
 DROP USER selinto_user;
 
-- 
1.7.9

ctas-on-command-triggers-01.patchtext/x-patch; charset=UTF-8; name=ctas-on-command-triggers-01.patchDownload
From 954990feb665b6a1ce35968d7acf77e0d1356502 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 4 Mar 2012 16:45:01 +0100
Subject: [PATCH] Transform CREATE TABLE AS/SELECT INTO into a utility
 statement

Conflicts:

	src/include/commands/tablecmds.h
---
 src/backend/commands/copy.c                |   20 +-
 src/backend/commands/prepare.c             |   69 +----
 src/backend/commands/tablecmds.c           |  335 +++++++++++++++++++++
 src/backend/commands/view.c                |   20 +-
 src/backend/executor/execMain.c            |  441 ++--------------------------
 src/backend/executor/execUtils.c           |    2 -
 src/backend/executor/functions.c           |    4 +-
 src/backend/executor/spi.c                 |   10 +-
 src/backend/nodes/copyfuncs.c              |   18 +-
 src/backend/nodes/equalfuncs.c             |   16 +-
 src/backend/nodes/outfuncs.c               |    4 +-
 src/backend/nodes/readfuncs.c              |    1 -
 src/backend/optimizer/plan/planner.c       |    1 -
 src/backend/optimizer/plan/subselect.c     |    1 -
 src/backend/optimizer/prep/prepjointree.c  |    6 +-
 src/backend/optimizer/util/clauses.c       |    4 +-
 src/backend/parser/analyze.c               |  113 +++++---
 src/backend/parser/gram.y                  |   28 ++-
 src/backend/parser/parse_clause.c          |   21 +-
 src/backend/parser/parse_cte.c             |   21 +-
 src/backend/parser/parse_expr.c            |   21 +-
 src/backend/parser/parser.c                |   38 +++
 src/backend/rewrite/rewriteDefine.c        |    3 +-
 src/backend/tcop/pquery.c                  |   13 +-
 src/backend/tcop/utility.c                 |   46 ++--
 src/include/access/htup.h                  |    2 +-
 src/include/commands/prepare.h             |    3 +
 src/include/commands/tablecmds.h           |    4 +
 src/include/executor/executor.h            |    5 +
 src/include/nodes/execnodes.h              |    9 +-
 src/include/nodes/nodes.h                  |    1 +
 src/include/nodes/parsenodes.h             |   20 +-
 src/include/nodes/plannodes.h              |    2 -
 src/pl/plpgsql/src/pl_exec.c               |   27 +--
 src/test/regress/expected/select_into.out  |   19 ++
 src/test/regress/expected/transactions.out |    2 +-
 src/test/regress/sql/select_into.sql       |   15 +
 37 files changed, 720 insertions(+), 645 deletions(-)

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 110480f..a3c5b4b 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1190,6 +1190,20 @@ BeginCopy(bool is_from,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("COPY (SELECT) WITH OIDS is not supported")));
 
+
+		/* Query mustn't use INTO, either */
+		if(IsA(raw_query, SelectStmt))
+		{
+			SelectStmt* first = (SelectStmt*)raw_query;
+			while (first && first->op != SETOP_NONE)
+				first = first->larg;
+
+			if (first->intoClause)
+				ereport(ERROR,
+				        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				         errmsg("COPY (SELECT INTO) is not supported")));
+		}
+
 		/*
 		 * Run parse analysis and rewrite.	Note this also acquires sufficient
 		 * locks on the source table(s).
@@ -1211,12 +1225,6 @@ BeginCopy(bool is_from,
 		Assert(query->commandType == CMD_SELECT);
 		Assert(query->utilityStmt == NULL);
 
-		/* Query mustn't use INTO, either */
-		if (query->intoClause)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("COPY (SELECT INTO) is not supported")));
-
 		/* plan the query */
 		plan = planner(query, 0, NULL);
 
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 4883abe..86e8cf9 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -43,8 +43,6 @@
 static HTAB *prepared_queries = NULL;
 
 static void InitQueryHashTable(void);
-static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
-			   const char *queryString, EState *estate);
 static Datum build_regtype_array(Oid *param_types, int num_params);
 
 /*
@@ -177,6 +175,9 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
  * EXECUTE, which we might need for error reporting while processing the
  * parameter expressions.  The query_string that we copy from the plan
  * source is that of the original PREPARE.
+ *
+ * If you add additional things here you should check whether
+ * CreateTableAs also needs them.
  */
 void
 ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
@@ -222,51 +223,9 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
 	query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
 									   entry->plansource->query_string);
 
-	/*
-	 * For CREATE TABLE / AS EXECUTE, we must make a copy of the stored query
-	 * so that we can modify its destination (yech, but this has always been
-	 * ugly).  For regular EXECUTE we can just use the cached query, since the
-	 * executor is read-only.
-	 */
-	if (stmt->into)
-	{
-		MemoryContext oldContext;
-		PlannedStmt *pstmt;
-
-		/* Replan if needed, and increment plan refcount transiently */
-		cplan = GetCachedPlan(entry->plansource, paramLI, true);
-
-		/* Copy plan into portal's context, and modify */
-		oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
-		plan_list = copyObject(cplan->stmt_list);
-
-		if (list_length(plan_list) != 1)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("prepared statement is not a SELECT")));
-		pstmt = (PlannedStmt *) linitial(plan_list);
-		if (!IsA(pstmt, PlannedStmt) ||
-			pstmt->commandType != CMD_SELECT ||
-			pstmt->utilityStmt != NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("prepared statement is not a SELECT")));
-		pstmt->intoClause = copyObject(stmt->into);
-
-		MemoryContextSwitchTo(oldContext);
-
-		/* We no longer need the cached plan refcount ... */
-		ReleaseCachedPlan(cplan, true);
-		/* ... and we don't want the portal to depend on it, either */
-		cplan = NULL;
-	}
-	else
-	{
-		/* Replan if needed, and increment plan refcount for portal */
-		cplan = GetCachedPlan(entry->plansource, paramLI, false);
-		plan_list = cplan->stmt_list;
-	}
+	/* Replan if needed, and increment plan refcount for portal */
+	cplan = GetCachedPlan(entry->plansource, paramLI, false);
+	plan_list = cplan->stmt_list;
 
 	PortalDefineQuery(portal,
 					  NULL,
@@ -302,7 +261,7 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
  * CreateQueryDesc(), which allows the executor to make use of the parameters
  * during query execution.
  */
-static ParamListInfo
+ParamListInfo
 EvaluateParams(PreparedStatement *pstmt, List *params,
 			   const char *queryString, EState *estate)
 {
@@ -666,20 +625,6 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
 
 		if (IsA(pstmt, PlannedStmt))
 		{
-			if (execstmt->into)
-			{
-				if (pstmt->commandType != CMD_SELECT ||
-					pstmt->utilityStmt != NULL)
-					ereport(ERROR,
-							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-							 errmsg("prepared statement is not a SELECT")));
-
-				/* Copy the stmt so we can modify it */
-				pstmt = copyObject(pstmt);
-
-				pstmt->intoClause = execstmt->into;
-			}
-
 			ExplainOnePlan(pstmt, es, query_string, paramLI);
 		}
 		else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1797e4d..cd59779 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -45,6 +45,7 @@
 #include "commands/cluster.h"
 #include "commands/comment.h"
 #include "commands/defrem.h"
+#include "commands/prepare.h"
 #include "commands/sequence.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
@@ -69,6 +70,7 @@
 #include "parser/parser.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
+#include "tcop/tcopprot.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/lock.h"
@@ -197,6 +199,18 @@ struct dropmsgstrings
 	const char *drophint_msg;
 };
 
+/*
+ * Support structure for CTAS
+ */
+typedef struct
+{
+	DestReceiver pub;			/* publicly-known function pointers */
+	EState	   *estate;			/* EState we are working with */
+	Relation	rel;			/* Relation to write to */
+	int			hi_options;		/* heap_insert performance options */
+	BulkInsertState bistate;	/* bulk insert state */
+} DR_intorel;
+
 static const struct dropmsgstrings dropmsgstringarray[] = {
 	{RELKIND_RELATION,
 		ERRCODE_UNDEFINED_TABLE,
@@ -673,6 +687,235 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	return relationId;
 }
 
+void
+CreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
+              ParamListInfo params, DestReceiver *dest){
+	IntoClause *into = stmt->into;
+	Query *query;
+	List *rewritten;
+	PlannedStmt *plan;
+	QueryDesc *queryDesc;
+	Relation intoRelationDesc;
+	CreateStmt *create;
+	int natt;
+	Oid intoRelationId;
+	ListCell *lc;
+	int eflags = 0;
+	EState *param_estate = NULL;
+	CachedPlan *cplan = NULL;
+
+	Assert(IsA(stmt->query, Query));
+	query = (Query*)stmt->query;
+
+	/*
+	 * Construct plans for the supported
+	 */
+	if(query->commandType == CMD_SELECT){
+		rewritten = QueryRewrite((Query *) copyObject(stmt->query));
+
+		if(!rewritten)
+			elog(ERROR, "CREATE TABLE AS/SELECT INTO query rewrote to nothing");
+
+		if(list_length(rewritten) != 1)
+			elog(ERROR, "CREATE TABLE AS/SELECT INTO query rewrote to more than one query");
+
+		plan = pg_plan_query(lfirst(list_head(rewritten)), 0, params);
+	}
+	else if(query->commandType == CMD_UTILITY &&
+	        IsA(query->utilityStmt, ExecuteStmt)){
+		/*
+		 * We cannot easily share more infrastructure with prepare.c's
+		 * ExecuteQuery because teaching it to return a started
+		 * executor would amount to about the same amount of
+		 * duplication as doing it here.
+		 *
+		 */
+		ExecuteStmt *stmt;
+		PreparedStatement *entry;
+		List *plan_list;
+		ParamListInfo paramLI = NULL;
+
+		Assert(IsA(query->utilityStmt, ExecuteStmt));
+		stmt = (ExecuteStmt*)(query->utilityStmt);
+
+		entry = FetchPreparedStatement(stmt->name, true);
+
+		/* Shouldn't find a non-fixed-result cached plan */
+		if (!entry->plansource->fixed_result)
+			elog(ERROR, "EXECUTE does not support variable-result cached plans");
+
+		/* Evaluate parameters, if any */
+		if (entry->plansource->num_params > 0)
+		{
+			param_estate = CreateExecutorState();
+			param_estate->es_param_list_info = params;
+			paramLI = EvaluateParams(entry, stmt->params,
+			                         queryString, param_estate);
+
+		}
+
+		cplan = GetCachedPlan(entry->plansource, paramLI, true);
+		plan_list = cplan->stmt_list;
+
+		if (list_length(plan_list) != 1)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("prepared statement is not a SELECT")));
+
+		plan = (PlannedStmt *) linitial(plan_list);
+		if (!IsA(plan, PlannedStmt) ||
+			plan->commandType != CMD_SELECT ||
+			plan->utilityStmt != NULL)
+			ereport(ERROR,
+			        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+			         errmsg("prepared statement is not a SELECT")));
+
+		queryString = pstrdup(entry->plansource->query_string);
+	}
+	else{
+		elog(ERROR, "unsupported command for CREATE TABLE AS");
+		abort();//silence compiler
+	}
+
+
+	/*
+	 * Use a snapshot with an updated command ID to ensure this query sees
+	 * results of any previously executed queries.
+	 */
+	PushCopiedSnapshot(GetActiveSnapshot());
+	UpdateActiveSnapshotCommandId();
+
+	queryDesc = CreateQueryDesc(plan, queryString,
+								GetActiveSnapshot(), InvalidSnapshot,
+								CreateDestReceiver(DestIntoRel), params,
+	                            false);
+
+	/*
+	 * We need to tell the executor whether it has to produce oids or
+	 * not because it doesn't have enough information to do so itself
+	 * as were not telling it that its inserting into a table (because
+	 * that would be too complicated for now)
+	 */
+	if(interpretOidsOption(into->options))
+		eflags |= EXEC_FLAG_FORCE_OIDS_ON;
+	else
+		eflags |= EXEC_FLAG_FORCE_OIDS_OFF;
+
+	/*
+	 * call ExecutorStart to prepare the plan for execution. Only
+	 * after this we have enough information to actually create a
+	 * target relation
+	 */
+	ExecutorStart(queryDesc, eflags);
+
+	/*
+	 * create the target relation
+	 */
+	create = makeNode(CreateStmt);
+	create->relation = into->rel;
+	create->options = into->options;
+	create->oncommit = into->onCommit;
+	create->tablespacename = into->tableSpaceName;
+
+	/*
+	 * If a column name list was specified in CREATE TABLE AS,
+	 * override the column names derived from the query.  (Too few
+	 * column names are OK, too many are not.)
+	 */
+	if (list_length(into->colNames) > queryDesc->tupDesc->natts)
+		ereport(ERROR,
+		        (errcode(ERRCODE_SYNTAX_ERROR),
+		         errmsg("CREATE TABLE AS specifies too many column names")));
+	lc = list_head(into->colNames);
+	for(natt = 0; natt < queryDesc->tupDesc->natts; natt++){
+		ColumnDef *col = makeNode(ColumnDef);
+		TypeName *type = makeNode(TypeName);
+
+		Form_pg_attribute attribute = queryDesc->tupDesc->attrs[natt];
+
+		if(!lc)
+			col->colname = NameStr(attribute->attname);
+		else{
+			col->colname = strVal(lfirst(lc));
+			lc = lnext(lc);
+		}
+
+		col->collOid = attribute->attcollation;
+
+		type->typeOid = attribute->atttypid;
+		type->typemod = attribute->atttypmod;
+		col->typeName= type;
+
+		/*
+		 * We check the column type here because collOid=InvalidOid
+		 * denotes an invalid oid for a collatable type in
+		 * queryDesc->tupDesc but GetColumnDefCollation - which will
+		 * be called by DefineRelation - defers the default collation
+		 * out of that...
+		 * XXX: A better solution would be welcome.
+		 */
+		CheckAttributeType(col->colname, col->typeName->typeOid, col->collOid,
+		                   NULL, false);
+		create->tableElts = lappend(create->tableElts, col);
+	}
+
+	/*
+	 * Actually create the target table
+	 */
+	intoRelationId = DefineRelation(create, RELKIND_RELATION, InvalidOid);
+	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
+
+
+	if(!into->skipData){
+		DR_intorel *myState = (DR_intorel *) queryDesc->dest;
+		/* setup the target of the DestIntoRel receiver */
+		myState->estate = queryDesc->estate;
+		myState->rel = intoRelationDesc;
+
+		/* run the plan */
+		ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+	}
+
+	/*
+	 * Check INSERT permission on the constructed table.
+	 *
+	 * XXX: It would arguable make sense to do this check only if
+	 * into->skipData is true.
+	 */
+	{
+		RangeTblEntry *rte = makeNode(RangeTblEntry);
+		rte->rtekind = RTE_RELATION;
+		rte->relid = intoRelationId;
+		rte->relkind = RELKIND_RELATION;
+		rte->requiredPerms = ACL_INSERT;
+
+		for (natt = 1; natt <= intoRelationDesc->rd_att->natts; natt++)
+			rte->modifiedCols = bms_add_member(rte->modifiedCols,
+			                                   natt - FirstLowInvalidHeapAttributeNumber);
+
+		ExecCheckRTPerms(list_make1(rte), true);
+	}
+
+	/* run cleanup too */
+	ExecutorFinish(queryDesc);
+	ExecutorEnd(queryDesc);
+
+	/* close rel, but keep lock until commit */
+	heap_close(intoRelationDesc, NoLock);
+
+	FreeQueryDesc(queryDesc);
+
+	if(cplan){
+		ReleaseCachedPlan(cplan, true);
+
+		if (param_estate)
+			FreeExecutorState(param_estate);
+	}
+
+	PopActiveSnapshot();
+}
+
 /*
  * Emit the right error or warning message for a "DROP" command issued on a
  * non-existent relation
@@ -10306,3 +10549,95 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
 
 	ReleaseSysCache(tuple);
 }
+
+/*
+ * Support for SELECT INTO (aka CREATE TABLE AS)
+ *
+ * We implement SELECT INTO by diverting SELECT's normal output with
+ * a specialized DestReceiver type.
+ */
+
+/*
+ * intorel_startup --- executor startup
+ */
+static void
+intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+	myState->bistate = GetBulkInsertState();
+	myState->hi_options = HEAP_INSERT_SKIP_FSM;
+	if (!XLogIsNeeded())
+		myState->hi_options |= HEAP_INSERT_SKIP_WAL;
+
+}
+
+/*
+ * intorel_receive --- receive one tuple
+ */
+static void
+intorel_receive(TupleTableSlot *slot, DestReceiver *self)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+	HeapTuple	tuple;
+
+	/*
+	 * get the heap tuple out of the tuple table slot, making sure we have a
+	 * writable copy
+	 */
+	tuple = ExecMaterializeSlot(slot);
+
+	/*
+	 * force assignment of new OID (see comments in ExecInsert)
+	 */
+	if (myState->rel->rd_rel->relhasoids)
+		HeapTupleSetOid(tuple, InvalidOid);
+
+	heap_insert(myState->rel,
+				tuple,
+				myState->estate->es_output_cid,
+				myState->hi_options,
+				myState->bistate);
+
+	/* We know this is a newly created relation, so there are no indexes */
+}
+
+/*
+ * intorel_shutdown --- executor end
+ */
+static void
+intorel_shutdown(DestReceiver *self)
+{
+	DR_intorel *myState = (DR_intorel *) self;
+
+	FreeBulkInsertState(myState->bistate);
+	if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
+		heap_sync(myState->rel);
+}
+
+/*
+ * intorel_destroy --- release DestReceiver object
+ */
+static void
+intorel_destroy(DestReceiver *self)
+{
+	pfree(self);
+}
+
+/*
+ * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
+ *
+ */
+DestReceiver *
+CreateIntoRelDestReceiver(void)
+{
+	DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
+
+	self->pub.receiveSlot = intorel_receive;
+	self->pub.rStartup = intorel_startup;
+	self->pub.rShutdown = intorel_shutdown;
+	self->pub.rDestroy = intorel_destroy;
+	self->pub.mydest = DestIntoRel;
+	/* private fields will be set on startup */
+
+	return (DestReceiver *) self;
+}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 09c6001..e12b17b 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -443,6 +443,22 @@ DefineView(ViewStmt *stmt, const char *queryString)
 	CommandContextData cmd;
 
 	/*
+	 * We check for SELECT INTO before parse analysis because that
+	 * would complain about the SELECT INTO without specific enough detail.
+	 */
+	if(IsA(stmt->query, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)stmt->query;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			         errmsg("views must not contain SELECT INTO")));
+	}
+
+	/*
 	 * Run parse analysis to convert the raw parse tree to a Query.  Note this
 	 * also acquires sufficient locks on the source table(s).
 	 *
@@ -464,10 +480,6 @@ DefineView(ViewStmt *stmt, const char *queryString)
 	 * DefineQueryRewrite(), but that function will complain about a bogus ON
 	 * SELECT rule, and we'd rather the message complain about a view.
 	 */
-	if (viewParse->intoClause != NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("views must not contain SELECT INTO")));
 	if (viewParse->hasModifyingCTE)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 36dcc8e..1979325 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -90,12 +90,6 @@ static char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
 										   int maxfieldlen);
 static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
 				  Plan *planTree);
-static void OpenIntoRel(QueryDesc *queryDesc);
-static void CloseIntoRel(QueryDesc *queryDesc);
-static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
-static void intorel_receive(TupleTableSlot *slot, DestReceiver *self);
-static void intorel_shutdown(DestReceiver *self);
-static void intorel_destroy(DestReceiver *self);
 
 /* end of local decls */
 
@@ -174,11 +168,10 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
 		case CMD_SELECT:
 
 			/*
-			 * SELECT INTO, SELECT FOR UPDATE/SHARE and modifying CTEs need to
+			 * SELECT FOR UPDATE/SHARE and modifying CTEs need to
 			 * mark tuples
 			 */
-			if (queryDesc->plannedstmt->intoClause != NULL ||
-				queryDesc->plannedstmt->rowMarks != NIL ||
+			if (queryDesc->plannedstmt->rowMarks != NIL ||
 				queryDesc->plannedstmt->hasModifyingCTE)
 				estate->es_output_cid = GetCurrentCommandId(true);
 
@@ -212,6 +205,13 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
 	estate->es_top_eflags = eflags;
 	estate->es_instrument = queryDesc->instrument_options;
 
+	if(eflags & EXEC_FLAG_FORCE_OIDS_ON)
+		estate->es_force_oids = ESTATE_FORCE_OIDS_ON;
+	else if(eflags & EXEC_FLAG_FORCE_OIDS_OFF)
+		estate->es_force_oids = ESTATE_FORCE_OIDS_OFF;
+	else
+		estate->es_force_oids = ESTATE_FORCE_OIDS_DEFAULT;
+
 	/*
 	 * Initialize the plan state tree
 	 */
@@ -310,13 +310,6 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 		(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
 
 	/*
-	 * if it's CREATE TABLE AS ... WITH NO DATA, skip plan execution
-	 */
-	if (estate->es_select_into &&
-		queryDesc->plannedstmt->intoClause->skipData)
-		direction = NoMovementScanDirection;
-
-	/*
 	 * run plan
 	 */
 	if (!ScanDirectionIsNoMovement(direction))
@@ -451,12 +444,6 @@ standard_ExecutorEnd(QueryDesc *queryDesc)
 
 	ExecEndPlan(queryDesc->planstate, estate);
 
-	/*
-	 * Close the SELECT INTO relation if any
-	 */
-	if (estate->es_select_into)
-		CloseIntoRel(queryDesc);
-
 	/* do away with our snapshots */
 	UnregisterSnapshot(estate->es_snapshot);
 	UnregisterSnapshot(estate->es_crosscheck_snapshot);
@@ -706,15 +693,6 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 {
 	ListCell   *l;
 
-	/*
-	 * CREATE TABLE AS or SELECT INTO?
-	 *
-	 * XXX should we allow this if the destination is temp?  Considering that
-	 * it would still require catalog changes, probably not.
-	 */
-	if (plannedstmt->intoClause != NULL)
-		PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
-
 	/* Fail if write permissions are requested on any non-temp table */
 	foreach(l, plannedstmt->rtable)
 	{
@@ -864,18 +842,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	}
 
 	/*
-	 * Detect whether we're doing SELECT INTO.  If so, set the es_into_oids
-	 * flag appropriately so that the plan tree will be initialized with the
-	 * correct tuple descriptors.  (Other SELECT INTO stuff comes later.)
-	 */
-	estate->es_select_into = false;
-	if (operation == CMD_SELECT && plannedstmt->intoClause != NULL)
-	{
-		estate->es_select_into = true;
-		estate->es_into_oids = interpretOidsOption(plannedstmt->intoClause->options);
-	}
-
-	/*
 	 * Initialize the executor's tuple table to empty.
 	 */
 	estate->es_tupleTable = NIL;
@@ -926,9 +892,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	planstate = ExecInitNode(plan, estate, eflags);
 
 	/*
-	 * Get the tuple descriptor describing the type of tuples to return. (this
-	 * is especially important if we are creating a relation with "SELECT
-	 * INTO")
+	 * Get the tuple descriptor describing the type of tuples to return.
 	 */
 	tupType = ExecGetResultType(planstate);
 
@@ -968,16 +932,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 
 	queryDesc->tupDesc = tupType;
 	queryDesc->planstate = planstate;
-
-	/*
-	 * If doing SELECT INTO, initialize the "into" relation.  We must wait
-	 * till now so we have the "clean" result tuple type to create the new
-	 * table from.
-	 *
-	 * If EXPLAIN, skip creating the "into" relation.
-	 */
-	if (estate->es_select_into && !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
-		OpenIntoRel(queryDesc);
 }
 
 /*
@@ -1230,7 +1184,7 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
 /*
  *		ExecContextForcesOids
  *
- * This is pretty grotty: when doing INSERT, UPDATE, or SELECT INTO,
+ * This is pretty grotty: when doing INSERT or UPDATE
  * we need to ensure that result tuples have space for an OID iff they are
  * going to be stored into a relation that has OIDs.  In other contexts
  * we are free to choose whether to leave space for OIDs in result tuples
@@ -1255,15 +1209,25 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
  * the ModifyTable node, so ModifyTable has to set es_result_relation_info
  * while initializing each subplan.
  *
- * SELECT INTO is even uglier, because we don't have the INTO relation's
- * descriptor available when this code runs; we have to look aside at a
- * flag set by InitPlan().
+ * SELECT INTO is even uglier, because we don't have the INTO
+ * relation's descriptor available - because its only defined after
+ * the query started - when this code runs; we have to look aside at
+ * flags which is passed to ExecutorStart via eflags.
  */
 bool
 ExecContextForcesOids(PlanState *planstate, bool *hasoids)
 {
 	ResultRelInfo *ri = planstate->state->es_result_relation_info;
 
+	if(planstate->state->es_force_oids == ESTATE_FORCE_OIDS_ON){
+		*hasoids = true;
+		return true;
+	}
+	else if(planstate->state->es_force_oids == ESTATE_FORCE_OIDS_OFF){
+		*hasoids = false;
+		return true;
+	}
+
 	if (ri != NULL)
 	{
 		Relation	rel = ri->ri_RelationDesc;
@@ -1275,12 +1239,6 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids)
 		}
 	}
 
-	if (planstate->state->es_select_into)
-	{
-		*hasoids = planstate->state->es_into_oids;
-		return true;
-	}
-
 	return false;
 }
 
@@ -2290,8 +2248,7 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
 	estate->es_rowMarks = parentestate->es_rowMarks;
 	estate->es_top_eflags = parentestate->es_top_eflags;
 	estate->es_instrument = parentestate->es_instrument;
-	estate->es_select_into = parentestate->es_select_into;
-	estate->es_into_oids = parentestate->es_into_oids;
+
 	/* es_auxmodifytables must NOT be copied */
 
 	/*
@@ -2423,351 +2380,3 @@ EvalPlanQualEnd(EPQState *epqstate)
 	epqstate->planstate = NULL;
 	epqstate->origslot = NULL;
 }
-
-
-/*
- * Support for SELECT INTO (a/k/a CREATE TABLE AS)
- *
- * We implement SELECT INTO by diverting SELECT's normal output with
- * a specialized DestReceiver type.
- */
-
-typedef struct
-{
-	DestReceiver pub;			/* publicly-known function pointers */
-	EState	   *estate;			/* EState we are working with */
-	DestReceiver *origdest;		/* QueryDesc's original receiver */
-	Relation	rel;			/* Relation to write to */
-	int			hi_options;		/* heap_insert performance options */
-	BulkInsertState bistate;	/* bulk insert state */
-} DR_intorel;
-
-/*
- * OpenIntoRel --- actually create the SELECT INTO target relation
- *
- * This also replaces QueryDesc->dest with the special DestReceiver for
- * SELECT INTO.  We assume that the correct result tuple type has already
- * been placed in queryDesc->tupDesc.
- */
-static void
-OpenIntoRel(QueryDesc *queryDesc)
-{
-	IntoClause *into = queryDesc->plannedstmt->intoClause;
-	EState	   *estate = queryDesc->estate;
-	TupleDesc	intoTupDesc = queryDesc->tupDesc;
-	Relation	intoRelationDesc;
-	char	   *intoName;
-	Oid			namespaceId;
-	Oid			tablespaceId;
-	Datum		reloptions;
-	Oid			intoRelationId;
-	DR_intorel *myState;
-	RangeTblEntry  *rte;
-	AttrNumber		attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
-
-	Assert(into);
-
-	/*
-	 * XXX This code needs to be kept in sync with DefineRelation(). Maybe we
-	 * should try to use that function instead.
-	 */
-
-	/*
-	 * Check consistency of arguments
-	 */
-	if (into->onCommit != ONCOMMIT_NOOP
-		&& into->rel->relpersistence != RELPERSISTENCE_TEMP)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-				 errmsg("ON COMMIT can only be used on temporary tables")));
-
-	{
-		AclResult aclresult;
-		int i;
-
-		for (i = 0; i < intoTupDesc->natts; i++)
-		{
-			Oid atttypid = intoTupDesc->attrs[i]->atttypid;
-
-			aclresult = pg_type_aclcheck(atttypid, GetUserId(), ACL_USAGE);
-			if (aclresult != ACLCHECK_OK)
-				aclcheck_error(aclresult, ACL_KIND_TYPE,
-							   format_type_be(atttypid));
-		}
-	}
-
-	/*
-	 * If a column name list was specified in CREATE TABLE AS, override the
-	 * column names derived from the query.  (Too few column names are OK, too
-	 * many are not.)  It would probably be all right to scribble directly on
-	 * the query's result tupdesc, but let's be safe and make a copy.
-	 */
-	if (into->colNames)
-	{
-		ListCell   *lc;
-
-		intoTupDesc = CreateTupleDescCopy(intoTupDesc);
-		attnum = 1;
-		foreach(lc, into->colNames)
-		{
-			char	   *colname = strVal(lfirst(lc));
-
-			if (attnum > intoTupDesc->natts)
-				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("CREATE TABLE AS specifies too many column names")));
-			namestrcpy(&(intoTupDesc->attrs[attnum - 1]->attname), colname);
-			attnum++;
-		}
-	}
-
-	/*
-	 * Find namespace to create in, check its permissions, lock it against
-	 * concurrent drop, and mark into->rel as RELPERSISTENCE_TEMP if the
-	 * selected namespace is temporary.
-	 */
-	intoName = into->rel->relname;
-	namespaceId = RangeVarGetAndCheckCreationNamespace(into->rel, NoLock,
-													   NULL);
-
-	/*
-	 * Security check: disallow creating temp tables from security-restricted
-	 * code.  This is needed because calling code might not expect untrusted
-	 * tables to appear in pg_temp at the front of its search path.
-	 */
-	if (into->rel->relpersistence == RELPERSISTENCE_TEMP
-		&& InSecurityRestrictedOperation())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("cannot create temporary table within security-restricted operation")));
-
-	/*
-	 * Select tablespace to use.  If not specified, use default tablespace
-	 * (which may in turn default to database's default).
-	 */
-	if (into->tableSpaceName)
-	{
-		tablespaceId = get_tablespace_oid(into->tableSpaceName, false);
-	}
-	else
-	{
-		tablespaceId = GetDefaultTablespace(into->rel->relpersistence);
-		/* note InvalidOid is OK in this case */
-	}
-
-	/* Check permissions except when using the database's default space */
-	if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
-	{
-		AclResult	aclresult;
-
-		aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
-										   ACL_CREATE);
-
-		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
-						   get_tablespace_name(tablespaceId));
-	}
-
-	/* Parse and validate any reloptions */
-	reloptions = transformRelOptions((Datum) 0,
-									 into->options,
-									 NULL,
-									 validnsps,
-									 true,
-									 false);
-	(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
-
-	/* Now we can actually create the new relation */
-	intoRelationId = heap_create_with_catalog(intoName,
-											  namespaceId,
-											  tablespaceId,
-											  InvalidOid,
-											  InvalidOid,
-											  InvalidOid,
-											  GetUserId(),
-											  intoTupDesc,
-											  NIL,
-											  RELKIND_RELATION,
-											  into->rel->relpersistence,
-											  false,
-											  false,
-											  true,
-											  0,
-											  into->onCommit,
-											  reloptions,
-											  true,
-											  allowSystemTableMods);
-	Assert(intoRelationId != InvalidOid);
-
-	/*
-	 * Advance command counter so that the newly-created relation's catalog
-	 * tuples will be visible to heap_open.
-	 */
-	CommandCounterIncrement();
-
-	/*
-	 * If necessary, create a TOAST table for the INTO relation. Note that
-	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
-	 * the TOAST table will be visible for insertion.
-	 */
-	reloptions = transformRelOptions((Datum) 0,
-									 into->options,
-									 "toast",
-									 validnsps,
-									 true,
-									 false);
-
-	(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
-
-	AlterTableCreateToastTable(intoRelationId, reloptions);
-
-	/*
-	 * And open the constructed table for writing.
-	 */
-	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
-
-	/*
-	 * Check INSERT permission on the constructed table.
-	 */
-	rte = makeNode(RangeTblEntry);
-	rte->rtekind = RTE_RELATION;
-	rte->relid = intoRelationId;
-	rte->relkind = RELKIND_RELATION;
-	rte->requiredPerms = ACL_INSERT;
-
-	for (attnum = 1; attnum <= intoTupDesc->natts; attnum++)
-		rte->modifiedCols = bms_add_member(rte->modifiedCols,
-				attnum - FirstLowInvalidHeapAttributeNumber);
-
-	ExecCheckRTPerms(list_make1(rte), true);
-
-	/*
-	 * Now replace the query's DestReceiver with one for SELECT INTO
-	 */
-	myState = (DR_intorel *) CreateDestReceiver(DestIntoRel);
-	Assert(myState->pub.mydest == DestIntoRel);
-	myState->estate = estate;
-	myState->origdest = queryDesc->dest;
-	myState->rel = intoRelationDesc;
-
-	queryDesc->dest = (DestReceiver *) myState;
-
-	/*
-	 * We can skip WAL-logging the insertions, unless PITR or streaming
-	 * replication is in use. We can skip the FSM in any case.
-	 */
-	myState->hi_options = HEAP_INSERT_SKIP_FSM |
-		(XLogIsNeeded() ? 0 : HEAP_INSERT_SKIP_WAL);
-	myState->bistate = GetBulkInsertState();
-
-	/* Not using WAL requires smgr_targblock be initially invalid */
-	Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
-}
-
-/*
- * CloseIntoRel --- clean up SELECT INTO at ExecutorEnd time
- */
-static void
-CloseIntoRel(QueryDesc *queryDesc)
-{
-	DR_intorel *myState = (DR_intorel *) queryDesc->dest;
-
-	/*
-	 * OpenIntoRel might never have gotten called, and we also want to guard
-	 * against double destruction.
-	 */
-	if (myState && myState->pub.mydest == DestIntoRel)
-	{
-		FreeBulkInsertState(myState->bistate);
-
-		/* If we skipped using WAL, must heap_sync before commit */
-		if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
-			heap_sync(myState->rel);
-
-		/* close rel, but keep lock until commit */
-		heap_close(myState->rel, NoLock);
-
-		/* restore the receiver belonging to executor's caller */
-		queryDesc->dest = myState->origdest;
-
-		/* might as well invoke my destructor */
-		intorel_destroy((DestReceiver *) myState);
-	}
-}
-
-/*
- * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
- */
-DestReceiver *
-CreateIntoRelDestReceiver(void)
-{
-	DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
-
-	self->pub.receiveSlot = intorel_receive;
-	self->pub.rStartup = intorel_startup;
-	self->pub.rShutdown = intorel_shutdown;
-	self->pub.rDestroy = intorel_destroy;
-	self->pub.mydest = DestIntoRel;
-
-	/* private fields will be set by OpenIntoRel */
-
-	return (DestReceiver *) self;
-}
-
-/*
- * intorel_startup --- executor startup
- */
-static void
-intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
-{
-	/* no-op */
-}
-
-/*
- * intorel_receive --- receive one tuple
- */
-static void
-intorel_receive(TupleTableSlot *slot, DestReceiver *self)
-{
-	DR_intorel *myState = (DR_intorel *) self;
-	HeapTuple	tuple;
-
-	/*
-	 * get the heap tuple out of the tuple table slot, making sure we have a
-	 * writable copy
-	 */
-	tuple = ExecMaterializeSlot(slot);
-
-	/*
-	 * force assignment of new OID (see comments in ExecInsert)
-	 */
-	if (myState->rel->rd_rel->relhasoids)
-		HeapTupleSetOid(tuple, InvalidOid);
-
-	heap_insert(myState->rel,
-				tuple,
-				myState->estate->es_output_cid,
-				myState->hi_options,
-				myState->bistate);
-
-	/* We know this is a newly created relation, so there are no indexes */
-}
-
-/*
- * intorel_shutdown --- executor end
- */
-static void
-intorel_shutdown(DestReceiver *self)
-{
-	/* no-op */
-}
-
-/*
- * intorel_destroy --- release DestReceiver object
- */
-static void
-intorel_destroy(DestReceiver *self)
-{
-	pfree(self);
-}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 6db42e7..40cd5ce 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -137,8 +137,6 @@ CreateExecutorState(void)
 
 	estate->es_top_eflags = 0;
 	estate->es_instrument = 0;
-	estate->es_select_into = false;
-	estate->es_into_oids = false;
 	estate->es_finished = false;
 
 	estate->es_exprcontexts = NIL;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 61f4622..ae8d374 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -535,7 +535,6 @@ init_execution_state(List *queryTree_list,
 
 			if (ps->commandType == CMD_SELECT &&
 				ps->utilityStmt == NULL &&
-				ps->intoClause == NULL &&
 				!ps->hasModifyingCTE)
 				fcache->lazyEval = lasttages->lazyEval = true;
 		}
@@ -1493,8 +1492,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
 	 */
 	if (parse &&
 		parse->commandType == CMD_SELECT &&
-		parse->utilityStmt == NULL &&
-		parse->intoClause == NULL)
+		parse->utilityStmt == NULL)
 	{
 		tlist_ptr = &parse->targetList;
 		tlist = parse->targetList;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 81f284c..ce6abcf 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1917,7 +1917,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 				if (_SPI_current->tuptable)
 					_SPI_current->processed = _SPI_current->tuptable->alloced -
 						_SPI_current->tuptable->free;
-				res = SPI_OK_UTILITY;
+				if(nodeTag(stmt) == T_CreateTableAsStmt &&
+				   ((CreateTableAsStmt*)stmt)->is_select_into)
+					res = SPI_OK_SELINTO;
+				else
+					res = SPI_OK_UTILITY;
 			}
 
 			/*
@@ -2042,9 +2046,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
 	{
 		case CMD_SELECT:
 			Assert(queryDesc->plannedstmt->utilityStmt == NULL);
-			if (queryDesc->plannedstmt->intoClause)		/* select into table? */
-				res = SPI_OK_SELINTO;
-			else if (queryDesc->dest->mydest != DestSPI)
+			if (queryDesc->dest->mydest != DestSPI)
 			{
 				/* Don't return SPI_OK_SELECT if we're discarding result */
 				res = SPI_OK_UTILITY;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6a20a6c..790ff4c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -85,7 +85,6 @@ _copyPlannedStmt(const PlannedStmt *from)
 	COPY_NODE_FIELD(rtable);
 	COPY_NODE_FIELD(resultRelations);
 	COPY_NODE_FIELD(utilityStmt);
-	COPY_NODE_FIELD(intoClause);
 	COPY_NODE_FIELD(subplans);
 	COPY_BITMAPSET_FIELD(rewindPlanIDs);
 	COPY_NODE_FIELD(rowMarks);
@@ -2419,7 +2418,6 @@ _copyQuery(const Query *from)
 	COPY_SCALAR_FIELD(canSetTag);
 	COPY_NODE_FIELD(utilityStmt);
 	COPY_SCALAR_FIELD(resultRelation);
-	COPY_NODE_FIELD(intoClause);
 	COPY_SCALAR_FIELD(hasAggs);
 	COPY_SCALAR_FIELD(hasWindowFuncs);
 	COPY_SCALAR_FIELD(hasSubLinks);
@@ -3207,6 +3205,18 @@ _copyExplainStmt(const ExplainStmt *from)
 	return newnode;
 }
 
+static CreateTableAsStmt *
+_copyCreateTableAsStmt(CreateTableAsStmt *from)
+{
+	CreateTableAsStmt *newnode = makeNode(CreateTableAsStmt);
+
+	COPY_NODE_FIELD(query);
+	COPY_NODE_FIELD(into);
+	COPY_SCALAR_FIELD(is_select_into);
+
+	return newnode;
+}
+
 static CreateSeqStmt *
 _copyCreateSeqStmt(const CreateSeqStmt *from)
 {
@@ -3638,7 +3648,6 @@ _copyExecuteStmt(const ExecuteStmt *from)
 	ExecuteStmt *newnode = makeNode(ExecuteStmt);
 
 	COPY_STRING_FIELD(name);
-	COPY_NODE_FIELD(into);
 	COPY_NODE_FIELD(params);
 
 	return newnode;
@@ -4273,6 +4282,9 @@ copyObject(const void *from)
 		case T_ExplainStmt:
 			retval = _copyExplainStmt(from);
 			break;
+		case T_CreateTableAsStmt:
+			retval = _copyCreateTableAsStmt(from);
+			break;
 		case T_CreateSeqStmt:
 			retval = _copyCreateSeqStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 137075a..58194df 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -900,7 +900,6 @@ _equalQuery(const Query *a, const Query *b)
 	COMPARE_SCALAR_FIELD(canSetTag);
 	COMPARE_NODE_FIELD(utilityStmt);
 	COMPARE_SCALAR_FIELD(resultRelation);
-	COMPARE_NODE_FIELD(intoClause);
 	COMPARE_SCALAR_FIELD(hasAggs);
 	COMPARE_SCALAR_FIELD(hasWindowFuncs);
 	COMPARE_SCALAR_FIELD(hasSubLinks);
@@ -1564,6 +1563,17 @@ _equalExplainStmt(const ExplainStmt *a, const ExplainStmt *b)
 	return true;
 }
 
+
+static bool
+_equalCreateTableAsStmt(CreateTableAsStmt *a, CreateTableAsStmt *b)
+{
+	COMPARE_NODE_FIELD(query);
+	COMPARE_NODE_FIELD(into);
+	COMPARE_SCALAR_FIELD(is_select_into);
+
+	return true;
+}
+
 static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
@@ -1927,7 +1937,6 @@ static bool
 _equalExecuteStmt(const ExecuteStmt *a, const ExecuteStmt *b)
 {
 	COMPARE_STRING_FIELD(name);
-	COMPARE_NODE_FIELD(into);
 	COMPARE_NODE_FIELD(params);
 
 	return true;
@@ -2812,6 +2821,9 @@ equal(const void *a, const void *b)
 		case T_ExplainStmt:
 			retval = _equalExplainStmt(a, b);
 			break;
+		case T_CreateTableAsStmt:
+			retval = _equalCreateTableAsStmt(a, b);
+			break;
 		case T_CreateSeqStmt:
 			retval = _equalCreateSeqStmt(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 25a215e..9cb8592 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -248,7 +248,6 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
 	WRITE_NODE_FIELD(rtable);
 	WRITE_NODE_FIELD(resultRelations);
 	WRITE_NODE_FIELD(utilityStmt);
-	WRITE_NODE_FIELD(intoClause);
 	WRITE_NODE_FIELD(subplans);
 	WRITE_BITMAPSET_FIELD(rewindPlanIDs);
 	WRITE_NODE_FIELD(rowMarks);
@@ -2187,7 +2186,6 @@ _outQuery(StringInfo str, const Query *node)
 		appendStringInfo(str, " :utilityStmt <>");
 
 	WRITE_INT_FIELD(resultRelation);
-	WRITE_NODE_FIELD(intoClause);
 	WRITE_BOOL_FIELD(hasAggs);
 	WRITE_BOOL_FIELD(hasWindowFuncs);
 	WRITE_BOOL_FIELD(hasSubLinks);
@@ -2805,7 +2803,7 @@ _outNode(StringInfo str, const void *obj)
 			case T_RangeVar:
 				_outRangeVar(str, obj);
 				break;
-			case T_IntoClause:
+			case T_IntoClause:/*XXX: This could be removed but dim would need to add it right back*/
 				_outIntoClause(str, obj);
 				break;
 			case T_Var:
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1d78cfd..0b596f0 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -198,7 +198,6 @@ _readQuery(void)
 	READ_BOOL_FIELD(canSetTag);
 	READ_NODE_FIELD(utilityStmt);
 	READ_INT_FIELD(resultRelation);
-	READ_NODE_FIELD(intoClause);
 	READ_BOOL_FIELD(hasAggs);
 	READ_BOOL_FIELD(hasWindowFuncs);
 	READ_BOOL_FIELD(hasSubLinks);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8bbe977..6b0541b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -233,7 +233,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	result->rtable = glob->finalrtable;
 	result->resultRelations = glob->resultRelations;
 	result->utilityStmt = parse->utilityStmt;
-	result->intoClause = parse->intoClause;
 	result->subplans = glob->subplans;
 	result->rewindPlanIDs = glob->rewindPlanIDs;
 	result->rowMarks = glob->finalrowmarks;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 40a420a..8006787 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1399,7 +1399,6 @@ simplify_EXISTS_query(Query *query)
 	 * are complex.
 	 */
 	if (query->commandType != CMD_SELECT ||
-		query->intoClause ||
 		query->setOperations ||
 		query->hasAggs ||
 		query->hasWindowFuncs ||
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 59a5268..0304c9d 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1129,8 +1129,7 @@ is_simple_subquery(Query *subquery)
 	 */
 	if (!IsA(subquery, Query) ||
 		subquery->commandType != CMD_SELECT ||
-		subquery->utilityStmt != NULL ||
-		subquery->intoClause != NULL)
+		subquery->utilityStmt != NULL)
 		elog(ERROR, "subquery is bogus");
 
 	/*
@@ -1216,8 +1215,7 @@ is_simple_union_all(Query *subquery)
 	/* Let's just make sure it's a valid subselect ... */
 	if (!IsA(subquery, Query) ||
 		subquery->commandType != CMD_SELECT ||
-		subquery->utilityStmt != NULL ||
-		subquery->intoClause != NULL)
+		subquery->utilityStmt != NULL)
 		elog(ERROR, "subquery is bogus");
 
 	/* Is it a set-operation query at all? */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index cd3da46..7d40e3e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4168,7 +4168,6 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 	if (!IsA(querytree, Query) ||
 		querytree->commandType != CMD_SELECT ||
 		querytree->utilityStmt ||
-		querytree->intoClause ||
 		querytree->hasAggs ||
 		querytree->hasWindowFuncs ||
 		querytree->hasSubLinks ||
@@ -4682,8 +4681,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 	 */
 	if (!IsA(querytree, Query) ||
 		querytree->commandType != CMD_SELECT ||
-		querytree->utilityStmt ||
-		querytree->intoClause)
+		querytree->utilityStmt)
 		goto fail;
 
 	/*
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index b187b03..182d6f7 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -62,6 +62,8 @@ static Query *transformDeclareCursorStmt(ParseState *pstate,
 						   DeclareCursorStmt *stmt);
 static Query *transformExplainStmt(ParseState *pstate,
 					 ExplainStmt *stmt);
+static Query *transformCreateTableAsStmt(ParseState *pstate,
+					 CreateTableAsStmt *stmt);
 static void transformLockingClause(ParseState *pstate, Query *qry,
 					   LockingClause *lc, bool pushedDown);
 
@@ -202,6 +204,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
 										  (ExplainStmt *) parseTree);
 			break;
 
+		case T_CreateTableAsStmt:
+			result = transformCreateTableAsStmt(pstate,
+			                           (CreateTableAsStmt *) parseTree);
+			break;
+
 		default:
 
 			/*
@@ -257,6 +264,7 @@ analyze_requires_snapshot(Node *parseTree)
 			result = true;
 			break;
 
+		case T_CreateTableAsStmt:
 		case T_ExplainStmt:
 			/* yes, because we must analyze the contained statement */
 			result = true;
@@ -455,21 +463,29 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 		sub_pstate->p_relnamespace = sub_relnamespace;
 		sub_pstate->p_varnamespace = sub_varnamespace;
 
+		/* The grammar should have produced a SELECT, but it might have INTO */
+		if(IsA(stmt->selectStmt, SelectStmt))
+		{
+			SelectStmt* first = (SelectStmt*)stmt->selectStmt;
+			while (first && first->op != SETOP_NONE)
+				first = first->larg;
+
+			if (first->intoClause)
+				ereport(ERROR,
+				        (errcode(ERRCODE_SYNTAX_ERROR),
+				         errmsg("INSERT ... SELECT cannot specify INTO"),
+				         parser_errposition(pstate,
+				                            exprLocation((Node *)first->intoClause))));
+		}
+
 		selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
 
 		free_parsestate(sub_pstate);
 
-		/* The grammar should have produced a SELECT, but it might have INTO */
 		if (!IsA(selectQuery, Query) ||
 			selectQuery->commandType != CMD_SELECT ||
 			selectQuery->utilityStmt != NULL)
 			elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
-		if (selectQuery->intoClause)
-			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("INSERT ... SELECT cannot specify INTO"),
-					 parser_errposition(pstate,
-						   exprLocation((Node *) selectQuery->intoClause))));
 
 		/*
 		 * Make the source be a subquery in the INSERT's rangetable, and add
@@ -876,6 +892,18 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	Node	   *qual;
 	ListCell   *l;
 
+	/*
+	 * Validity-check whether we got called from somewhere where
+	 * ... INTO was not allowed
+	 */
+	if (stmt->intoClause)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"),
+				 parser_errposition(pstate,
+				                    exprLocation((Node *) stmt->intoClause))));
+
+
 	qry->commandType = CMD_SELECT;
 
 	/* process the WITH clause independently of all else */
@@ -963,8 +991,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 												   pstate->p_windowdefs,
 												   &qry->targetList);
 
-	/* SELECT INTO/CREATE TABLE AS spec is just passed through */
-	qry->intoClause = stmt->intoClause;
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
@@ -1185,9 +1211,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			 errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
 
-	/* CREATE TABLE AS spec is just passed through */
-	qry->intoClause = stmt->intoClause;
-
 	/*
 	 * There mustn't have been any table references in the expressions, else
 	 * strange things would happen, like Cartesian products of those tables
@@ -1253,7 +1276,6 @@ static Query *
 transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
-	SelectStmt *leftmostSelect;
 	int			leftmostRTI;
 	Query	   *leftmostQuery;
 	SetOperationStmt *sostmt;
@@ -1286,20 +1308,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	}
 
 	/*
-	 * Find leftmost leaf SelectStmt; extract the one-time-only items from it
-	 * and from the top-level node.
-	 */
-	leftmostSelect = stmt->larg;
-	while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
-		leftmostSelect = leftmostSelect->larg;
-	Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
-		   leftmostSelect->larg == NULL);
-	qry->intoClause = leftmostSelect->intoClause;
-
-	/* clear this to prevent complaints in transformSetOperationTree() */
-	leftmostSelect->intoClause = NULL;
-
-	/*
 	 * These are not one-time, exactly, but we want to process them here and
 	 * not let transformSetOperationTree() see them --- else it'll just
 	 * recurse right back here!
@@ -1330,7 +1338,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->setOperations = (Node *) sostmt;
 
 	/*
-	 * Re-find leftmost SELECT (now it's a sub-query in rangetable)
+	 * Find leftmost SELECT (it's a sub-query in rangetable)
 	 */
 	node = sostmt->larg;
 	while (node && IsA(node, SetOperationStmt))
@@ -2099,6 +2107,25 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
 				 errmsg("cannot specify both SCROLL and NO SCROLL")));
 
+	/*
+	 * We must explicitly disallow DECLARE CURSOR ... SELECT INTO We
+	 * have to do this before transformStmt() as that will holler if
+	 * it ever finds a intoClause
+	 */
+	if(IsA(stmt->query, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)stmt->query;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+			         errmsg("DECLARE CURSOR cannot specify INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	result = transformStmt(pstate, stmt->query);
 
 	/* Grammar should not have allowed anything but SELECT */
@@ -2107,14 +2134,6 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 		result->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR");
 
-	/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
-	if (result->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-				 errmsg("DECLARE CURSOR cannot specify INTO"),
-				 parser_errposition(pstate,
-								exprLocation((Node *) result->intoClause))));
-
 	/*
 	 * We also disallow data-modifying WITH in a cursor.  (This could be
 	 * allowed, but the semantics of when the updates occur might be
@@ -2183,6 +2202,28 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
 
 
 /*
+ * transformCreateTableAsStmt -
+ *	transform an CREATE TABLE AS/SELECT ... INTO Statement
+ *
+ */
+static Query *
+transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
+{
+	Query	   *result;
+
+	/* transform contained query */
+	stmt->query = (Node *) transformStmt(pstate, stmt->query);
+
+	/* represent the command as a utility Query */
+	result = makeNode(Query);
+	result->commandType = CMD_UTILITY;
+	result->utilityStmt = (Node *) stmt;
+
+	return result;
+}
+
+
+/*
  * Check for features that are not supported together with FOR UPDATE/SHARE.
  *
  * exported so planner can check again after rewriting, query pullup, etc
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e9872d3..553dfc9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3058,23 +3058,28 @@ ExistingIndex:   USING INDEX index_name				{ $$ = $3; }
 CreateAsStmt:
 		CREATE OptTemp TABLE create_as_target AS SelectStmt opt_with_data
 				{
+                    SelectStmt *n;
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+                    ctas->into = $4;
+                    ctas->query = $6;
+                    ctas->is_select_into = false;
+                    $4->skipData = !($7);
+					$4->rel->relpersistence = $2;
+					$4->skipData = !($7);
+                    $$ = (Node*)ctas;
+
 					/*
 					 * When the SelectStmt is a set-operation tree, we must
 					 * stuff the INTO information into the leftmost component
 					 * Select, because that's where analyze.c will expect
 					 * to find it.
 					 */
-					SelectStmt *n = findLeftmostSelect((SelectStmt *) $6);
+					n = findLeftmostSelect((SelectStmt *) $6);
 					if (n->intoClause != NULL)
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("CREATE TABLE AS cannot specify INTO"),
 								 parser_errposition(exprLocation((Node *) n->intoClause))));
-					n->intoClause = $4;
-					/* cram additional flags into the IntoClause */
-					$4->rel->relpersistence = $2;
-					$4->skipData = !($7);
-					$$ = $6;
 				}
 		;
 
@@ -8451,20 +8456,25 @@ ExecuteStmt: EXECUTE name execute_param_clause
 					ExecuteStmt *n = makeNode(ExecuteStmt);
 					n->name = $2;
 					n->params = $3;
-					n->into = NULL;
 					$$ = (Node *) n;
 				}
 			| CREATE OptTemp TABLE create_as_target AS
 				EXECUTE name execute_param_clause opt_with_data
 				{
 					ExecuteStmt *n = makeNode(ExecuteStmt);
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+
 					n->name = $7;
 					n->params = $8;
-					n->into = $4;
+
 					/* cram additional flags into the IntoClause */
 					$4->rel->relpersistence = $2;
 					$4->skipData = !($9);
-					$$ = (Node *) n;
+
+                    ctas->into = $4;
+                    ctas->query = (Node*)n;
+                    ctas->is_select_into = false;
+					$$ = (Node *) ctas;
 				}
 		;
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 7f4da92..52f449f 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -481,6 +481,21 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	if (r->alias == NULL)
 		elog(ERROR, "subquery in FROM must have an alias");
 
+
+	if(IsA(r->subquery, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)r->subquery;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery in FROM cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *)first->intoClause))));
+	}
+
 	/*
 	 * Analyze and transform the subquery.
 	 */
@@ -495,12 +510,6 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 		query->commandType != CMD_SELECT ||
 		query->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in subquery in FROM");
-	if (query->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery in FROM cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) query->intoClause))));
 
 	/*
 	 * The subquery cannot make use of any variables from FROM items created
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index c0c6240..4ea8ee6 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -241,6 +241,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
 	/* Analysis not done already */
 	Assert(!IsA(cte->ctequery, Query));
 
+	if(IsA(cte->ctequery, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)cte->ctequery;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery in WITH cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	query = parse_sub_analyze(cte->ctequery, pstate, cte, false);
 	cte->ctequery = (Node *) query;
 
@@ -253,13 +267,6 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
 	if (query->utilityStmt != NULL)
 		elog(ERROR, "unexpected utility statement in WITH");
 
-	if (query->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery in WITH cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) query->intoClause))));
-
 	/*
 	 * We disallow data-modifying WITH except at the top level of a query,
 	 * because it's not clear when such a modification should be executed.
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d22d8a1..d4d9121 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1398,6 +1398,21 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		return result;
 
 	pstate->p_hasSubLinks = true;
+
+	if(IsA(sublink->subselect, SelectStmt))
+	{
+		SelectStmt* first = (SelectStmt*)sublink->subselect;
+		while (first && first->op != SETOP_NONE)
+			first = first->larg;
+
+		if (first->intoClause)
+			ereport(ERROR,
+			        (errcode(ERRCODE_SYNTAX_ERROR),
+			         errmsg("subquery cannot have SELECT INTO"),
+			         parser_errposition(pstate,
+			                            exprLocation((Node *) first->intoClause))));
+	}
+
 	qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
 
 	/*
@@ -1408,12 +1423,6 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		qtree->commandType != CMD_SELECT ||
 		qtree->utilityStmt != NULL)
 		elog(ERROR, "unexpected non-SELECT command in SubLink");
-	if (qtree->intoClause)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("subquery cannot have SELECT INTO"),
-				 parser_errposition(pstate,
-								 exprLocation((Node *) qtree->intoClause))));
 
 	sublink->subselect = (Node *) qtree;
 
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index f9ec2b2..854925c 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -37,6 +37,7 @@ raw_parser(const char *str)
 	core_yyscan_t yyscanner;
 	base_yy_extra_type yyextra;
 	int			yyresult;
+	ListCell *c;
 
 	/* initialize the flex scanner */
 	yyscanner = scanner_init(str, &yyextra.core_yy_extra,
@@ -57,6 +58,43 @@ raw_parser(const char *str)
 	if (yyresult)				/* error */
 		return NIL;
 
+	/*
+	 * Some things are rather hard to properly diagnose in grammar
+	 * without complicating/duplicating it too much. So we do some
+	 * postprocessing here.
+	 */
+	foreach(c, yyextra.parsetree){
+		switch(nodeTag(lfirst(c))){
+			/*
+			 * The grammar currently doesn't disambiguate between
+			 * SELECT and SELECT ... INTO. Do that now.
+			 */
+			case T_SelectStmt:
+			{
+				SelectStmt* s = (SelectStmt*)lfirst(c);
+				SelectStmt* first = s;
+				while (first && first->op != SETOP_NONE)
+					first = first->larg;
+				Assert(first && IsA(first, SelectStmt) && first->larg == NULL);
+				if(first->intoClause){
+					CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
+					ctas->into = first->intoClause;
+					ctas->query = (Node*)s;
+					ctas->is_select_into = true;
+					/*
+					 * this way everyone can complain if this is set
+					 * without further checks because it shall never
+					 * be set but here.
+					 */
+					first->intoClause = NULL;
+					lfirst(c) = ctas;
+					break;
+				}
+			}
+			default:
+				break;
+		}
+	}
 	return yyextra.parsetree;
 }
 
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 03c1296..eff4c75 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -329,8 +329,7 @@ DefineQueryRewrite(char *rulename,
 		query = (Query *) linitial(action);
 		if (!is_instead ||
 			query->commandType != CMD_SELECT ||
-			query->utilityStmt != NULL ||
-			query->intoClause != NULL)
+			query->utilityStmt != NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("rules on SELECT must have action INSTEAD SELECT")));
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 42a0fb0..7c50284 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -260,8 +260,7 @@ ChoosePortalStrategy(List *stmts)
 			if (query->canSetTag)
 			{
 				if (query->commandType == CMD_SELECT &&
-					query->utilityStmt == NULL &&
-					query->intoClause == NULL)
+					query->utilityStmt == NULL)
 				{
 					if (query->hasModifyingCTE)
 						return PORTAL_ONE_MOD_WITH;
@@ -285,8 +284,7 @@ ChoosePortalStrategy(List *stmts)
 			if (pstmt->canSetTag)
 			{
 				if (pstmt->commandType == CMD_SELECT &&
-					pstmt->utilityStmt == NULL &&
-					pstmt->intoClause == NULL)
+					pstmt->utilityStmt == NULL)
 				{
 					if (pstmt->hasModifyingCTE)
 						return PORTAL_ONE_MOD_WITH;
@@ -395,8 +393,7 @@ FetchStatementTargetList(Node *stmt)
 		else
 		{
 			if (query->commandType == CMD_SELECT &&
-				query->utilityStmt == NULL &&
-				query->intoClause == NULL)
+				query->utilityStmt == NULL)
 				return query->targetList;
 			if (query->returningList)
 				return query->returningList;
@@ -408,8 +405,7 @@ FetchStatementTargetList(Node *stmt)
 		PlannedStmt *pstmt = (PlannedStmt *) stmt;
 
 		if (pstmt->commandType == CMD_SELECT &&
-			pstmt->utilityStmt == NULL &&
-			pstmt->intoClause == NULL)
+			pstmt->utilityStmt == NULL)
 			return pstmt->planTree->targetlist;
 		if (pstmt->hasReturning)
 			return pstmt->planTree->targetlist;
@@ -430,7 +426,6 @@ FetchStatementTargetList(Node *stmt)
 		ExecuteStmt *estmt = (ExecuteStmt *) stmt;
 		PreparedStatement *entry;
 
-		Assert(!estmt->into);
 		entry = FetchPreparedStatement(estmt->name, true);
 		return FetchPreparedStatementTargetList(entry);
 	}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 69d6479..61ce5a6 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -128,9 +128,7 @@ CommandIsReadOnly(Node *parsetree)
 		switch (stmt->commandType)
 		{
 			case CMD_SELECT:
-				if (stmt->intoClause != NULL)
-					return false;		/* SELECT INTO */
-				else if (stmt->rowMarks != NIL)
+				if (stmt->rowMarks != NIL)
 					return false;		/* SELECT FOR UPDATE/SHARE */
 				else if (stmt->hasModifyingCTE)
 					return false;		/* data-modifying CTE */
@@ -383,6 +381,7 @@ check_xact_readonly(Node *parsetree)
 		case T_CreateSchemaStmt:
 		case T_CreateSeqStmt:
 		case T_CreateStmt:
+		case T_CreateTableAsStmt:
 		case T_CreateTableSpaceStmt:
 		case T_CreateTrigStmt:
 		case T_CompositeTypeStmt:
@@ -1299,6 +1298,10 @@ standard_ProcessUtility(Node *parsetree,
 			ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
 			break;
 
+		case T_CreateTableAsStmt:
+			CreateTableAs((CreateTableAsStmt *) parsetree, queryString, params, dest);
+			break;
+
 		case T_VariableSetStmt:
 			ExecSetVariableStmt((VariableSetStmt *) parsetree);
 			break;
@@ -1507,8 +1510,6 @@ UtilityReturnsTuples(Node *parsetree)
 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
 				PreparedStatement *entry;
 
-				if (stmt->into)
-					return false;
 				entry = FetchPreparedStatement(stmt->name, false);
 				if (!entry)
 					return false;		/* not our business to raise error */
@@ -1559,8 +1560,6 @@ UtilityTupleDescriptor(Node *parsetree)
 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
 				PreparedStatement *entry;
 
-				if (stmt->into)
-					return NULL;
 				entry = FetchPreparedStatement(stmt->name, false);
 				if (!entry)
 					return NULL;	/* not our business to raise error */
@@ -1595,8 +1594,7 @@ QueryReturnsTuples(Query *parsetree)
 	{
 		case CMD_SELECT:
 			/* returns tuples ... unless it's DECLARE CURSOR or SELECT INTO */
-			if (parsetree->utilityStmt == NULL &&
-				parsetree->intoClause == NULL)
+			if (parsetree->utilityStmt == NULL)
 				return true;
 			break;
 		case CMD_INSERT:
@@ -2187,6 +2185,13 @@ CreateCommandTag(Node *parsetree)
 			tag = "EXPLAIN";
 			break;
 
+		case T_CreateTableAsStmt:
+			if (((CreateTableAsStmt*)parsetree)->is_select_into)
+				tag = "SELECT INTO";
+			else
+				tag = "CREATE TABLE AS";
+			break;
+
 		case T_VariableSetStmt:
 			switch (((VariableSetStmt *) parsetree)->kind)
 			{
@@ -2348,8 +2353,6 @@ CreateCommandTag(Node *parsetree)
 							Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
 							tag = "DECLARE CURSOR";
 						}
-						else if (stmt->intoClause != NULL)
-							tag = "SELECT INTO";
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
@@ -2398,8 +2401,6 @@ CreateCommandTag(Node *parsetree)
 							Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
 							tag = "DECLARE CURSOR";
 						}
-						else if (stmt->intoClause != NULL)
-							tag = "SELECT INTO";
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
@@ -2473,10 +2474,7 @@ GetCommandLogLevel(Node *parsetree)
 			break;
 
 		case T_SelectStmt:
-			if (((SelectStmt *) parsetree)->intoClause)
-				lev = LOGSTMT_DDL;		/* CREATE AS, SELECT INTO */
-			else
-				lev = LOGSTMT_ALL;
+			lev = LOGSTMT_ALL;
 			break;
 
 			/* utility statements --- same whether raw or cooked */
@@ -2724,6 +2722,10 @@ GetCommandLogLevel(Node *parsetree)
 			}
 			break;
 
+		case T_CreateTableAsStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_VariableSetStmt:
 			lev = LOGSTMT_ALL;
 			break;
@@ -2836,10 +2838,7 @@ GetCommandLogLevel(Node *parsetree)
 				switch (stmt->commandType)
 				{
 					case CMD_SELECT:
-						if (stmt->intoClause != NULL)
-							lev = LOGSTMT_DDL;	/* CREATE AS, SELECT INTO */
-						else
-							lev = LOGSTMT_ALL;	/* SELECT or DECLARE CURSOR */
+						lev = LOGSTMT_ALL;
 						break;
 
 					case CMD_UPDATE:
@@ -2865,10 +2864,7 @@ GetCommandLogLevel(Node *parsetree)
 				switch (stmt->commandType)
 				{
 					case CMD_SELECT:
-						if (stmt->intoClause != NULL)
-							lev = LOGSTMT_DDL;	/* CREATE AS, SELECT INTO */
-						else
-							lev = LOGSTMT_ALL;	/* SELECT or DECLARE CURSOR */
+						lev = LOGSTMT_ALL;
 						break;
 
 					case CMD_UPDATE:
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6a3778d..abbc152 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -311,7 +311,7 @@ do { \
 
 #define HeapTupleHeaderSetOid(tup, oid) \
 do { \
-	Assert((tup)->t_infomask & HEAP_HASOID); \
+	if(!((tup)->t_infomask & HEAP_HASOID)) elog(ERROR, "HEAP_HASOID"); \
 	*((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \
 } while (0)
 
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 472a357..6b545dc 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -54,4 +54,7 @@ extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
 
 void		DropAllPreparedStatements(void);
 
+extern ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
+                                    const char *queryString, EState *estate);
+
 #endif   /* PREPARE_H */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 2e0b1f1..e8efce1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -16,6 +16,7 @@
 
 #include "access/htup.h"
 #include "commands/cmdtrigger.h"
+#include "executor/executor.h"
 #include "nodes/parsenodes.h"
 #include "storage/lock.h"
 #include "utils/relcache.h"
@@ -23,6 +24,9 @@
 
 extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
 
+extern void CreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
+			 ParamListInfo params, DestReceiver *dest);
+
 extern void RemoveRelations(DropStmt *drop);
 
 extern Oid	AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 7f27669..47bd1ee 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -49,12 +49,17 @@
  * AfterTriggerBeginQuery/AfterTriggerEndQuery.  This does not necessarily
  * mean that the plan can't queue any AFTER triggers; just that the caller
  * is responsible for there being a trigger context for them to be queued in.
+ *
+ * FORCE_OIDS tells the executor to build a tupleDesc that includes
+ * the oid column. This is used from tablecmd.c's CreateTableAs.
  */
 #define EXEC_FLAG_EXPLAIN_ONLY	0x0001	/* EXPLAIN, no ANALYZE */
 #define EXEC_FLAG_REWIND		0x0002	/* need efficient rescan */
 #define EXEC_FLAG_BACKWARD		0x0004	/* need backward scan */
 #define EXEC_FLAG_MARK			0x0008	/* need mark/restore */
 #define EXEC_FLAG_SKIP_TRIGGERS 0x0010	/* skip AfterTrigger calls */
+#define EXEC_FLAG_FORCE_OIDS_ON 0x0020	/* force oids in returned tuples */
+#define EXEC_FLAG_FORCE_OIDS_OFF 0x0040	/* force oids in returned tuples */
 
 
 /*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5207102..d268fb9 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -322,6 +322,12 @@ typedef struct ResultRelInfo
 	ProjectionInfo *ri_projectReturning;
 } ResultRelInfo;
 
+typedef enum {
+	ESTATE_FORCE_OIDS_DEFAULT,
+	ESTATE_FORCE_OIDS_ON,
+	ESTATE_FORCE_OIDS_OFF
+} EStateForceOids;
+
 /* ----------------
  *	  EState information
  *
@@ -371,8 +377,7 @@ typedef struct EState
 
 	int			es_top_eflags;	/* eflags passed to ExecutorStart */
 	int			es_instrument;	/* OR of InstrumentOption flags */
-	bool		es_select_into; /* true if doing SELECT INTO */
-	bool		es_into_oids;	/* true to generate OIDs in SELECT INTO */
+	EStateForceOids        es_force_oids;  /* used by CreateTableAs */
 	bool		es_finished;	/* true when ExecutorFinish is done */
 
 	List	   *es_exprcontexts;	/* List of ExprContexts within EState */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1b7298a..9a260c7 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -305,6 +305,7 @@ typedef enum NodeTag
 	T_DropdbStmt,
 	T_VacuumStmt,
 	T_ExplainStmt,
+	T_CreateTableAsStmt,
 	T_CreateSeqStmt,
 	T_AlterSeqStmt,
 	T_VariableSetStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d4d1333..433cbc8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -111,8 +111,6 @@ typedef struct Query
 	int			resultRelation; /* rtable index of target relation for
 								 * INSERT/UPDATE/DELETE; 0 for SELECT */
 
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
-
 	bool		hasAggs;		/* has aggregates in tlist or havingQual */
 	bool		hasWindowFuncs; /* has window functions in tlist */
 	bool		hasSubLinks;	/* has subquery SubLink */
@@ -1009,7 +1007,7 @@ typedef struct SelectStmt
 	 */
 	List	   *distinctClause; /* NULL, list of DISTINCT ON exprs, or
 								 * lcons(NIL,NIL) for all (SELECT DISTINCT) */
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
+	IntoClause *intoClause;		/* target for SELECT INTO */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	List	   *fromClause;		/* the FROM clause */
 	Node	   *whereClause;	/* WHERE qualification */
@@ -2430,7 +2428,7 @@ typedef struct VacuumStmt
  *		Explain Statement
  *
  * The "query" field is either a raw parse tree (SelectStmt, InsertStmt, etc)
- * or a Query node if parse analysis has been done.  Note that rewriting and
+ * or a Query node if parse analysis has been done. Note that rewriting and
  * planning of the query are always postponed until execution of EXPLAIN.
  * ----------------------
  */
@@ -2442,6 +2440,19 @@ typedef struct ExplainStmt
 } ExplainStmt;
 
 /* ----------------------
+ * analyzing, rewriting and planning are handled as in explain
+ * statements (see comment above) only that query can only be a
+ * SelectStmt and not some other type.
+ */
+typedef struct CreateTableAsStmt
+{
+	NodeTag		type;
+	IntoClause  *into;
+	Node        *query;			/* the query (see comments above) */
+	bool        is_select_into; /* plpgsql wants to disambiguate */
+} CreateTableAsStmt;
+
+/* ----------------------
  * Checkpoint Statement
  * ----------------------
  */
@@ -2555,7 +2566,6 @@ typedef struct ExecuteStmt
 {
 	NodeTag		type;
 	char	   *name;			/* The name of the plan to execute */
-	IntoClause *into;			/* Optional table to store results in */
 	List	   *params;			/* Values to assign to parameters */
 } ExecuteStmt;
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7d90b91..b67d988 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -54,8 +54,6 @@ typedef struct PlannedStmt
 
 	Node	   *utilityStmt;	/* non-null if this is DECLARE CURSOR */
 
-	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
-
 	List	   *subplans;		/* Plan trees for SubPlan expressions */
 
 	Bitmapset  *rewindPlanIDs;	/* indices of subplans that require REWIND */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 0b126a3..231210a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3286,29 +3286,18 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
 			break;
 
 		case SPI_OK_SELINTO:
-
 			/*
 			 * We want to disallow SELECT INTO for now, because its behavior
 			 * is not consistent with SELECT INTO in a normal plpgsql context.
 			 * (We need to reimplement EXECUTE to parse the string as a
 			 * plpgsql command, not just feed it to SPI_execute.) However,
-			 * CREATE AS should be allowed ... and since it produces the same
-			 * parsetree as SELECT INTO, there's no way to tell the difference
-			 * except to look at the source text.  Wotta kluge!
+			 * CREATE AS should be allowed ...
 			 */
-			{
-				char	   *ptr;
-
-				for (ptr = querystr; *ptr; ptr++)
-					if (!scanner_isspace(*ptr))
-						break;
-				if (*ptr == 'S' || *ptr == 's')
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("EXECUTE of SELECT ... INTO is not implemented"),
-							 errhint("You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.")));
-				break;
-			}
+			ereport(ERROR,
+			        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			         errmsg("EXECUTE of SELECT ... INTO is not implemented"),
+			         errhint("You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.")));
+			break;
 
 			/* Some SPI errors deserve specific error messages */
 		case SPI_ERROR_COPY:
@@ -5759,7 +5748,7 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
 	 */
 	if (!IsA(query, Query))
 		return;
-	if (query->commandType != CMD_SELECT || query->intoClause)
+	if (query->commandType != CMD_SELECT)
 		return;
 	if (query->rtable != NIL)
 		return;
@@ -5833,7 +5822,7 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
 	 */
 	if (!IsA(stmt, PlannedStmt))
 		return;
-	if (stmt->commandType != CMD_SELECT || stmt->intoClause)
+	if (stmt->commandType != CMD_SELECT)
 		return;
 	plan = stmt->planTree;
 	if (!IsA(plan, Result))
diff --git a/src/test/regress/expected/select_into.out b/src/test/regress/expected/select_into.out
index c8327f6..d124382 100644
--- a/src/test/regress/expected/select_into.out
+++ b/src/test/regress/expected/select_into.out
@@ -44,6 +44,25 @@ CREATE TABLE selinto_schema.tmp3 (a,b,c)
 	   AS SELECT oid,relname,relacl FROM pg_class
 	   WHERE relname like '%c%';	-- OK
 RESET SESSION AUTHORIZATION;
+--
+-- disallowed uses of SELECT ... INTO. All should fail
+--
+DECLARE foo CURSOR FOR SELECT 1 INTO b;
+ERROR:  DECLARE CURSOR cannot specify INTO
+LINE 1: DECLARE foo CURSOR FOR SELECT 1 INTO b;
+                                             ^
+COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blub';
+ERROR:  COPY (SELECT INTO) is not supported
+SELECT * FROM (SELECT 1 INTO f) bar;
+ERROR:  subquery in FROM cannot have SELECT INTO
+LINE 1: SELECT * FROM (SELECT 1 INTO f) bar;
+                                     ^
+CREATE VIEW foo AS SELECT 1 INTO b;
+ERROR:  views must not contain SELECT INTO
+INSERT INTO b SELECT 1 INTO f;
+ERROR:  INSERT ... SELECT cannot specify INTO
+LINE 1: INSERT INTO b SELECT 1 INTO f;
+                                    ^
 DROP SCHEMA selinto_schema CASCADE;
 NOTICE:  drop cascades to 3 other objects
 DETAIL:  drop cascades to table selinto_schema.tmp1
diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out
index e9d3908..6981692 100644
--- a/src/test/regress/expected/transactions.out
+++ b/src/test/regress/expected/transactions.out
@@ -139,7 +139,7 @@ SELECT * FROM writetest, temptest; -- ok
 (0 rows)
 
 CREATE TABLE test AS SELECT * FROM writetest; -- fail
-ERROR:  cannot execute SELECT INTO in a read-only transaction
+ERROR:  cannot execute CREATE TABLE AS in a read-only transaction
 START TRANSACTION READ WRITE;
 DROP TABLE writetest; -- ok
 COMMIT;
diff --git a/src/test/regress/sql/select_into.sql b/src/test/regress/sql/select_into.sql
index 09d210b..0c07f76 100644
--- a/src/test/regress/sql/select_into.sql
+++ b/src/test/regress/sql/select_into.sql
@@ -50,6 +50,21 @@ CREATE TABLE selinto_schema.tmp3 (a,b,c)
 	   WHERE relname like '%c%';	-- OK
 RESET SESSION AUTHORIZATION;
 
+
+--
+-- disallowed uses of SELECT ... INTO. All should fail
+--
+DECLARE foo CURSOR FOR SELECT 1 INTO b;
+
+COPY (SELECT 1 INTO frak UNION SELECT 2) TO 'blub';
+
+SELECT * FROM (SELECT 1 INTO f) bar;
+
+CREATE VIEW foo AS SELECT 1 INTO b;
+
+INSERT INTO b SELECT 1 INTO f;
+
+
 DROP SCHEMA selinto_schema CASCADE;
 DROP USER selinto_user;
 
-- 
1.7.9

#52Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#24)
Re: Command Triggers, patch v11

Tom Lane <tgl@sss.pgh.pa.us> writes:

FWIW, I agree with Thom on this. If we do it as you suggest, I
confidently predict that it will be less than a year before we seriously
regret it. Given all the discussion around this, it's borderline insane
to believe that the set of parameters to be passed to command triggers
is nailed down and won't need to change in the future.

As for special coding of support, it hardly seems onerous when every
language that has triggers at all has got some provision for the
existing trigger parameters. A bit of copying and pasting should get
the job done.

So, for that to happen I need to add a new macro and use it in most call
sites of CALLED_AS_TRIGGER(fcinfo). That includes the setup switch in
src/pl/plpgsql/src/pl_comp.c doCompile() and plpgsql_call_handler() for
starters.

Let's call the macro CALLED_AS_COMMAND_TRIGGER(fcinfo), and I would
extend CommandContextData to be a Node of type CommandTriggerData, that
needs to be added to the NodeTag enum as T_CommandTriggerData.

With that in place I can stuff the data into the function's call context
(via InitFunctionCallInfoData) then edit the call handlers of included
procedure languages until they are able to init their language variables
with the info.

Then, do we want the command trigger functions to return type trigger or
another specific type? I guess we want to forbid registering any
function as a command trigger?

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#53Thom Brown
thom@linux.com
In reply to: Andres Freund (#51)
2 attachment(s)
Re: Command Triggers, patch v11

On 4 March 2012 15:50, Andres Freund <andres@anarazel.de> wrote:

Ok, I rebased my patch ontop of dim's current HEAD. There was only one trivial
conflict in tablecmds.h. I had written the patch independently of the command
triggers stuff because I though, and still do, that would make applying it
easier.

Attached are two versions of the patch, one based on command triggers and one
without. Both pass regression tests for me.

I have conducted testing against Dimitri’s latest patch, along with
the incremental patch to fix the build, and also Andres’ CTAS patch.
I've attached a copy of how I configured the command triggers
(command_trigger_test_setup.txt), and also the set of tests I've been
running against the changes (command_trigger_regression.sql). I've
left comments in that last file where I haven't been able to conduct a
test for particular commands.

Creating a command trigger using ANY COMMAND results in oid,
schemaname, objectname (function parameters 4 & 5) not being set for
either BEFORE or AFTER.

There is no support for ALTER CONVERSION.

When trying to create an AFTER command trigger on CREATE INDEX, I get
the warning:

WARNING: CREATE INDEX CONCURRENTLY is not supported
DETAIL: The command trigger will not get fired.

This should probably say that it’s not supported on AFTER command
triggers yet rather than the general DDL itself.

Command triggers for AFTER creating rules don’t return OIDs.

thom@test=# CREATE RULE "_RETURN" AS
ON SELECT TO test2
DO INSTEAD
SELECT 234 id, 'test'::text stuff;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE
RULE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='CREATE RULE'
objectid=<NULL> schemaname='public' objectname='_RETURN'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='CREATE RULE'
objectid=<NULL> schemaname='public' objectname='_RETURN'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='CREATE RULE'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
CREATE RULE

Command triggers for creating sequences don’t show the schema:

thom@test=# CREATE TEMP SEQUENCE test_seq2;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='CREATE SEQUENCE'
objectid=<NULL> schemaname='<NULL>' objectname='test_seq2'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='CREATE SEQUENCE'
objectid=25130 schemaname='<NULL>' objectname='test_seq2'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='CREATE
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
CREATE SEQUENCE

Command triggers for AFTER creating extensions with IF NOT EXISTS
don’t fire, but do in the ANY COMMAND instance:

thom@test=# CREATE EXTENSION IF NOT EXISTS file_fdw;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='CREATE EXTENSION'
objectid=<NULL> schemaname='<NULL>' objectname='file_fdw'
NOTICE: extension "file_fdw" already exists, skipping
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='CREATE
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
CREATE EXTENSION

Command triggers on CREATE TEXT SEARCH DICTIONARY show the name as garbage:

thom@test=# CREATE TEXT SEARCH DICTIONARY test_stem (
test(# TEMPLATE = snowball,
test(# language = 'english', stopwords = 'english'
test(# );
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE TEXT
SEARCH DICTIONARY' objectid=<NULL> schemaname='<NULL>'
objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='CREATE TEXT SEARCH
DICTIONARY' objectid=<NULL> schemaname='thom' objectname='�Ч�l '
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='CREATE TEXT SEARCH
DICTIONARY' objectid=25139 schemaname='thom' objectname='�Ч�l '
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='CREATE TEXT
SEARCH DICTIONARY' objectid=<NULL> schemaname='<NULL>'
objectname='<NULL>'
CREATE TEXT SEARCH DICTIONARY

Command triggers for BEFORE CREATE TYPE (exluding ANY COMMAND) don’t
fire if the type isn’t created due to an error:

thom@test=# CREATE TYPE thom.type_test AS
(a integer,
b integer,
c text);
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE
TYPE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ERROR: type "type_test" already exists

The ANY COMMAND trigger fires on creating roles, but there’s no
corresponding allowance to create the trigger explicitly for creating
roles.

Command triggers for AFTER CREATE VIEW don’t show the schema:

thom@test=# CREATE VIEW view_test AS SELECT id, stuff FROM public.test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE
VIEW' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='CREATE VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='view_test'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='CREATE VIEW'
objectid=25155 schemaname='<NULL>' objectname='view_test'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='CREATE VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
CREATE VIEW

Command triggers for BEFORE and AFTER ALTER DOMAIN show a garbage name
and no schema when dropping a constraint:

thom@test=# ALTER DOMAIN us_postal_code DROP CONSTRAINT dummy_constraint;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
DOMAIN' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='ALTER DOMAIN'
objectid=25085 schemaname='<NULL>' objectname='�'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='ALTER DOMAIN'
objectid=25085 schemaname='<NULL>' objectname='�'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
DOMAIN' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER DOMAIN

Continuing with this same trigger, we do get a schema but a garbage
name for OWNER TO:

thom@test=# ALTER DOMAIN us_postal_code OWNER TO test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
DOMAIN' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='ALTER DOMAIN'
objectid=25085 schemaname='public' objectname='�'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='ALTER DOMAIN'
objectid=25085 schemaname='public' objectname='�'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
DOMAIN' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER DOMAIN

When an ALTER EXTENSION fails to upgrade, the AFTER ANY COMMAND
trigger fires, but not command triggers specifically for ALTER
EXTENSION:

thom@test=# ALTER EXTENSION file_fdw UPDATE TO '1.0';
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='ALTER EXTENSION'
objectid=25087 schemaname='<NULL>' objectname='file_fdw'
NOTICE: version "1.0" of extension "file_fdw" is already installed
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER EXTENSION

Same on ALTER EXTENSION, when failing to add a member, the BEFORE ANY
COMMAND trigger fires, but not the one specifically for ALTER
EXTENSION:

thom@test=# ALTER EXTENSION file_fdw ADD COLLATION en_gb_test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ERROR: collation "en_gb_test" for encoding "UTF8" does not exist

Specific command triggers against ALTER FOREIGN TABLE (i.e. not ANY
COMMAND) for BEFORE and AFTER aren’t working when renaming columns:

thom@test=# ALTER FOREIGN TABLE test.dict2 RENAME COLUMN word TO words;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
TABLE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER TABLE'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER TABLE

Specific command triggers agains ALTER FUNCTION (i.e. not ANY COMMAND)
don’t fire for any changes except renaming, changing owner or changing
schema. Everything else fails to trigger (cost, rows, setting
configuration parameters, setting strict, security invoker etc.).:

thom@test=# ALTER FUNCTION test.testfunc2() COST 77;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER FUNCTION
thom@test=# ALTER FUNCTION srf_test() ROWS 5;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER FUNCTION
thom@test=# ALTER FUNCTION srf_test() RESET ALL;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER FUNCTION
thom@test=# ALTER FUNCTION srf_test() SET work_mem TO '1MB';
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER FUNCTION

There doesn’t appear to be command trigger support for ALTER LARGE OBJECT.

Specific command triggers on ALTER SEQUENCE don’t fire:

thom@test=# ALTER SEQUENCE test_seq OWNER TO test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER SEQUENCE

Specific command triggers on ALTER TABLE don’t fire for renaming columns:

thom@test=# ALTER TABLE testnew.test9 RENAME COLUMN stuff TO things;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
TABLE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER TABLE'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER TABLE

Command triggers on ALTER TYPE when changing owner produce a garbage name:

thom@test=# ALTER TYPE testnew.type_test OWNER TO test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER TYPE'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='ALTER TYPE'
objectid=25170 schemaname='testnew' objectname='�'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='ALTER TYPE'
objectid=25170 schemaname='testnew' objectname='�'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER TYPE'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER TYPE

Also renaming attributes doesn’t fire specific triggers:

thom@test=# ALTER TYPE public.type_test2 RENAME ATTRIBUTE a TO z;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER TYPE'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER TYPE'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER TYPE

Specific command triggers on ALTER VIEW don’t fire for any type of change:

thom@test=# ALTER VIEW view_test OWNER TO test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER VIEW
thom@test=# ALTER VIEW testnew.view_test2 ALTER COLUMN id SET DEFAULT 9;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER VIEW

Specific command triggers on DROP AGGREGATE don’t fire in the IF
EXISTS scenario if the target object doesn’t exist:

thom@test=# DROP AGGREGATE IF EXISTS avgtest2(bigint);
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
AGGREGATE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: aggregate avgtest2(pg_catalog.int8) does not exist, skipping
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP
AGGREGATE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP AGGREGATE

This appears to be a general thing, as the same occurs for DROP CAST
IF EXISTS and DROP COLLATION IF EXISTS. These do, however, fire if
the object does exist.

When adding objects to an extension, then dropping the extension with
a cascade, the objects are dropped with it, but triggers aren’t fired
to the removal of those dependant objects:

thom@test=# ALTER EXTENSION file_fdw ADD TABLE dep_test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='ALTER EXTENSION'
objectid=25207 schemaname='<NULL>' objectname='file_fdw'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='ALTER EXTENSION'
objectid=25207 schemaname='<NULL>' objectname='file_fdw'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER EXTENSION
thom@test=# DROP EXTENSION file_fdw CASCADE;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP EXTENSION'
objectid=25207 schemaname='<NULL>' objectname='file_fdw'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP EXTENSION'
objectid=<NULL> schemaname='<NULL>' objectname='file_fdw'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP
EXTENSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP EXTENSION

Using DROP OWNED BY allows objects to be dropped without their
respective specific triggers firing.

thom@test=# \dt
List of relations
Schema | Name | Type | Owner
--------+-----------------+-------+-----------
thom | role_test_table | table | role_test
thom | seq_table | table | test
(2 rows)

thom@test=# DROP OWNED BY role_test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP OWNED'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP OWNED'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP OWNED

Using DROP SCHEMA … CASACDE also allows objects to be dropped without
their respective specific triggers firing:

thom@test=# DROP SCHEMA test6 CASCADE;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
SCHEMA' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP SCHEMA'
objectid=25250 schemaname='<NULL>' objectname='test6'
NOTICE: drop cascades to table test6.test
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP SCHEMA'
objectid=<NULL> schemaname='<NULL>' objectname='test6'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP SCHEMA'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP SCHEMA

Command triggers on all DROP commands for TEXT SEARCH
CONFIGURATION/DICTIONARY/PARSER/TEMPLATE show the schema name as the
relation name:

thom@test=# DROP TEXT SEARCH PARSER testnew.test_parser2;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP TEXT
SEARCH PARSER' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP TEXT SEARCH
PARSER' objectid=25265 schemaname='testnew' objectname='testnew'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP TEXT SEARCH
PARSER' objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP TEXT
SEARCH PARSER' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP TEXT SEARCH PARSER

Still no command triggers firing for CREATE TABLE AS:

thom@test=# CREATE TABLE ctas_test AS SELECT 1::int id, ''::text test;
CREATE TABLE AS

Or for SELECT * INTO... :

thom@test=# SELECT * INTO another_test FROM ctas_test;
SELECT INTO

Regards

--
Thom

Attachments:

command_trigger_test_setup.txttext/plain; charset=US-ASCII; name=command_trigger_test_setup.txtDownload
command_trigger_regression.sqltext/x-sql; charset=US-ASCII; name=command_trigger_regression.sqlDownload
#54Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#53)
Re: Command Triggers, patch v11

Hi,

Thanks for the extensive testing. I'm adding your tests to the
regression suite, and keep wondering if you saw that lots of them were
already covered? Did you try make installcheck?

Thom Brown <thom@linux.com> writes:

Creating a command trigger using ANY COMMAND results in oid,
schemaname, objectname (function parameters 4 & 5) not being set for
either BEFORE or AFTER.

Yes, that's documented. It could be better documented though, it seems.

There is no support for ALTER CONVERSION.

It was missing in the grammar and docs only, added.

WARNING: CREATE INDEX CONCURRENTLY is not supported
DETAIL: The command trigger will not get fired.

This should probably say that it’s not supported on AFTER command
triggers yet rather than the general DDL itself.

Edited.

Command triggers for AFTER creating rules don’t return OIDs.

Fixed.

Command triggers for creating sequences don’t show the schema:

Documented already, it's uneasy to get at it in the code and I figured I
might as well drop the ball on that in the current patch's form.

Command triggers for AFTER creating extensions with IF NOT EXISTS
don’t fire, but do in the ANY COMMAND instance:

Fixed.

Command triggers on CREATE TEXT SEARCH DICTIONARY show the name as garbage:

Fixed, test case added.

Command triggers for BEFORE CREATE TYPE (exluding ANY COMMAND) don’t
fire if the type isn’t created due to an error:

Per design, although we might want to talk about it. I made it so that
specific command triggers are only fired after errors checks have been
made.

That's not the case with ANY command triggers so that you can actually
block DDLs on your instances, as has been asked on list.

The ANY COMMAND trigger fires on creating roles, but there’s no
corresponding allowance to create the trigger explicitly for creating
roles.

Roles are global objects, you don't want the behavior of role commands
to depend on which database you happen to have been logged in when
issuing the command. That would call for removing the ANY command
support for them too, but I can't seem to decide about that.

Any input?

Command triggers for AFTER CREATE VIEW don’t show the schema:

Couldn't reproduce, added test cases.

Command triggers for BEFORE and AFTER ALTER DOMAIN show a garbage name
and no schema when dropping a constraint:

Fixed, added test cases.

Continuing with this same trigger, we do get a schema but a garbage
name for OWNER TO:

Fixed, added test cases.

When an ALTER EXTENSION fails to upgrade, the AFTER ANY COMMAND
trigger fires, but not command triggers specifically for ALTER
EXTENSION:

Same on ALTER EXTENSION, when failing to add a member, the BEFORE ANY
COMMAND trigger fires, but not the one specifically for ALTER
EXTENSION:

Again, per design. Let's talk about it, it will probably need at least
documentation.

Specific command triggers against ALTER FOREIGN TABLE (i.e. not ANY
COMMAND) for BEFORE and AFTER aren’t working when renaming columns:

Specific command triggers agains ALTER FUNCTION (i.e. not ANY COMMAND)
don’t fire for any changes except renaming, changing owner or changing
schema. Everything else fails to trigger (cost, rows, setting
configuration parameters, setting strict, security invoker etc.).:

I kept some TODO items as I feared I would get bored tomorrow otherwise…

There doesn’t appear to be command trigger support for ALTER LARGE OBJECT.

Do we want to have some? Those are in between data and command.

Specific command triggers on ALTER SEQUENCE don’t fire:
Specific command triggers on ALTER TABLE don’t fire for renaming columns:
Also renaming attributes doesn’t fire specific triggers:
Specific command triggers on ALTER VIEW don’t fire for any type of change:

Kept on the TODO.

Command triggers on ALTER TYPE when changing owner produce a garbage name:

Fixed along with the DOMAIN test case (same code).

Specific command triggers on DROP AGGREGATE don’t fire in the IF
EXISTS scenario if the target object doesn’t exist:

So, do we want to run the command triggers here? Is the IF EXISTS check
to be considered like the other error conditions?

When adding objects to an extension, then dropping the extension with
a cascade, the objects are dropped with it, but triggers aren’t fired
to the removal of those dependant objects:

Yes, that's expected and needs documenting.

Using DROP OWNED BY allows objects to be dropped without their
respective specific triggers firing.

Expected too.

Using DROP SCHEMA … CASACDE also allows objects to be dropped without
their respective specific triggers firing:

Again, same expectation here.

Command triggers on all DROP commands for TEXT SEARCH
CONFIGURATION/DICTIONARY/PARSER/TEMPLATE show the schema name as the
relation name:

Now that's strange and will keep me awake longer tomorrow.

Still no command triggers firing for CREATE TABLE AS:

Yes, Andres made CTAS a utility command, he didn't add the code that
make them fire command triggers. I would expect his patch to get in
first, so I don't expect him to be adding that support, I think I will
have to add it when rebasing once his patch has landed.

I'm not sending a revised patch, please use the github branch if you
want to do some more tests already, or ask me for either a new patch
version or a patch-on-patch, as you see fit.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#55Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#54)
Re: Command Triggers, patch v11

On Monday, March 05, 2012 09:42:00 PM Dimitri Fontaine wrote:

Still no command triggers firing for CREATE TABLE AS:

Yes, Andres made CTAS a utility command, he didn't add the code that
make them fire command triggers. I would expect his patch to get in
first, so I don't expect him to be adding that support, I think I will
have to add it when rebasing once his patch has landed.

That was my assumption as well.

Any opinions about adding the patch to the commitfest other than from dim? I
feel a bit bad adding it to the in-progress one even if its belongs there
because is a part of the command trigger stuff...

Andres

#56Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#49)
Re: Command Triggers, patch v11

On Sat, Mar 3, 2012 at 2:25 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:

Right.  What I thought I was agreeing with was the notion that you
should need to specify more than the trigger name to drop the
trigger.  Rather like how you can create a trigger AFTER INSERT OR
UPDATE OR DELETE, but you don't need to specify all those events to
drop the trigger -- just the name will do.

The parallel between INSERT/UPDATE/DELETE and the trigger's command is
not working well enough, because in the data trigger case we're managing
a single catalog entry with a single command, and in the command trigger
case, in my model at least, we would be managing several catalog entries
per command.

To take an example:

 CREATE COMMAND TRIGGER foo AFTER create table, create view;
 DROP COMMAND TRIGGER foo;

The first command would create two catalog entries, and the second one
would delete the same two entries.  It used to work this way in the
patch, then when merging with the new remove object infrastructure I
lost that ability.  From the beginning Robert has been saying he didn't
want that behavior, and Tom is now saying the same, IIUC.

So we're back to one command, one catalog entry.

I hadn't made the connection here until you read this, but I agree
there's a problem there. One command, one catalog entry is, I think,
pretty important. So that means that if want to support a trigger on
CREATE TABLE OR CREATE VIEW OR DROP EXTENSION, then the command names
(or integers that serve as proxies for them) need to go into an array
somewhere, and we had to look for arrays that contain the command
we're looking for, rather than just the command name. That might seem
prohibitively slow, but I bet if you put a proper cache in place it
isn't, because pg_cmdtrigger should be pretty small and not updated
very often. You can probably afford to seq-scan it and rebuild your
entire cache across all command types every time it changes in any
way.

But just supporting one command type per trigger seems fine for a
first version, too. There's nothing to prevent us from adding that
later.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#57Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#54)
Re: Command Triggers, patch v11

On 5 March 2012 20:42, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Thanks for the extensive testing.  I'm adding your tests to the
regression suite, and keep wondering if you saw that lots of them were
already covered?  Did you try make installcheck?

Yes, but I felt it better that I come up with my own separate tests.

Thom Brown <thom@linux.com> writes:

Creating a command trigger using ANY COMMAND results in oid,
schemaname, objectname (function parameters 4 & 5) not being set for
either BEFORE or AFTER.

Yes, that's documented.  It could be better documented though, it seems.

Is there a reason why we can't provide the OID for ANY COMMAND... if
it's available? I'm guessing it would end up involving having to
special case for every command. :/

Command triggers for creating sequences don’t show the schema:

Documented already, it's uneasy to get at it in the code and I figured I
might as well drop the ball on that in the current patch's form.

Fair enough.

Command triggers for BEFORE CREATE TYPE (exluding ANY COMMAND) don’t
fire if the type isn’t created due to an error:

Per design, although we might want to talk about it.  I made it so that
specific command triggers are only fired after errors checks have been
made.

That's not the case with ANY command triggers so that you can actually
block DDLs on your instances, as has been asked on list.

I don't have any strong feelings about it, so I'll bear it in mind for
future tests.

The ANY COMMAND trigger fires on creating roles, but there’s no
corresponding allowance to create the trigger explicitly for creating
roles.

Roles are global objects, you don't want the behavior of role commands
to depend on which database you happen to have been logged in when
issuing the command.  That would call for removing the ANY command
support for them too, but I can't seem to decide about that.

Any input?

If that's your reasoning, then it would make sense to remove ANY
command support for it too.

There doesn’t appear to be command trigger support for ALTER LARGE OBJECT.

Do we want to have some?  Those are in between data and command.

*shrug* But ANY COMMAND triggers fire for it. So I'd say either
remove support for that, or add a specific trigger.

Specific command triggers on DROP AGGREGATE don’t fire in the IF
EXISTS scenario if the target object doesn’t exist:

So, do we want to run the command triggers here?  Is the IF EXISTS check
to be considered like the other error conditions?

Maybe. If that's expected behaviour, I'll start expecting it then.

When adding objects to an extension, then dropping the extension with
a cascade, the objects are dropped with it, but triggers aren’t fired
to the removal of those dependant objects:

Yes, that's expected and needs documenting.

Using DROP OWNED BY allows objects to be dropped without their
respective specific triggers firing.

Expected too.

Using DROP SCHEMA … CASACDE also allows objects to be dropped without
their respective specific triggers firing:

Again, same expectation here.

If these are all expected, does it in any way compromise the
effectiveness of DDL triggers in major use-cases?

I'm not sending a revised patch, please use the github branch if you
want to do some more tests already, or ask me for either a new patch
version or a patch-on-patch, as you see fit.

Hmm... how does that work with regards to the commitfest process?

But I'll re-test when you let me know when you've committed your
remaining fixes to Github.

--
Thom

#58Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#57)
1 attachment(s)
Re: Command Triggers, patch v11

Hi,

I believed I fixed all known glitches (all were minor quibbles here and
there because of me trying to work too fast), all I know about remaining
in the TODO list is passing the arguments the classic trigger way, I'm
waiting on a "go" from Tom on the way to do it, but well, will try on my
own if that's how busy he is (I just had enough on my plate to be able
to wait for an ack from him).

Thom Brown <thom@linux.com> writes:

already covered?  Did you try make installcheck?

Yes, but I felt it better that I come up with my own separate tests.

Ok, great then :)

Specific command triggers agains ALTER FUNCTION (i.e. not ANY COMMAND)
don’t fire for any changes except renaming, changing owner or changing
schema. Everything else fails to trigger (cost, rows, setting
configuration parameters, setting strict, security invoker etc.).:

Specific command triggers on ALTER SEQUENCE don’t fire:
Specific command triggers on ALTER TABLE don’t fire for renaming columns:
Also renaming attributes doesn’t fire specific triggers:
Specific command triggers on ALTER VIEW don’t fire for any type of change:

All previous cases and those are now fixed.

Is there a reason why we can't provide the OID for ANY COMMAND... if
it's available? I'm guessing it would end up involving having to
special case for every command. :/

It's not available where it's implemented, to get the OID you need a
full integration into each code path and the idea being the ANY command
trigger is still to be able to catch more DDL than we are supporting for
specific commands.

Roles are global objects, you don't want the behavior of role commands
to depend on which database you happen to have been logged in when
issuing the command.  That would call for removing the ANY command
support for them too, but I can't seem to decide about that.

If that's your reasoning, then it would make sense to remove ANY
command support for it too.

I did that in the attached, version 14 of the patch.

There doesn’t appear to be command trigger support for ALTER LARGE OBJECT.

*shrug* But ANY COMMAND triggers fire for it. So I'd say either
remove support for that, or add a specific trigger.

I tried adding that, but it's implemented in catalog/ and my tries at
having the catalog include file include the commands/cmdtrigger.h file
have been failing. I don't know how to implement that, dismissed for
now.

[CASCADE will not run the command triggers for cascaded objects]

If these are all expected, does it in any way compromise the
effectiveness of DDL triggers in major use-cases?

I don't think so. When replicating the replica will certainly drop the
same set of dependent objects, for example. Auditing is another story.
Do we want to try having cascaded object support in drop commands?

As the current code is not going through standard_ProcessUtility() in
such cases, I don't think it's possible to do for 9.2.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

command-trigger.v14.patch.gzapplication/octet-streamDownload
��xVO�]ys�F����='���m��eU�$���"�J��K�@`Ha
�L6��u��0<$�I��*k	s�t�����g�}��9q����O��t�'t����$i��%Owww�x]���|&���c�O���!�(	�4#����;)��$�����#AD�� %~�P/���H� ���SBN�r�������8�H�gp3:�d�U1���J]��&�t������`���u�:7�0t� ��=Y';���y6s#��)Mj�Y�(�����h�
�$d���-7s�nJ��}_�-�K'nfWIps5��������j )��qr`��|&x�Z'�j��
Y�m�0�Z9��I�M��Do��w�qR��'��(��:�H*Tj4V@��)a��}VD�x��Yq>�[����7��v�9������O�y�(.�j%4�A	��K�����G��
X��FP��� �)���Ja �E�t�;��������
�����(Wl=���LH���uxp����e���/�����1�����26WP���S��7�'�������:�f�����������g����h����cd!=c���FD���r#�4p�Q>;{u�_<(�� ��h�!����F���R2�)
�2���m_cN2�3�we,X�)�V��y2�Sz���)%�[Q��,j�}�����F���O?�,��oqD�m����l�(�-��H37Y,cWV��m]�t��4H���T��<t=����i�n������A��h��8Cc���@���V���������Z2���K����*��&`
��oiB%����)��l|�)���`�}�)=�~�o���s�i7mQ�jD7�M@��U�U�2`���oV.��N?~� �@#�C���U��U=��Q�B�
���
�s�����s\_���b��B���T0�li�j�WnJ�`�i��'.����A� ��^��=>���6�M��Y��9P�S����{�`�Z��/��������;7	p2q��Z���e@	����'�	#�d��������Wo�Y���=�_'p�_�t�*�'�hZ�����+Iq�����h�(���4�����c*#JUC��q7<Ki��<�`��8�(4oA�Ez�~DY��.$����q->�i���G�~�e�o(����3ia��Tx�3th����h�A�0�kr�F��g�9xA�nI�ft6������3�cmt�W��}Aqg��p7t�WD���������iA��8���c�{����K��U�O�i
K��'{�f����e,~�m&�/�w��9���ob���!�{#)K�'�y
�]�{M�u
���4�#�Yd���.��a
���%�}�����l����a_\���l��i���^�c��n�����T�[�K�].r~:�ZC���E����M~��WWN��`�%�����������E�e���^�XH�{0xp��}���l\7h~�\��"��k��t*��*�K>]BK���'��}q��O����f���7K�>�����sy���D,�r��U��5�%�0���E������(hv�CEH,�*0��/��,B����MQ^�(��S��g��>K��PQ������E���
j+�%lV�J���*%L��2(U��JQfx.
�j�4�)!�P_��lQ��,,�)J�5<[��U
��P#�@QV��JK0d%U��,����+��ce:�XAz���RY	v���X�9�\/)�M�i`c%5������Z��j�����xQ/�l+������i�y����y}}����V-�@�tscS��3���Y�� ��%���CQv�zs���$�3�OEhJ�<����JzQ���U�xW"��������$��&�����t�
���Lp��t��8!��oZV�S�A��������B`L��>	��$*rwq���
G!�x����������~��k`� ���c�2�2��J7f�����4���j��t2�"�3(��m�-����b���S�w��9G&���~,.'����������_P?�f�e�5N�MO=
�2�aw>1�sJ1�I�O�����|�$��8�df�$^����"�o
�����3�HV���t��I4CPa,����'Lq�I!��gNW[��/���w�Th��6����p��|a�'I<ST�Rfx!�X.j	v��!�{��a}!Rq�{�t�����!���|7�}��)h���������a#r�l}�f�"���<K}b�Q��P��2#8����%�&�����{�9�t�O�xp[�`�ge88�?���c�:x|^��
&��Y���:���� �E���R����a��k���/��G���2wZ;?��f3��|�J����ho�e����\@��Z��%,2���1����a�����$����#24s8S���[,�OkL-����M)��0��������
n���E[/��"�^�9h<U�Pix��t��2���iu����4��t-0��}��DrO���.�P��t�b{���
�[IR�[�9LKT���(5�/��� !�}�Q��5�3P�����Y������)�b�n�m��`BK��pZ��T�H8T�
�2D-^G�������s�D�T��OX���8�]���E�%4t���@4�Ku]^���*u�����;�6��6+,��>a`U�`�S����P�Q�-��tzM��>V1qy���|���,��AZ��K9w���J��4��=M�-)i�^�����N;m�H���	@yB������F��/pB�O6��%���YvRg�,9�	S���W,�c��X�`���Z����02XE�L�����z�&�������m���R����A�=��a���Gm>-������X������_�7$��NG3r�D����@�����3��?i�y8���8��������3�������������������&�8�!���Gcz����GS����u|�f&U�.N�b�K?���!p���o���`\�!N�����@GO����~��p������"y"|P�8YP���(R:�&�"M"7��w���y��&���n`|^�%��H��K��'1��2)T�<*-���#�g4vJ0��Z��d����l}�,V���6\�Z;���������< 1q�(�7F�8rd���������%�
2����L�6�0�C{�/����)�5�������z
�<�+��y�I�u\a<]����;��PuS�Id��0R�fh�ZEj���e)v����<�7���Xylj{v�h�u�]@M�����|]=���9c��-n�&�2f�kb�k�
����a�lxS��8�vwq���GW@EI�(]���8~=��@���<�T��'�\�/!j�4z}v��ng�<s;J#m)5�5�7�G��M,��P/U����������N
��?���{@�=�f,�`"����N��cr[���l7��$�6<
��B7���_���}��Z����9��7!�[5�5vpH�KSU<'�h�
������b���#p��p�f�;xF��Xl�o�5������2<

V�xh������A�X��Aw��%�mw<��-��_�d��w��w�<��A+���0�y�����%_o���e�a�����>j4n��!�n�9Y��c�<��h�`=�*��s?�E�eJ�����S�K�h1;�/�.�g�F1�US��r�����_xG��������k/��X/�[�H?k����_�c�!�C��!��Z|�����qa>3��l���=`�A��t�
�0������OI�����	��J��w
7x���`��`F��\����������U����//�;����qW}�,~�E`QXE�s;?��,��_��Y�����k���%��O��b��������&���X�z��P���S}��<_��������6��>��4�i��$�g�K��{E`_(K?p�=x���b��i�3`\��n��O��s�Sf��
Z�:?�y�ILL��\�h��:�EO�U�U�<���r��x{�c�u4�*��2��(��D3�\t���I[�h�����,���*M���"�h)�L^�	���V9~���{7n�+�	i�V�$^��Qda�n���H�_.���*�F |�R������A!�#�E��v�J?��q���W7����m�n4�|���Moa
(^Y/_A>���o� ���$o��C
�����\Z�ze��������
���`��;���xk����ux�F��N��C=>���gUYZD��%1}.��A���\�[/��#���6%�/@��]��UYxYUVZ���}�RcM|��Q��
�uI����+����K��t�)b�R�������1���x��x�c<~�n�CtN����I;t�!�[x�
o��<���2
�e��)Ia�'���_!7�;���}3����^g�D�=[��\yk���}����z�-������$�%Om�
$�y1)M��G'h���jYp���r�,�>t���(�,��D�W(�#���
�����fc�K���O��P��Nxe�Iy��"�d_rz����V������qgx1x��b��{���3��7���n��{h<j��m�6�Y�-��D�<;<��G<�H�-��j&n��W�z����Y�W������xD��/���~����D����e��Z��iJ�������)���_�es���&V���Hqj��p��i��<��nO�/�?�gXt�&�����j��
������=����<k��?C|���x>�� �a#��>U#Y�����#7�07{�����|�@>|���"7�x�������B6`	�����x.���i��f���S�c�&jqk��J�+�H�+F���<�$�� � -
���C���@VO�\��[�1�%?�'a\��P����c�
���k*8Q�����Z��it`�
|!*�=���Plz�Oey�"B��%��>�����B�rs�^L[�y+LR���
8����'|T!���C�$9[K[��6�����Z���4��>7h3��nV,�-��N����JI'�����J��Q���c�bE5�3�f<*��Kx��+�����g�������M�$��e�9<ye���ek��/���D�f����|i�4����m�w\&�qB�i4��&}q���[O���t$^������}��E�z/���=�f�N�#��.��r��f[�qrd�h�Do������@�c!��PK���g�i-�7g2����h���)��^��9x	�9x����[4�H�!@�d�~B��<�x�9�W����d�!�� �?x���vm������I���B�_�}q�iW\�m���OA��e�xMH��������#=�q>|��::�_r������ki�v���ut$?�K4�
.8o�W'^���M�/tY�_��I��`����v�b�$��Q���,��UE����x}�D^�\���/��_O��O�#;"O���.���$�@?���)�bk�MS�S�|G��0���I��e!����)�����'jl���@]��a� �����BH���;�q;
��7���_,Q?=Zv�`�g�����x?t�]����.D=9��-�{���~�Y}a�|q�l�"e�hL�A��
)���v'��,,fbF���u�{�ZlrJ��s},�ZD?f
���0���Wj���xo��wv���������n��T�C�I��D��2M�������gb4B����:Dv��3���!c��7x;�l���K����{�eS���kwo�v��w�@�;��v��M��7���5��i�0mk�4�6(:�;�������P]IN7oM�]q
�G_�	�gk���j�4-8��������Wa��K��G	��i�F�{�a��[ZE���^�"V�<V\J�F��������G�V �oi{�rY��8$� �o��;�"}N�7�<h%��p:�����{����q-
V~��kS1"�~�O�F����*JI��}�@$$q�"��������f�R�,w��lGsY�f��/����g������S'�j�3�(Fw���������;8��K�(��� �-�/\a������g�WF�3
u����2�a�F�5��l
a������Lv�z��������X�M��q���K���$0G�q�V��{�Z#��y7&A���<D��j��1�+V�6���e�s$�4�G�R�a��}�����L����JZ'���YL�0��JZa��xM4��0o�fH;]�|����;iJ��Z9f(��bU�s��s�n����W����g��g�oG�N���?�-�S��]��
�-����0��|�������$���$T�j��8q��r�������m����;���@�����3@�����<�D�!��r2�+��j�i�Sz���'���j�][�*�  Ckdd/������4;h�����Q�(����V��2a� ��v����U]<��������x�����VL�)��r=��e}S��a����{���[��aT�$I�����;N�����oI�-F���sP�"��3{"�6�xp�P\8O�ge1C�����N�t����;t_�NNBb���O����cX2�u���-�����I9�����u2A��q�%�y)�kx�;��;�i�e���{+U;��-1��h�� ���v�r�
Ko�5y��I�Y�}����V}�MvP���n�)'e���b�J��NTsPD��(�m0�����=��K��*�5&;@t@@q��]z�ZcW�WHh�c��x�;������}1�S8����������������l�������\�.��z�U�jS3��V�������&��F�fh7���n]�X�7�F�1-jG�~yV��lI���2Y_������nUxtJ3+O�&�=�)&����w�r��[�h^>���F����`&q���4�FS��o*��t���s��p��0�W�"c<�;�)�1p�uX�F���F�I����]/q+���GB9�h�����7xy�f�c����*E��+���9Fzho�D5=���C�m]��
k������h�=���M�p��s�$��A[Y.)>�AS��;$�]^xjR3��~���k_�)�!���8>:K��H*
}�P���3��|�c����_��|H����z����1��&����,Y���[��:9���P<	$���� �3#5���r�4	N*�u �LW�S��UzN��9�$��6��^�$���_)���c�vz�}��u���}��P\���t;q�y�Qy���w���EWAVM=�m��M����R��v����r��I1v�������
���J(���:�)����S�{���*�l���8�wL���H���?�>��s%�+������~�7�T�h��z�M
�o�K��,g!�h
8Be@��yRAcvSX��d.�
b4_��&�2�l�c��	���-�uT����o��� ����8C��EM��`��@b�����?���(�	���q�\�[�Y�[��"M_�w@!R��V�R�R�V�9�.}�W�#���j���U�|�����*������,�R��p�'%Z���+���^��9��g��L�_�{�m��c�o|;Iu�������b�)�&���Wv�U]�? ��p�`�@=�n4�k��j2��W3���6������s��{N�]�#!��"���wcr���	7r�����~o?�4��"J��XQ=f��L��!����V/���FS�I��?Y��:����O�a
��bW8x���s��~��Gl�f��So����n� �u���F�{kX���dM=�c*�{���}tC)�����w�A��{�8[1r$�t8��E�-X��F�Z���4[��
8�/U~?
�C�j���^������E�U~�:���p�'�O�g�" ��)-%����q�b��M��ar��nbu���Fo�q�!����{�l�%Z��_����������d�A^4��M����z�R�[��S���>$������r������[w �W��[��r������@�"h����*����Nf�7>��`R����ya��)�-�ti�c^��Hj�*j-f=���sa�������+��6�	�5�R)�����
�K���9��S��]�����s�Ps�����4U9��si���qk�����A?WG'iB~��7]a�{��,���4 �|�c��"�M����1��:�z��H�� ��4�n���i
W�'oQ��i�������5�.��P��.S)�����W��g�FRl+nU���#�5��aZ.�Z�Jv�q�����S��t��|�?��xFZ��7��/�%�����7i���a�'��Jj�y�	�O�P�"��^���[������ju�[���7T�����g�<���AH��6$��5d�G�x����^_���QJ�{g���Dz����\!����:���1�GC����*TF}64�u���l�
�V$���P�{�� ����������~3����������\���=�N���10Y�w��71�G�jT��
������h#��d <�>��0�\���,�v��}!��3���4���)F>���X�� ������Y���H4��Y�w �����*y����CI����d���%}�lcZ������� +4dpv$�x��
h�I3F?]#�\�Y���F�I7�$rm�����-�5�)��$����G��m�����lV��?CJ�����d|�NNtI]���j?���I�C�9�K�Y����	����#Q�G�K���� ;�1f�[Y.R�������"/���S�H�8C�bu��m�kn�,��mA�*��V��8.��?
�>�������������lO
�J@��2�6
C`0��n��[�$�WUar��o��f�[fVMXn(��B�_bQ���"���������u��?!��!���1���
M,Z������rL�g�����-i_���w26�C���X��z��b���JFoI�
���<���&:�}��3G�CI4��z�1�Y���V]�i<��W�u6���Y*��'�;m�p�V�������l&|3Q0Z�0��M��v�hw>%���dGh�V����;R�/�H������G�D��)b�1������\���e�]�}���bq�Z�����|f�6V6��V�3��r<Y���X	�3��-n�����?���Ny&�p�39�>Wef��)�KS�p\�������_���?|�+�������w2N�p�)�8}hN��CH����Cy�4g�@�@�?��b1��<��3�w�mJI�h����
eM��ZPh�x���O)���$������X�L�[�rC�]�"��� ��1YMm���vE`��^e-����z(=f��5����R�kR��6���D�]�����0���}�JX�2!�RK�0!�}��Wsa�c�&_�M�83D��������0���F]�>��X[���� ��p�-B�4��8B8�%��8[���������)G�O�_D��K�������sC������7�-�>�H����v����r��V��������\�h��[�#\Z�J{��|-%	t����]��z��<G��<����H=Q�p_��W07W����'��^l��mk���T�>��&�6��AD�E	������j��^gEu+Jo����"ST��e��x'h�]a�*���!k�]�Uo�u��������O�,�����)+���p��1$W��|��B�/��o����=�P�b]�O���c�+�S�J����Mg"n��]�������%���)��3�+�
��(���TDz�������f�c���t�$�_�|�����J��A� �b��r�e�Kj/B���Y���>'�EHu}���G�D�	��3�<blW�S�_�t0�E���y��J&�e��+��D�|�L4�s�n���ce������D��s�BO�A��uY<��!�+�
k-�Y��R���4���?��C�W�:e����Gj�t=������c&�
����u�/������vL#.J:�C)�u�5:?��w6H�.��a����$�����B����&��l��-D�1�������N�$�z;��s"�k��-�4��v���{5�m�j�����5��`6��l�a�/�g��;��X�������l����K���+1�u���l���� �V���&�:������7�t�x�"���M���?�T��%l>������_��b[��,�d�x�� �m�dQ:��,<�v���p�ci�����G�����F{;��[!���{�����x ��Bp�{&�.t�6�SQ��JWQ)7�J}��M�����/��|��$���������4J����Iz��9p?\(���1/�
�k��5J&E?W�
������N��GD��g�&�?��
oa?�kgT0��>�=���8K3�2?�r�A����{dAt�1G%SU�����t�+Fe��?kp����#�@�d�cQ|���=��d+��p�������^5
t��j4��4iJ�w#}d}���	T����9-�P��HEW.��>�2�X	V��E���P��y�5LBpO8lm -�V<�����ab���K��d�d@���~2*��R|H]8#�l�?�7'���#�����s���E�D1���[-�����G�>����p#"��q3RTt����U�H���[&N��/N�6�dl�P9&WVv����;1;��Z����$���K��~��b�{��%Xp1����8��
�\���$�;�A�����WQ����Z���[n�VLs�{���)�m,�m�m��s�~
��Xn����!������l�����q�(�;1��1�*%���^�*����\'Xhy4���$���?��7�'*�F��|����P4X,�����"���JQ�����@��m2�0�*E�p�	'�(�i�����#�������i�SL�**�8:���t�D�9���g��V����[�����a��^���3��O����c��h<�]��@S��0��&�'���iV{�ab���0��*��5���E��
��C�v,j��[k4��}���
O9�$�5x`J�-�H��!���gR�������ly;d���)(�z&��	�����<��0	�7�?�	_�8��Y����TK�	"qT����
)�d�p��L6l�W:n�Z�&����W��z�H=��c+p
�
K�z��\����7��U������;���W��%*�,���(O�x�+�c�������m0�4��E�����X�X%a0Fe�<U��_�b�!�������xa���3am���e8���@������p�Z���|������Ec$T�y��h��y1l\�b�2$"����T"N[������0����H�46Z�LXSD��G�P,��p�)0�L�Mo�
���kz��JN��zN�H�����|X�I����(+��i�6Wg�������v4�y{�-u�6��wy���\���5p��&<^� ��#����q�3���������Xg�{�7��F��6*�b��[��N�K�5�X���������X�@kIIJ�@q����{y�>F'����G�lCH��+S�:�d�=�:���H���m�-�d��V��^��b��
dkl�6�b�0�vpd[N��J�m������)�)B������K�P���k���f]��X��6���C!	��NG$��bGr��m�
�D8Pa�0�5���7�z��g��mZ����n�
{m<">(b��xX���,�g��!��l�R.c�A��0�$���n���p��-&�?�d*Wk�d)A����C���1	�Y&�y0?�?��n_{~���w@Iu^|�V�X��X�I�q�h����S����~VF�
h��,����w��0��rF��	.%�`��R�]N9y e!���KI�@��~S��ct#�������[ik���|j�yHL�
�v��t����V�N��)�1���Z|
��'�t��j2�*?8��z2Y�u�Q8
".��'t��*��Ja���~)��b�?�r����#�g<�hH��������X<��7y�)����]���������1W�}�;��a�����uO�X����Y��U��|�h���-�{��"4���yx�%g�He�:]�7]���?�K���<^�z�'�����O����cl��1��'ge�u��*�I0OLD��M��s�*����[5���01M����
���l�iZ�uI��)��Z2y��S?��M�_���f�9s��o'��w-,����O�y���1��Da8����y��kF�.��[�V�����z��X���5��"Q�Ev$
�p�0z8��C1	�$f��Sj��h��a~	�����v��iZ�v/��j~�*l������He:��lhX����m�����c��%�;A�e��]s@@�>��`IUp)�zcYKh��_������w>���}x�`9���.���K��b/���#+��N}��Z/��z�eX�e�SB���-tC�v���DO6G���Tb�Z�������M��`U���.&�������p���W��@i��^��U��
d���3��w�-���'~��w��+2��a��z�h$	C9�g��������7pt��b�{+)��8I���i��/���{?��gp~������O�6��	��x����{��h���It��a&�HP�;��~�	E�wO�j��J�2��@�J^�e������� ��N8�/FW%x|x!Q@XM�C
v�����D��n���������<�Y��c	
�)�u�P���D��>�XC����;x�'Q���t����"7Y
�0��j����x'�
3Qc`����/� �5�z%���=	���6�`���*<ko���7���������R�89^�m����.�o��?�
��>��at��>v�>tO������$cQ�����v�����(��S-W@��{O)���8��#�yl�\����wr~���O��V�R��]0�1��y)!�4^y�,�AJ��&����}�EQ:��c(���z����Z����2{�[�'��+�3M��>��L���z�k$��61�,Z�dta��o4�F�q����?]��eTi�K������WTxO���o��������{�U�x�(���&��^]�.x0�o���V�^�qW9i�y�Jy�� ��3�ptE�6�����������LQr���`�5���s�MB�;�f����'�b���G���� �"���P�x�8��?�����P�����	�P��c6��

���D�
?P�phF������s��"5�0��F�v��b�W(S�C�����1"X�%��1gCW��=�P�I<i<,����9it_QDmW��F��T}PB���R�B�Y�^��u����g.�~�E��N��B8��axzv���q�}oO�wJ�vW���|Q�K"��t���D��|I��#����G�on��DF����"�s	��1V�tq�L�����|�l�������y���!�w���N�wS'
9}�d�D�E�94�M���] ��S���r�w�����c��wj���v��h�����8tn�9����������D����%�if�RD�����D�������G,��+���$�;�wQ�����K��yr{!=���?��B*��%��G�l�n�R���� ^�@��]{
�kl�E���wG ��'�><8:Q��`��X�C�V�i�Z��)����o��X��?�����������@�������p��z�	�;b�����k�NR�Sa�[����"o)���2�]
�����{ �So�������t��
��4������H����J�[j�W�o�KjP�K�O�6��P��Dcm0�����;�?q�EVz)i���[�9���N@�8�ux�?��=��'��9^a�*�����
������\bZ.oGz��6����m�/����Z�ger L~+���R�c��o��l����v	d��u��)�������l��]�
;�_AH��6�������sX���?P�6�?�xk�e8�ks�~\����wv�=Nv*o2I�W��8���J�T��H:�?R�{�9����s~A.Q?v�A�������$Br�O>!��&L�E�N-I�����n�������S����<������b����0����I����)���}�����#.7M" ^]�pf�%> �������U���	�f|k�%����|� o���Njg&��
m��������M
���jIDE'�H�Q�X�O���q��$�g�cw��c�l�,RyG����L��hN�DW8u�:$����4ZX��v�����	/1�nr�x
Q�e���2��J��qA��'!�Y�
��	��M����D�-0w�5.����������n��������r6�(��8������IG����~��=\��{�:�c��{$��FT,��fK�Sa1��w�����Q��x�DB���%�`1�����]6f�yxC�7h���,��1[�3��;�S����e���z!���&
��D�I6�u/�~���{pq��a!�:8�aa��8�����3���M�g��A�����w��>G��<@{�D�������p��w��#QH�F����	�kl���Y�����s4��KA��&�����K��#o�H"� ��
c�����#+�E?w���J�:��z����������R�g��/_b���v.>S��.o��vQ^K�~��8��tN����y�0�����{nU� T���\[\�0�>�J3
�O�T
�����{vrt�~K+�<8����\�l�G�dZ�=�I�`���xP�5�O��
a��x�!��w���S�.^|�����z����G�C���{wtr�5�4���m�O��v������m�����*�E��G�y�I������v����{�����:��"S�f������$d���(D~#�Wc�K����|t�������
�/��%�
�C��0g\�!�������{r�?����=�kSP��G����l���C���K�]��rW��I%g�l
���=���{}/^:�����?';1�`}�>)�������!;�]��A�C��1��
Y�#���a���<A����ue�>��z����t�B��t��"�|B���8�hVB&�,h�u!��D54�+�I��!��][��r�
1�G	�`d�?�+���cP�+��0q�
i �
�8i7F��#��ux4���O��������N���������D����=�����;5�����-���B���B4��"��|�'���R(p�!�X����}��V@I� B�{����+O6zj�G���u�M������Nx�C^��8���'/���+1I\������Dm��d>�iwo|�F�-�aYJY{�Q�p���'�>���Y�tG 6�(��>Y��-�:�k^?�o�J_��P0Y���J����AV�WA�4J'�P�e��@��b���.��D��KQAI��0e �`��dV����Sv�p
*��+1�	,��+�����=�g��������H3pl��S��Z�f���X&3`}.*���7��I&�8���Q^�u���N\iazq;U]���)j�RfGK��ET�kg�
���w��.������Dv"GA~0|n����]�k�ct��MCwR=��YtJ\9�D8��W��C��G��c�C,�7Fz��KO.�K� �wA�������DR�s1�1;���t�@��"k����|Q��d=� a��/�X>7f�C����D;�tZ�r��$*�����T�G2�uezOh�R��6�S�	���m���>	����f�J��p��W9���crQ*r�m�x����^����.�#1��6
%�T����yx�	��-�*i��/a���N
/a:��"�����3��HN��n����o�K?�@�N�#��Gt�8v���K���[m"�n�8�f����)z�;��'��S�$�6��B�+�K�}"tXy�������M:`0/)����0�����(����5$�Om���w)G��D�r��eH����i�Ac�#�^�6j!������^��;�~�"�sN�����D��r^�c�0b\x�H�4g<��L�c�'n�{C:*���6�u���������;�pY��A��,����P��s��s/��UQI��d�XJ���T7�RY�x������������qi��{���GT����Ao��N�B%}N��{��1���%�_�fgM
�=n���������ioe��uG5�l���nU��<%F	��������8�S�K5�|�q5���S�Q�����$��1��������$����~s��+3����O$���I6�����V���X��I����)��Y,?fy�}�Zy��u���yJtUW�������*���N�?�WW=&��wD��F����f<��]7|5Z��� �q9�w�Z���T$��W�e
�s9���;m�~��
	\����J�p�%�&my���(���h7r����#F�`��-V����>�dg����X�6W���btw+������	{�&����^�=���j��1�)�P:W�IJ<�!�L�4�[c�c�tS#�h'���D/�|/�H�
F�Hxz�mk����� He
D�H��
]�@����H�A������Ec����m�]b�VX�2~����a�*xX }�m�u@���p��>��C��&M�t�E�/���U<t������,�a@I��&�t2��w�rJ*�}��Lrq.8�6��:�������`#���/��,YS��=�����M2�L������;���1�t�o-oi'�~}�3�5���qHEJ������s�d�QO��l/*�1�c�#����Ua��NL�a�J�1�;D�r(���.��������]�t��s��)���Rm��f���?4wv
/��'!N�y����]~����S �;���/S��0��t����:PG7��p1�z�@=v
q��^(���3t��AQ
���q��>�a�{��UR��j�N�&q�����ahdYN�JQ�0��~�P�0�����xA�n67��5�����
S�g|	������n^Ma��&��	�2Y2N���^{����'6�,X����!]���d	
�j].�����,�5:�N��1�7Z`�j}������z�"�@H��h���H�_
�E-'^T/����(���i~�a�&U%x(�m�MMU�t��f�Y��#�i��
4p��s���4�b�����EJ9k��jCL�E������uT0T8�{�Z�(�'c��������U� 5���Z�������>�PM���i�+�Xu+T9��f{L����w����I��t�
;qV�["\��m���
�*�R�P��%��(�m}�'��hS���oDW�	$��T,z
�������#�.�������
�]�����v!�)m!���9�w�;��c��C[��3)�=f��_�#�T�����uu��=��
e)�w|��Y0��r�h���+hX���q��%��k(�;8�^�����E�5��F�'�����]A������+��F�lf����}��~k���V�$���;%r���SB��tj��}��5�M�Y,X���}�o��=%��
Y.Y���	u�*y�kf�%��\��g��pI��_����&Yt�X����.����$G����.����h���d�C�99���!�����{|�?����{q|>�pt28��z+Z���f��V��������<(����&XQ&nI�5I�09J�e�C���
8FYa��-YX���[���Kx�.�f�zl�����O����� �xhV�[�X#]AXoh�<��X)a�!Ab3�N�/���?��	0+��(����>�J�fEM��q��1���	������-g3��0��}����[9;��kA�p!�nIQ��k���K�B$��5��|���=]��������@�\f2�����r�3�|��6����Mk�����q(K���������9�'d�=�,\Q�"������V[q�?��R�����N1���6��t39;�*
�W�v`./��������Z��4������z����dd�rRO8f����Ye|������k6�w���X���t�q����h�-�����f����Jm�-�gD��H/��	�d�I����j7�G��Z����i�y2��dM��������8�Yuk-��i���P	��~t"lJFU7��RF��l`�	�N����hI�#Y�_`6XDvJ.�a2p�.����,Q�Z�va*:��K>�-�����)w3D.>�-�:�����B%D���� 	3b$���j����eI�J��V�e�������Pb;o�`'�#�����S6O'���Ea���/R���.�����C�x�"���5��
E����m\.���(�����x�)�m�#h	~���u�����N��*�9�k9nm�{{Z�8.�I��b�9�d��^O���g��gu���5�M�J���D�����J�����zb"f �[����>&��hd�X�Vn��z5&1�����W���[u��*o��rJodzh[��R7��� Z����=���5�u�(��Ze8�
�O����@V���/�*$i�`#�d����<�A��1�]��J��Oy�Q�F ����k�������	9��"�%�D��
����B�x��.L��u�j n���nX>�T�j�xR�U��d� �?�--;Z8�Yp�Qw�qq�'`�R^���C�p��f&j�;�#�)����c��h�?~�J]�����T��j�t���U5d��uM�q�����$l��M+1���K�9�����X����N�l�?�����Z��������y�]�KY�����5HW�3�_��^�.��W�g|�����F�)_��U,�V(����R����>�����K���{9WV�	������^�c/\+��Z�����p�U3��`�����%����|�[����Tx�c\u�0�����kB�Z����1���'N!1��P������*U@������C�V���38������y(}q�R��E��vk����Vo��F]���p�%"����W������^t$��H*4�[=�_�90�R�m�u8�o!-�R�-���`�t\<�qM������U�Tk��Z���]�����)��y��d����vZg�b��
U�������h�"�Y(��m�:Q����.�Z)��J3��=��m ��g�g���*h�O���Wp8b��c��TS�L]�N}�s�#]���d8����2��X����|��8qU�\:e���v{����	���9��I�f���g�V�M�����L
\�vAWd����R��z���0e��?I�����v�Qvb'7Y�����4]*�����������\��C��J5L�N���@	�q08?;:y��Z�Z�J���+��@z������/�"8��:���Q[��5x�`�^z������ ��y����>���I+��6�)��H$?!Q�]�h���uY������z��� ���5@����y�*��M�����!0-����k����);���P�b'�$|�D���9~��k��Dz����N���jA���v��(\�G��z�������e��z��7�MG��P+-�%Y&S�2m��+S%�d���n�H�lU��yT����b�|�S =��?�<�p�3�Bz�h:�b��b�QSl�r�Il���^�Y2���p����-$�9�V������>0�)��t��~����-B���]0{�nf��+B6�n���!�"�^�=O9iQ{���y$r�:�G���V	U:M�Zi�*�����#R��<'�������jMT�]q+����ZMH���U>�2��.2�_a����"
�����v����"��������R��V���wizf{7���5��23�G1_��:���2��[�ul�!�
�-8�<@���2�J=�����'6T���Q������g[��\C��BC�*�RVt�w y���6��-�V�t��+6s-��,V��9�$V�����=������caS��!��-�]�h{�&`��x��yQ#7u�pVx�X��<O�q���������A�����X]/�U��>�D���[7�
�|�N���^�p�vAw�"�����	;�n������V/��X�jE[��e����t��+���
���TP[�}�|�rj�V��3Y�J�%-�>���hgC,`�#4���(Xx����|����0���Xx�Y
���T>���7��0�R����7��_\.'�h2K����NC��u�U�������4'�����1����i������1��������p��/����(�1�dZ�*d\0�f�v�;�|�z���i~=�T��V�����hS|~��#���t:��++:�$>3� �$~��a��;0��>Z+�}���r����St0�/J)����z����<$;G�TC
@y��I*-��8|s�}�����e\���1��������������2��>�)CY�������5l��k��Y#x�'R�D�iq���A������D�;����0��b�1�'b�7�J^a�!J.@����XA�EIk�KX��R3M�YS'��2V�!�
?�#�L�Z�>��Y�nN����`A��$S����A����.��������J3��7��\%��$b���}�\`I]�)(6:n�0�m �1����6����N�4�d%��o-.c���
V�!�%��n3y��_z-�]-��v������q^
IU�o90�����]��xF\G��J=I�����7����Pj!�J+��[�6�����f�\��'�6�,�14�B@�qL-��I����y�qy��[��Cr�����������j����=��������K:B����FQ���^�?\'�?������"�)�"��;cU��v_�����~X��6��T�p!,�.%�.���$X�N9����"+Z��!JD@�:��;�^�N���!\��B|h�8��O�*'�R?����)M\u�fQJ�Q��]�&wr�o����!��0I��l�O[I��F���/�>uf�9��������i��e��Ro�������ns^�a�#�����S"�������C�Z^�T
(��������l&�$svq����k[�b9��5�k��"mrv�?GjY��z���<��n��DaN����U�N��G=��������CS��Y�/���	���,�}�����g��R�����4S�`U�l5�=�J$��U����'��+����R���������YF$�^D����/Dr�Xwp���bH�?�!(������;V���N�gy��7�G8P��W�����S��l������g����wb������G����z���O{0T���������{�$u:�G�$%5��_,��:z2��l��zp�l/�u?���#rhX�4����{_te`Z������/���x���4��"��<2�~�FD����?C�c��Cof#�3������B�h9��-x�$��(�d�������P���.,t� -��z�COv\J�N:(�o���^l<��BM���(]�t�y:�A(��j����M���t�=�"�����xTqN0���K_U7|0W
��&��:rh��c�qs"�;�I~9�������\���co��Gi�197�����������c-��$�a�Eg����^@�s9����2���;�j1�0��df�!Do�~8�H���'�'����;ns������`��S��x���D�IQ���4a�?"��*�N���Q�
�_���_zR
<����I����_]c���,cB��eD��uM
�}��@�H��Va��|+��c|����� �xW�Vjk� �}�����i�.RH\4U��c�����.M�5��������O�Tb���B)�<w&Q�&�br���������}�����U7��:�
��M��|>�W��{RX���:sTb.���`�h�|��(���Q����j;�n���7��6��Vy��F�OG]'RhJ��\���y���\�����wQ��>;j;l�1�*�"����d����\�ej���X��b��nc����5t��
�,|���NU(HVj�3���C�
���a��� ��u��c@��H��x�J��]h�"o��o%���s�E��DGXJ��}����U�imikkm���ba�������&�k��Z��X��4M�w���&��n�VQX5u��J1��
��������J������,�"R�z3�/�R��~oU�e8j�Jl������1���n*XL����������R�"<�����
�}�c�(���J�y3;4���~��F��-3���8k�.Y���D+�z"���_�0D��On=QL��%��$���������b�(6`so"�BQ%�9���(V��:�C+*����@2�a���R�����d�zq��x^5jX���;�0�@�~^�?|�zY�j�,H��e���)i����E���+��
fy����M�k��pUC�k�z����Y���f[�l�W%	�����t���R�j����C�'�~����>#a!M���������.�j�G��_��a��;*���U�ZoNU(�gQF���y�������pE�����M�<m<����&�w$3�������3�j'/y�z=I�����^��a����P�c��u#A���M�'�I!l�������p�: ����v������ITt���gnt�]�X>�%����v��;I���o�z����O�]��\�����u�=7kr] �����-��[�I�EB��-��;P�����LG�8��d���t�/��
n��[�]��3������l"���W����Q��y��jQB9��U���_�
BaM��� ��B�Cu3���t�~��4<��������D?��S�����6��O�
�/g��Sx�s�����2�P��CD��@IK>��1�����Tk(X���Qu�km]���T6����'���m����m�ll���ij�H��R�(���/W��m�h���!�|\F������Z���g���CF;]
���������p�V���X��e�ZK�md��m@��S����nn��4��M��N
����66�d���J[wP�,�6���d��_�msk�F���W R�Ij$R��3�\Q�j	a��
�V�=W�T�+t���-��('k�A�{+r[>7�8�&��$5��w�RA�XK4�[e�����T��5��J�;���;���fJyTV���H >n�Vy$xE#�/�	�VCi��U}�RBaV�x��'?&~��q3���A��p��@��{}|��5�	S\�u�Z��
A���vqr�{wt�;L��)�7+����rx���2S�J��%{��s�
���[����@K��*�X�R�U���l5Ww����p��V���?-�`�<b��&�Wk��O�L[:�����k�]�~�W��%Z�?����J�"�B��*d~�����#��<���W�n��s��xe:����\�
=��
��J������,Z��%~��	&����J.����Rd'�4���RF�!l��H>�P}H��*X��n"���m��4f������$�,�iL��_�6�Y.�m�8��I�T.������l�y�w��/z������u��z����)�cr�^��Vd���j��n�R} �������7p�YDq}l�NUv[��\��~�]�U�F���������j����f1%���T*�z�m6��R�H��~����/x�u���T&s��g�:��
�r�m��k��v����=�r3�N�����4K�+���h��c@�X�V}���Y�u�08p�'���bF������V}S�h��n��p�& u4���:)`s���^��4�MB�� ��q�Xx����V�m�cu	G�BzQ�^�t�n�:pn���0�/>#]jUjn�(|� ������w�	ElL�X�u�/��^� :�]�I)���OK�n�����������\�"'�|&���-1�J,�l�/,��k{�'+-V�xR�W�g"Y��q;�XC�������V�D�������V�B�����Y
F@�:�����8#V�%6���us��VK'L�l{�v)K�\���r5�qOU5H4�*���O,r(��x�c�)�{��P�#��b��c�x�\f��e�U~ ^`E���N�|)Y��P�� �X�w�������w�u2[��b�6qwZ�4��1P�`����)��6�R���7�G����'^�i}��5.J,*[�
��z�s�0�?U��r�A`�\��zm����������0
���|=c��Rn����H���?������C	��n�[���(u�(����d`Q��%/7�Pm528-;C�0�~���giS��BUH�����p+���,V�����q��W�M�,�`��N�?o���
�b���p�����~Jf
:���[��x�J_)o��5�=�VymF*s������0�A�Nsk�fZ�t�:���B�Az��}��N������e���@z�u�s��M�]G��kY*�$�R ��pu�dg���".�ps9w���?&w<<�8<����������!T1Z~v���*�75���!^���������g���b�j:��nI.�P���[�J��7f5,��,�!��B`"�j��`Vx?��{S))X��v�5`���!���6���b��:�����J�xR=�N��yb����a��O���rR�l\�������q�5#���f��$(�%���/���L����b���a��������;�)#D�A��fK�
�Y���O�Y!�����<���h��~��t���l6]��.@oV�c��^s�cm�=���
���r[z���'����A���
P���-5rBW,�U2������m�/����4��\-�9�����Ep9�
�����s�v~�Z^�X�Q�OWO~���VW�bZ�p�Gf�Sov����+\����/��V�U;����T�����o�t���T�y/���p�J��p����"a�#EZc�=�/	L�3�7�hl.������
���@���x �*6wZe��V�����Y��'��H0	�%������b?0���#��_p]�Jp���|������$zX&�������w������(���*�k��5@��,Rz4Z/u&'��N?\��K���])��8���g���|J�����<�G��Q�$�Q�����+���8��C�a���
V�]����A/��-�vM
[u}���hq�I��d6�ob0����7nu�{����l@a~�����T�N�2�_
�����3�J�Z�85Z�e6U=~�
���	�K���k�dO
�%����7m�&w�!������v�����Y�%d.����u�f��DiO��J�j��W���4J���w�P+)��de�,��Q-+���[���)WA�e�]i�e�^i�e�_Yl
V���a9D�K�1��� Wn�f��hE�V*�^ �x���#}Q@�{Q@�]n �mj%��p$",��(���g���'f�lV�
^���aGn��,\�iUN�Rn�`
���c�17����{AU��apdCr���
�Lo�C�I�Cop�=���	yw
��
�P�a��)W�(Q�M������(f��OM�j�[�<����KJ�Q9.w���i����%��1��$��s"j�����<�i�E�
2��������So��������x�$Z�q����f�Z�}�SP'Rh8=��@c�T��@����.BY�GVE~���Q�o�P�dP*h���[gG�?���������-A�Y��cE��7�-aE�������
��\��%b��YVg�Z~����F��l��&n��0f]�a�@������6��N����CY��hlH���rd�l�(��7T7%�����,�<�F�w������-���[�h��q$C����g�n�(�S�j}s��X��c���_��q�Y�@����j=��
�r�%����j��R�-��K���wyV�{�������g"��h2V�n6���B�9e;���Y��(xD�����o%;�RX�z��*��0�=0	���}/�%�-���V��g�/�J��i7�Z�k*��r��L8s[i�J�d,#�7*� ��
�h\B'���d�p��"_�-�`����J�\]V-�o�*u��������>���J�</�����9o��E�g��s���s�`����+4BBgla{V&�
\�T���7k��-[����B�E���2�k@��Z	���a�gp��M����<��[-	\i�7v�*(=:n!D�jT�i%�8��{
�IsT�f����!�4��o�����
�����S���O6��T�b����5�l���g�h\�����!�J�O���0�J�R{J�d;&�<�o�U���������B�~��6�M����	`UF6SL�T*� U�
�sJ,�<2�1Zv�l�n��02�xP�i6��/�pkz��f.�^W�p��&���!j�6��`}��`<��g�!R.�;Ff�X�(��^���`��K�|FX��]G������������� �,{~x�T��Ym���2�CQ�ptp�s&>fyO@��}�k����\�B����d��'�3�~��*�)q���`�%��������d>D_������D�q���l�\����/c���6���fLy&��o��5�>E�t�\{��-i��nC�;)�%��
�������������J,zy4�
��p���F��U�9�������g�h��g�]�����E5�9���gp�}x�b�P�M�ca8�o�w�CD@*������Ho��63%K�������HCDo^H%�r�N'�����T�"R%?�_����4�������I�u��3E7�{y����@7eP�R�,���M<�fpU�1R�F�|����rA���������f��6����T ����R���������
X�<>�(�(�P�z�����u�=�F)d�O�D(����E�"l{����NeMJ��u�N=U��b6��#�����K���W��!��v�Y�CR�>V�	��/<�6P�Y���EW�9�p�aL�NL�H���c��'�
zN�/�S����$�ng8� �k6��52B0����n���
��P���K������ �.o�7������!K�;;g�p���p��&W����Or����lc�	!��")n*�7:�c�X�a(��?�RK�	���u�����AkG��qd+D_���4���f�}���sT��?��%^R���55�����g�43J���
�
r������Hx�}���&f������{K���V��(�G�}�s�����^��c�Rz��	�xX�T'E`���O2��C��}��T���#��|��}��N� ��k�N���+����h�����,<g��Y�����r���P����������`x���e��1C����Q�cY���I"a���a
r���rX�d���'����n����+>��0+6vju����Z�.��3R����O�<��>����7���J����c�A���0������!�����;�I��f�<�Ak�I��t�t��O�@�-�k���{��@����4�B������>m�����x-cmGl�3|�������u0h,�h	n�l����y�u��'��� �����
m��]qe	V=y�_�/0hF2������L��~����
�JN��_9����}�����ws�aR*x�??��6�jAD��t=��Mo�AV�������e���@�m�7��B�k��[k4t�X�D��������C;��[j#�y���x���`�N\��'����N�mt�Z3���	���n_�����;��������`�exW��� r4(f���M.���=�w�U-����H����V=a�C:�51��D���
�=�#������j�U�pa��gb�@�?u�'�q��A������@U�U"t��,�]��I(���������~�����d�v��Vv��	�X@VCM�������@��e�����yz�C�~���[���'}w������j����^I��(Dut2�x�����wr���G����t9*i�R��5@{3�5�7�'o�G��K��C�'#�4�v�Y���@��=�\CI�Z�\^	�=
�.����-dC�����o�w�G��~��<�Ox�G&"0�� PN�}�{h>�ak1�xZF�~�W��O���c�vpn��Qn;Y�^�����p�uk�����E���v��-Fr�D���i�����k�/#��\�����J��t?`�9HN_��yL�<#�V�(��F�m��K>>�����z�����������I�P������
o�9�.�"����ruj3$�"Y�J6�KT������)�Y.���M�F����������d��9��r_v�����GA��;!����F�*W���L�eD��s�XO���k��O���u��uw<��HRW���Y���3�r��i���I�6�����Gh�y{���f��4[5=�J�����Q���5�O�Y2\�(�o����[��	&�C&�E���h����,�u����h�����9rs����1-FlUZz^�m@TX�;�y'��L�Wj�-9K8�]�����<��+%�~!�5* b�0) ��{��XZDIv�y�m������t:�D��b�t�*��t0����p�~h"��N��K��T[��mrk�6+�����Pn���Lb��v��*���
�ck������zEq�����m�-*o���K�Ef���!OYL�� a6��8�N2E�\`��N������y�����r��lX+)q���D��b �!�m�����#��3
^��)�X���>�s���Z6�2r�H����O�K�uVa�_�h�_�&p���5)'�]��r���B�r�?�~c�N��@�����Pn'�G�l?'��d�qp�����H
W�@�vQ �>c'|K6����V�m:g�"�2�tZUM���k����$����>�2FG�Y��y��j�\w��J��MiN|U��|��9�������Bq:;E��_�y$M���5�����oz-S��?�A�����1&/���ao���QP��GC����Au[��y+=��M@!���V�5e��v^w�/��iM�|`B��@�*�:�-n"���z�{�c�b���������������u��ap�$8k�<1��(��y�k�R�lWx�`�N�r�����wgK]A;t�]����oFA�8�6�J[�����[-7���f���s�#?�d�b�[���(yQmy�x����KQ��CwXlp��n$�������rA�Mn��n���b���J��@e�����J�j���5�b�9�w�>��X�y=T�SX(�R�����_��bk��e�j� 1ja|��<��X�
�'��
��./���>	��!�:�'��&���lm[���e>���ZnT7����z
�Q��Fd�����������l���L�-�����ya62Q��{�g�Zm>��na�|�����\�g��V�x���w�C��V$�WG��|TPA�|,%P�Rm���Dv>D9��.�;����R����}�+{����a�?����Dtj���=�muz.���X#�j�������_�I�2du�leI�.G[�;�����n�t��j������~�e� Od��mj�4�����Na�������Vn��Z����l��T	����o�"y?��6jT\������&2���e4��o���d���]�<,���������Et���<2�{��0II�Y&i����#����UW���=]������+�d�-��&/<s����<�q���e������'t����k��p�:���g��m��Z���*����PL�0'\�B�1R{�?��z����t�QIZ��m�����S|��J���OG������]�TD���#�����7{o��7�����`a��#��K[��d�4nh���^sk��q-�':���$Q\Y�9Vg��u[VBB�C
�IA�Z�������g��b]Y^���]j<h���B�.���~F����aU�:�����2d�Z6���]}��)$�9y�N5�n�����
g�`	Z��&"��7�'8cY��m�mH�g�^k�&@?���@�B!�-��9�]�6$�WQ�42��&���W���z5�T����G*.��r=E��4��HV���_Wik[��V���u�^�$�Ua�X�f�I<��k�5��MI�X��j�	�!��|�+l3uF��nG�0�A|5���1�D�W(���[o�����)M�Y�j%�������G�6�ii�ii�-���H�Rq�CAJ�C�I�f1n��Y��m�!���n�����>�*���K���L?�-�5�R4�c��p�����,h��Q�L��u�^TkeE��+������b9��Qt;�N'�oqO������*��ZF��P�8���>|�E�`�u��@�:kC��a��K�Z�b���5���ft�3&��b�fOfS�T��]��'oJ-�gXf�FB�x���W��x(GF�y�t�nb���>��CI��
��P�dB��OW�B���������xj�W�q�"�HC��Jk�������\�H y���#u������G�b��d���i�`�W�N<�|�_+�V��:�B_(��&�A����?�"��\��\�@�����n:���������-Q���,7'?��k&i{��0�Z��!sc��a�Y�c")�#�S�A]�}��sR2�$a��^�)+����F�a�"�3Y��:�
�6�K���7�#i�I�2�k��t�p�
���z�m�HEy�=y�}���@V$!������������I�K/�|xi�>�w���&�����l3g
��
������J*��&7����\��8K(�c'����rk�����+�N����2�
W��
����`����c���������E~�h�%����T��)��]������L\���[��O��$X7Js��K�>]�:Y����������Ze���P)Hq��dR	��*�_�G�]0�#�����������������3�\{���Z��,��fCO:��H�)��fE�;M�����;�ix�?�9A����Ib���C�����~��4#�^�(�mr��i�bg\��T'���^������r�*����e�*g�S���������3�ivH�f��6k���S���=���7a���
t�S�l!�c��T������������������+��
�������>	�]�!!��K��}!i		�,�4��o*��7�h|��`y�k1m_���p���k(c�cK�c�+�p�m2�v���l��8!�s[D����~V��0����@�~	o�E����]W��x�(No���@q�u�$on4���������g�y&K(����>���n�Q�������l��n�Z1b-?�B(T�6
��(��>^I*�y�	�a��ap5\p[�Rx��f��e���+�W�I�$f;��X����J(�g��T��'������t��?���?{9�
��}<�Q[Z��������m���-E�f9#0>��2���������8n��C��^o���dv�Q���}#�P1��k���4LW�7��l2��f6���hr;	G��v2�6��*n�a�k��#�%T����T�U7�^�4s4�/���a|��6�����(K���b��)�����OE���m���������@T
G��l���������H�����29\6�mW�vk�@i7�J��]�E�~�ji���9���� ����d��aM�/���p�,)�r��	��g��n>�h9����e���qG���b�����?�2�.�]Ow�'��v��'4�;����N����Pn�%��tX����7���8�\�=ql=Z.��sEGhW
�O�` R��0�g�`���=�n[~)l�Xtv7�n��zE|��7����+��[�1n[��
&3�e:���
�3���8���$�w��L��~���m9�9����a����z�Q���bf�:�p�K�1��	��7eD��vm=��8�J�����-Nh��4J�T1
��
#b�ju�j*���i9�����5Ar0�z�����v��L�C�3�olq�#��&�Y8�C��v����
��j��J�lB�BKO4�
�z���:��$�Rq��*F+������y���g���hH��~T��v16���d�n�^�k=��a=��cLi��_�E��
�mX�����/,��^�}���O���:`A�6@&*�����+Z�Jvu;p�V�U�(������v)`����0{XA+��m���lM=��@	���y�������q�8p3�% �N�?���
��p���" v#���},��&����nG��b07�����EB�N�����`���t����F
�
�c�@�a�}"ne�9��N�>`O�U����!v	���$v��f4�d�$�m�V� �q��Gq'a,�k����:5G��^����;}��(��A��$�O~��������4��Rk��S�Ja[s��p+���m �+5��oB���F�u���������A�xx�������'�7�@�[v�W]i/f�+�>�y���0�pR$u��3���.��0BN�2����}q�����@�E'�v>��^���w�9g������hp�;9@����}�PK@��E$��,��]�{~q�#����i��:Hq���5a����YQ�����B9Ha8=�\/e��L�0�,t!���.zc���H��tr<���9�J�����s+�1��7��w�J�tef��Kt�Rs�����"�D�
~I��\
a�C�sBxMM�s�1G��|���n��N�oOfoR<����25������//����d@VCC�?��7���I��2���8���S�.%6��k/��:�@���
C������T��L�Z��U`��:;���+	��<��_��q�x�
C�h�6�z��ui\�����R�K�����X)sJ���|+_ �5^���q.�b4�<3��)l�"r���&�!�������/�$NJG>�D
��2lKKb������Mu�Jp��n����"N�;���5�l2�}3��E1^Hfh ��TV�d6��F����o[4��fe�6)7��FWk���,n�W�f�&q�������������Q���h�o���%�'���t���B�N�0a
�i��n�^�!X�H;�Z:,��l�K�20dM���2�l���X*���fV�W���B�le��B^���E!e��/��7��#�Ey��`M��6��j��S%�-�v9Gjd������g�`0���u��V��{+HV%,a($��1�%Q����&F�;���3���q������l�.���q#�/�X�	����0<����:�)��R{�YnC�������9��`T��������b8�a�iE&c@&��t�=�X\�9�6c��-uK�I^�S��v0�����3�+c�<��L=c�}��1���@V9R3$�s5��
+���2h���!�)�+�8�B��\#_	����ucHQ��'��\"H�`i�;n[*����]��Y�4�dJ�H#*H�Q��� v�C�����$��~')U�Su{���u��G����N^���.X�c�d��T8z�V�O��8<�����EC�=�d7pzIv��
$F����Z��A�6+]�%���9�v���A^s^�DBB����,{�@���F�:��1S�fX�>Ug��K|���oU+��X\����:X\������7������9p��8u�^?������i,T�|�_����.�D���3��3���k�]����=�L��0��_�i��Km�%w��/C�t�����y��+��[��E ���z�@�q�����/����Y[��"�'~���f��n\����Z��KQ�`��CIK$,s��?�����z�����Ao0��������0��2������g�y�����F�V.���t�R����aq$�p�
�-:_��F01)�)����W�I�*�/�vv�ai��(F\�5��������*���z*	o/K�BQ7��0����
����K~�Y�����?��V�>ny��h��8���BrE^!��k����e���a���,o�d���&>�
pF&�8
�u���$��r�L����)q[�@�m1PPok��j��p�n�C�]��0G��=�mN��u_���m������,�MY�P��	f6�����7Db�H����G�_� ��?�@�N�z�3�+R��5�k�m�,�c�Bi��.x,'���}��{���N����r�x��w���������i��X;^�^/�"��s�e�2��
���Fax��H�iv�u�~��.�W���D8�N"�D����{���W���XT��Yj#��+w��[�8 �m��Q�@�������VQ]�83��
z�W��]���������I��e��V$Yo��u�F�Zf���&#(�)�\lW��q�nW�6�.)��/� �����\��������z��Z�B%|���CW�
����s��)
��	����1������R�d���W��P�a#����4�90��R��f�������v���G��NsOZ��4d
�s�u0�`\�l�K����7����4(�?��&\�7]�GjpG���m��N#	j4�4��$j?��(�H�G����|�,<EI��a4��n�|W�}@[X�Q�c0���sL�"�K�_���q������6�".�D���B@����'#�V?�|p���|�9��C��@x����E0�����=�	���������^p��~a�
��8��7���_4s�s���Y��`���s�U�����]&���7�/��q��M�O������� �I+Q��tg��+��E6������D�X!��x�������y���>|���4S����FfK����%,���������=�u�5�)�=�S"b�:p8�e=)���������7jh��cN�d<>BUvL��hv8_�
�)*$�&�
���B�q�q2�qq�=������";3�������g����
�G��L�3p��!����p����C �����W�v��u;nk��}��`�:������J7�����AO��$F]��7N���9�����G'�������CZNG X�K��[�;+xR��8���|"����I��i7+Y���/����(��
��G�&p�o���G�^���z�h��S��{�FW�o
!.6�����t\Dc���f ����i~_	��E��@bF)w����m��r2�+f��Rh���!���T�m�98��!u$���p�?�X,��e�����8�,+-]������H�u�@����/���&�N��� U�
S#v6Hfa������N������Q
7S�A��F��Si�qX&�T��1��FI���s�\�f��������W��*�6��^��X����6��UR��3'o����1��s���o�����D���D~�BUz� 5r2��#��Gd-�cFaq�x�Jq���-�2�����gY�})�&����+^�(�*��F��4GdI:!Q��{�dnhLN@$��"�����&�'{F��@�R�'N�
���!>nd���jL���v��o�k��f���9N�����Ce���Jo���n�N����F<�I��Y4��Q�Dv
�������/�Ri��-EQt�����1}������Q6-����V�.P����"~��f�m����8@�ox�g��J��I�� ��|b�&�����h@��H���R��
���������'��g���_�\GG�|����8�O��W8�s��joy��**��V5�����w�<z������kR&e�_�3��#�����q�5n�U�3���f�A�l��E-�I��	��8�c���F=���`YXL�(�����������G'?�l��Jm�iT�nC�M������
`������'X�����M��0�eMkWn[���V���C?9!��V>5S(�O�;R�����('�������dZ����-F7�m�P����]�og���e�K��$#���ne�����A��t-���dN�Pl��@���4��|�/��I�E�O>�V�l��$B<K)�U *.����6�U)R�`VL�bp	2�*����zC����3�����[7K�>0�j��k�2c}�%�R��������"+)vcJFD�X73��^#."31�w����@��$��\!�:0%�7��-H��J[��8Q.��}��P��p���>��5�$��%l�6,k5+n�����Z�g�C:����>O�}���(����u\u-�&2�������z���f���������6/��Zn��j����^n���"4`�9��+��:�8�-7�g*����JQ��B+�8�kY�a�:9��0�L�
^\��Vj�z���3�y��&�@�r������#m�C�����xa��W���Q�cO�~�g�2�}v��nR��t~��������RX�q���%a\���	�$=�"�b����J��Y/j �WjZI���D�;e��xC��A���!q���������!�X���������CYa}��j�Th6���B�3���/��!|�\�T��T�-��b.�>��i�D}?4�N���W{9w������i�l�;KRiNTs>��K$�)NC���f����DSp9v4_��L�_>��G���^��Q&�{���
��F;N����+�[5K0�r$N2.��L��(0~t�9c=�$-\�j���[�%��
On#��z��A1��i�yst��� ���h���3`B�
$wv���'��[mW6��v��i�x2*�&Xk^BNEn;���!�cE`R��,�f�f��1�[BV���1URsq{��M������h�Z�����������jM��0�j�Ak�+��k�����p�����?�>����`���C����#��~����ovOT�����;�7ln�s�1)J���D� ,�����X�������L���I�T������mt���H�E���������t���F>�q.l=����L���f�EX�y`��?���&��i���88������VN�^/W�z�mj��r�f!��3�n\��s'o�T��>��^8h�J1�~����+��@�l���������>������(����u��
�O�bru?OF�HxL����	��f+�(kd���,��pB� ����R )R�&��L2��j�mT[�J��M0���m_����WM��8������(���^�V��uU1(?�:�����I ��pnq� �l��d/C�o��n�����fR������fj�[������r>|������g����I�i���f�[Y3�kcN��K��i7��e��D��=��������5��x�6W ����f����7��{_DuP�.�C6����g�>�"�M1������� <�y/�I���5R�k�a�6�Bi�'2��i��&B~�mG�A4]�*����M��J�mU�'�b5[S@4�u��*��n������X
���)��l7�6���z-��x&����U��-����&��$�oZ������C/Z��'�|:���'^�Z�F.����T!����[��^�����pTM�}z��N-Xg;�d��S��q��V����-.�gt�+�qX�������A���:)oy;�q�6��^��3��5+]�����`X����.���a_��t���F����V.~[�q������R3���:����ZXo����N9���z�fF7�Q�I��'���k$����nt2px5>Pt'w��6�9��K�����T�3A�������z���(�w�~���u�G+Z4�t1>-0~���r��4t?��**�����!�mZa����2�s��tM8r����b��l��z��pz�Jj�e�[0d��M�*�f��V���3���Xj������UX�Y��8�
�jK~�W:I6}]'�����*��y}]��+������uD�Fm�]/l���)�v6�,����lW�J���_ZkZ��V]�z����j���m�K��[@���.��&j�te��Z�u����u_e�T]�
��P%�d�J�*�r��r��v��=�E<�w#�T|&��/��S
l�*x
����)���^�������I��yyn���a6���k�Z�K��h$lQO?��Y�@.i�f�����,Or���'�^�9�X p����LVm��M�k
[�.���Z�?���_M���?O��:h�a��S�W���O���8���Yr�1nA��bWGW�g��������-���4�J�Z�W1l{��4X�uk
��o4M�s-p2l:U����)��@E����m��v���f�4�"�)��Y[��I�z���px'��k��g��!c���Y��c�����h�������#��������x�m��������������6��������k��$�{�������hdl��*�a��Y�i6c�Q1��:�~�>��%l�Y���D�e��b���P�9iD>��/������(���n��C�RZ�d�
<�k�69��q��Wc[6��4[ h7��l������Sd$em�\PJ��B��Yp*�b��������J�H{^�����j��fJ	Tf����<&E��03z���_��h��5���<�L���7�����b�O�V�Z��B�G��������y�����S����8^q�pt��a��<�x�����)�{�'��c��J����������[��l��'���l��z��������a*Gk�c�}�������HT��jS;=����3�E�G���/��D)������B7�Ck����T7��,7�=��)"R�����'�������Axm� vqY�DP��zC�����`���n �x�Hv���������T'�;;�E0wn��P#�L��D=d��s��rL��n�M<E�`�9���ll�gx�`��sr,���	��V���w\����%�Ou�Y�&/��[7���zx5�~ne���8��IF��f�mj��X��Rc���o�?C��yj�F����@��Y���`~t�FpGBe3n��N�V���t�y_�*��'�������	V�����$7�OJP�����A��joc�V��Z���5Xa'�cr��P���m��65���~hQ��z�m$b�lW=\F���D���?@Y&W���1��z����%��Q��^S�������G
��
���7��O�%��
�����7��f��6�F�6��)_�{oIh���
��"���G/���U������Y�����a�`�)�[���������7�#c^��j�~C��Xw���O�w���>����vl�����Po>������W\2kHz�'�Q(L�F������q5����PZ��}q��%km��4;n�U_p�pv����t$��g�8��Y]g��������{����������=0��G��� 2���=� XQ4�S�.X|�-U)�@�,��=!�r�4��I����\
n��a���\�'6.�����V�kE�(6j0e�%�f�[C�3
A4
T4�@w4=#��2�k�$���[&:���ky�<��Q6�2�*��z����J9���|����Bg�^8r��O���hR	?���`�a�,N���������%�dUv�.U���#T�U��up�ZA'`#�/pB1�
�Bje���$S64���n������]������:P����*��.{U��W�8��,;���-���<*�� OG�G��}{���n��;�� V�{��%
�E)p9�KX������B����+�@:�R	�r���w��K�&=r��?�s����;y[����r�;dx(?/j�E�������r��i0B"�?:�
!���#����yVT�k
HQ_����m�r�pF1M����Q�F%h�ED7���]�+�	� �0Tpt���\�g@���0R�G���$���b�
D���.L�=Eu;,|���)%R_�!���A8�X���"�?y)���5���**��c�(��;
���[�[�e���i�r��V�zY�g�\����H�$lR�l�r��E6�2�2��n�����\���Y��S�����^�_3�J?��-t�n2(��$P�l�q����e��!5�:\�'QqA����x��s�;&��A��ipt�"�]��$�'�8����;�}����m��h6���d]c�aNUc8
8���r�:�|��'�<X���oB0���9<��:��w���s�EP�0��'"+/`Z���
�9�1�:���8�)u�3S�i���&T�Z����\+C�������m�k]��B������M���y�����Own������	\� �!Oc��3}�sb�v���PL�T���z�p3�
9'o���Kc&�����f�S��'SC��/��}��G��J������o��%��_
�a���gV+�
 O�� ���n���@��\]�>��s�T��,��M��\�6�uj�����q=�v�?'���t�$�N`��YP0���lJ��`����f�y��xL�\� Q��R;\��Z��Vk����3��G���m����P!Fo�c��]1�[���O%����
I;_:+fU V���1�
���YIm�.�F������s?/��6���F�3��c��E0��m�>�h*FVi�0 �����>M��^�?��t�g�������^���^�2^~�5<&��"��Z��&c�U����,�_2�5�%��n��5�����#���%�������	%9����	KXL��Cn^0[$�����?����:��Z��Eo��H�������YV[�0�W���q��H��\���	�k9�v�Qy��{�<�{�y��^�:���������In��c���N�������M�����N������7�
We�C��ky�����g�]Km���f�U�zne\�zgg�3��]�P�����=��5f�������T9%����{�%s��p�k�y>�x��t��s�J�vx7�F7���7�|��K��3� o���4�
������l���y�����C�A��{|t�Z�u���h�v�?A�7���
���j�8�}�%=&�mT����z}�3Q��*�,��lM�w<R�K�o�s���ey��'�[��M���?i�$��w_�u�A�]�f�r�����n��l�S�*�T�cac��!lOC�E|�k�N������T�}e���M�`}IJE6��tkdz�?��TDXpg(����s��1Y�]=-��D�����;��?�
p�]Y�cKk��R�Qsk��[U4��s%���3tA���>�Ko����_��'4�D���������{���w@�l�l%��4���Tm'=��@@�+]
�d�,U��&l���.�_Mw����g�>���E�����& |���{�
;���l����9��H������cc��2�X�H�
3"�[�H
���I'-v����S�n�x@wF��A��V��������Joc����X�|��FA*�v=�l��z�m������Tv+w(Z�-�|���{f��tB4}y�]M~�-�j���"�u��H��9}�h'�;Q��c����Q�������XM�z��($VR1����h�l�����\�b�;J/jOsP+�P.A������xVm�W��X��d�bqb�L�w"�:[�eR�"�n���5j�|f�iV /�G���n����E�#��r�!y���I��d�����	���t�H�����Pz���0fo8��&$�3�*.7a��J����������q�co4k�	���=f��	Ku����]tkg���
�f��"���Kb�=�lA{����0~��k�C[�b�l�+-�~B���>��J2������mJ
�j�.��f�c��,�)��n�5���$�hC��6�?;�l2�:m7/:�c�C��[k�U*��8q�E�2��o(J�F��^c}�f��J�]N����Ly��T��^�����p�������wW�[+�{�F*��!/��sJ�MT��t|"���<�?��m0�\�����.���Y��&������[������0��F��]&Q&k�(�����7G5�3��(���vD+�?���X�A�
�kkz���D���=r&%[�����"�_�Ajk����VX�Vl�y�+��o��27j�����$��uvZ�O�h���O���Xq�F��*o�tN3����gc�s��41��43*����b��������d��
kB����G�g����k���
� ]E�`F�}CQ��.(E�d6@�Y�F�w���,�(�����4	����g��������=��������Q�]F���~���"�aX�s^�t�B�`7��3#zV;�[N� eFT	���@��X�t@uj=���t�����}}�KS���`3��<�U�x�����i!�����,Ja4q���]g9��N��-|�:H��2R"fi?�G�cF���(	"�?����x�Q���e������V�71��h���7��r�I�C&��v7�'o1�f�����b\�['����y�����Pn(�q�<��Ns�	���O��?��~�z{��e�������`�]���|A��U���������P^0�I'���������Q)����VW��V���@���|�n���-l�
K-���g=}�-���$����r�g�BI4���&�m��<XDO�s����-���`�"��9	��H���I��yS�;UG_:��qd�G����8���'�h�MU�q0+�����(^V��O�R��h#P6������q������z �/3`b��)�[@��b	V"�l��VM�M~������[[Q'9�}����Nw�l�awt�A��N&�����QV�����-���u^
xh��W�i~>�����
��e,��a���qbWD���W���k@�:&��wgK�9�����>f�������D�2De�����w�;8�3�����N�J4������O*X���{�������zg���G#��j�c�
�>9��Q[f���������-%�G������������[���v�\i�������m�,Vb�g��u(�n8!�v$2\Q�
�N���]�4W4�}b@�-&�,�J�mTk���0��	k��p����k�����u�(�o�4�� "�-����ud~���;0����^*�*1G�t�{�Q[��������c=M�O
0��������\� ���
f��L���@��}F0p8A�n���	`a)l��W����Y*��)L
d���z��\��vnE�^�e�D|���J1V$=��[Oi
����S�#^�F�m���e�U=�
�Q���n�����$y��;M��x�'�1w)����&s|�%"���7����h�	�6v����PS��!������M���|�|}�n��u�1�B������
��k�}s_���;x����2�7>��v���b�t�,�������N����K��3O&Z������|?�E����.�!���� 	����Y!�� �7�p�_[��l�&0�������;&�"���^��5���@eE�f�LaGG��
,:D�Z�P�������:�3��9Y�PaC���?���[K����Q��G���P��sL�����|�7�[����.����@6�kg�^E@�������L���\��{'�	��1vNo������	�b����n:1����5�E���S#!��a��J~���x�_�$��7���7�:Tv�E �����$���5/�#�9�xb����u�V�A{=�r��@��&����$8� �$mw��Nx;��������4
�-k�m]�$N����k�lY;M�MK���+�����0Y��uS��g���6�6���b�-j;�
]�����/.eL���n7#_P���H��
��xw_�����^��#�d_�������l,�����~���
��9+p�MZy�&6H{sg��>f�m�Zm7j.�#��hF1R��O���6��k�n_��� ���L�.���4�����������g'��W�&��u��-l�Cl}�����?X.�`AS�d���[��!-�#>OA���Y)CX�C��������<���FN� ��D/s�3�?��J����58���J{�)�<�p,���`9?�z��OZ7�����XZ�)�P.���j�*U�_j��R=��{~�ci����_w]�!<��L
=�(o��I��"���}�����C�����s���^���Gf��g��������A���cl��rtr��%����g9Z���������+��m{��
�j���6 �d3�����<p��ob����F������T$����X�FY>�"���r�\N�S|8@��ci"���E)�U��D	�;M�?��������s���q��|2��I���B+-<���O�H{�����G����]��]������'?&�4?cN@��R�H�s��� /ub%��6f]|��I�����y����6c�I�2�>�*��i9�#@�D�Ot!-~� m<���Bu�)���t��G�����Ed��g�49��%�O]�B�*���b����������.)\��r}�ia�����b���m��S������������d7
$�C��36��-RkIo��Pj�w�I�!{�G�����7���_rj�RL��Z#
�>�rxc�	���IM���!%�����u0+~5�0�o}�\���3����gD������^B�:��G���u����w�sqy���$48�}o��#L3���i?�O�J�~���;�m�|{�'�~rL������_��O��������o�.����x���?3�������W�)�����M���z��~t�1����E��;��AM]x{rh:�|}�6�+���t�f�����`Cl��Lt�\y��C*����=���!�gp�=�����u��w�������G'I�����= S���hp~t0��������>f�Ik����M�������&�:>��^�_�]|��|��������\������]<�8;������wG�/�d7''���L�9��N��c\"�utr��?��aM2�q�=#8H�����������B��N��Hd����{r����v|����l��s�.���������+zA��������-�u�C>������;�Pp��|q���J����6L�s�Y��Y���<�4m0�r��a���V���*����Z�������JC�i�-U/�S=��X���Q��W���F=��F�mA���YK��}i:�)��7���E��e������������Lxeu�
��Z+6�n����vvP�x�8��F�O��#ij������h�^=��P�RY��_�/��m��
+w��+#>��9	�D3��wp����z�g=*������+���+n(C�v��4�i���7�Ke8|�_u���a�EC!(|��_H�8�����_h4^�3���������A�=�U��������\�\t;�a�@H>���O��B?k���������u��T����������y�E(R��0���r+�_{���[Y�I�]�g7j������#�{8!��
)���B��y�3�
��AN�0�^��?s�����<rC�"4��QQ���B~���8�D���	<��8w�W�
�:������K��������=������w'�������_Rv�*����A���~��2f	Hz��$39S�}~��|���Y��g��6���^��������eJ�k{G'���XAAo�����u��;���!k
_�?9���a|a�����Y�=�e�L���Xw��g�O�;��J��}�^f�1�-@�A�{v��s�=X��M�tvxD��{�k^q�"}��>�|s����thHuy�
3z���v���qf`�Y�+~�1_J���]����_����A���/�1�����+:y��pt�k^/�Bt����D��5��IL�_�/��;�����2�����
��0���������b���Ic�`��dy��A���n�q3q�����'�b�K����W��o��W��_�o��;���]��2��0_����}~��hm`��~e%����za%q|��e�K��r����l]���t����;�e��2����m%�������J|d��D�{�x�� �-����[J�V�T���%E��Z�/��/�t�xe��B%�s[{�M6?�q�<c��������}d`���,^fll����K[{a��������l�V>�Qmr���'�Iu�Id,�iL��c��NaR�jl�w�W�C��@��OR���a�a4�b?R�2�j��|��Z��F*�7���ot���������V&�xe�������cJ��2�&.�[A=���f{���a!��i�-�S�I���I�������i��q�{����fBW���}���@Z�`
���P�{M���a(�B��
����
>Q2Th����������[�Dd��i.�?�_��_��=c9��:.Q�(���nv��uy�:z7�k�hp>�b���V+X2�g5��eD�H���h�W=����%Xxe��\��Y8i���_JXu�,���Xd�o�����������AW'�v��_���f]�����2b��o������1m�p����MM��n�L��*9��������O[�C��5��w}�z��P�~r���rkvj
�����L<���[��:��ub	��+���H�Y�MpL����z{Q��$YF8G_�Y�����h���@�S���z�N���������Z�p��8�z^t�-)O�"yA���Ty0N���������9nGKu���G�B9��S#���H(a�V�Q�S/������K1#�K����U����D�Eo�<8z~�w�;������e�q(M����I�)o
�f����s�V+U�Zio��q�-l���>�a���"�y��%K�����_��7�j5�
l����^&��aX+��ZK���C
S���[��
�p�"]��#I�g�&V �$���$���tf|C���,�/��E���<�����Te�_��
<��TryMt@)�*��C�l��z������{L�S���(k�J��1��d?DIi[I�d>��Hx���%��{?�3?����0��W2��$��Q
��4�f�g.�mP���$Ri����]��N��1G���R_�%�JT3k��`�=|+����o����^4�\�G������	��Q��/���3Qg@?`���8��0[{���DS0���_�
�IV@�Q\�����)��+�j��}t~v����4�vw]q�k��6*qz���� tV2w�������cc�qz���6�Ss��cm���4�e�Q]��5jp�e�����!q-�s��������*h����d��j�&1qE6���nEh��Bz�zy�H*����QD(���m�x����h��H�H���*�T�*d�*sdt{��lT�"��E�,���~�}��]k�0�h�5�"m����^���z���p#o�;�����l>]^�Q�[
�Q$X ;4sD���Oi��|:��~������UT%�FD�4<|��S/���W@������F��49��(<B��M��$FY0��Q�V-g�1)�I}B�.�s�//�i�B�[�^q�5}3�$�H��&�dO9.���wtH-aH��w]�W��6-�M��m��;�"��dJK�� �JD3�_�8d������84�l�^�"��JQ���ky�/K�0]k�H���z��.[�����g��������4"�mo��M�u��\!���e��+�dm_��+���D-n�x����O������f�@�(N�8�I>���zf+�XN��E
X�\���{���g��Z�5��Y���"��Be,/2RX>�mb
�Jw���:�=����S5U����O���$!E
n�g�J	]��,��%����_/�n��E�.�Th���4��
��I+#�����������U&nE1Y���iM�L�:[������3s�1�a��&��M����
�~��u6�&6d2t];EtYY7��e��R�J��=���.���x�(�n�������������v��L�:�2cT]�y����\�Yh���_���;�)���/|��L;�������#����W*������3����QU�_]���]��������h��]�T�w�Z�����3&�s�1q�T#�1I:�e�1N���iR
��$�Bgvl ���������-���0������F���������	L��z�����Lp�����]+�q����m���<N��1������;�9��V~�y� cNx(+���	��q5Y�+H4.^T�����Q���"48�<>��5�v��=��Y���O�Q�7)�t��jO5�<���������o����U�����'S����i��eW���Q�igGwz1�����QD���x������|�����*�~tD���
�%aw����L�I����T��*lu\�p���l*w�K/(5�������r���`IW�F�XKLk����D��I�6�b�#���\]�����5��w����(m%
- ���D��,��V� �����t/.i.��B�%���h6ZP*����o*8���D[�����y����+�q_��W���?�b���rh����)��NYO�U��������si��Z��A����N\*�������}�]��4T��J�mU+�@�Rs;���� v_+�G{��~smDI�������$�?���
�WE�L����V�I�eE���TQ6�'������������[IE�q4U�bZi�>�j[���a��v�u����x�I���OR<Vj���[M���������b`��~t�s�m4(��makWR�UJCm�^�<��gJ�0�|���;��tU=<I)��Y�NXI-�/v��Q�"l���u�.Z���s�_��;����Nk������H� B��32���Nf c���1�wF�R#�� '������=�������(�����x>�� @L:r�8	���t�j�~dO�o��tmgB*�����������>jw������7F/�	�	�@��Pf�s
b��w2�/5}�>nZ���y?w��EF���[���b5��9�����<�G���u�wv�?s����{p=����������Q}�o�?�w=���JY����<�9v�N�|���D�L�����B��R�w�E��=��_���}����t\U�o��?C���/���b��g��8��d}������o�0����o5�1����^���/%��$��z�k�?��0�����TvBBL.������t��H)�����H��?�^,2&��D0���@���&0�T0�Lb@����� N#��db���&$�l4�s�v>�F��'c$��hF���v��r'm0���T��s5�����%��J�^F�Ru;D�������|�p�	���yL���4��2�5����kM�i��ZSzjZ�����b��7��V��p����<r����qRd�<�6{����6��f�M�J��/�ZsMN��-$�G@y�+��"��2[��FJ�N�������^���zC���Z���)���F��J��F������O�J���|���\w|*�m����8��
">G�S���[��������p����k}P*����-&�7�!���:���'���r2g��m9�����#�m�o��N�V=��c�YhUL��g�����j���hU�Nk���%9cVZ{f�?�/��{���;��*21���v���,��}������������R�T�R��f_��q���e�(m�+�����z���J}T�roq'1�����a�OY�9:��4�k�E���;��|5K{o1�{�.���=e����I�FVuT8�L-��	�b-�������`�
b���e�����)�	��,�Ku��ui/�yF�B6������67qd�����K8�����������)1��j�_�f,���Re�<&�^k#�>��*K�gd��a�i(u/��@NriZ �/��=e�����/�wN���������^�fl�edH{�!���>���V�r]�N��u�`��r`}�tV�e���,>Z���?���|2���T�]%-�������!yM�&I�������s6�N�Z��������r5u�lm����w��ut��b�N
��S�N��uez��z�z�`�����X���$^9������7�C��i��D�������^JBZ�.J�q=�R��~�\�C���c�J3���$�Y�][�\y�� s���G"���y��&cU����N���^A_��r�M�4��A�i6]@?�?0�j�f|�R�
0�e�1q��1�uN�>1!+�:����r�W�Zi��jU��'�|m�K�=�#W!^i
d-����KF�D�*�:��k��^�� ��.�:��O����s�W�)�Uo���{vrt�>$�!ov��8>�}0���������kJU�O��[L��p�u�R&�]���~���"��A�_;�W�1�����VX���)�!H&z<���k�3����g���0�?�~���?�5ux���(���"R��{���F�,�_��\_��J�^��S��D��q��=��5����mwk���s���N:�bT������v�K���_�Q.��j����uo����Ll��G��.X�S�����!:�6@|���U��C*1���?�>���G89S��z�@
W��E��>0�g�?2��{����I�a�A.yx�->���@3�S��b������]�[g�r������������(�� ��_��o�C
�B������x2�_���GH�(�%�)���q=����	4Z*�4\����������r-3�m�m����J����ep�WJ�������
���LPM;[���F&`�}��p�eh&�`O�,�9�G%p���;�(�=8�\��f!�R������%�D,�
oC��.��]��P8b]Y����^��X������|��s��9O��pJ/�cD����*��J����'V�:o�dFB�5��T����6�X�|���<c�)��*�����{���n�b�YObo���oS�Ea;�?����I�(�^c��TT�}v���07-Y7�VSoq����X���ycx�E#+�
0�M�F���A����I��7G��A?�����V�$U�O���)�r�/|�}-DG���������]_g~'5��o�c�>���N �Q&��0D9� Dr
�PL�2�S�/�u��"�2�?��m���k.��V�N���	����3�	�j��p>����&��gpbVX�tq�q��K��Q��]���������_������G	
�A�ur��H��W������{��X^]�HM�o��j���4�������5������(�j�i)f����>�0�����*��3�kD�&�b���P���������S��3���pq�
}S�O1{[�\�$&3`w����U6���5g������8��t��rYK.���l���.������w�;8wF�
r�7�gWo&��HP8(qU\)t��FfW�����;#����N�!�4r����}�*��s���K����~���jE^�5���h�ZF���)�^@%|�=�|�9}��/N��?���i+���?�_p0K?To������/��&��{��[T7�J<�) �W����.�i���}��6zl��Wb����fz���V���2��O���Fo))���x������Ur��������F�4��������bo����z�E���N��?F�����a?U�*����x_�/
������P~��K��7���vOV��w����.e�;�}4.�Lt�1�7�_-���}����h��.>}��)������z���������^H���km�L�t�����A��K��>E�U!h7^"���)��	Y6f������b����L�/)����!H�_e��kUZ5�\����`kF�K0�:_�r��*��WI+�DS?����i)A]���<��-�j	Vt�����J�?�_���TR���'9��� �f����R/�&�.l��J:iu
���z�����������/����y|����Dn���d�AY�G�=�������vI��	Ib�{@	ry�����A�����>|wt�;r���\F��yv��>��?� K����6@|��T%�����'W%90�E����l�u��g��}
g��?�/ULr��������=�5��$���r�J�]�����%����{��������;��;=��/�z�Z�b�
��TR�pO�a/����r���������B����E��#{DX��;)��8V�����f�N���Uy�����������y���L����ff�\����?V���n�n���bzo�}]���N\	�Y��.�9����cbg�@Nz�a*#h����Q_$'
=
v�����R ���]��L���cT��A&z=k��'�����/��^��5tk(���d�|��Q������;�Ri���p�D��� �!���a�]���|�=8v����?���zAT�2$	.E6a��#`��^�FC���3����LF=P���gd�Qvk��#�}3YlJ�jK�?7Z�(]t
�3�s�����5V�P��gKp�����j��&L=W~���+���`�'�~��[��Dp1�w=	�x%�6��kZv���cp�b�s�G�2{�x��	�)�N{�b3�����za/
��4*}��2�>5m��p�����a�cn$6���`�Y;������I���TW��/Ke�5!�_tF�q�A������%����=�&4,|/���G	�g��[�d�W��W5Kk4�g���a���������GQ�4��
�`�2��F����I:z���qLN���������Si��)Q~�	'�����xS��_�@U��"Z��o�5A�������������������m�a@�o������jg������W��5����H�j#wV�;Q+�������q2�5��ZL.��n(�R�
�����s�U�ByQ�4����qx>�IuJG� g:}��ro7&A���D
V�(����h���G�l��I^�h�b�1����*9S������$Ki
��t�s�J^�+�UUe�DAXK�dr����M��9�������4k��t�E����}�O�HJ����KrRp�S���T���>��2��w��C:����,��^I���_�!���}�_�5��������^eO�8<�L������]J����V���N��V~�<Z�1)c�({�O�Z��g%p����X��o��c<�����N���d����7�r����O���v���z����JrS����tSGm��Yx��-���������A�������d�S����$�s�me

��A@�����y���sB��~�?�����<������E�M���9���J��Ci�f�H�_�d�H�d6�Z��([�0�y����?���l��������������?�?!������04����}e4w��q�ds�7�l$��2�N���
p�Y���"&z�����y�9�)�_�m�d���t�._�����tv�NJ{o_��d�����7(����HF��:����{B6����*������I���9�y��Kt(���CH��3(y�7_��4k�U�f�<��N�L�������R���m!_)_���N ���Z�8�|�vk=�\�M�4wa��/e�DE�g����=.^E�h���ai�`��$9k��������$�K��B$��]f�v�!��K��e�G:�w$Z��ip�!�
xIc�rPJX��6>�*������so���1��v�5�n�b�T�^�]��B7���������
]m,�$Gz��*4q<�9���!���v�#c�����������S�����*��[U"��o.B�8_����#T,����B���W���JH+��sD[8U������:x�w B�=�������i������g#������dN��_��_O��p[����GP:�L�������3z�b��o�dgv3B�����G=J#��/����9�\����m�s�B�L{��(�6����w���=0_c�p�~#}��>������mf��z��4*i#��
�,+"��e���?8���A�l��{z��u�hN�so��������98&�
7���������Nh�Pm}�x>�e����j�
�����!�����;?`��@�i�C_t�d$!K�-�����P,	�i���f�9?���8>)~��� J,��?�H4��/~���j��������)l�L�����#oV:����s����u%�'��zH`�,�u>�7g���7��9��cO$a�;[p�������%LI[7���J)�a��M�k�rq���N&
(�}�!/� �z�Cb�����7;;����������3l����`�jF��{?iZ�6�k���&���X�xKSK�?��B���&�����������9�H��P�������q�X�V��rX��?1�� ���j����Z#=l��d��7�89��E��]�+B��atM�����u��r�EY6n�,q9"�c�����Z�Uk��%�����y0���� g�E���P_|dM ��;��G��l�yh��>�����|^����<J�jN��6����+K�L{�dTV����O(&m�����Y%v�6}��Gj21&D�u5����j��T#BW�v��xB�xR�����p.&������V��?���I����\2�hh�\��#�07v������8��p�^��w��^����;W�a�vj'g�*,�epdt�<��(��0z����CX�7��Y��{@0�_!�����"��M��*:��3\4���YCj�$#c���P������;R��7�'��L+��Z���m	�5(�l��	#9!k'���D���n)��:��pL��'��u�Q��D7��l�*th�<�@����D��)�*�������:���L��T���O����������:;�$*�����`9_�U�����K��MH1���`Q���M<mf{k������{	�^�90�$�SY�����r��A@#����>4���q.W����C���^�B��vQ�
�9Ec��k�:&�|e���i�P�:�1wv�k6�����|���V8�@|�p�{PC������'{��;[����n������&���^���0��;�c�4
�)���Yy!��zH�;L| O�PxP�oa[�����+K8���O9�|����H_���
g�K������\M�<B+��rv�N0�S�<��V����
�T,���
z�
5��a�,��3<R��9�`�eP��1��rC��p�\fW�T��@�l�E8_�g�ra��H����X4���M������2��]ihm��eb�R.����
6��TW6E�>k+"P�a}eC��� ��`�'�`8r�n&���d���7�3����s*y�Q���niQ�E1>������7��%>w�E�[/����>��[-%.v�<����0�~�#;83,�^JD��;�
��b�d��q�|����Jd$%�������,�pn�Op��df��;&���������~
����`�^��*�!Q��W�W��0y,�1]ml�,�1���m34�U�@��+���IR&H��� ����$�g��q�R�"	Mgh�Ep��|��V���l�i��|.��5c����f*�����S\K����qI����'Y��	��#@�	k=���^A`_!v<�����@T�Zu��vve�[m����s��{,�j���(n`�0�h�z�Z)@�Zu��|-�yY0JEEaX��i��4_��(�3R�����_\K����7[CS��@��e�G�Q���m�6)����q��������Bj�?��0�@'�q��8���Ci�����P�5���P�S
��"_��v��r�3o����:^����D
Z�*���a�[dt���w/{0N�*m��nU�����������le��� ��J��OB��������?Y�E��tZ;~R����y�N�;�C���n\�5�|-��^������?ga�>�Om]�&����x����z$�f�_���K���k���;����3�s|t��)���	���!��Or
T����c�������h���X����8�/\��w� �9��3\y��F�c���_��N�_�Y�!G��y��,~��]�o������n=�e�g��x����>�~\�*���S4��%@�Y���~�;�<&���d-_��?���d6����_|���L
�PL��B���g�����1���~��?�9�[������X*F����jq��V�8G������!�����R���U�&�I�7���h<���fh:���f�1+��Z����._�]��O`�����el�����:
b[de-V������N~\/�%����#�?��6sZ�5�J�/�w��|�0��'o1agf�2��*Z/�RF���w��8��O���I���d:���D'h�J��=����(���uO���D��JX������G����[�X�����6@/���-+���!���'!*nES_��6���Uu��2��������������<���>����Z9Aa�2�Qp+yH��A�p6f��~�L��%�`+�%�����|���~�{�Y�uK��^Q�������6X�K�L����w���o�
:a�X��:����/}|���LV���|m>���{�<5f[�oR5�,%��B��d�?�nR�+'}���nX�t�2�cx��>��F�Ss�d�tA��''L�H�����k�(�����liC���Hc�P%)
EF�zk�je`��3]���{��>���7��Zzs�o��06������T�C_�
.��*�a]���Ii�abSzK�) �%��er19
�C�W��?#����c�:����Ye�@���������a+?��>�-d��b=F�z�n�{Rz�tG���	��*��� ��J����0��V���`Q���A�U����m�.a�������:K�����t���u.}�ayEl�����
�����.%�3���������!���oJ6����V��N��z����`�'cbEI�����A�v�5U�������q���d��7%n+�O�X9�k|)���\F;�j��F�����p|���p����?�
CTSO-Q�!>�����iJ��ag�=o��2K4��_Q~�i�N�q�s�X��1~��|6?��R���9�A:����#a_���i�������#�X��6�������U�m�G&��6�8�� ���Cu+��!y�����q7����(�fR^��s�w%���o����-@��r�a-7�V�=�=�3���k��-��I���V���zdMz��jK�7f�;��0bS��9�J����*����]#i��OIE����x��/���p�l��Yw;����wB�_2S|��2eJ��f��$
=�����B� �����s�[��Cwq��M���c�Ug�e_���saB�� ��p
E���G�]�2�������b�����_9w����0*����K��Jo�a�}cv�7	#q{���[���V*5ZSNQJ������`�?i (}>,[uR�?\�#����|bcw�����O��I���Tb���Xm���u�u���_��W��$��L�
��1���c��������<�9R{���������*F;�b���*��~T�18�������"{u��i��*������}1,�9�#DJ�J	1����$\���_]���^g����+���[^{97�*Xov�*��������,�����g�������K�x-w��������v�;:/���m�D��3��H+!���A�����y������=���{��S�3�����>@'��Ol�/a�e�(����&HG�X93�1�w�=��H3NcSN��x��_���m-J���CK�u�
f�y�T��:G��G����6;��Ea��<������D��w����nW�i��b�������$���}��3:����%j��bt����NQ�Z`#��WXQ,�!;x�<�����@6+h$Wv��-}�g��)Aw�����~C��:X�%����>��S��l������Rm7s��{x��[�R��P��t�
=�o�S�}Zg;�ordL�'�^?��[�y�|F����F���(������9����Sb4����<%���~�	HtF��@$'�t�3��3������._�?9�������.QQ3��,��:�����8����<�GV���cH�
aR�+hJ-m�I���tu7>Z�e
�a����.G}�1��K|J.
��[�Y���tZ.�M����-���8���(�vx&����z�V��I��|���g�6��[��8�D���l
�8��@l-�F�\�]�k��k�M����m$��H�A4^=����"�@�2����a]Q{���%���`��"�(�1}=q����&���k8�|.�)~1bN��I%��c&�f��M��{F���tk��Y�kn��6}�$m=��QH"N�f}�������_��J��[���G�&���}l��9���l%�Wf���32�^w�<~_��
��b��	��+��zYE�,����jK~�U&O��Ie���H��LL���������^����X�7��l���af��C��F��"?@�p��X��=��dV��nO�yp�����Y��,VmJ�^����_,
�t�C�����j9d�)���
F;��V��y5���f�<?K�[������:�t2�|�)���D�;yI�z�]|���"�+kp�c�C�Le��t�����	\�������sH(���3�7�r
��c~V�e��?��	�X���&Z�7�����z�M�a�� \�����c��t�d�������p��$e�,V+Jo����x)��0|A�EQI�zbl�y��g|b��WE��$���\]7;j�+��D�����v�����c[����yQIc@�8R�D����J��T`�v�^k+
d�_w{�v��p���<���������E����b5R�=��%���dX��2Z�VRy��0�ZLj�������q>���'�G'Ue"��h;C�c!B���\���#Wof���������������WK��})�T��j����V����;�@
pq+�� %OB��5kR	���rF�����9��9�}��y�����`��&���a�GL5��'�q`�iq��G��J��f�[����=�����t�4���n��� �(�k�������j#���������br��<*}�S�1�`po9��
�T�c~u����{��$�N���������}��������N�"���=������H�[��I�:h���(�&{K��b?�7��I�p.��~%�����$�	�)���>	a�w����}%���P7N���E2pk}@�G{��D��Z�	�Gr)+�'���Z���������c�1��jA��9�7����O��[,�;�������_,�EF�zT<}B��	�Ve����X�(TW1)N�n�J7}�kd��������������*��W������v����R�I�/3p^�������vy��gz��gx)�j�Z�&�(|k�#��&5Z� �mZ�3�{Zd8�����)�U��N&|����9���@���s>�t9x0\���i'^���`5px��s��#>|��r*~�VR���Z��b%�JZu���,
�n�;}*�1���ZG,CX"��"�O�UfL�q�LkyL�$������X7�V]�jZ�5��q�TcfF�i������.~���rM$���r��s
[������N>0i:��i@���E��y ���[k��i��YN���zi��]���G
�f#����R]�_��e|��^����7�O�N�����+MPe�����F��J����V����Re��s��xL�x�
*�v�����]��9��
z��Y����{���{��Zo�I��FV�[��r+��
)�;VhN�iM�������Z��?�����\�������p��]a��B��@�\b1yT2:�	a����5,og�tFvt��P�oUN��|���|r28?���'�q;jUZ���4�I�M�-*��*�A���6[���9��vv�#����C��PZ(�AO��s[Q;�:]
	eU7qLvv�����r_��������Vr���W�V��l��>���o�������F��I:,KS]=����aw�7QA������t����:k4;L�����7���W�"�*�&��%�V���<:����$�(�u��xj�����O������j��$��������;����'9J��|+5.����L.� (y��]@�c�������d�����W�~p
���q���W:���������NGG��}A
@KF��_�<��@�EG3��_xX�q:��=��x��T�����I%���D����-�����=,8�:ap����0�����[�}����(Z�_����dl��Y�v�8��(�x7�u��Ij$����I_,qu�]��F�j���vJ�����������'�Oy��s���??���stp����u�=���k��vvb}�(z�q2g9�]���-�S�+���������!U�3�`=��X��x�J6+��la;W��������O�_���vG�7����Rw�{������I�CO6��1���<jfX����z�
����;�{�}(�>B���8)�!p+�2��^'>��������(��]���3�?
��E?����2FJc���[L"�_��Z�#������8��rvt�;��;:�����m���}�2=���Y������g����h��y���E��.�����z��� 
'�0��q�
�������$�#U�"eb`�#�^I�!L�,�FL�'�d�;a����%��+x��i����������r�a�s�@�H��9\F�XC�p?XFVl2��Wk����'���+.����[��0gX7��V��&
`t�3�#�:~�ZW�8����F��C3o�r��������)4�B����{�_Of�.j��^E�d������7>�[�"�.XN��%,�����ah=h������b��{�k�2���&����	��[�O�r���+��J�Q�W���?��_o����z�G��8����w@�%�9E��p�dC��xq-n�(��V�L���n��={$4�O9�#�� ���',���B��=��v��|�����|���(
���}��wf���!=D�I3�>g�9�3���K6�F�4���������������P=���w7��?�2%�y�pk�M�\�x�t$jb$?������D���,'J(��2$fL�p�r�lg4g����2f�)�wC
"��������������'���x*�<	-�t�����!��=\�w��[�\G�o6[8�4w�s�����*���|���[_
��n���P�����\�
q?K���!�
���E2�����8U��{K�4c�����g�A��I�������Hq��N������=����h��_1���m��1��{jz���_u�	���ep�A��J�t�*J 1^N�9����/����^�%Q���T��M�S9�D�U���.L`8#��KV1
a|�����nA���|�;9��TN�t�G'���d���szv��{���S�W����)/w����>�C�
SsN��2��1~�f;D'���������:%F�r&b�'�������k���H\U>���r�������dE���*&�x�������U�d���m������AP��q�Lf��������R���//�0��k��[�q�'��[�6�u@��')��_a��C��xJ���*� @���:|�����7��x�w��3]���&��y�z}B��[�j���������+l����>�Nb�_����D +��d:��8{J��� /}q��������8a�9syi6T~��9���[�/�V.��2���	U��?��/p��>7��S�#��Qr_�����.���1F ��M��`9��2���=Y4�?c6V������pIM
~���aQE������V
��?�@Sg=�D��h�55!��7V|�4���7d7�Amg��h��#o!�@���9�
���_��o'3�{����Y���eM�����!���<B]����8�;�V����h��AP���Z7S�?Cf����AY��,����6Zk��C!���_$����?���dU
N���:���R�V�e8�V��s�n�Y�.Y�8W2���54�-YS�v������}���Z��|�H��J�q|1����'o�\6���=����>+��8+�H=��\zv�`�XV7��lG��"d���b(l��4�g��	Gx`M�j8��,0��.��K�[�J�Y����Q�	h!����8�/|D������n� ������V^>�!����B��zOx���dT�h����m����2"x�����f������m����+�����-j7M��`$� �'V��Pl�u�,���]����;iJ:�v�nJ�&O���O��s��s�3�V������#��mR�<�G��[Hl:����&]p�l��/��5���+�a�����\g��'��N�l~&S&}���_	��<�)/�#��z0��Ix3"71��[�|hw[�n�EI��y��E���/��PO�������%K}�K�����`�v:�%�|�"��}>�.�%�a�_x;
j��<L�@�[��C0b�d��^��/�����F��_���Y8�����	�7���W���N�}��"��8:��L��QFX@
3�6��%�Tt�e�`����7G�Q�l~�v)n�.�d!����Y��u��N���v����`��@I��d�.�G�&lGvMeQo�Y?� �{2%0�X	�[�]��y�C�d��L��6�\?~@�&�E|�Q��Uk^�:�p�*����K7�l��e�b[�4���E������\�?����d`�+!
�����Qu*��)����A�vL7�.YF�d,���m�l53CP��u3/D����Z�v@���a�`��R�}�G����.�L��<�0��(�=���eJ-����
�f�c��QS1|�R�Zu�U�A�|���w:�����\�+�d�d������|�'a}�s��x!0��{�s�������oR��c�����o�/��>���u���a5}�y�:�7�U��"J��#`�!���m:��T.����}y>���@�o���$[��w>�
�ko�,JGdi�����v���/�7������S��E*�
�U�G�����uD��p����h{"����ba����zP��
��PF�������H ,��LA��2��v��.�%SX���!lL?�g��	n�WUoB�0I5��
��\� m�\:�v�J���&�#�]�
�`���D�~j=�(x�-�������"8o���?���oO����kN}���!��9�%��8���FG����e8��C��9
��l4���*�����7�X�]��������A���Kw�1������=��t�)���)��j��P<����Q�$�O�L�He�+��-!�{SBB�AcQ��Y�8
S��������i������GU3Mx+[6��E�WL�p�G0�a#���T��s���s�~��,�+�!CA�oi,8�R9@y����4����b&p+�R�@{��g��i@��� 0��EFd�#�
(�-������E�d@$V$�%��@���E�\5�>��XB����$���a�������
�p���M�\�#�*�C�I��AA�f0O�`mPZ0}O���3��5l<g'sK�K����U�U��%�_�!�s��C��o\��o(w"8!|��WG�����.��l<7�OsZW�6����NNa�S�E:��.��K������Q+T�|�������������
���F��a�D,�"�����0cr�V��8R��O�i]!�"ESI.�0�e������� �Fg��92-�|�~Q���D��+j[ ��.
c4���1�q�:�)�
\Wjx��z��N@���`=q����������PK���>�������'��U,=��W������e?9o���/ �C�'���F9���TfS���"O���w��b�l�?Z1��t��b�l����^R���{J1�tj;3�����_�+	vJ&WQ���\Nm���r��������k�q(~��^��f��%G��@�%�1���7;<9��-7�A|O�qx�>���tNO� �d�I�%,�{E�997-����I�JI����i�6�l2���k��D�9��A`��h�@%3�t��W�S	��=�h��N���H����j���S��7�|�Tu�������N�|7{������_������d���'6���������7�����orUP��)s���o��7e��2���r���U��)����o��7kM��2���yg��^T�G�����6/�y'0����:��4����p������2��{Q�����@_
���]��]������6�^cNpa`O�]<�����+4�����
~����<Z+�/�"��F���v^q��
6nNa�U�M=�������x�4�/ye�8bE��08V)�Y'��v�P����H����C��~P�<�
<��F��Xt_`G��(>�$�p �PEQ0
�O������.��dG���	xG��67w��\(��7�{�L�|wm(6x���_������
��&���*�2"�5����+x��7w�[?�f{#�Xi�b�1Mk
!R��Y�;b����dE(���+7��*���t�/������HvJ��C��*k��f�������5��{�G���n�]�o~�Z8�������q���u��	��Yrs/�tAPy�\�����Gb���u2�q�lcG���Bq@'Mz����	P^3O��f�8�
#59Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#58)
Re: Command Triggers, patch v11

On 6 March 2012 21:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

[CASCADE will not run the command triggers for cascaded objects]

If these are all expected, does it in any way compromise the
effectiveness of DDL triggers in major use-cases?

I don't think so.  When replicating the replica will certainly drop the
same set of dependent objects, for example.  Auditing is another story.
Do we want to try having cascaded object support in drop commands?

I wasn't sure if auditing was one of the rationale behind the feature
or not. If it is, it presents a major problem. How does the replica
know that the objects were dropped?

Thanks for the updated patch and the quick turnaround time. I'll give
it another review.

--
Thom

#60Thom Brown
thom@linux.com
In reply to: Thom Brown (#59)
Re: Command Triggers, patch v11

On 6 March 2012 21:18, Thom Brown <thom@linux.com> wrote:

On 6 March 2012 21:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

[CASCADE will not run the command triggers for cascaded objects]

If these are all expected, does it in any way compromise the
effectiveness of DDL triggers in major use-cases?

I don't think so.  When replicating the replica will certainly drop the
same set of dependent objects, for example.  Auditing is another story.
Do we want to try having cascaded object support in drop commands?

I wasn't sure if auditing was one of the rationale behind the feature
or not.  If it is, it presents a major problem.  How does the replica
know that the objects were dropped?

Thanks for the updated patch and the quick turnaround time.  I'll give
it another review.

Okay, here's the update:

The message returned by creating a command trigger after create index
is still problematic:

thom@test=# CREATE COMMAND TRIGGER cmd_trg_after_create_index AFTER
CREATE INDEX EXECUTE PROCEDURE cmd_trg_info();
WARNING: AFTER CREATE INDEX CONCURRENTLY triggers are not supported
DETAIL: The command trigger will not get fired.
CREATE COMMAND TRIGGER

The detail suggests that even though the command trigger has been
requested, it won't be fired. Might I suggest:

WARNING: AFTER CREATE INDEX CONCURRENTLY triggers are not supported
DETAIL: The command trigger will not fire on concurrently-created indexes.

CREATE VIEW doesn't return schema:

thom@test=# CREATE VIEW view_test AS SELECT id, stuff FROM public.test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE
VIEW' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='CREATE VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='view_test'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='CREATE VIEW'
objectid=16606 schemaname='<NULL>' objectname='view_test'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='CREATE VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
CREATE VIEW

No specific triggers fire when altering a conversion:

thom@test=# ALTER CONVERSION test9 RENAME TO test8;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
CONVERSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
CONVERSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER CONVERSION

Note: I hadn’t previously tested this, but I don’t think it was listed
as supported until now.

No specific triggers fire when altering the properties of a function:

thom@test=# ALTER FUNCTION test.testfunc2() COST 77;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER FUNCTION

No specific triggers fire when altering a sequence:

thom@test=# ALTER SEQUENCE test_seq2 OWNER TO test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER SEQUENCE

thom@test=# ALTER SEQUENCE test_seq2 RESTART WITH 4;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER SEQUENCE

No specific triggers when altering a view:

thom@test=# ALTER VIEW view_test2 SET SCHEMA testnew;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER VIEW

thom@test=# ALTER VIEW testnew.view_test2 ALTER COLUMN id SET DEFAULT 9;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER VIEW

The object name shown in specific triggers when dropping aggregates
shows the schema name:

thom@test=# DROP AGGREGATE testnew.avgtest2(bigint);
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
AGGREGATE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP AGGREGATE'
objectid=16539 schemaname='testnew' objectname='testnew'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP AGGREGATE'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP
AGGREGATE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP AGGREGATE

Same for collations:

thom@test=# DROP COLLATION testnew.en_gb_test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
COLLATION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP COLLATION'
objectid=16542 schemaname='testnew' objectname='testnew'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP COLLATION'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP
COLLATION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP COLLATION

When dropping domains, the name of the domain includes the schema name:

thom@test=# DROP DOMAIN testnew.us_postal_code;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
DOMAIN' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP DOMAIN'
objectid=16546 schemaname='testnew'
objectname='testnew.us_postal_code'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP DOMAIN'
objectid=<NULL> schemaname='testnew'
objectname='testnew.us_postal_code'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP DOMAIN'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP DOMAIN

Dropping functions shows the object name as the schema name:

thom@test=# DROP FUNCTION testnew.testfunc2();
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP FUNCTION'
objectid=16557 schemaname='testnew' objectname='testnew'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP FUNCTION'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP FUNCTION

Same with dropping operators:

thom@test=# DROP OPERATOR testnew.<<|| (int2, int2);
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
OPERATOR' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP OPERATOR'
objectid=16566 schemaname='testnew' objectname='testnew'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP OPERATOR'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP
OPERATOR' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP OPERATOR

...and operator family:

thom@test=# DROP OPERATOR FAMILY testnew.test_ops2 USING btree;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
OPERATOR FAMILY' objectid=<NULL> schemaname='<NULL>'
objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='DROP OPERATOR
FAMILY' objectid=16571 schemaname='testnew' objectname='testnew'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='DROP OPERATOR
FAMILY' objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='DROP
OPERATOR FAMILY' objectid=<NULL> schemaname='<NULL>'
objectname='<NULL>'
DROP OPERATOR FAMILY

… and the same for dropping text search
configuration/dictionary/parser/template.

I hadn't previously tested triggers against CLUSTER, REINDEX, VACUUM
and LOAD, but have tested them now.

When creating a trigger on REINDEX, I get the following message:

thom@test=# CREATE COMMAND TRIGGER cmd_trg_before_reindex BEFORE
REINDEX EXECUTE PROCEDURE cmd_trg_info();
WARNING: REINDEX DATABASE is not supported
DETAIL: The command trigger will not get fired.

My problem with this is the same as what I reported previously for
CREATE INDEX, in that it suggests the DDL itself isn't supported.

But more importantly:

Creating AFTER CLUSTER command triggers produce an error (as expected
since it's not supported), but AFTER REINDEX only produces a warning.
These should be the same, probably both an error.

VACUUM doesn't fire a specific command trigger:

thom@test=# VACUUM;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='VACUUM'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
VACUUM

REINDEX on a table seems to show no schema name but an object name for
specific triggers:

thom@test=# REINDEX TABLE testnew.test9;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=16558 schemaname='<NULL>' objectname='testnew'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='REINDEX'
objectid=16558 schemaname='<NULL>' objectname='testnew'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
REINDEX

When REINDEXing an index rather than a table, the table's details are
shown in the trigger. Is this expected?:

thom@test=# REINDEX INDEX testnew.idx_test_2 ;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: Command trigger: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=16565 schemaname='testnew' objectname='test9'
NOTICE: Command trigger: tg_when='AFTER' cmd_tag='REINDEX'
objectid=16565 schemaname='testnew' objectname='test9'
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
REINDEX

REINDEXing the whole database doesn't fire specific command triggers:

thom@test=# REINDEX DATABASE test;
NOTICE: Command trigger on any: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE: table "pg_catalog.pg_class" was reindexed
<snip>
NOTICE: table "information_schema.sql_features" was reindexed
NOTICE: Command trigger on any: tg_when='AFTER' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
REINDEX

The same happens for the REINDEX SYSTEM syntax.

Documentation:

I previously noted text which needed correcting that doesn’t seem to
have fixed yet. The email I reported those in misleadingly began with
“Right, hopefully this should be my last piece of list spam...”. The
only one which I feel no longer needs fixing is the first thing I
reported due to the fact that command triggers can now only been
applied against one command at a time. Also disregard the comments in
that email for ALTER and DROP.

You appear to have added support for ALTER CAST, but there isn’t any such DDL.

OPERATOR CLASS and OPERATOR FAMILY are listed twice.

“Note that objects dropped by effect of DROP CASCADE will not provoque
firing a command trigger”
should probably be:
“Note that objects dropped by the effect of DROP CASCADE will not
result in a command trigger firing.”

“That also applis to other dependencies following, as in DROP OWNED BY.”
should be:
“That also applies to other dependencies following, as in DROP OWNED BY.”

Another typo (which was in the email I referred to earlier) is
s/exercize/exercise/.

“Triggers on ANY command support more commands than just this list,
and will only provide the command tag argument as NOT NULL.”
should be:
“Triggers on ANY command support more commands than just this list,
and will provide NULL values for every argument except for the
argument that determines whether the trigger was before or after the
command event, and the command tag.”

This will no doubt need changing to refer to the new trigger variables
if you introduce them.

--
Thom

#61Thom Brown
thom@linux.com
In reply to: Thom Brown (#60)
Re: Command Triggers, patch v11

On 6 March 2012 23:25, Thom Brown <thom@linux.com> wrote:

On 6 March 2012 21:18, Thom Brown <thom@linux.com> wrote:

On 6 March 2012 21:04, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

[CASCADE will not run the command triggers for cascaded objects]

If these are all expected, does it in any way compromise the
effectiveness of DDL triggers in major use-cases?

I don't think so.  When replicating the replica will certainly drop the
same set of dependent objects, for example.  Auditing is another story.
Do we want to try having cascaded object support in drop commands?

I wasn't sure if auditing was one of the rationale behind the feature
or not.  If it is, it presents a major problem.  How does the replica
know that the objects were dropped?

Thanks for the updated patch and the quick turnaround time.  I'll give
it another review.

Okay, here's the update:

The message returned by creating a command trigger after create index
is still problematic:

thom@test=# CREATE COMMAND TRIGGER cmd_trg_after_create_index AFTER
CREATE INDEX EXECUTE PROCEDURE cmd_trg_info();
WARNING:  AFTER CREATE INDEX CONCURRENTLY triggers are not supported
DETAIL:  The command trigger will not get fired.
CREATE COMMAND TRIGGER

The detail suggests that even though the command trigger has been
requested, it won't be fired.  Might I suggest:

WARNING:  AFTER CREATE INDEX CONCURRENTLY triggers are not supported
DETAIL:  The command trigger will not fire on concurrently-created indexes.

CREATE VIEW doesn't return schema:

thom@test=# CREATE VIEW view_test AS SELECT id, stuff FROM public.test;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='CREATE
VIEW' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='CREATE VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='view_test'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='CREATE VIEW'
objectid=16606 schemaname='<NULL>' objectname='view_test'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='CREATE VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
CREATE VIEW

No specific triggers fire when altering a conversion:

thom@test=# ALTER CONVERSION test9 RENAME TO test8;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
CONVERSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
CONVERSION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER CONVERSION

Note: I hadn’t previously tested this, but I don’t think it was listed
as supported until now.

No specific triggers fire when altering the properties of a function:

thom@test=# ALTER FUNCTION test.testfunc2() COST 77;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER FUNCTION

No specific triggers fire when altering a sequence:

thom@test=# ALTER SEQUENCE test_seq2 OWNER TO test;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER SEQUENCE

thom@test=# ALTER SEQUENCE test_seq2 RESTART WITH 4;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='ALTER
SEQUENCE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER SEQUENCE

No specific triggers when altering a view:

thom@test=# ALTER VIEW view_test2 SET SCHEMA testnew;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER VIEW

thom@test=# ALTER VIEW testnew.view_test2 ALTER COLUMN id SET DEFAULT 9;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='ALTER VIEW'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
ALTER VIEW

The object name shown in specific triggers when dropping aggregates
shows the schema name:

thom@test=# DROP AGGREGATE testnew.avgtest2(bigint);
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
AGGREGATE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='DROP AGGREGATE'
objectid=16539 schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='DROP AGGREGATE'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='DROP
AGGREGATE' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP AGGREGATE

Same for collations:

thom@test=# DROP COLLATION testnew.en_gb_test;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
COLLATION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='DROP COLLATION'
objectid=16542 schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='DROP COLLATION'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='DROP
COLLATION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP COLLATION

When dropping domains, the name of the domain includes the schema name:

thom@test=# DROP DOMAIN testnew.us_postal_code;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
DOMAIN' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='DROP DOMAIN'
objectid=16546 schemaname='testnew'
objectname='testnew.us_postal_code'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='DROP DOMAIN'
objectid=<NULL> schemaname='testnew'
objectname='testnew.us_postal_code'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='DROP DOMAIN'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP DOMAIN

Dropping functions shows the object name as the schema name:

thom@test=# DROP FUNCTION testnew.testfunc2();
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='DROP FUNCTION'
objectid=16557 schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='DROP FUNCTION'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='DROP
FUNCTION' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP FUNCTION

Same with dropping operators:

thom@test=# DROP OPERATOR testnew.<<|| (int2, int2);
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
OPERATOR' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='DROP OPERATOR'
objectid=16566 schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='DROP OPERATOR'
objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='DROP
OPERATOR' objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
DROP OPERATOR

...and operator family:

thom@test=# DROP OPERATOR FAMILY testnew.test_ops2 USING btree;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='DROP
OPERATOR FAMILY' objectid=<NULL> schemaname='<NULL>'
objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='DROP OPERATOR
FAMILY' objectid=16571 schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='DROP OPERATOR
FAMILY' objectid=<NULL> schemaname='testnew' objectname='testnew'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='DROP
OPERATOR FAMILY' objectid=<NULL> schemaname='<NULL>'
objectname='<NULL>'
DROP OPERATOR FAMILY

… and the same for dropping text search
configuration/dictionary/parser/template.

I hadn't previously tested triggers against CLUSTER, REINDEX, VACUUM
and LOAD, but have tested them now.

When creating a trigger on REINDEX, I get the following message:

thom@test=# CREATE COMMAND TRIGGER cmd_trg_before_reindex BEFORE
REINDEX EXECUTE PROCEDURE cmd_trg_info();
WARNING:  REINDEX DATABASE is not supported
DETAIL:  The command trigger will not get fired.

My problem with this is the same as what I reported previously for
CREATE INDEX, in that it suggests the DDL itself isn't supported.

But more importantly:

Creating AFTER CLUSTER command triggers produce an error (as expected
since it's not supported), but AFTER REINDEX only produces a warning.
These should be the same, probably both an error.

VACUUM doesn't fire a specific command trigger:

thom@test=# VACUUM;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='VACUUM'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
VACUUM

REINDEX on a table seems to show no schema name but an object name for
specific triggers:

thom@test=# REINDEX TABLE testnew.test9;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=16558 schemaname='<NULL>' objectname='testnew'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='REINDEX'
objectid=16558 schemaname='<NULL>' objectname='testnew'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
REINDEX

When REINDEXing an index rather than a table, the table's details are
shown in the trigger.  Is this expected?:

thom@test=# REINDEX INDEX testnew.idx_test_2 ;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  Command trigger: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=16565 schemaname='testnew' objectname='test9'
NOTICE:  Command trigger: tg_when='AFTER' cmd_tag='REINDEX'
objectid=16565 schemaname='testnew' objectname='test9'
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
REINDEX

REINDEXing the whole database doesn't fire specific command triggers:

thom@test=# REINDEX DATABASE test;
NOTICE:  Command trigger on any: tg_when='BEFORE' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
NOTICE:  table "pg_catalog.pg_class" was reindexed
<snip>
NOTICE:  table "information_schema.sql_features" was reindexed
NOTICE:  Command trigger on any: tg_when='AFTER' cmd_tag='REINDEX'
objectid=<NULL> schemaname='<NULL>' objectname='<NULL>'
REINDEX

The same happens for the REINDEX SYSTEM syntax.

Documentation:

I previously noted text which needed correcting that doesn’t seem to
have fixed yet.  The email I reported those in misleadingly began with
“Right, hopefully this should be my last piece of list spam...”.  The
only one which I feel no longer needs fixing is the first thing I
reported due to the fact that command triggers can now only been
applied against one command at a time.  Also disregard the comments in
that email for ALTER and DROP.

You appear to have added support for ALTER CAST, but there isn’t any such DDL.

OPERATOR CLASS and OPERATOR FAMILY are listed twice.

“Note that objects dropped by effect of DROP CASCADE will not provoque
firing a command trigger”
should probably be:
“Note that objects dropped by the effect of DROP CASCADE will not
result in a command trigger firing.”

“That also applis to other dependencies following, as in DROP OWNED BY.”
should be:
“That also applies to other dependencies following, as in DROP OWNED BY.”

Another typo (which was in the email I referred to earlier) is
s/exercize/exercise/.

“Triggers on ANY command support more commands than just this list,
and will only provide the command tag argument as NOT NULL.”
should be:
“Triggers on ANY command support more commands than just this list,
and will provide NULL values for every argument except for the
argument that determines whether the trigger was before or after the
command event, and the command tag.”

This will no doubt need changing to refer to the new trigger variables
if you introduce them.

I've also since found that if I issue a VACUUM, CLUSTER or REINDEX on
a read-only standby, the BEFORE ANY COMMAND trigger fires. I don't
think any trigger should fire on a read-only standby.

--
Thom

#62Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#60)
1 attachment(s)
Re: Command Triggers, patch v11

Hi,

Thom Brown <thom@linux.com> writes:

The message returned by creating a command trigger after create index
is still problematic:

Fixed. I'm attaching an incremental patch here, the github branch is
updated too.

CREATE VIEW doesn't return schema:

Fixed, and as an added bonus I fixed the CREATE SEQUENCE oddity about
that too.

No specific triggers fire when altering a conversion:

Couldn't reproduce, works here, added tests.

No specific triggers fire when altering the properties of a function:

Fixed.

No specific triggers fire when altering a sequence:

Couldn't reproduce, added tests.

No specific triggers when altering a view:

Same again.

The object name shown in specific triggers when dropping aggregates
shows the schema name:
Same for collations:
Dropping functions shows the object name as the schema name:
Same with dropping operators:
...and operator family:
… and the same for dropping text search
configuration/dictionary/parser/template.

Fixed in the attached (all of those where located at exactly the same
place, by the way, that's one fix).

When dropping domains, the name of the domain includes the schema name:

I'm using format_type_be(objectId) so that int4 is integer and int8
bigint etc, but that's adding the schemaname. I didn't have time to look
for another API that wouldn't add the schemaname nor to add one myself,
will do that soon.

I hadn't previously tested triggers against CLUSTER, REINDEX, VACUUM
and LOAD, but have tested them now.

Cool :)

When creating a trigger on REINDEX, I get the following message:

Fixed.

Creating AFTER CLUSTER command triggers produce an error (as expected
since it's not supported), but AFTER REINDEX only produces a warning.
These should be the same, probably both an error.

Fixed.

VACUUM doesn't fire a specific command trigger:

I though it was better this way, I changed my mind and completed the code.

REINDEX on a table seems to show no schema name but an object name for
specific triggers:

Still on the TODO.

When REINDEXing an index rather than a table, the table's details are
shown in the trigger. Is this expected?:

Yeah well. Will see about how much damage needs to be done in the
current APIs, running out of steam for tonight's batch.

REINDEXing the whole database doesn't fire specific command triggers:

We don't want to because REINDEX DATABASE is managing transactions on
its own, same limitation as with AFTER VACUUM and all. Will have a look
at what it takes to document that properly.

Documentation:

Fixed.

Thom Brown <thom@linux.com> writes:

I've also since found that if I issue a VACUUM, CLUSTER or REINDEX on
a read-only standby, the BEFORE ANY COMMAND trigger fires. I don't
think any trigger should fire on a read-only standby.

Well I'm not sold on that myself (think pl/untrusted that would reach
out to the OS and do whatever is needed there). You can even set the
session_replication_role GUC to replica and only have the replica
command triggers fired.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

more-cmd-trigger-fixes.patchtext/x-patchDownload
*** a/doc/src/sgml/ref/create_command_trigger.sgml
--- b/doc/src/sgml/ref/create_command_trigger.sgml
***************
*** 41,48 **** CREATE COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFOR
      ALTER LANGUAGE
      ALTER OPERATOR
      ALTER OPERATOR CLASS
-     ALTER OPERATOR CLASS
-     ALTER OPERATOR FAMILY
      ALTER OPERATOR FAMILY
      ALTER SCHEMA
      ALTER SEQUENCE
--- 41,46 ----
***************
*** 137,146 **** CREATE COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFOR
    </para>
  
    <para>
!    Note that objects dropped by effect of <literal>DROP CASCADE</literal>
!    will not provoque firing a command trigger, only the top-level command
!    for the main object will fire a command trigger. That also applis to
!    other dependencies following, as in <literal>DROP OWNED BY</literal>.
    </para>
  
    <para>
--- 135,145 ----
    </para>
  
    <para>
!    Note that objects dropped by the effect of <literal>DROP
!    CASCADE</literal> will not result in a command trigger firing, only the
!    top-level command for the main object will fire a command trigger. That
!    also applies to other dependencies following, as in <literal>DROP OWNED
!    BY</literal>.
    </para>
  
    <para>
***************
*** 192,198 **** CREATE COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFOR
        you are connected to.
       </para>
       <para>
!       Commands that exercize their own transaction control are only
        supported in <literal>BEFORE</literal> command triggers, that's the
        case for <literal>VACUUM</literal>, <literal>CLUSTER</literal>
        <literal>CREATE INDEX CONCURRENTLY</literal>, and <literal>REINDEX
--- 191,197 ----
        you are connected to.
       </para>
       <para>
!       Commands that exercise their own transaction control are only
        supported in <literal>BEFORE</literal> command triggers, that's the
        case for <literal>VACUUM</literal>, <literal>CLUSTER</literal>
        <literal>CREATE INDEX CONCURRENTLY</literal>, and <literal>REINDEX
***************
*** 215,220 **** CREATE COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFOR
--- 214,224 ----
        not to be able to take over control from a superuser.
       </para>
       <para>
+       Triggers on ANY command support more commands than just this list, and
+       will provide NULL values for every argument except for the argument
+       that determines whether the trigger was before or after the command
+       event, and the command tag.
+ 
        Triggers on <literal>ANY</literal> command support more commands than
        just this list, and will only provide the <literal>command
        tag</literal> argument as <literal>NOT NULL</literal>. Supporting more
***************
*** 244,257 **** CREATE COMMAND TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFOR
        tag</literal>, <literal>objectid</literal> (can be null in case of a
        BEFORE CREATE or an AFTER DROP command trigger
        timing), <literal>schemaname</literal> (can be null for objects not
!       living in a schema, and for sequences due to an implementation limit)
!       and <literal>object name</literal> (can be null for any command
!       triggers).
!      </para>
!      <para>
!       The command <literal>CREATE SEQUENCE</literal> lacks support for
!       the <literal>schemaname</literal> command trigger argument, it
!       provides <literal>NULL</literal> in all cases.
       </para>
      </listitem>
     </varlistentry>
--- 248,255 ----
        tag</literal>, <literal>objectid</literal> (can be null in case of a
        BEFORE CREATE or an AFTER DROP command trigger
        timing), <literal>schemaname</literal> (can be null for objects not
!       living in a schema) and <literal>object name</literal> (can be null
!       for any command triggers).
       </para>
      </listitem>
     </varlistentry>
*** a/src/backend/commands/cmdtrigger.c
--- b/src/backend/commands/cmdtrigger.c
***************
*** 185,197 **** CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString)
  		ereport(WARNING,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("AFTER CREATE INDEX CONCURRENTLY triggers are not supported"),
! 				 errdetail("The command trigger will not get fired.")));
  
  	if (strcmp(stmt->command, "REINDEX") == 0)
  		ereport(WARNING,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("REINDEX DATABASE is not supported"),
! 				 errdetail("The command trigger will not get fired.")));
  
  	if (funcrettype != VOIDOID)
  		ereport(ERROR,
--- 185,197 ----
  		ereport(WARNING,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("AFTER CREATE INDEX CONCURRENTLY triggers are not supported"),
! 				 errdetail("The command trigger will not fire on concurrently-created indexes.")));
  
  	if (strcmp(stmt->command, "REINDEX") == 0)
  		ereport(WARNING,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("REINDEX DATABASE is not supported"),
! 				 errdetail("The command trigger will not fire on REINDEX DATABASE.")));
  
  	if (funcrettype != VOIDOID)
  		ereport(ERROR,
*** a/src/backend/commands/dropcmds.c
--- b/src/backend/commands/dropcmds.c
***************
*** 316,323 **** get_object_name(CommandContext cmd, ObjectType objtype,
  		case OBJECT_FOREIGN_SERVER:
  		case OBJECT_OPCLASS:
  		case OBJECT_OPFAMILY:
! 			cmd->objectname = strVal(linitial(objname));
  			break;
  		default:
  			elog(ERROR, "unexpected object type (%d)", (int)objtype);
  			break;
--- 316,331 ----
  		case OBJECT_FOREIGN_SERVER:
  		case OBJECT_OPCLASS:
  		case OBJECT_OPFAMILY:
! 		{
! 			int len = list_length(objname);
! 			if (len == 1)
! 				cmd->objectname = strVal(linitial(objname));
! 			else if (len == 2)
! 				cmd->objectname = strVal(lsecond(objname));
! 			else
! 				elog(ERROR, "unexpected name list length (%d)", len);
  			break;
+ 		}
  		default:
  			elog(ERROR, "unexpected object type (%d)", (int)objtype);
  			break;
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 1324,1329 **** AlterFunction(AlterFunctionStmt *stmt)
--- 1324,1330 ----
  	List	   *set_items = NIL;
  	DefElem    *cost_item = NULL;
  	DefElem    *rows_item = NULL;
+ 	CommandContextData cmd;
  
  	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
  
***************
*** 1433,1444 **** AlterFunction(AlterFunctionStmt *stmt)
--- 1434,1461 ----
  								repl_val, repl_null, repl_repl);
  	}
  
+ 	/* Call BEFORE ALTER FUNCTION command triggers */
+ 	InitCommandContext(&cmd, (Node *)stmt, false);
+ 
+ 	if (CommandFiresTriggers(&cmd))
+ 	{
+ 		cmd.objectId = InvalidOid;
+ 		cmd.objectname = pstrdup(NameStr(procForm->proname));
+ 		cmd.schemaname = get_namespace_name(procForm->pronamespace);
+ 
+ 		ExecBeforeCommandTriggers(&cmd);
+ 	}
+ 
  	/* Do the update */
  	simple_heap_update(rel, &tup->t_self, tup);
  	CatalogUpdateIndexes(rel, tup);
  
  	heap_close(rel, NoLock);
  	heap_freetuple(tup);
+ 
+ 	/* Call AFTER ALTER FUNCTION command triggers */
+ 	if (CommandFiresAfterTriggers(&cmd))
+ 		ExecAfterCommandTriggers(&cmd);
  }
  
  /*
*** a/src/backend/commands/sequence.c
--- b/src/backend/commands/sequence.c
***************
*** 213,233 **** DefineSequence(CreateSeqStmt *seq)
  	stmt->tablespacename = NULL;
  	stmt->if_not_exists = false;
  
! 	/*
! 	 * Call BEFORE CREATE SEQUENCE triggers
! 	 */
  	InitCommandContext(&cmd, (Node *)seq, false);
  
! 	if (CommandFiresTriggers(&cmd))
! 	{
! 		cmd.objectId = InvalidOid;
! 		cmd.objectname = NameStr(name);
! 		cmd.schemaname = NULL;		/* can't publish it easily enough here */
! 
! 		ExecBeforeCommandTriggers(&cmd);
! 	}
! 
! 	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
  	Assert(seqoid != InvalidOid);
  
  	rel = heap_open(seqoid, AccessExclusiveLock);
--- 213,222 ----
  	stmt->tablespacename = NULL;
  	stmt->if_not_exists = false;
  
! 	/* Prepare BEFORE CREATE SEQUENCE triggers */
  	InitCommandContext(&cmd, (Node *)seq, false);
  
! 	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, &cmd);
  	Assert(seqoid != InvalidOid);
  
  	rel = heap_open(seqoid, AccessExclusiveLock);
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 415,421 **** static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
   * ----------------------------------------------------------------
   */
  Oid
! DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
  {
  	char		relname[NAMEDATALEN];
  	Oid			namespaceId;
--- 415,421 ----
   * ----------------------------------------------------------------
   */
  Oid
! DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, CommandContext cmd)
  {
  	char		relname[NAMEDATALEN];
  	Oid			namespaceId;
***************
*** 608,613 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 608,623 ----
  		}
  	}
  
+ 	/* Exec BEFORE CREATE view|sequence|table|type command triggers */
+ 	if (CommandFiresTriggers(cmd))
+ 	{
+ 		cmd->objectId = InvalidOid;
+ 		cmd->objectname = pstrdup(relname);
+ 		cmd->schemaname = get_namespace_name(namespaceId);
+ 
+ 		ExecBeforeCommandTriggers(cmd);
+ 	}
+ 
  	/*
  	 * Create the relation.  Inherited defaults and constraints are passed in
  	 * for immediate handling --- since they don't need parsing, they can be
***************
*** 670,675 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 680,686 ----
  	 */
  	relation_close(rel, NoLock);
  
+ 	/* AFTER command triggers are fired from well outside this function */
  	return relationId;
  }
  
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 2110,2130 **** DefineCompositeType(RangeVar *typevar, List *coldeflist,
  	}
  
  	/*
- 	 * Call BEFORE CREATE (composite) TYPE triggers
- 	 */
- 	if (CommandFiresTriggers(cmd))
- 	{
- 		cmd->objectId = InvalidOid;
- 		cmd->objectname = createStmt->relation->relname;
- 		cmd->schemaname = get_namespace_name(typeNamespace);
- 
- 		ExecBeforeCommandTriggers(cmd);
- 	}
- 
- 	/*
  	 * Finally create the relation.  This also creates the type.
  	 */
! 	relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid);
  	Assert(relid != InvalidOid);
  
  	/* Call AFTER CREATE (composite) TYPE triggers */
--- 2110,2118 ----
  	}
  
  	/*
  	 * Finally create the relation.  This also creates the type.
  	 */
! 	relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, cmd);
  	Assert(relid != InvalidOid);
  
  	/* Call AFTER CREATE (composite) TYPE triggers */
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
***************
*** 123,130 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
  	else
  		in_outer_xact = IsInTransactionChain(isTopLevel);
  
! 	/* CAll BEFORE VACUUM command triggers */
! 	if (!IsAutoVacuumWorkerProcess() && vacstmt->relation != NULL)
  	{
  		CommandContextData cmd;
  		InitCommandContext(&cmd, (Node *)vacstmt, false);
--- 123,130 ----
  	else
  		in_outer_xact = IsInTransactionChain(isTopLevel);
  
! 	/* Call BEFORE VACUUM command triggers */
! 	if (!IsAutoVacuumWorkerProcess())
  	{
  		CommandContextData cmd;
  		InitCommandContext(&cmd, (Node *)vacstmt, false);
***************
*** 132,140 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
  		if (CommandFiresTriggers(&cmd))
  		{
  			cmd.objectId = relid;
- 			cmd.objectname = vacstmt->relation->relname;
- 			cmd.schemaname = vacstmt->relation->schemaname;
  
  			ExecBeforeCommandTriggers(&cmd);
  		}
  	}
--- 132,143 ----
  		if (CommandFiresTriggers(&cmd))
  		{
  			cmd.objectId = relid;
  
+ 			if (vacstmt->relation != NULL)
+ 			{
+ 				cmd.objectname = vacstmt->relation->relname;
+ 				cmd.schemaname = vacstmt->relation->schemaname;
+ 			}
  			ExecBeforeCommandTriggers(&cmd);
  		}
  	}
*** a/src/backend/commands/view.c
--- b/src/backend/commands/view.c
***************
*** 169,186 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
  	lockmode = replace ? AccessExclusiveLock : NoLock;
  	(void) RangeVarGetAndCheckCreationNamespace(relation, lockmode, &viewOid);
  
- 	/*
- 	 * Call BEFORE CREATE VIEW triggers
- 	 */
- 	if (CommandFiresTriggers(cmd))
- 	{
- 		cmd->objectId = InvalidOid;
- 		cmd->objectname = relation->relname;
- 		cmd->schemaname = relation->schemaname;
- 
- 		ExecBeforeCommandTriggers(cmd);
- 	}
- 
  	if (OidIsValid(viewOid) && replace)
  	{
  		Relation	rel;
--- 169,174 ----
***************
*** 208,213 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
--- 196,211 ----
  		 */
  		Assert(relation->relpersistence == rel->rd_rel->relpersistence);
  
+ 		/* Call BEFORE CREATE VIEW triggers */
+ 		if (CommandFiresTriggers(cmd))
+ 		{
+ 			cmd->objectId = viewOid;
+ 			cmd->objectname = RelationGetRelationName(rel);
+ 			cmd->schemaname = get_namespace_name(RelationGetNamespace(rel));
+ 
+ 			ExecBeforeCommandTriggers(cmd);
+ 		}
+ 
  		/*
  		 * Create a tuple descriptor to compare against the existing view, and
  		 * verify that the old column list is an initial prefix of the new
***************
*** 282,288 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
  		 * existing view, so we don't need more code to complain if "replace"
  		 * is false).
  		 */
! 		relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid);
  		Assert(relid != InvalidOid);
  		return relid;
  	}
--- 280,286 ----
  		 * existing view, so we don't need more code to complain if "replace"
  		 * is false).
  		 */
! 		relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, cmd);
  		Assert(relid != InvalidOid);
  		return relid;
  	}
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 691,706 **** standard_ProcessUtility(Node *parsetree,
  				 */
  				InitCommandContext(&cmd, parsetree, false);
  
- 				if (CommandFiresTriggers(&cmd))
- 				{
- 					cmd.objectId = InvalidOid;
- 					cmd.objectname = stmt->relation->relname;
- 					cmd.schemaname = get_namespace_name(
- 						RangeVarGetCreationNamespace(stmt->relation));
- 
- 					ExecBeforeCommandTriggers(&cmd);
- 				}
- 
  				/* Run parse analysis ... */
  				stmts = transformCreateStmt(stmt, queryString);
  
--- 691,696 ----
***************
*** 717,723 **** standard_ProcessUtility(Node *parsetree,
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
  												RELKIND_RELATION,
! 												InvalidOid);
  
  						/*
  						 * Let AlterTableCreateToastTable decide if this one
--- 707,714 ----
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
  												RELKIND_RELATION,
! 												InvalidOid,
! 												&cmd);
  
  						/*
  						 * Let AlterTableCreateToastTable decide if this one
***************
*** 741,747 **** standard_ProcessUtility(Node *parsetree,
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
  												RELKIND_FOREIGN_TABLE,
! 												InvalidOid);
  						CreateForeignTable((CreateForeignTableStmt *) stmt,
  										   relOid);
  					}
--- 732,739 ----
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
  												RELKIND_FOREIGN_TABLE,
! 												InvalidOid,
! 												&cmd);
  						CreateForeignTable((CreateForeignTableStmt *) stmt,
  										   relOid);
  					}
*** a/src/include/commands/tablecmds.h
--- b/src/include/commands/tablecmds.h
***************
*** 21,27 ****
  #include "utils/relcache.h"
  
  
! extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
  
  extern void RemoveRelations(DropStmt *drop);
  
--- 21,28 ----
  #include "utils/relcache.h"
  
  
! extern Oid	DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
! 						   CommandContext cmd);
  
  extern void RemoveRelations(DropStmt *drop);
  
*** a/src/test/regress/expected/cmdtriggers.out
--- b/src/test/regress/expected/cmdtriggers.out
***************
*** 26,31 **** alter command trigger snitch_before set disable;
--- 26,32 ----
  alter command trigger snitch_before set enable;
  alter command trigger snitch_after_ rename to snitch_after;
  create command trigger snitch_create_table after create table execute procedure snitch();
+ create command trigger snitch_create_seq after create sequence execute procedure snitch();
  create command trigger snitch_create_view after create view execute procedure snitch();
  create command trigger snitch_alter_table after alter table execute procedure snitch();
  create command trigger snitch_alter_seq after alter sequence execute procedure snitch();
***************
*** 54,59 **** create command trigger snitch_alter_domain before alter domain execute procedure
--- 55,70 ----
  create command trigger snitch_create_type before create type execute procedure snitch();
  create command trigger snitch_alter_type before alter type execute procedure snitch();
  create command trigger snitch_before_alter_function before alter function execute procedure snitch();
+ create command trigger snitch_alter_conversion before alter conversion execute procedure snitch();
+ create command trigger snitch_drop_agg before drop aggregate execute procedure snitch();
+ create command trigger snitch_before_drop_tsconf before drop text search configuration execute procedure snitch();
+ create command trigger snitch_before_drop_tsdict before drop text search dictionary execute procedure snitch();
+ create command trigger snitch_before_drop_tsparser before drop text search parser execute procedure snitch();
+ create command trigger snitch_before_drop_tstmpl before drop text search template execute procedure snitch();
+ create command trigger snitch_vacuum before vacuum execute procedure snitch();
+ create command trigger snitch_reindex before reindex execute procedure snitch();
+ WARNING:  REINDEX DATABASE is not supported
+ DETAIL:  The command trigger will not fire on REINDEX DATABASE.
  create schema cmd;
  NOTICE:  snitch: BEFORE any CREATE SCHEMA
  NOTICE:  snitch: BEFORE CREATE SCHEMA <NULL> cmd
***************
*** 63,72 **** NOTICE:  snitch: BEFORE any CREATE SCHEMA
--- 74,85 ----
  NOTICE:  snitch: BEFORE CREATE SCHEMA <NULL> cmd2
  NOTICE:  snitch: AFTER any CREATE SCHEMA
  create role regbob;
+ ERROR:  role "regbob" already exists
  create table cmd.foo(id bigserial primary key);
  NOTICE:  snitch: BEFORE any CREATE TABLE
  NOTICE:  CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"
  NOTICE:  snitch: BEFORE any CREATE SEQUENCE
+ NOTICE:  snitch: AFTER CREATE SEQUENCE cmd foo_id_seq
  NOTICE:  snitch: AFTER any CREATE SEQUENCE
  NOTICE:  snitch: BEFORE any CREATE INDEX
  NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
***************
*** 166,171 **** NOTICE:  snitch: AFTER ALTER TABLE cmd test
--- 179,185 ----
  NOTICE:  snitch: AFTER any ALTER TABLE
  create sequence test_seq_;
  NOTICE:  snitch: BEFORE any CREATE SEQUENCE
+ NOTICE:  snitch: AFTER CREATE SEQUENCE public test_seq_
  NOTICE:  snitch: AFTER any CREATE SEQUENCE
  alter sequence test_seq_ owner to regbob;
  NOTICE:  snitch: BEFORE any ALTER SEQUENCE
***************
*** 217,223 **** NOTICE:  snitch: AFTER ALTER SEQUENCE cmd test_seq
  NOTICE:  snitch: AFTER any ALTER SEQUENCE
  create view view_test as select id, things from cmd.test;
  NOTICE:  snitch: BEFORE any CREATE VIEW
! NOTICE:  snitch: AFTER CREATE VIEW <NULL> view_test
  NOTICE:  snitch: AFTER any CREATE VIEW
  alter view view_test owner to regbob;
  NOTICE:  snitch: BEFORE any ALTER VIEW
--- 231,237 ----
  NOTICE:  snitch: AFTER any ALTER SEQUENCE
  create view view_test as select id, things from cmd.test;
  NOTICE:  snitch: BEFORE any CREATE VIEW
! NOTICE:  snitch: AFTER CREATE VIEW public view_test
  NOTICE:  snitch: AFTER any CREATE VIEW
  alter view view_test owner to regbob;
  NOTICE:  snitch: BEFORE any ALTER VIEW
***************
*** 243,250 **** cluster cmd.foo using foo_pkey;
--- 257,270 ----
  NOTICE:  snitch: BEFORE any CLUSTER
  vacuum cmd.foo;
  NOTICE:  snitch: BEFORE any VACUUM
+ NOTICE:  snitch: BEFORE VACUUM cmd foo
  vacuum;
  NOTICE:  snitch: BEFORE any VACUUM
+ NOTICE:  snitch: BEFORE VACUUM <NULL> <NULL>
+ reindex table cmd.foo;
+ NOTICE:  snitch: BEFORE any REINDEX
+ NOTICE:  snitch: BEFORE REINDEX <NULL> cmd
+ NOTICE:  snitch: AFTER any REINDEX
  set session_replication_role to replica;
  create table cmd.bar();
  reset session_replication_role;
***************
*** 254,278 **** NOTICE:  snitch: AFTER any CREATE INDEX
  drop index cmd.idx_foo;
  NOTICE:  snitch: BEFORE any DROP INDEX
  NOTICE:  snitch: AFTER any DROP INDEX
! create function cmd.fun(int) returns text language sql
  as $$ select t from cmd.foo where id = $1; $$;
  NOTICE:  snitch: BEFORE any CREATE FUNCTION
! NOTICE:  snitch: AFTER CREATE FUNCTION cmd fun
  NOTICE:  snitch: AFTER any CREATE FUNCTION
! alter function cmd.fun(int) strict;
  NOTICE:  snitch: BEFORE any ALTER FUNCTION
  NOTICE:  snitch: AFTER any ALTER FUNCTION
! alter function cmd.fun(int) rename to notfun;
  NOTICE:  snitch: BEFORE any ALTER FUNCTION
! NOTICE:  snitch: BEFORE ALTER FUNCTION cmd fun
  NOTICE:  snitch: AFTER ALTER FUNCTION cmd notfun
  NOTICE:  snitch: AFTER any ALTER FUNCTION
! alter function cmd.notfun(int) set schema public;
  NOTICE:  snitch: BEFORE any ALTER FUNCTION
  NOTICE:  snitch: BEFORE ALTER FUNCTION cmd notfun
! NOTICE:  snitch: AFTER ALTER FUNCTION public notfun
  NOTICE:  snitch: AFTER any ALTER FUNCTION
! drop function public.notfun(int);
  NOTICE:  snitch: BEFORE any DROP FUNCTION
  NOTICE:  snitch: AFTER any DROP FUNCTION
  create function cmd.plus1(int) returns bigint language sql
--- 274,310 ----
  drop index cmd.idx_foo;
  NOTICE:  snitch: BEFORE any DROP INDEX
  NOTICE:  snitch: AFTER any DROP INDEX
! create function fun(int) returns text language sql
  as $$ select t from cmd.foo where id = $1; $$;
  NOTICE:  snitch: BEFORE any CREATE FUNCTION
! NOTICE:  snitch: AFTER CREATE FUNCTION public fun
  NOTICE:  snitch: AFTER any CREATE FUNCTION
! alter function fun(int) strict;
  NOTICE:  snitch: BEFORE any ALTER FUNCTION
+ NOTICE:  snitch: BEFORE ALTER FUNCTION public fun
+ NOTICE:  snitch: AFTER ALTER FUNCTION public fun
  NOTICE:  snitch: AFTER any ALTER FUNCTION
! alter function fun(int) rename to notfun;
  NOTICE:  snitch: BEFORE any ALTER FUNCTION
! NOTICE:  snitch: BEFORE ALTER FUNCTION public fun
! NOTICE:  snitch: AFTER ALTER FUNCTION public notfun
! NOTICE:  snitch: AFTER any ALTER FUNCTION
! alter function notfun(int) set schema cmd;
! NOTICE:  snitch: BEFORE any ALTER FUNCTION
! NOTICE:  snitch: BEFORE ALTER FUNCTION public notfun
  NOTICE:  snitch: AFTER ALTER FUNCTION cmd notfun
  NOTICE:  snitch: AFTER any ALTER FUNCTION
! alter function cmd.notfun(int) owner to regbob;
  NOTICE:  snitch: BEFORE any ALTER FUNCTION
  NOTICE:  snitch: BEFORE ALTER FUNCTION cmd notfun
! NOTICE:  snitch: AFTER ALTER FUNCTION cmd notfun
! NOTICE:  snitch: AFTER any ALTER FUNCTION
! alter function cmd.notfun(int) cost 77;
! NOTICE:  snitch: BEFORE any ALTER FUNCTION
! NOTICE:  snitch: BEFORE ALTER FUNCTION cmd notfun
! NOTICE:  snitch: AFTER ALTER FUNCTION cmd notfun
  NOTICE:  snitch: AFTER any ALTER FUNCTION
! drop function cmd.notfun(int);
  NOTICE:  snitch: BEFORE any DROP FUNCTION
  NOTICE:  snitch: AFTER any DROP FUNCTION
  create function cmd.plus1(int) returns bigint language sql
***************
*** 306,311 **** NOTICE:  snitch: BEFORE any ALTER AGGREGATE
--- 338,344 ----
  NOTICE:  snitch: AFTER any ALTER AGGREGATE
  drop aggregate public.avg(float8);
  NOTICE:  snitch: BEFORE any DROP AGGREGATE
+ NOTICE:  snitch: BEFORE DROP AGGREGATE public avg
  NOTICE:  snitch: AFTER any DROP AGGREGATE
  create collation cmd.french (LOCALE = 'fr_FR');
  NOTICE:  snitch: BEFORE any CREATE COLLATION
***************
*** 423,430 **** NOTICE:  snitch: BEFORE ALTER TRIGGER cmd footg
  NOTICE:  snitch: AFTER any ALTER TRIGGER
  drop trigger foo_trigger on cmd.foo;
  NOTICE:  snitch: BEFORE any DROP TRIGGER
! NOTICE:  snitch: BEFORE DROP TRIGGER <NULL> cmd
! NOTICE:  snitch: AFTER any DROP TRIGGER
  create text search configuration test (parser = "default");
  NOTICE:  snitch: BEFORE any CREATE TEXT SEARCH CONFIGURATION
  NOTICE:  snitch: AFTER CREATE TEXT SEARCH CONFIGURATION public test
--- 456,478 ----
  NOTICE:  snitch: AFTER any ALTER TRIGGER
  drop trigger foo_trigger on cmd.foo;
  NOTICE:  snitch: BEFORE any DROP TRIGGER
! ERROR:  unexpected name list length (3)
! create conversion test for 'utf8' to 'sjis' from utf8_to_sjis;
! NOTICE:  snitch: BEFORE any CREATE CONVERSION
! NOTICE:  snitch: AFTER any CREATE CONVERSION
! create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis;
! NOTICE:  snitch: BEFORE any CREATE CONVERSION
! NOTICE:  snitch: AFTER any CREATE CONVERSION
! alter conversion test2 rename to test3;
! NOTICE:  snitch: BEFORE any ALTER CONVERSION
! NOTICE:  snitch: BEFORE ALTER CONVERSION public test2
! NOTICE:  snitch: AFTER any ALTER CONVERSION
! drop conversion test3;
! NOTICE:  snitch: BEFORE any DROP CONVERSION
! NOTICE:  snitch: AFTER any DROP CONVERSION
! drop conversion test;
! NOTICE:  snitch: BEFORE any DROP CONVERSION
! NOTICE:  snitch: AFTER any DROP CONVERSION
  create text search configuration test (parser = "default");
  NOTICE:  snitch: BEFORE any CREATE TEXT SEARCH CONFIGURATION
  NOTICE:  snitch: AFTER CREATE TEXT SEARCH CONFIGURATION public test
***************
*** 453,458 **** create text search template test_template (
--- 501,522 ----
  NOTICE:  snitch: BEFORE any CREATE TEXT SEARCH TEMPLATE
  NOTICE:  snitch: AFTER CREATE TEXT SEARCH TEMPLATE public test_template
  NOTICE:  snitch: AFTER any CREATE TEXT SEARCH TEMPLATE
+ drop text search configuration test;
+ NOTICE:  snitch: BEFORE any DROP TEXT SEARCH CONFIGURATION
+ NOTICE:  snitch: BEFORE DROP TEXT SEARCH CONFIGURATION public test
+ NOTICE:  snitch: AFTER any DROP TEXT SEARCH CONFIGURATION
+ drop text search dictionary test_stem;
+ NOTICE:  snitch: BEFORE any DROP TEXT SEARCH DICTIONARY
+ NOTICE:  snitch: BEFORE DROP TEXT SEARCH DICTIONARY public test_stem
+ NOTICE:  snitch: AFTER any DROP TEXT SEARCH DICTIONARY
+ drop text search parser test_parser;
+ NOTICE:  snitch: BEFORE any DROP TEXT SEARCH PARSER
+ NOTICE:  snitch: BEFORE DROP TEXT SEARCH PARSER public test_parser
+ NOTICE:  snitch: AFTER any DROP TEXT SEARCH PARSER
+ drop text search template test_template;
+ NOTICE:  snitch: BEFORE any DROP TEXT SEARCH TEMPLATE
+ NOTICE:  snitch: BEFORE DROP TEXT SEARCH TEMPLATE public test_template
+ NOTICE:  snitch: AFTER any DROP TEXT SEARCH TEMPLATE
  create function cmd.testcast(text) returns int4  language plpgsql as $$begin return 4::int4;end;$$;
  NOTICE:  snitch: BEFORE any CREATE FUNCTION
  NOTICE:  snitch: AFTER CREATE FUNCTION cmd testcast
***************
*** 489,498 **** NOTICE:  drop cascades to type cmd2.us_postal_code
  NOTICE:  snitch: AFTER any DROP SCHEMA
  drop role regbob;
  NOTICE:  snitch: BEFORE any DROP ROLE
! NOTICE:  snitch: AFTER any DROP ROLE
  drop command trigger snitch_before;
  drop command trigger snitch_after;
  drop command trigger snitch_create_table;
  drop command trigger snitch_create_view;
  drop command trigger snitch_alter_table;
  drop command trigger snitch_alter_seq;
--- 553,564 ----
  NOTICE:  snitch: AFTER any DROP SCHEMA
  drop role regbob;
  NOTICE:  snitch: BEFORE any DROP ROLE
! ERROR:  role "regbob" cannot be dropped because some objects depend on it
! DETAIL:  3 objects in database foo
  drop command trigger snitch_before;
  drop command trigger snitch_after;
  drop command trigger snitch_create_table;
+ drop command trigger snitch_create_seq;
  drop command trigger snitch_create_view;
  drop command trigger snitch_alter_table;
  drop command trigger snitch_alter_seq;
***************
*** 521,523 **** drop command trigger snitch_alter_domain;
--- 587,597 ----
  drop command trigger snitch_create_type;
  drop command trigger snitch_alter_type;
  drop command trigger snitch_before_alter_function;
+ drop command trigger snitch_alter_conversion;
+ drop command trigger snitch_drop_agg;
+ drop command trigger snitch_before_drop_tsconf;
+ drop command trigger snitch_before_drop_tsdict;
+ drop command trigger snitch_before_drop_tsparser;
+ drop command trigger snitch_before_drop_tstmpl;
+ drop command trigger snitch_vacuum;
+ drop command trigger snitch_reindex;
*** a/src/test/regress/sql/cmdtriggers.sql
--- b/src/test/regress/sql/cmdtriggers.sql
***************
*** 30,35 **** alter command trigger snitch_before set enable;
--- 30,36 ----
  alter command trigger snitch_after_ rename to snitch_after;
  
  create command trigger snitch_create_table after create table execute procedure snitch();
+ create command trigger snitch_create_seq after create sequence execute procedure snitch();
  create command trigger snitch_create_view after create view execute procedure snitch();
  create command trigger snitch_alter_table after alter table execute procedure snitch();
  create command trigger snitch_alter_seq after alter sequence execute procedure snitch();
***************
*** 59,64 **** create command trigger snitch_alter_domain before alter domain execute procedure
--- 60,75 ----
  create command trigger snitch_create_type before create type execute procedure snitch();
  create command trigger snitch_alter_type before alter type execute procedure snitch();
  create command trigger snitch_before_alter_function before alter function execute procedure snitch();
+ create command trigger snitch_alter_conversion before alter conversion execute procedure snitch();
+ create command trigger snitch_drop_agg before drop aggregate execute procedure snitch();
+ 
+ create command trigger snitch_before_drop_tsconf before drop text search configuration execute procedure snitch();
+ create command trigger snitch_before_drop_tsdict before drop text search dictionary execute procedure snitch();
+ create command trigger snitch_before_drop_tsparser before drop text search parser execute procedure snitch();
+ create command trigger snitch_before_drop_tstmpl before drop text search template execute procedure snitch();
+ 
+ create command trigger snitch_vacuum before vacuum execute procedure snitch();
+ create command trigger snitch_reindex before reindex execute procedure snitch();
  
  create schema cmd;
  create schema cmd2;
***************
*** 113,118 **** alter view cmd.view_test2 alter column id drop default;
--- 124,130 ----
  cluster cmd.foo using foo_pkey;
  vacuum cmd.foo;
  vacuum;
+ reindex table cmd.foo;
  
  set session_replication_role to replica;
  create table cmd.bar();
***************
*** 121,133 **** reset session_replication_role;
  create index idx_foo on cmd.foo(t);
  drop index cmd.idx_foo;
  
! create function cmd.fun(int) returns text language sql
  as $$ select t from cmd.foo where id = $1; $$;
  
! alter function cmd.fun(int) strict;
! alter function cmd.fun(int) rename to notfun;
! alter function cmd.notfun(int) set schema public;
! drop function public.notfun(int);
  
  create function cmd.plus1(int) returns bigint language sql
  as $$ select $1::bigint + 1; $$;
--- 133,147 ----
  create index idx_foo on cmd.foo(t);
  drop index cmd.idx_foo;
  
! create function fun(int) returns text language sql
  as $$ select t from cmd.foo where id = $1; $$;
  
! alter function fun(int) strict;
! alter function fun(int) rename to notfun;
! alter function notfun(int) set schema cmd;
! alter function cmd.notfun(int) owner to regbob;
! alter function cmd.notfun(int) cost 77;
! drop function cmd.notfun(int);
  
  create function cmd.plus1(int) returns bigint language sql
  as $$ select $1::bigint + 1; $$;
***************
*** 185,190 **** create trigger footg before update on cmd.foo for each row execute procedure cmd
--- 199,210 ----
  alter trigger footg on cmd.foo rename to foo_trigger;
  drop trigger foo_trigger on cmd.foo;
  
+ create conversion test for 'utf8' to 'sjis' from utf8_to_sjis;
+ create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis;
+ alter conversion test2 rename to test3;
+ drop conversion test3;
+ drop conversion test;
+ 
  create text search configuration test (parser = "default");
  
  create text search dictionary test_stem (
***************
*** 205,210 **** create text search template test_template (
--- 225,235 ----
    lexize = dsimple_lexize
  );
  
+ drop text search configuration test;
+ drop text search dictionary test_stem;
+ drop text search parser test_parser;
+ drop text search template test_template;
+ 
  create function cmd.testcast(text) returns int4  language plpgsql as $$begin return 4::int4;end;$$;
  create cast (text as int4) with function cmd.testcast(text) as assignment;
  
***************
*** 218,223 **** drop command trigger snitch_before;
--- 243,249 ----
  drop command trigger snitch_after;
  
  drop command trigger snitch_create_table;
+ drop command trigger snitch_create_seq;
  drop command trigger snitch_create_view;
  drop command trigger snitch_alter_table;
  drop command trigger snitch_alter_seq;
***************
*** 247,249 **** drop command trigger snitch_alter_domain;
--- 273,285 ----
  drop command trigger snitch_create_type;
  drop command trigger snitch_alter_type;
  drop command trigger snitch_before_alter_function;
+ drop command trigger snitch_alter_conversion;
+ drop command trigger snitch_drop_agg;
+ 
+ drop command trigger snitch_before_drop_tsconf;
+ drop command trigger snitch_before_drop_tsdict;
+ drop command trigger snitch_before_drop_tsparser;
+ drop command trigger snitch_before_drop_tstmpl;
+ 
+ drop command trigger snitch_vacuum;
+ drop command trigger snitch_reindex;
#63Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#62)
Re: Command Triggers, patch v11

On 8 March 2012 22:24, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

We're getting there. :)

Hi,

Thom Brown <thom@linux.com> writes:

The message returned by creating a command trigger after create index
is still problematic:

Fixed.  I'm attaching an incremental patch here, the github branch is
updated too.

Confirmed.

CREATE VIEW doesn't return schema:

Fixed, and as an added bonus I fixed the CREATE SEQUENCE oddity about
that too.

Yes, working now.

No specific triggers fire when altering a conversion:

Couldn't reproduce, works here, added tests.

My apologies. It seems I neglected to set up a specific trigger for
it. It does indeed work.

No specific triggers fire when altering the properties of a function:

Fixed.

Yes, tried every property available and working in every case now.

No specific triggers fire when altering a sequence:

Couldn't reproduce, added tests.

Again, I wrongly assumed I had set up a command trigger for this. I
think it's because I based my specific triggers on what was listed on
the CREATE COMMAND TRIGGER documentation page, and those were only
recently added. This is working fine.

No specific triggers when altering a view:

Same again.

Working correctly. (see above)

The object name shown in specific triggers when dropping aggregates
shows the schema name:
Same for collations:
Dropping functions shows the object name as the schema name:
Same with dropping operators:
...and operator family:
… and the same for dropping text search
configuration/dictionary/parser/template.

Fixed in the attached (all of those where located at exactly the same
place, by the way, that's one fix).

CREATE OPERATOR CLASS now shows objectname as 'hash' instead of its
name where it's declared as "USING hash". This isn't a problem with
ALTER/DROP OPERATOR CLASS. Everything else above works as expected
now.

When dropping domains, the name of the domain includes the schema name:

I'm using format_type_be(objectId) so that int4 is integer and int8
bigint etc, but that's adding the schemaname. I didn't have time to look
for another API that wouldn't add the schemaname nor to add one myself,
will do that soon.

Okay, skipping test.

When creating a trigger on REINDEX, I get the following message:

Fixed.

Could we change this to "REINDEX DATABASE triggers are not supported"?
This way it would be consistent with the "AFTER CREATE INDEX
CONCURRENTLY" warning.

VACUUM doesn't fire a specific command trigger:

I though it was better this way, I changed my mind and completed the code.

Yes, working now.

REINDEX on a table seems to show no schema name but an object name for
specific triggers:

Still on the TODO.

Skipped.

When REINDEXing an index rather than a table, the table's details are
shown in the trigger.  Is this expected?:

Yeah well.  Will see about how much damage needs to be done in the
current APIs, running out of steam for tonight's batch.

Skipped.

Documentation:

Fixed.

ALTER CAST is still listed and needs removing, not just from the
documentation but every place it's used your code too. I can
currently create a trigger for it, but it's impossible for it to fire
since there's no such command.

All these corrections I mentioned previously still needs to be made:

“A command trigger's function must return void, the only it can aborts
the execution of the command is by raising an exception.”
should be:
“A command trigger's function must return void. It can then only
abort the execution of the command by raising an exception.”

Remove:
“For a constraint trigger, this is also the name to use when modifying
the trigger's behavior using SET CONSTRAINTS.”

“that's the case for VACUUM, CLUSTER CREATE INDEX CONCURRENTLY, and
REINDEX DATABASE.”
should be:
“that's the case for VACUUM, CLUSTER, CREATE INDEX CONCURRENTLY, and
REINDEX DATABASE.”

Thom Brown <thom@linux.com> writes:

I've also since found that if I issue a VACUUM, CLUSTER or REINDEX on
a read-only standby, the BEFORE ANY COMMAND trigger fires.  I don't
think any trigger should fire on a read-only standby.

Well I'm not sold on that myself (think pl/untrusted that would reach
out to the OS and do whatever is needed there).  You can even set the
session_replication_role GUC to replica and only have the replica
command triggers fired.

All other command triggers don't fire on read-only standbys, and the
inconsistency doesn't seem right. On the one hand all
CREATE/DROP/ALTER triggers aren't fired because of the "cannot execute
<command> in a read-only transaction" error message, but triggers do
occur before utility commands, which would otherwise display the same
message, and might not display it at all if the trigger causes an
error in its function call. So it seems like they should either all
fire, or none of them should. What are you thoughts?

--
Thom

#64Thom Brown
thom@linux.com
In reply to: Thom Brown (#63)
Re: Command Triggers, patch v11

On 9 March 2012 00:28, Thom Brown <thom@linux.com> wrote:

On 8 March 2012 22:24, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

We're getting there. :)

It was late last night and I forgot to get around to testing pg_dump,
which isn't working correctly:

--
-- Name: cmd_trg_after_alter_aggregate; Type: COMMAND TRIGGER; Schema:
-; Owner:
--

CREATE COMMAND TRIGGER cmd_trg_after_alter_aggregate AFTER"ALTER
AGGREGATE" EXECUTE PROCEDURE cmd_trg_info ();

There shouldn't be quotes around the command, and when removing them,
ensure there's a space before the command. All variations of the
ALTER statements in the dump are fine, so are CREATE statements for
ANY COMMAND command triggers.

Also I notice that CREATE/ALTER/DROP COMMAND TRIGGER appears just
before CREATE/ALTER/DROP TRIGGER in the documentation. This breaks
the alphabetical order and I wasn't expecting to find it there when
scanning down the page. Could we move them into an alphabetic
position?

--
Thom

#65Robert Haas
robertmhaas@gmail.com
In reply to: Thom Brown (#61)
Re: Command Triggers, patch v11

On Wed, Mar 7, 2012 at 4:53 PM, Thom Brown <thom@linux.com> wrote:

I've also since found that if I issue a VACUUM, CLUSTER or REINDEX on
a read-only standby, the BEFORE ANY COMMAND trigger fires.  I don't
think any trigger should fire on a read-only standby.

Why ever not?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#66Thom Brown
thom@linux.com
In reply to: Robert Haas (#65)
Re: Command Triggers, patch v11

On 9 March 2012 14:09, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Mar 7, 2012 at 4:53 PM, Thom Brown <thom@linux.com> wrote:

I've also since found that if I issue a VACUUM, CLUSTER or REINDEX on
a read-only standby, the BEFORE ANY COMMAND trigger fires.  I don't
think any trigger should fire on a read-only standby.

Why ever not?

Sorry, I meant any command trigger. It's because none of the commands
can be run on a standby, so the triggers don't seem appropriate.

--
Thom

#67Robert Haas
robertmhaas@gmail.com
In reply to: Thom Brown (#66)
Re: Command Triggers, patch v11

On Fri, Mar 9, 2012 at 9:22 AM, Thom Brown <thom@linux.com> wrote:

On 9 March 2012 14:09, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Mar 7, 2012 at 4:53 PM, Thom Brown <thom@linux.com> wrote:

I've also since found that if I issue a VACUUM, CLUSTER or REINDEX on
a read-only standby, the BEFORE ANY COMMAND trigger fires.  I don't
think any trigger should fire on a read-only standby.

Why ever not?

Sorry, I meant any command trigger.  It's because none of the commands
can be run on a standby, so the triggers don't seem appropriate.

I'm not convinced. Right now, it's fairly useless - all the triggers
could possibly do is throw an error, and an error is going to get
thrown anyway, so it's only a question of which error message the user
will see. But we discussed before the idea of adding a capability for
BEFORE triggers to request that the actual execution of the command
get skipped, and then it's possible to imagine this being useful.
Someone could even use a command trigger that detects which machine
it's running on, and if it's the standby, uses dblink to execute the
command on the master, or something crazy like that. Command triggers
could also be useful for logging all attempts to execute a particular
command, which is probably still appropriate on the standby.

I think that it will be a good thing to try to treat Hot Standby mode
as much like regular operation as is reasonably possible, across the
board.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#68Thom Brown
thom@linux.com
In reply to: Robert Haas (#67)
Re: Command Triggers, patch v11

On 9 March 2012 14:30, Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Mar 9, 2012 at 9:22 AM, Thom Brown <thom@linux.com> wrote:

On 9 March 2012 14:09, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Mar 7, 2012 at 4:53 PM, Thom Brown <thom@linux.com> wrote:

I've also since found that if I issue a VACUUM, CLUSTER or REINDEX on
a read-only standby, the BEFORE ANY COMMAND trigger fires.  I don't
think any trigger should fire on a read-only standby.

Why ever not?

Sorry, I meant any command trigger.  It's because none of the commands
can be run on a standby, so the triggers don't seem appropriate.

I'm not convinced.  Right now, it's fairly useless - all the triggers
could possibly do is throw an error, and an error is going to get
thrown anyway, so it's only a question of which error message the user
will see.  But we discussed before the idea of adding a capability for
BEFORE triggers to request that the actual execution of the command
get skipped, and then it's possible to imagine this being useful.
Someone could even use a command trigger that detects which machine
it's running on, and if it's the standby, uses dblink to execute the
command on the master, or something crazy like that.  Command triggers
could also be useful for logging all attempts to execute a particular
command, which is probably still appropriate on the standby.

I think that it will be a good thing to try to treat Hot Standby mode
as much like regular operation as is reasonably possible, across the
board.

I see your point. My suggestion to Dimitri in another email was
either enable triggers for all commands or none. At the moment it's
only available on utility commands.

--
Thom

#69Robert Haas
robertmhaas@gmail.com
In reply to: Thom Brown (#68)
Re: Command Triggers, patch v11

On Fri, Mar 9, 2012 at 9:35 AM, Thom Brown <thom@linux.com> wrote:

I see your point.  My suggestion to Dimitri in another email was
either enable triggers for all commands or none.  At the moment it's
only available on utility commands.

Yeah, that's clearly not the best of all possible worlds. :-)

I think we had better look seriously at postponing this patch to 9.3.
Your reviewing is obviously moving things forward rapidly, but I think
it's unrealistic to think this is going to be in a committable state
any time in the next week or two.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#70Thom Brown
thom@linux.com
In reply to: Robert Haas (#69)
Re: Command Triggers, patch v11

On 9 March 2012 14:47, Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Mar 9, 2012 at 9:35 AM, Thom Brown <thom@linux.com> wrote:

I see your point.  My suggestion to Dimitri in another email was
either enable triggers for all commands or none.  At the moment it's
only available on utility commands.

Yeah, that's clearly not the best of all possible worlds.  :-)

I think we had better look seriously at postponing this patch to 9.3.
Your reviewing is obviously moving things forward rapidly, but I think
it's unrealistic to think this is going to be in a committable state
any time in the next week or two.

That's unfortunate if that's the case. I'll dedicate any bandwidth
necessary for additional testing as I would really like to see this
get in, but if it transpires there's more outstanding work and
polishing needed than time Dimitri personally has available, then I
guess it'll have to be a 9.3 feature. :'(

--
Thom

#71Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#67)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

On Fri, Mar 9, 2012 at 9:22 AM, Thom Brown <thom@linux.com> wrote:

Sorry, I meant any command trigger. �It's because none of the commands
can be run on a standby, so the triggers don't seem appropriate.

I'm not convinced. Right now, it's fairly useless - all the triggers
could possibly do is throw an error, and an error is going to get
thrown anyway, so it's only a question of which error message the user
will see. But we discussed before the idea of adding a capability for
BEFORE triggers to request that the actual execution of the command
get skipped, and then it's possible to imagine this being useful.

Um, surely the "you can't do that in a read-only session" error is going
to get thrown long before the command trigger could be called?

regards, tom lane

#72Thom Brown
thom@linux.com
In reply to: Tom Lane (#71)
Re: Command Triggers, patch v11

On 9 March 2012 15:05, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Fri, Mar 9, 2012 at 9:22 AM, Thom Brown <thom@linux.com> wrote:

Sorry, I meant any command trigger.  It's because none of the commands
can be run on a standby, so the triggers don't seem appropriate.

I'm not convinced.  Right now, it's fairly useless - all the triggers
could possibly do is throw an error, and an error is going to get
thrown anyway, so it's only a question of which error message the user
will see.  But we discussed before the idea of adding a capability for
BEFORE triggers to request that the actual execution of the command
get skipped, and then it's possible to imagine this being useful.

Um, surely the "you can't do that in a read-only session" error is going
to get thrown long before the command trigger could be called?

Yes, at the moment that's the case. I said that this wasn't the case
for utility commands but I've noticed the message is different for
those:

ERROR: cannot execute VACUUM during recovery

vs

ERROR: cannot execute CREATE TABLE in a read-only transaction

So my complaint around that was misleading and wrong.

--
Thom

#73Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#71)
Re: Command Triggers, patch v11

On Fri, Mar 9, 2012 at 10:05 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Fri, Mar 9, 2012 at 9:22 AM, Thom Brown <thom@linux.com> wrote:

Sorry, I meant any command trigger.  It's because none of the commands
can be run on a standby, so the triggers don't seem appropriate.

I'm not convinced.  Right now, it's fairly useless - all the triggers
could possibly do is throw an error, and an error is going to get
thrown anyway, so it's only a question of which error message the user
will see.  But we discussed before the idea of adding a capability for
BEFORE triggers to request that the actual execution of the command
get skipped, and then it's possible to imagine this being useful.

Um, surely the "you can't do that in a read-only session" error is going
to get thrown long before the command trigger could be called?

Hmmm.... yeah.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#74Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#67)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

I'm not convinced. Right now, it's fairly useless - all the triggers
could possibly do is throw an error, and an error is going to get
thrown anyway, so it's only a question of which error message the user
will see. But we discussed before the idea of adding a capability for
BEFORE triggers to request that the actual execution of the command
get skipped, and then it's possible to imagine this being useful.
Someone could even use a command trigger that detects which machine
it's running on, and if it's the standby, uses dblink to execute the
command on the master, or something crazy like that. Command triggers
could also be useful for logging all attempts to execute a particular
command, which is probably still appropriate on the standby.

There are some other use cases, like using plsh to go apt-get install an
extension's package when you see the master just created it, so that
your read only queries on the hot standby have a chance of loading the
code you need.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#75Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#69)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

I think we had better look seriously at postponing this patch to 9.3.

I understand why you're drawing that conclusion, but I don't think
that's the best we can do here, by a long shot.

Your reviewing is obviously moving things forward rapidly, but I think
it's unrealistic to think this is going to be in a committable state
any time in the next week or two.

What's happening is that I've been abusing Thom's availability, leaving
him with the testing and fixing oddities along the way. Those came
mainly from an attempt at being as automatic as possible when writing
commands support. I'm now about done reviewing each and every call site
and having them covered in the tests.

What remains to be done now is how to pass down arguments to the
triggers (switching from function arguments to trigger style magic
variables, per Tom request), and review REINDEX and CREATE OPERATOR
CLASS support. That's about it.

The API and the call sites location have been stable for a long time
now, and are following your previous round of review. The catalog
storage and commands grammar are ok too, we've been hashing them out.
We've been very careful about not introducing code path hazards, the
only novelty being a new place to ERROR out (no fancy silent utility
command execution control).

Really, I would think we're about there now. I would be rather surprised
not to be able to finish that patch by the end of next week, and will
arrange myself to be able to devote more time on it each day if that's
what needed.

Remember that we intend to build an extension providing a C-coded
function doing the heavy lifting of back parsing the command string from
the Node parse tree, with the goal of having Slony, Londiste and Bucardo
able to use that and implement support for DLLs. I really want that to
happen in 2012.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#76Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#75)
Re: Command Triggers, patch v11

On Fri, Mar 9, 2012 at 12:51 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

I think we had better look seriously at postponing this patch to 9.3.

I understand why you're drawing that conclusion, but I don't think
that's the best we can do here, by a long shot.

Well, if you get to the point where you're done churning the code in
the next week or so, I'm willing to do one or two more rounds of
serious review, but if that doesn't get us there then I think we need
to give up. The energy you've put into this is commendable, but we're
about to start the third month of this CommitFest, and until we get
this release at least to beta or so, we can't start any new
CommitFests or branch the tree. That basically means that nothing
else of mine is going to get committed until the current crop of
patches are dealt with - or for a good while after, for that matter,
but getting the current crop of patches dealt with is the first step.
Of course, I also want to have a good release and I understand the
necessity of spending time on other people's patches as well as my
own, as I believe I've demonstrated, but I don't want to stay in that
mode indefinitely, which I think is an understandable position.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#77Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#63)
1 attachment(s)
Re: Command Triggers, patch v11

Hi,

Please find attached v15 of the patch, addressing all known issues apart
from the trigger function argument passing style. Expect a new patch
with that taken care of early next week.

(The github branch too, should you be using that)

Thom Brown <thom@linux.com> writes:

CREATE OPERATOR CLASS now shows objectname as 'hash' instead of its
name where it's declared as "USING hash". This isn't a problem with
ALTER/DROP OPERATOR CLASS. Everything else above works as expected
now.

Ah yes that needed a special case, it's properly handled now, and
tested.

When dropping domains, the name of the domain includes the schema name:

Fixed.

Could we change this to "REINDEX DATABASE triggers are not supported"?
This way it would be consistent with the "AFTER CREATE INDEX
CONCURRENTLY" warning.

Sure, done.

REINDEX on a table seems to show no schema name but an object name for
specific triggers:

Was a typo, fixed.

When REINDEXing an index rather than a table, the table's details are
shown in the trigger.  Is this expected?:

Fixed.

ALTER CAST is still listed and needs removing, not just from the
documentation but every place it's used your code too. I can
currently create a trigger for it, but it's impossible for it to fire
since there's no such command.

Removed.

All these corrections I mentioned previously still needs to be made:

That's about the docs, I edited them accordingly to your comments.

Thom Brown <thom@linux.com> writes:

All other command triggers don't fire on read-only standbys, and the
inconsistency doesn't seem right. On the one hand all
CREATE/DROP/ALTER triggers aren't fired because of the "cannot execute
<command> in a read-only transaction" error message, but triggers do
occur before utility commands, which would otherwise display the same
message, and might not display it at all if the trigger causes an
error in its function call. So it seems like they should either all
fire, or none of them should. What are you thoughts?

The others trigger don't fire because an ERROR case is detected before
they have a chance to run, much like on a primary in some ERROR cases.

Thom Brown <thom@linux.com> writes:

It was late last night and I forgot to get around to testing pg_dump,
which isn't working correctly:

Fixed.

Also I notice that CREATE/ALTER/DROP COMMAND TRIGGER appears just
before CREATE/ALTER/DROP TRIGGER in the documentation. This breaks
the alphabetical order and I wasn't expecting to find it there when
scanning down the page. Could we move them into an alphabetic
position?

I don't see that problem in the source files, could you be more specific?

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

command-trigger.v15.patch.gzapplication/octet-streamDownload
�tZO�=is�F���_1�K"����XVMB
k)R!�$���� ����d��_w����C���[U����kz�{�g��1g��q������O���x>�������.��,�����ux���5����	g�',��4vF>g8��a��0�,��b6�"�&a�����s�>e��+�3h
n�3
���~�7��}����0�:�M�����O@'7����[6����xaP1�U}j�?J�g3'"o:�Qi6u�����G���B�-��tg���U�?�}K������U����� ��|�?��
R�v�X''�H=N���%�p#��I[!,�VJ[i8qR�.9����M=)N\�'jv�����4EA1T�c���19])L_�+rr8s���qc�[��^Y�?��x}`�~QR�q���@c�]+U��0w�b����(L[�bn�R���TB@0mG�
��y����Bh����Ee`�c~�����g���T�R$vh]@���%�s`����8�����	��	�
�vwQTfC{�`������v�����w��^�����\i��'=d/���br��N����D��]������~��������Ix�����LSg�Y��HDLp�0�m�@N!83>��2���S����i4c~���)gpZx�G[6��$�i���1V�6/� ��=����7��&�B����4s��2tU�XN�6��A8��X"q����*������X���o������=@%@��4=����A�������v��?!
=��l�];�����]0��������?� �:q���$ Jy����m��?�o����}�n5������i��-l#T�j�B�;�	��r�L:��
�{����s���{BO�M-���Z=��;��.�C��n��q��k�!�%�S���������1����Q����m�z�`��x������j���7a�f)�
x�9��hKB�q�YXPrE���WJ�q�wN��0Qf�-���doe@B���M�IcB@��W5��?P!"��qV2@�fO��7��8�/���$V���P��5��q*E�WXi�H�O�<@Y	X�1U��>�z R�����<Fl�{.9�(�9-F0m����W�M7���������g<��q�&�7d������0�,��:4q�h�h��`u?Kt�Fd���</��m3>��lc�����v>:��g������6s7t�WD�������u����I�F���{�b��K��j��i[��G{����w�����~t5�Gx��w�y�g���9��'�wnd�"�l7�a��^�a7�a���&i��E�X��#���4��R��~Nb��o�>Wy����_\���l��Q��
�v�>hu;����v�_lnv/��|�������Z�����^����UA����4J�������E~d���^e#k���~u�y�����������|�����i�����R�$�)0��k|��<o]\��<6G5[Ds�w�t(Z����=�/��Ei���o������D��\�:~l�?��F��?����-j���y������9��mE���+��0�������lmu�����������Zs�V������]��i�j���n5tP�U�N+����bLN+���F�4Q5f�([*tQ����u�J�G��u�U��E���RKQ	�q�
��My����Q��|�PP=j+(^�-�v����Q��r��S8��W7�f(��H[�h�A%5+��)Y���b�?�`�)S/z�P.j7T���+_�h���z������O2-s�,y��tf��:P��{���g#t=�:���V�����&4)8���OeD��4����K9?�|�UN8E29��:����(
����|���2���M<��F|��0b��fF2E@�E%��=V/��3�1'�D<I� KM���8KC��VB%��!��qm����2rF9^�9-�o��h-K:!	x�������DZv2�f�h�qK��`M��m$RHw�0J��O0U[J7"QKP�Hr�������������H@�4&���v�8d�|�cND�����C��:	}?�'DdN�$�u��r���1d���=�~)�XHCH$��3�k����l4W� �4Q������K?]JX5����0�m�&l1��S�`�31&kh��]H1k=��C�	"��-��t����[>svK�L����*u�h�^&�S��t�:�7Qp3/�3*��V
��:��[��+X����G����������������s:��9�?8S�L�L�Ac�N.]0_�5�����i�������q1�6i��
L���;�J�	�
���|\Bj�<��7
d�1i�������3l���E���&�K�8kp�4��O�������6����k1/�����GEd����Pv�A���{x��1�F]2���lH
n���@�c�Ql��\/&k�E,���:r��{�'�ZO���h)�Mq���d���qvJ������M��g���d\n�i8�#�E��q����A����h�y��Q����������l���Ur����)]l2���1xi2��� ����u4�9�@a��5�9
��gC�aWr�i���n�i��I.��THv���^�H���m�"����}���'hm40u����g��<��+�v��`��4��!��?xi�%��g���aB�LA��1�VfYXS��%wa%br*,#�%s*HD�2S
��0��"A[�����0���J���]�o������+� Q��kl����$�������R���Qx��9�\���~Jp����P�S�+���v�T��E�t\�H��6�&,T��3���A���;7Vs9�4�
F	Q,����E����H��������N:xN!~�MPx������g�f<�0)`7�C�}1�����
`����N�/����������]��!�vY�OX��ay4� ����-�X�(����?�����[F�pc(JG(�o�k[�XT
v=�XFT
���#
�MH&a�KN	�G�D����J�i�,�o�L�Qq�<�����N��,_$��O�����"�Rn��Rv�6�&�1�c�S����GU!t�m�Umg�!�Q���cm���q�6�4�&�Fx��%�#P
L^3���` o��)0%���:5�1��A����V��

���P�)#���@�PY;���E�=��H�������
�@;�����|`r��1��L�f��x�������z�TO�6t
J�a)
jo�g�<|\q�j���l���O�a4��(��!I)��������
;
���}Qeg�!t{T=Zod�m"�9HCcK��~�������K�-��0���u��W�U��N���D?>��z�}�5��H��[}�el�W�����7t�x���x%jo`��i����~�tU�� n����(T��2��j;�$�/��FZ>�����8�!]>���q����3���`��evJN:]�T8b"�(m�@����s���8��u-h��=�a���:������h���k
�i96��V�]%��p����HQ0�;������
�<@���������y]W�n2Y�p��V�fAd�`=�Vr�
�Cr���I��\7y��Q,fgfu��B�5��2��j�?����e�����-������k+3�O����9�H�`��?����__�F�!�C�&�
WM�H�|}����*/J���� ��������|�������,w[��n����u�Ks�Av������zW��\�Z�/���.E���u����j��}.���e�F�#+]���.�������a3<�b�u���
>��V���/\�Q�/}|	��o�do�J��(���|���Q����B��q)�tm�Iy�Z���
E�6
��yQ��W���N�`1V|���fn&z�89�Z|yW����(��/MUqD������LLz�S�.X��E�J6���I��%.%��0�\�L���)�T*���e���>��x�1��U9�ZU��i�`���/����M�G6���_+\��H����D�"��,���{k��fNoy�^y�
h�W#eQ>0�`���q�oz���{|\�����
��5q�F�����"6sDU@���z�2*���X}K��Wx���78�l�7jE�E����r�5\���Kg>�}����������k#2:8����[!�"��H�l�SR�=����r]Zl=5������H�y'&5h�����"%�������`�|�.,Q������k#�����QF=<�h��S� P�d}�=5�!Vld��	u�B�`3���5��U�y��������9��z����
��A�O��T��d&_b��U�J�������������O������FE��������n����}��W�y�-�9��$!�h��{I���kLLJc�<JP�F'����E<��oC��U�	�,�.���*��5c�X����s��W���^s����������P���]#�E��Ga��G���/�~�[���~\��|}h�������(ha�������'�����x���uZ��g��X�������N��0v���<�\����?��aL$%����([T���*����/�"����e�9���*T?�\9%��?@,��v���������w�*6]iH�p�c�������}�w�l
��F��e_���x���'�|+��h�V��O�\�d�/�;��$�����2xp���$��ny�%�z��'v�$��I
�g��5�������\�&�5f����Ae�\��CL���R����g��W���\1$��@��
zg�C�:�� ����A%��Q�[t����9�=���em��D�]��9x��K3��!z��%���P�
����^X>����Fm��v�����,7G�������$��U�'��q���:��iv��g��������|�����6������c�����M�U���#���f���u�z%��x�����1{����3���%�����C�.������2K��I�g��sx��:|!��J{�e�T��Ja���������.���+{�69#�M�>8�<������z�����C���P�a����<#j5g��-��|�YN�_����9M���h�h0N�Jce�����{����O�����d����R2a|c��&_���K��,��R}^����6����&���>G<��3�O;2'����6��w��c���<yB��%����'=����z�h�ei��,m���'O�_.�	I�\@<���2x��Wi�_�������<�h�^Z��yb�/�2�
�K��{�E���*9�J��������?%�)"�l'H�/���Tr������,p��:������k4E����?��'��'h��"O����/���,~�����1��h�Lc�S�|��� ���I��U#��0w(RvC���^��-@@W/�U	�bu���o!�l������xI�[B��WK��OW*mNo�&���c�x7��u�>��	�.d?;v[M��
P<�����0�,��EL�p��^��5E�e�#�8'#�f��3�K:���-Nn�tn���IM`a�&����X+��k0}gG������^�g�Q�I5�4�x�I�Y�i*^%�ne8�J#d���Ud�]����w�J���qg�Z\b�q��M��@�����ao��M�@�J����L��������4�q�2mk''VZ�e[�g���C�%��f���a2N��$�/�	�����a�
���p����0<�(�W��/�~+
"������+�
���o�=�{��XO�XqU(�FT����^[G/_�������������m9
C���?�1��`����������(L�� �	��X�Y����`���1���W�~��������c��\Rm�=�K�E�:,y���VwB,�fi#�?����m#Y�Y�+`�wc*�e�K�2�0��E5��L����$^S� �hf����
4@��dg���7�F/���������8l���{��vMp#�B�oc���8�=��b�<g���x�o�&�=q�����'<?�I�����Z��a�i���	�
2���@"z�<���(�X���Kr�����3)-`�����������JF?��
w��F1|O������@�I���������p������X��0�b����B���)\���Et-C�|u�X,<�����H���|�O{H��aW��B"D��8+8��x�p.����1�f�:�m����n��B;�d�
��W��n�m-�s�,S�D��&�
`�;���L���b^�&����L��A�&��y]yW���AQ��Z#�rH_IkN������`�b����nUq�-�k��l7�Z�\5E3c<����?-�C�7��[1�\��c�'����-v@�[Q��{�y[��aT�$��x9��<��3��Qd|D���������y
���;��*�-'&U���Y�|	��wje�^���F�Cw5��$&�;�Ta� \vKF ��8���3�3A���}�R�I&��}�Y�����5<����8�����7R�����
c��g�����mml�(7���f�����56�"+sorW��o��*R1��&�rR�x ��[���<^��@t.����E�;�����5�Jh�d��Nd���w��}1_	 ����������W�&���Y���g�T�k�Ep������3��-�1�X�+�%[:o��_m&"����~�=�!�D��'����� ��_7��
��oH[�#_�<�Fv�m
D�����ot���_���D���3.�s�.��G�����Q������G�{�F�����$�2��&��Bz�����;I����_����������xN�~��������6��H]6�L��&��z�[��5>��Fm������������ U):x4]���1�C$���_Z#�s�/�\�������T��@76q(��"���
me�H,��<lMq��\t	x��I�8��z�C��(�.��(���d��'�4��G��w�P�a.��^X������!�{����
N[c�#Mp��9Y�
�"Ww�+tp�Y�%��xH<?)�ARgFjq=����C$P8�L6��2[�O�8W�9�����8��4R{!"~�~����w��ap�=����x�)���1�Bq��J���Xs�Qy��������E������l���S�$����/=��Fp��odB�={�b()=wc�3�%���pv��)����S��w�%��$�{*0/;"=0P�nC��������8L�D�'�����PA 5�����$��V��yY�B0�p�������~�T�����\���h�?M�U<��y��	���-�:*�����Rn`�KC�D��D��&�b4
Q q��s�}��nc��g�:k�!��������F��/�;��OB)Py~)L+�("%����T8�T�
��z>
[���K�@���_XV	�n)�r6��
�wy�*kf�P������d&�������d�1UB0����fY�5��r��D
�	�ud�����pSW5���%1�6PO���<��������.�����+<��3�^g���HHz��H�t���\3�a��B�vo����LS�-���Y�U�[F:����*h��D0�2M�T%������k�5C���3���0wx�~���:��g�������m�����,
v���/����Z|AO6�S{�����g�7�����>����G���#G"H��Y\Dr��|na4���~�����`�q�R���h1>Rn��Y����8�^�����}��w�������i��}Wc��4�e��f�47N B�Q����Ln���V7��n����.	?������
O��o�K��+�j��L��h�[Nf)E����^���������4���<��.���Z~���z������+L�-��8Kb�LIO ~4�{bD�f�?�'�%�7>��a����ya��)�-�ti�S^��Hj�*j-f}�+����Y�k�;e_:.m
*k~�R6Y	L�s_���������}�p��{��N[���b��*��y.M9�
nMp[:�x����$M�����|oz��Gw�����O���(b����X�]�S�W��$B�J��W�Z�Mk�Z�!y�
/N�A�����d^�it���mv�J1<bO���rF<7�
^[s���Q�I�
�r���W�����M���td������&�3������pq-����� �I.c
�<��WR3H��LP?-Bu�L�y��j~���p[��������I1�l���$���$y�������mIL_2� \���N�/����)��7�,q"5�T\_�PDG�M8�r����s����t�+�����hv���F��|�t���3|��]-��������~3�������ci��3{�=���=0Y�v��71�G�jT���k�s��h#��d<�>�8�\���,�v��}1������4���)F=���X�� ���������H4��Y��;Ohky|�8@iP���w@�X���C��9q
��~6`%s-<�

��I1����Y�@�����(�a�ze���Df�M#�\�ya>�dm�k��4���k��h���f��7��|���R��#�,*_��SS�EGzU�;��v�����4�%,?�@����>EhT���h^j�1c�5���"e������-�bk�?�gZ����3�kV���V�������%���r�oU�h����h�=���}8;9���C���K�^i�6Q�"�3�a�,����yy�d��*D�~��O������U�[J�����X�m,�����f�h~�c�����vbx�%f����BE@[.F�����rl�g�����-i_���w26�C�M�X����b��JVoI�
������&:w}��7G�CI4��z�1�Y���V]�i|s���
\�y��T�1O�w�j����8��mC��l�f��r
��T37m��c|@��9�1����[�f'F��TH�?�"	��vn��������X���20���!���e�]�>Z_�bq�Z�����\f�6N6��W�3��r<Y���������������k�?x#
1��L���gr�.��D�SL��|5����G�d�_���?B�+����_���;�'�8��R�>'p�!$�v~�PN'�Y:���t�X�8��,�����_�?Z�r��`DC�BS��� `x��S���'	u�C��is�!���Hn������96�bLVS�i��}�-���Y��l�J�s�((�����To�
a9'Q�9��>��`���}�JX�1!�QK�0!�}���pa��c�&_�r�J���t0�d|��@"��-�Q7��5�����rb,i*d�'
�m��Nn��Hg*�������}2�'����i��������7e����$�1?�Fp�M{�m��,���D�.M��Zn��J���|���3%�%����V���9Bx�HIy5�s+9����H��'��_y�'J���
����������&���F��Je��`H`ibhas#D�^Q�����P@�kY�V)�uVT���V��e_d��R�����tr&�V��_�����Z��Z-�$�'%�z
g�L:����$���B��\V�1�
�2|
����r���%'�5���Z;�:���>�� i?m:q��:�G�C���4�j��:����+P����*����ZS�jd�^�	T������I���t#m�����?��0A�0�x������^�":������� �EL5}���G�B�	��3�<bl�����v:��B'���fJ���3����E"m�\&���[��0��2����cV��b��N��'��T��,�������;�����Ka�)��f���J�O����N�2o�C��#�n��VRDL�1��w�{�{�l��e]aI;�%������9�����;�{�[���u�&��m����M���M0)*`pL2j�.}
�|�t}��ir�����o`K$�x�]/>��^
�����dF����p�]!�� 4�~GX��Y�@o�~� �o#"�����r!������J�\���8��C��pz�B+y���l�tO�}����t�x�"�g�I���?�4��%l?������_��B;��"�d�x�� �]�dQ:��,<�n���p�ci�v���G�
����E{;��;!���z������x 1�Bp�{&�.t���SQ��KWQ)7�J}�����������|��$�������|�4J�����������x+f����`�6�_�dR�s���X�,��0��bptL��{�K��#��S��:��5�,����9����43*�S�;D������@G{T2Ue���zN��bUE}�C�w��Q(L<�D�N�=�x��(>��H�AvrP���q��Y�U�@�J����I���$|7�G�����H��Y/���,��Tt���\�C���*�rg�.���n�:��c�a�{"�ag	��8������<����*������*s��dT\1����p F$� �`<N0��G�1$��5�����S�d7�~���nI.];�e��m5�[��u�������� m��xD�T\��1q�N�p��&c�����\9��&�hvh+��	�M]I�{g����i�{��%Xp1���w8��
�Y\���$�;�A7�7���Q����^���[~�VLs�{���-�m-�m�m��s�~
��Zn����!������l�������S�vb�anUJ6A=��UzUO�9�^�0�hx�+�IdGa��Co�OTD�&��x9���h�XY%	U�EN�������+��@��m20�*E�p�	'���=�i�����#�)�������
&g�W�T�\6y"���s��soZ��W�
��h��	]~����kK�M������s�W4���s�`(ARXga����uj�4���0���(,�e�
?l���G�p�!v���ih��E���k�����u8T�)��$�L����<�� ��@*p!����V�Cfk�����g"[�9��h�s������#���5�Y���[M�U�Z�M���5��TH�GI�������z�����l"�)�8{U������{{���0����7�x�7�Hy}3�Z�>���]9����z�S�����9��t�g��;��[�����FcN��Y���U�U��hT���QE���*�.a���?��������0�v�`p�~�|�^�i��
�nD_���Zg�}-X��P
l�YT[d]�Y^���q������5',�TB�-LoN�uK�_R�R$m-&�)"�&�#R(��t8���P&���W�j$�������x���5�&}��5�{�+��7�J�y�����o�6�����x���J��1�]^�- 2.d\*�	���$vt��x4�t�����a�q��7��]���s�(7�F�Y��r'��I�`)>�'��;@�Z���e�pKqh-)Ii(��^�s?��8��d�gF���m��qe��d�l��N�i�����%�,���0K�|��p�����#����k�R������@y�s��#�1�����z+�/f���F�0�����a�PH�>����B�cX��Hn0�+R��*���Y��FX�f������K��.���^a��G��E�<�����e���8d��E�+�2��
�J�[���v]��b���@��`��,%��t`b��!��I���2�/��I�)�p����5D��H���;�"h!�b�'1���ikFN�W�D������*-Y�q�5���a�;����]J:"�"��ep9�������b/%AF-/#�������FZ�Z1h[-��6r??����������_��7�;���J�P�cFJW���QO���J�dRU~x�a���dmT��F�4Zr�TD8���V���y�����Ql��� G�0�A{����
�r��Y<�1�gr���3%\b��7���������h:��s���w`�9�q7�>p�I_V�<��J��g�6a����w�l�F�Y0�o��l�T���V�M>�����py�W��Kjk����r��O[���Y��L���N�S3�����7��sNbWe��V��V��?.LlS�C ���F��6+�`���E]R�h�y��L%j���"�tS���n��`������l\��xF���t��t��*QNs�Ha?�ts���%�v��j����[Z��u�fxP$����D�NF��x(&�C����{J
���8�/��<�n�2M���g�R��C���u�0��L���-
��_,1p�/��d����~�f�Nc�o�����2Z�C\J��X����`G8
0��]H��e�"Z��{g��6���������
�A�S�-���-�^iY�|�B�T�P���P����?���3��}��]k]���>�H:��2C��z�i2,:��_
�W�*|�(�f����_Qe��@�o8�[|p�����:y��ixG��"�*��ip�Fr��0�sx�;>������~��H
��\'	y?���eww�C��G��.�A��P�����9a�?s�0�]t�Rm�w7Y��i����3T���C{�vB1���Z���Ro�Bf �����@n�kc0}��������U	_DALV�}�C��I����c�c|7���s��I�-y;�X%~�8�$S:�$�Rw$��0sZC�xx�'��	1���t|En��a2�m�����N�f����3�.j_�AZkT�J~_��$p���I���V�Y{=?�F��\��N4�VK�ur���*�[�%\6�v�?�
��>��E��z�|������y"��W�9fI��>�7�<$)�0��C7K���^�\Q�����2�H4G�p��)s��������[�t�?�sZK��n����8����G�z��)mp��3(^��q�\���<�����c�n���N�d���*<��6X9�J���h*l�9Md������� �����e��$�gN}��5���O���b��(�J�Y�=��,����{��mxK��f��W������[���kl2Y��b*���M�6j��6�*'M<oS)o�D�{�����H��{������>�)J.�,\�0>���Ir�����S���������H��3T.^uNm�M�w�!:T�u�*n�;������\�S�_�M��(ux4#h��a���CC��'��m�Q���\��
ejx(>�x!Q���C#��t�5y����<�9C�3TxO�����0� �(�h�*����I��%���)5�T�%��
!^'X����7]4��t��-\�p1���:>������^I����@�1w�/
wI�x.����x-�������c�77CP"#oQLo��R�+C��N&zSG�K�
>H�Op�q4`i%�dH(�)�������BN)Y4�/G��%94�M���]$��S���r�	w�_O�Z��������&`��3Z�kvw<���{�n��3f��:�����3���;��@�����8\�htm{a�qz��8b_am� $q����b��%-�$���'��3����S��+�����P��~����*U�n
�j:���k�^cg/�U�=I|�?�����6|�*2��L��R�L�=��x���fv�	�����W����@	���l�nII�W.��7��#�(Z���Q�T �8��F��,������P���I0�.�r>
Fh����}����TUx$���:t�E�&'�*A�4��&
��V���,��O�1��P��Dcc0�����{�?:��?+}-i���[�;���NA�8�ex�?�=��f��W��
����Ta32�t�W�����v��F�`��i�19��������]+��L��o���T�v��-�/��}��}O����[>%���O|.������#���$!��������\��}O�����@���G/����
��������i�$����L$��`qHE�9���|K�t���^Xs�����~F.�<v�A�����C��d��$�|B
M�����Z��{w3�H��7���};�y=���=#i����0����I����)��.}������\n2�,C�xuiz��-"���l/�kV��*b�VTa~'�������B���U�����;#�����#4�,O>'w�3�ck7	4�rk�%m��"F	c>�G��k����m�	�9����H�y/�
�2a���9�]��Y���'" /�ha�E�A����&��48����5De�)b_�T�$*a�����$f�k(��'�r`6�+�jy�����\�"�����
j*�!B��W�B���P�,_�Z2Ww$�S*f(�	f�p��[����Z�K��4�bYx�[�
�	��s�vPl�F-tH���	�"(W�����
����n��Q\���0�7h���,��1[�3��;�S����e���z!���&-��D�I6�u��~���{���{��X������q����O?���w��6Y�w�=�����j�<�'���ar
f����F �E��D!]�!�&��M.3g�R�����
�.����?�
��/%V��9P !����r6�q4brK`������M�S�"(q�8�����[�������&K}4���2~�5�_�n��3%�=��x��]�7����>���a� {Z���w��m)k����;�/��#?���*����L�.��d�z��������3Nj��,�+7;��`2-=��I�`��JmB�:d~G�p����+�S���4D��[	�����������()��������L��S���h(��{o�O�������>���������%��9�:}�\i�(��H/"b����-T�^BnOlv�jT���y�l��my�g'������1�(����P����w�3�^�����k��m�T7���'�<p���'?�?~��5<���Z�[Z?���3��"�k�x��  &�~���iv���]��*���3<���_�������^����{Nvb���\�R.A����cv������r�5�[�����40H�i�"�^�	�����z���Z�y�vE:�Q�G>���ZO4E!���&�m ��D54�+�I����j+����J4�G	���������2(�J�;L��B�����O���[�w��s�::���>�����_������n��qt�V������;v{���}���a?X(��R��0�\�7�p��x(���Y�z��k������c���^{���C�C�#���i��o��:��y�k��)��=y�o_�I��%H�$���'�9��{��3� ��!h�/P��s����%E�` 0�	�����{��E)���z�Eh��)\���P���H����2�/��6�
���
2gP:Q�:-&x�8�i�Y��Y�W�S,E%
,B��QQ���)f
�N������V!^a��O�`q�`�'4�x���`A�� m�pd�eO����@�ZF����1$�k��������f����z&���T��H?���/%�M�J3K�����4�MQ��2;�2�,���8lGS������{��c���p6�������_hv=��1D���7�4t"u5��E���S�_%�s0���
X[,����g�;Q���\.��A�3��4�i'i�����c�b�����@��"�p���|Q��d�� a��?����M��F6�eP�Mh�n���� ����i�S�R�����qa�Wm.���Oe3t���N�I�����Y��|���.��i�?��[S���:��p#����@.z�2>�ic�P2Oe�)J���������J(��F��H��������`	�;�����	�}��Mp.'��-��!��-�w���"����g��Y�����3EM��t��su*������SLsEwbi�O�+/�X�	���{�N�K�@��L�j�0�\������UW�����-�s�X��r�2eH����i�Ac�#�^X�6j!����_�I��;�~�"��N�O6��D��r�^�c�0b\x�H�4g<��L�5��w��!�p\R�{I�F�W������E�TO� �k�w�bL(C���7�������dxW�y,�D�N���B�,�e<�F�Nf+FF�A�	���4��=�=��0#\�w��7���C������'=��c����b�������p���a���T�2����ZSv
K����K�A�A��i7'
�V�n��)����Y>�o|O�r�|��T�(1����m�'�Fj8z�v4	|�~/_jn5ye��_��Db�
�ds�I�8��n��������i=^&�Q7���k�]^��/]G�K��D�SuU���^�������a����1i�#J5R���0����q�W��K�D�\���2q��%]�KE�
|�/S���m�i3���hH�b���W��{_�j��7��P�����#w!��?b�
F�b�O^����l���J����`z�0X�n�n�����Ha��y?���G�};M�8�7%�K�*� �@�G9�I�zc��al�njd���&{�������)��H��P�mM���!	���(	�����&��Z)5(�5�\��c���w��K��
k|�/��v9�X����-��C�����c����hH9���������9T������z��_��5�(Q��$��c�RrbYMI~`��IC��C���l2[���^u�X�a,|I�s��bm�)�N�7��3YC�7Rw��
c�.�8��N����g�jXU
uFJ�����1��d�QO��l/*�1�c�#4�p�U���NL�a�J�1�;D�r(���.�������c\�t�����)���Rm��f���?x���'1N���kOV����O�k��Nk|�^�
oa�-�~!�6%u(��n"��bz\�xn����B�
�����B���0L?�)�����0�����>P#�p"5�s��KF�C#�j���������
��2����xle�"���3��U�7L	��%���^�w�y5Y�K�6��LB���qZO���g����8����
�u��c4�����,���ZXYh�Y�5&�N.�����-0�p��ZMe��WB=C�k �sH5���e$�/�������/*��xLuV'J��bw��eG�	y�
Jy�S[�#���cV���|�8
"nZcnwv��X�PU�q�H)gY\]�����?F*?���
�
G5��LG���2�L�)�.��b�
� �~m��M�������$l�.H�Y�_	���[��1%5��h:�-o���?�W��:a'�*pK�K��
��������Z��	��J5��>���R���p�7�+���y*~=�^���@��QL��@:s��s��;P��t�h�A���������XD����=��1���-������H���W*��hU���T�������[����������UF��0����s�eD���-���2����H����P�`*�s�&r�~%������,wpp@�"[�oc���4���w{�D���|������Nm:����9�������m����{��1�%���� �?��pB%Ow�,�d���2�L�.������a:=� @ �r�O]k�iI���G{],s7��!S������y��
m����''��A����N.���O��_oM+��/��}�/�J�X��h/���j�6��2qK2�I:�	U�%[F`/0����c�If������e���*�M�������,b����+y��.�����p�B���v����1�u�
�zK@3������ 	��t�:��|���qL�Y@9��/��U�7+j���o�ZNd&h�2�����B�~���)�*�n����o=��H�%Ef���[�.�
��Z�D������L��s������&���y
�)W9S�g9h#(��@�t�|��L����|�c{:H~�S�B�����5E+M�	��~����O�����Mv�y���^����TQ0%��{yY���m�&��"�D{�<�u_����N'0�4�f�2GE�cv�X���z�VZL������c��R@��dU��Nu��;Bg�P������"X��%I2"%9�&T�'uJ�����,QkQ�k�����`��3��c�.7���tf����JC���xC%����1��dU�c(eq��F��������x)�x�� C���@	�L �"�%�P��"'
\+��cBE�t���%��}t��=�n���k������v����*TB4Mn��p�0#F��wn����*�{_�����~�Y�K�?�>X%���v=�Yk��?e�q��_�_�HCo�,U@��>��'�`34X�!�3�x����{�7��m\.���(������)�m�#h	~������'��O����	�������1��}#����q%�_#���?�����45�m5�����:�^�W�o�#�'�L�T�]#�'j����$n�J)��������bE�Z��W�UMbw���[W���[u��+,o��r�udzh;��R7���`�(m���f�k���@Q���Jw���b����0�2�_lUH6�$P�
F���E��y����b~�,g�
����j�*������������	9,��!�%�D��
����EE�x��)L��s�j n���nX>�FT�j�xR�U��d� �?�--;Z8�Yp�Q��� ���X��/��C�p��f&j�;�#�)����c���U8~�J]��s��T�j�L���U5d��uM�q�����$l���(K�.��$sw��;��S���,�zH+U���j');�]9s�]�KY�����uK��3�_��^�.��W�o}��������/��*��(�����Q)�p���T��G��%Zt����++�mxX�`~/�q���k~�R~l�Ex�*����U�hY���t�fZi���d��i*<����W���p�������k��F��;���)$�����0s�R�
��~T�`h�J�a����?��w�9�/�S
�!w��n���RW��m���7�N��D���s���Vs2���DtIe�&�'���S-E�^����2I %��~�-���S��n4�
�:5M�F��������p���gNt��'[������&[�
^*T�������1�4f����iu�"��
\��R���f�C����\D��i��}�K��?��}_��|��Z�Q�vSMm3uQ;��O�s�t,�_{��x����p������_o�q���U
s��-��z���f�J$`�R��c&a��Z~;�I[uw4='C��2)pA���"�\(��������}�)���I����������;�����s�z�t���Jf'����"^��C�~��'��uY���8\���KV:Q-~���G2��#���0������]���t=�����1��U0�?����jR��h�����lm����
��^K[���m$���(m;�-kU��lZA���{�� ���5@����y�*L����5%0-�����������W�H�e��o>�u",�A�<�}�5�G"��8��u�0f�����;T.����J=Q�}�]L�2Sd=���Cu�_s�������
�:m�2�Y%�d���n�H�lU��y\�������$�@z���y ��g����1t����:���~�>��~u�=��d��Y����[HX/r������Y�}`�S"-��S��^
3�4�	O�w��Kv�(0��E8���+�������y�I��C]�#��36�=J���J��i��J�T	m��<����py�=�����DU���nY��M���s\�s�.�(��"C��iS�-b�7�L��+m�Z��."O>�|��5i�.u�~�\�z���g�w��X�����A<��2��6qfxl����j���yl���9���@����`V��8]�v�8��Bn�g���3�5�<������Z�T!��Bp�K��C�����n��r����]����d�2��A$��M�C�����C��
9�=6����������lN���y��1r[�o���s�Z���g��

k]d�q� M����Z�k:���'ZP�pL���m��;n��!�R�[����i�0g�L��M#��6.��z�����V�3_&~��M�����
� �J�������+��l��7|!�S�Cw��`���z7X�\�,���c������@���u�z2%���+�#,�}V���:��`	\�k�F�,S���B0��_\�&��d�J;�o1%0��8+�@�V�[5�#j_�e����%L;��|������Mwt�?SE�W�x���V)��%��T� ��95����2��cT��~�_�/���U����)����H��$�Nb����?��L,H5���<l}y������h��O�o+��z=;E����R^��i����C�s���k�(�NRiAf������c�����(���5�3�`�G:�/So���F>JX�h��J�e)�g$��z��]�orf��
�H��?���f4�v��������/a��b�5�'��O��	���%��b�A�!���5�%.a_)��������[v���f����L�U�8�V�,�H7'Xv�����Yn������ ��a������<��l�����#��m����Gn_ XRl
����,�f��f?����&����F��$����e������:D�$�S��a&���K����E��n�� �����RH
��|����e�L����3�{��(�$����/�f���u��B��V*
�Rm]+4�+���Z�O�m�Y6zcid�.���l-��I����[y�qy1�[��Cr�����������j����=�����a���t,�������s_�Z��{��z���x?�$���xz��U����J��L��~��|R1w���$����o�O�h�{�����h!0�(}�M�����e�|`�	����CK��t}�U9Q��)��Ni�j7��Rj�J���7��{}E�a��Ib_`���}�J"�5�~.����dP���/���
��6+�PVl.�6<|$*���6��5<���h���)1%R���(��8d��%N�������9K��fb1^b1�Nz��r]�X����u�X�g,B����Z@�>�^��,��C��_�)Q��<�<e������a(�(����q����o���.!��bBtz;�f��.�z��Y`�T~��$���XU/�C�cO�I$eU)���	b���/g��������{|�m��j�$�cx���6JVM��S�\d�X�����m�q#������4t��(��5�������@N�~st|�u�q����?��8v�b3y{���y�D{��������������w��w��g=��X�I�����;�7I��&T����x�����!��'WW�����`�z�����D�V��(��-�xD�R�c����o=����3U�N�Y-�g7=vw):�jL�f�os�.�hW�Ez���?����0�6�`�����{����NQ�/�M�w.���$��P[��%2����1�V��S���V�H�Z+��-�������Z0/��%�ND�r����K���(%//�����h����9J�;S�0�Q4�Q�g%w���uW���k���[/�r}��gf�b���{�Yzx��^yP��'�F�G���9��4���������z��@��C��������a�w�=W������xeH���\t6�uY-�G�k���.WK����8�	��VUkB�v��1�$�2�d�J�����z9\�;�����%��,H��J�;Z�"��L�7&W��S:Y�T���t����P������^R� �$��l)���kq�4fQ��13�9����z��Y6����Y�oyb�7z-V����7���u��f{��.��2�?��
��P�u�Pd9����Tj�E1Q�j�<w6Q�&������.@���tnY���@��v[VG��a8
�������3���K��|o��E45�3<���8�'~g�@������$���(�
v��S�Qi���#�n)�C��\���y�������w�k�^��'�.��x�g���n2ob���2����^,�|��8��Zo��f���@���]�jFc$�\��r���t�OC�p��AH�	@�#�
r���W�:�$�Jz1y��e����	�k������h�DGX�������U��������]G+�a���i_WQ�k�We��G�Cq�����hQR3_-Z�(����T��1-eG�mIi����v
��5k��3�����K��~d�[�z�Z��������1�Z�"�������C*x}=�.�)�}�M��\PU�/C��%<�T}�`��F��@�y�A�h���e��<o�%7>���$XO$�X��9��s���m *1�C�����FK2Sq��
��d�m,�<�Hy�P�'t���`k *�������0�u-`��2�D��{K)R:BR�A�m��v�!x��QTJ{�����ic��AD��T���d��!:M���(���2v�|e�U��F$���#4��XW���t�j�t�P��g7���nk�����$n8"����/;�#��[�ZQM�������Z��$,�?��a�bFm+�����Z�eF�Z�w,�`�UPF��"F-B0����(��2R�<fSG�3�������M�<m<����&��$������ 5�R1_����A6z�����}��b���R����u#A���R�Gr�!l�!T�uF�W�7Z�}cG;����It*�iU�n�H��X{�%=�v�v��;I�e:��b����O����\�����M�=7kr] 3����-��_��E6$��.���{�������(�HH��
5�n���~�����x��������&N!"����e�����52O9U#�*G5�n����A(���gRV�t��HVr�����������a������%����f~$1Z��F�0P����y��~��Hz��V�Ja$Pr�(9(�Hft5�=�q2�j
��n1�wm�m*�v�h�y��@n`��������-{�4m@M��"WjU�y���*����M���1��o�������Z_�Z��?�u�h�������������������;&����K!�m�l���}�W@t����������'�IAY����w����Z�a�����3^$�O��\�[+7%{v��NR#�5����r�HVK�@�o`�����&�B��,������
z��&1���g�c�$���`�>�.Yg�k�ff������c"R���Z�dQ{.x��B2L)���>�	����*�O�hd#Z�%>!�j,-��U��)3�p���S��_�g�|-d�"�n�i�^��m��j�Vo�G�BPv��}8=��=>�%}������:���sl���2��Z������F��
���_�X��f���?�U:�D�Z����~�����v1�J�y��J��f�t�Q���^�=b<�2]����G��vm��^Qb�h��x-���O�[���	-�����2�����Lln�^m������+3��M����]�Q�T��W
=�g`��-#��������?WfN�|��";+���o�oS
�b�E����C��P����t����"��5C��p#?'�fAL{`�����j�����1��m������OOd��{�S|1l>�dw�����~��OI�3���Uu"���V�������'X��<w���[�!��cKv
��c���|�r�4���z��7j����O��0��V�o��4�)Q�������~���K�"�/�����=^���2��L*H�x}�0�����#�7F��7�
{
	�v��e'*7J�i��Wu��}��b};�)�b`f1�����9�����u�F��7[�mq�Uo��f����|���?�M�����=\�f�i�����A����q�"&�������jku	G�B�h1���n�0u����pa?T_|F����|�[�
�r�oW��kB�Lv�d���%��n�zR�d������6�pv�SL��c�P��S>����U%�n7����g{�OVN�v
������D�N��wjZC�������V�D�������V�A�����Y
F@�:�����8#V�%6���u{��VK'L�l{�u)K�\���rU������&�r�gS�G9
��D�R<������:�/�_b����L<~.�����*?/��`�����>�|�]J(�C},������b�;�{��-��]�S��;�A����@���T����R�P)��
�7�G�����`������\�XT�n�%����a@��g�������+����3�'~/�'�a?9�����J��$k:,� Y3��5�%=�����8w�i�Q��Q�YL�����GRs���jdpZn��a�,>���2��%��4�7:I��Wj+Y
��	"���(�4�a�>�x��5?>}�e3ODVp��v���,-�I���d���sx����J���������_C�Sk�7f�20wu�K.*��K�4w�Ja�EL�hr	�.$�7�
�������@*����q6Y���Z�;���v�u�������i(���WKvVz\���M7WskT�crg���������9�d>o��*����D}�S���Z����Y�L}���R
��ZYE�`�LX�%J(c���G�J���@q�
t�x�����Z|��������TJ
*��f
X3�nl	�2�
]zS,Q]���x:��_�O�9���3O��J)��\9��Q$�Ip5�\N������E�6��:n�a����V���D{Q0G���;|�U,�p�=�1�[�wsv'3e�2(�3�n����7�:�<�T�2*����SY��8�����y��f�>��vY?V@n�5�k�9o�0�����2�?w>���j��\��-m���b1���SR;6���pq������9_�ANe5�/���l/��k>l��/�e�,e�t�d.���*RL+����4�]���0�
Wm�m�K����}�6*�h�@9�e)h�O��Dr4�g>���'\p��;z\�y��3�+�(�c��������f46�JB�gYo}� V�Qa�q�;�2�{+��q��������.=�H0	�%����p����c	�y�K�i^p]�Jt���B�&����$X&�������w���
������4�q�{����H��h������:�p=��J�v%
Yq��
R�d����JI���<�G���G�(k��O���@�O�#��0��L���.�Z������r��
����^��4�j�<���;�
�p�Z���V'a�@]���|���N���.c��X=>��8�9�t�%@�3��~fS��{,�[���������M���XR��}s�FQlcAt[����.�����){�UXB����Y�j6KL�������-E�y���K��}�}G��b9IV�r-�r����9���r$]n��&^N��&_.��E�6�`�IXa�C��P�3��3r�v�`&��Q4h���R�gi
�8�g��g�������V�<����"��"(Fz&��+wb&�f�T���b��^v��������V�*�V����{���8����Xz`����#�k�Vhfz+��N��{���a��O��S7����C�M�&G��o�eFH$6K1��W~j�u��y����v�X
P
���V�C��K��6���.�
��,���9���l����+���������N�
`k>ZV��G���h}��w���-j���OA�H����<bN	�U��L�Bg����>�L,�sHl��R|��z��A��1��n},�q��N��Nd�����Z�+et�i�+�N
�^VT�@��$,,����:g���L/�5Z7e�5q���1���8���fp��A�tv�?��z��cK
PLM�#f�D-����(y��O�Lf	(���6����o��'m�}�IF\�'�ew4='vsO1�U���[�h���=��~=��f��Z
���4W+�����zT�
t�K��p�.)���	�Y��%�s{@�K�y�@SL��X���`T��m�0���VCg����y���&����0Jaiz��k�h
�����$0���8����#dV�X��Us�H����������v�PfG���d	��$�'4����7
o=|�Z9j�L
PKg7�����i�Ub>3��.���i�	A����q�����V�Q����yl��YkYG�6�L�>I�b%�M�5���x&.�X�;��P�1�Z��Ej�\?@��K��q����3�����������R:M�8���!�V�Q���?q�jq<.!U����%o�[-��n�E^� #�ruX�Z��mf�����(�0^v���Gday{Q0��\�D� {�h��V>a��pWQ^*[�Ud��Rp���&l�v"C=������FEG��M�dcf�Q+�[�A���V��1��]W.�z"D�5T�,�����Y;�)����$������4Q�a!
wPe����O8���[b����5a?�.�����W���|[�;�V-o���
b*~*
.4=@�����dz���H���M�e�U�J��b��mZS�}�V��35{�*��f�/(Z��@�@�\h�1�"^�]������Mh���iW�*s��F=��ea�^�V}��V��,�j��5�bI��h<��gO#�����Y4C�!���6�7J�{XV��������;y�� hwj� ����t~�\nd�����8$��������l�E���7������P0AQf}4����\�F� �Of,$��Q���\P�N�SF�9��n��G?�?N�Ct��O���(�/�<��V�6����%��~c�ES��~�*]�g�SO��������F��7d��"[B1 @q#�&m�� J�?��,�����U����p�n���9�O�zI8s���{.�k��o
	��s�g���^�0T��Q�iS���~EK ����������eD����`aX�zP��!�� ��[9g?��5u[������/����3��Y����M�MW��E7�;����@7eP�R�,qg��/'�	R3�*8�)z#o'>o��W^N��j�o���aw�^��
}��)u��H�i?��8b;�������,���g�K�T�85����~8�Da!�,p�;��CR�HU�k/P
�T6�4�Z���S%�?��8=��h1�.}��t�
�0�2�}#+�sD������� ��#���J1��������.P�����b�rL�!�	�������a���@�=b(I����E�4������0���Vw�H��TZ5`�[uQsb��F��-�FB��~Q����w
������hr5����F����������-����|c<V�5q-����'W@j)8a�6�A�n5��h1hI\���/�dp�^|�u�b����sT��?�K�$��355B#��N
LO5��L��@� �/	��������1H��5�\�X����F)}�=������g��6*��M������h:mk��X�t��v�Q��J��u�`b�/��]�z�����M�cC�I�~�;����)������N�.���I�������������N��������B�JY3T:�_k�����M9�o����h���b���E��7R9��l�,��
V|�`Nl�������SghI�^���1OI�Ib�8���2���1��d���2��S���)GJd�����.f��Y0�o�.%[y��o����p ��`��a��}�k�]Im�`���o�v������.��m����#.�>�y��<+�QJ:�#'����|^�~������)��5?vC��jW|Y���]0��?g'��h~�N��l?@��FR%'_����T_��c�����ws�aR*�B~�+.��45�=/���1�[��v��M+�["N��.Xf���5|S �.kF�Q�k��)������w:/!����0��g��G�M6I!�	G������Fgs�5�8E���Yx��V�k}*�g���E��;<��
i��D�G��>N�	�mB�|����O��e�U�Z����7lD@Z�51��D�v�D�=�#CO{gI5[������\�1H�3AU���&��9|��@�z����@Y�u"���0�]��k*���������������d��,��Fv��	��XHVCMd������A��e�����~�����s%<�|qW��;�s���n5m�T`�$�o��;>|x�����wzB��O�'�w�t;i��W@�(��t0X3y�x�vV��x~=T{2b�V/�-�Ha���k(��(��+��g��g�1�3���h9���#`�M�������?�U/���������!������=4w�ag�;����
*��T��j�G�H������v���Ds�[�:���$�K���1a�����	"f#H����^F��P�r�6\�D�Jp�v�c�
��8,YE�lyF��J P�r���n�W�}�!G��(;�}Gc�
�>���7�!�P��^	r�]�G,	]���4fH�E���llV��1�"GS�"������&��D��4z����'�U���{�Rra�*����c/F����_�j�����Djw:����T�~�V�����~�t�Zw�c�$�Ei]�% [�9-��h���{�(�����G���c��\��u���{�k���������'�3	.m���R�5}�M��!��"i�`4����v�+�����D�2Gn*���;7��1����<�
��B���;u}N>O����aG��n�3���\#_L���q��Z�S1�)!��{�)�ZDIvwy�]���9��t:�D��b�t�*��t0����p�~h�f'��eqz��Q���uo���e�M(7m��&��C��S�|1�V�;#�W<R���Dj�����W�Bk��6z�w�p;��'n!;-[�x���EW���X�W/���.��o�Z{l�[��|��yO�M:}�Z����#Q�����l�N	��G4�g��*ABR.6_��D�G��$Y�������(�tj��������*��+�����"�.�3���$����Pr-y^(���s�7��4���_�M���D�������A�}[�f��Q)�PZ��P��[F"�S��;��m���n39�59���y�����/}��C��K�,���)���nd���	���r���+���y�u����$ySk{b
���	�|����4n�O���������R�B�����lfop�����k�[�%e>���dawP���d�JA�jP�(���o��@��7����~�5�.;����Nu����s�������0���[�
z�������Z�Z;dXk	���%O�j������E��H�[�����;u�1��v����L��P�9m?���Tz��m�&�}2�v��,z4�	o�����\��qo9&m�4����<��T���+
��p)������V6��Hr)�y).Ya�������:��s]l�M]�t(�,1�]�U-w��j����s�	�Si��p�U�?�Cu<��bH!_I���u-;#0v/�W+u�U<�s-�t>��T�?iem�qvy9���I�L���<��7<�fk���]/�1t?�r�
��h?76S�(_��4"�d�L�S��L�Vg;��gZn�d�������B���8��j��@n���%\7��z~��j����}G=�j5@�z�qT@<�G����RU+�6lNM��C�n�b��C��O�
(�h�G��G�h�S�{�MD�V9[�������[Y�jU��Z������7�P��o��,��r�%��[�}��&�I�����<k���X��D�{��H��y��,��N!�<x�y�k��_�U��;���l��N���^��VK
5*��D�Q��d����r2�_��1�V7�y��LG�$�G`����EFz��@L����$U�g���U";�D�"\�oV}i���ty/t��, ��[Q�'y���6��Xf��=�b����h*��1�����)�=�s���k�j��_�$��BM0t��p-
���9�������O�h�����G���4����������pq������G2�R5�!�o��X
o�Q(2��a�@.V��m����R���(r#H�����Z6O����$�+wy,���|Z��$$D?���T���~�Q1�N�|�k.���hk
����v)]*�������)��R�Uq=�V�\���V��Tk�5���O8�"e$2'���f����Y;����:�`4�j"�-��<���?�`nKnC:�<S�Zk6�I�'�*�������`u!y��z��I���l��z������Zn�-�>Rq���������*"QXik�~S��k�_���^���Z&a��@����Bd0�e���^����D]J*�:��T�:CU�42z�m��{�
��1�W�>H��r���
e����
'���4����W"1SP�����<-
8-����<	S
"�v(H�Sch7��,�v9����6�z���[�L����bg�Q�����d�SZ�B@�.A��=�-�W�IZo���Z7���i0���������%��s?���\�FKKq���O���E��'��<X���*��JF��P�xg'!|,C��:?�~FU��8/���V����KO�'����gL���N
�������i]���`J-�gXf��t�z��7W��x(G.������h���9S��$���R(g2����'�oD��x�����Mv<��k�8�\����������4��T.R��<�����&`�������?�a�2Dx��o0}���Y'�J�����$�U������*��yPn��-�����"7��&�3}�!���N�1��$Z7��%�_�h���d�c�|��l�>�U�U4dn�c{!L�!�pL����|
2�/�nrqNJ��$L��k>e����s~��L��
���B������h�x�'$�&)�Bfr-��n~�aX��/���I(O���>t���r�I�g�-�����@5����P>|��>�w�V�&[�����l3�u'	���g���J*��&7����\�,���1�����i��J����]��mB�n���V�������b������������������Hv�>S�/�H9%�ZE����3Ip��_o%>=O�`�(�UW�.mU>B��Lte�d]+n��~�.jKu}�m���������IU.�V7�	�h:NG�%s��
Yis��o�9gR���uuB�Y����tlH��S.���w��h�=<9��w����1��|�*/�����l'��O`7����[kF(��.;P"'����;��u}�>S�D0G�:xE3�q���������l��N���&O[Z�b���Y�= }������=V�Ou�9�v�	�'wm����a�+�<��8=��=>�
������E:�����9�0/z����&��9W����sH������Vo�d�7����,_�<X��������v�Y��5�������a�
>o������6�~S'����mm��U`+���'������h���t��/&�l��;|��P�g](����)�n��6i�|��%s��]�s��m���en�v�FH��M�U�X���E	*Q����(��>^I*�y�	�am�at5\Dp[�Rx��f��e���+�W��f����?�].K(�g��TU��'��+�B:������s(F(F;�x:���r�;	�u���
�-�u;���rF`|Z�e�s�-f����q|����D)��Tpz2�����{����������FN�+���F6_�?3�KO4����`|;��Pj�����
�����*D�ZG*����?�����-��E�_����i��9����3m��������=��W~���M7>���JXa�������W��a��4������ )#����N�M~�U���1P�
�R�i����8\�Zz~�����7�G���@2�z���O��[�����.&����	��g��n>�h9V
VK��&� ������"�%��>��4���Hw=�%������Oh���q��xO�]�����Y���a�Qn�wq<��{t�l��h��X���]5Z<����H������AS����	l����c���dy�=D��h��{|3�81�R����3��`2C-�	� oX��4����\-&�{OO�t�'�8��s�S:yvP�Y���[�}�*c��c����_�{SF~���s�c@�t0mk-m@���v�M��H�D��0"6�V���RJ��V3��1_��&H�UO��r�����iq(t��-:"(mr����09T+m�Z�=����6�D�6��)��D3�`����`2�O�)'��bt���kI��7������
i��j���.j��XH����u���j�1SX=��v�5ZDL�p��%��I��"����e�'	}�L���[�,H�f�D���z�`EKR��.c�^���y�j%?<����.���6f+he�������i��(�x���~����>�n&�������G���Q{qN�sY�nh���"�Ih���m��n��*sk��+�-�t�~�������m�(x�5R(n����k�������:Q���a<�V�d����%�z����2JP��|�e����Z��D��E�t'�2Z�����uj��:�f���w����h��
��}�;�6uw��\M���,�V~;5��5W*
�Rm����R�]����Ao]8&@�~��<<��O����o��B�	��e�}��F��p�:D {��N����p��U�|4Fh���Z���jz �=5�������s8C��}��;��wr�;.z���������	a	hp�X
y)�)|��^|8������Y��:Hq���5a���YQ�J���o�]zHa8=�\�d��=L�0�,t1���>zc���5�,�/�����^s��J6W���sk�1��7��w�I�H������
��D�-�H��\
a�CkbxM���j�+���Zbl1��bp{|��h\�{�8a�DG�Lk���	��1p���8��O��y�5������>��'�lbu���6���������4Q^
����0�S���9V���
��r>b&��"u���y��<{�kgj�a0���S�rZ/���@����B
��5�z];�I�T�&������H
��I{���> ��)l����j���##�S�����0�\��3.�N��.#�}V���_��Vk����u���E�Fw*w����:{5��E�^�phC����X�n.����a^F}$��sY�i�2��R�����`�|c���>���w�NYw������1\������iI�������+��M�����*y:M�Q�l�<
8��Z5�`�"�4k!���[�].����
)�Z�d����*���+V���@������le��Bl^��K!?e�^FA�ns�J�x�-������m]i�|��J�e��r�x	��-^��	,<��"c^�`p7�Y����N:W��,� ze����V[�f�7�O��"�����d1_IM��P�f �����0�����ME	-7��������aOz�32�X$|>�r�	��������j]8�,7�f��)���W����U��Ta���"�L���&������/�m�B���������-C���q��@5�\��u�R|��dr����p���o��Ex t�j
�B�N�"���,�C0�\t��	�����s�B���oD��{�h�b	L*|	�2�>�3�2tcM6�U����9���y�'�-F�-�������#�]����2��q���C�$��`��P�jue�/�����L1��������1�%B��Kq��	���1 p��	FDN��6�KI���+#��p���:�}o�	��p���7%X"w��3����3�|�`
W`	 #o��������������,�c������Q���C��n<k��puw6t���V�d�!R����G�N~��
)��B^��:�=�@�#l��f�0��	c�W�����q3����0�)l�G
)R�}{�B�:��.�*���X����6]���@"�X@��������zh�|_�F��o�D��T�%�>��t�<)
�����/�f*��1o2[q223E6�������g�C.��g���K�qH#1��(�h����mV��ub~��u��l�Y����e*7a������X c���2���x@��i���-���`rbq�����`q8w]���L����|���{A�$Z^�7��I�P9JR���v�%"�o�����^�48���G��R��s�\;%ui��d$��p��y���M�s�U*��_��E(���}�@��2,�?�y�$��#'dfI��"���*���jl�����0���/E��EH_%-,�����w|z�����w2<;����E�;���$���+���+���~��gj����u�F�V.���.�������\��"\#�1�5������W�� ����+���?��r�_%��TO%�oka�?�@"T`�3�VG��z[K_O3��V�L���N�5�e��~^h��ep&�r��HA����c��V<&��7�a���,�'�\��F�Q��j��DX�0���%q�%m:FH���me��Pm��{7�XQ�E��f9{��hE����'S��[�os�6If7�h5[��,o(W���E����k"1�����O���_#��;�@�P)�<�"%�mx\[-S�r�|TP�\#�=e�G��R�����������("
{->�;����>��1K��0�^�^7j��j0?YH.����0�-
�{	
������8�E���YIBZ�%�����{���W���XT"�D*���+��e�q@�����z������Es��qf�%H����y�~�����na�D�:rY��I6�71��z����<Z�d�{0e����kWH��5�e��G�/�����I�`h~EN�j��C�*�W��CW�q��t�s;�+
���D�o+I����Q���*upp�N�	��M��T�:P�GCKc��x(�1�2����a�|h��{����$���N�H�"=XG#T5��i�����8 m�A�JR��-4\��r�8RC�P����4�p�6*u��!j��	5��pF8��3�	%�
�B���$h�@F��i�A�>40��Q7��.��tB{�K�/LF������Q��y�Cw��GR! z���J��:<Du�G�"�wx�����#�{�h:�=��3z~a�;*��i��Z�"���-o����YO�)>F�����E
8��ho�.�[�iE�D���g	��"�/�jD����p���d����]gd8���{�gQ@6������,C�B$�1
��GlC�#!���U?�}����i����{�+��
�z���@�E
��=��C���)���,c�^����7���u�F
�=}y�/��������������"�XA2I�d�D�7�=7��x�����16�����`(�6����<��g��N�
�G��L�3p�V1����{����C ��EUdn�+����ig�����{�:������P���wP:���/�{��:1�������GG�a�tpq�=>��>�O�y���0�% }�d��e�	Ojp�3��O%��L�3)~�5�����Zy�	�
x���tF�W@�>"5���e�>��R&l���F�e�������j�B.\l�[��2q�UZgqp<G��W�w�g��;�A��Q�s����0Pa&|@�����hIE����3�bH	u�$�������T�'cY�����+����~���~�_���8t����i6S��|dj���\N��wZ���i�4�.|������ I�s�Si��X&�T��|��'c7������]�2�?.Cm3��x���-���y8V���_1�
#b��'#}(h.��t�J}o&�M[�}����ou1j�WXR#'8F�~D�1T;f�|��s���D
�R-C�>9�/-|���0��v����^��p-�H�bykD�`{D���K�I����p2�x	����2D�d��Q"��(G*���iv�3�g��,�_�	a���2Xg�[����]X���6�)�t��P9)v���CV�<��k��~��T��:tT$��f�����d��P-
Ft8����hk���,�(<��n�f��4��k�R�lZ�a}��T]��[M�G�<����AS�7p��a5�h��S�Z���AF�����1*L���#��$=�H�+��`#��3N���@
��Np��4��/
.���T��fMg�X�$�9E�������YK���#�F����&eRfr��qf���ty��D���
��*�9^Y�Lt3� I6k ��R�\�P*����[j\�3/�������~���~yr|�����_8���F��7��
�`�����2|���K�R5]�����Y��v����:h�99
���o�S;��~��2��,�Gy��He��_h|%s���`1�IlC�B�]���Z}7���/wfXc&)��t+3�j�;'	p'���$�$s��b����\�Ah�F�}���tZ�T�i��{G*j�����)��t$�f�X����~���&b1����lj\L�aJ�OE���=���_���?0�j���Z�Yaq�����
��u�����18����'�����bf!��������]��JR��
���I�_a~� 	�*m��D��*�@tC)��y4�����@��z�����������fsGX�ju���u�G%X�}�l�(!�Q������ZtLd �I����6��4��7�"#���y��r��T+�5��rs���9/PA0��C��r������r�>q��xy�PI�1as36���{��������d�\P��:�R3|_���`�DK��|���-�o`�HW�f��1�;^�C�6������(����?�2s��>7��6-�T:�dz�����E)���bt���S?j?<��'��.-p;��V��>7��E�xT3����'���)�>������*����[/�w�j��,,���?�l�%~3�H<�Z,SGF��i-���l]mK��,�e�@'��5rB���4[~EF�c��`��J�:	c;��T���{���o������u���$������\� qN2���Dw!�'��F`G�E������sy{4�Nl�e%eb�S\�����F['[�N�X�����[��I�B���)<&\�Dc��>IEKG�����?n���m�t��};*f�MC&o�N��wE�KMfh�7�3`B�
$wv���'��_mW���v�����%�y���%8���\/Cx�����kY�
��O^9:cp���*�c����L/2/�*�W;+����53P�����r�Z��5l��f�ZG�
D��Z4/�B0���B� ����~�%�!=�Po� ��&���������`vOT_����;�7ln���rM�R��d9)HK��t'"�nVt��I��L���I�T������mu���H�E���������t���F>�q.�<����L���v�EX�y`����wg+g�C��a���zU���3������,���_/�m��SN�.%Tp���k�@|��m�*�%�'��-�S�W>��0(���NXI�~��L���~�	��bpm`�]�4��������~8���"�<m��5.�O��6[ptL�x���d��Gr	��I���0qN��1nT+~���W
�n��t���]���ahL���������
��r�.��pk�M�T�A�y�8w�����Ww�1��V�O�3$�F��7�[o~a!_``&�_�h�/a���3!�Y��M�����j7H�
�lk��n����J��f+6��B����������D��=���)m�AJz��W ����f����7��{_DuP�.lB�����/D}��<6����&�B������&�M��H����J��
��3��Pb�[���-�%7dtE�`�j'��3�*m�UMx�|��������Vy�v+%�@�l-�z�O!��d�y�Adl������Lc��T��UB�bSO��7�v�ob�Q�\��'�|:����`�Z�F�T��U)����?�Vk��C�%,��bYj���%'�Z��v��X�v�����<�A�[\���,WJ��!w��e�q�X��R��n0$�\]|���,�9�T��p�uM�?d�-�a�f�����}�r�oW�[a��S���mQ����|��J���F�lC�]�������������z7fF��Q�M)�'���k$(���������;���������V���%����D���� W�C�v�f~�z|.���*zu����*]�B��n����9
��l���/�k�o�m����tR�t�����)����b��l��z�{v�Jj�e�;0d��M�*�f����1e�fz[����]{Q)W����|q�*�����u�l&��N����U�������W�=��+c������v�^��#Po[P�l�Y$1����*�F����z������N����o����J��d��a��b�z��KWV��C^G��r�������k�a�����WiCU�Z�YQ�;��"���������ne�*��_�u�{V�)v}<�}�	�x����;��
�pWd����$H��<�Q��C�������R��%Ym4������,O �4a3k���T�'��L����/�xl�����A&��R������Y�`Qu#��p��&�+N���A�=������d���A�����o��}8���Yr�5nA��bWGW����������-���4��]Y�	�[,AC�58����h�F��>7'��S�\A����HT4X@�<���mg�\jJ�/R1��bW��L�T����
�w�1�	�Ao�q�12vo����%_:v�nwZ�����m8�/�&�k��cn��5�VO�mk:�A��<5w��;��:�9I��Hanc\�P4�6q{�0C���4�ZxT(�q���k	[�D�����c��b���P�K��|��_@7�����Q.#������P��0��;x��"�mr�]u��Wk[���4[ h7��l������Sd$em�\PJ��B��Yp*�b��������*Ws�z?��v�}i��������B����>33�M�<���w��Wi5�{��$=�4�����*�py���S����������ro�8�|^a��#��nq�D?�W �f�A��;O8xk/�W|��D5BsLq_iW���{���b~���m������xX��[�xXr=L�hmcb��6�����y���DEj���1���3Q�I9CP�h
�{��&J��/����Z�U7��tg�����D �H�\D�<��m���
����q����'�B��
���4�#DL�p���D�K�����<?��:����� .��w���qf��&�!����� �c:�v�n;�)*����;dc=�Sc|��c�~H��[�����EBG��?�������[n�������l����R���$i��U�id�n��f'
6"����o������9����Qj����e�f���8*�qtLfp�����K���T�<5EW,p^O�*���d��p ���AR��>x�i��(��Fk��f�5��[�v����+��z��][���Q���@�M��~#�����2����&����2�R7~�������/!��������w4�,�=j���nO�g�]|�,��W��6�G���F6km�i���mES��������{��"���G/���U������y�����a�h�)�[����X������7�#c^��jZ���A���tw��'��Zuo������8����\L��7�������FW\2kH�'�Q(L�F������q5	���PZ��}q��%k]��P������t��+W�#!�=���Q�}���:������f�K|��k�g�;�����O��������`E�L��h�1�<T�\��\���T�-3xo/QT4����jp�
������8�u�]3��V���8+�f@�Q��)�-�7[�
��i�i�b����9����1� &�t��r41iL]�������
��W������v}�U����m��v�C
�{��U�>����I%�h?�����\`qrdm�v�����,�(A'���v��N��J���l�#�
:[y~���+�
��)2�L��d�
�EX�w\���w�Z�{�Zo�@�Zs��w��U�^Ut��Y����[��7yT��A���TY���HU����w��A���B�H�R��nW����{1y�������H������w��K�&=���?���/}�-v��=����w:��P������R��%7k�u��h�D�|DB"MGVo�*B���T����*50��m�r�pF��rm��h	�F%h�E,o���6W�4���0Vp�=���|�g@���0R�G���$���b�
D���.L�=Eu;,�z����o��`��(�,Y���?��������WH���Q��f����B`���t��L�1�Zn��j�,���,Wy��(�v	�T��:@�f�"[m����Nfpf��3,kCx�Z���xS��$���L8D�)����}��wB����������7��[<��1j���g�������^Y&k�1��������}����G�Fr��!ik�_��u_��p�n��[8����G�%|���	�&X���dB\�.^����������2P�8�='Sk`Z�����Y�1�:��A�w�T��l��!�3����������y�k(�8�v7q������!�D��&�z�N��9���pr.�B��M{�pq;��*d>,�2�A���1F��g�S=l���5�`�!�Vu�y
�����:@�.sL�v���-�@G��"E��)K��g��
eH�+J�n���������4���j��W�	�������G0��*t�e��F�X���4�����#�%�6%k��r�-H�N��)��=�����m�s����n��#��^�cR�J��N+��`K����`a���P�/�*3+�Ah��p��+z�d.�u�����6s*��,�z���sbV�r[�Y���Rq�����}9��f�����~^�+��� g��"��hn��X}�aO��{T�:~�}x|��I���������m�m�
�L���,�����1�m�m�Am�YA3nT��������X�����q}��#���%������	e#����	��&�J�!7/�-e�5��w�u�o���}��Eo��H��m;�SI�O��	+jt%1Z<Ga����� |������n���'X���c����_��P�#�l8��-�O\���f�I�yp�u�����<0�
y���}�@��
��1�m����C��ky����I�]Km���&�5*��5�[c/�F����y�����f(S_�@�����[����Ty%���]�%{��p��y>�x�iz��{H�:��,G7���7��������z�g����tj~�����?���w��Y��~0�����'�G����*���h�I��~[�,o��T���'[�c��F���1����F$��������T��#E���;���PV��'"��O��?���Ob�y��]WD�l�:/�j���QO�-q�U���e��lg���0��ih��!���a����5��*�/��<��	�����R��47/��O���
�a���<��bL�g���(�l*��0�����?�
p�][|cGkp�R�Q�k��[U���s%���3���u�e�Z����%�t�Nh,��}��A�����rmv@�lJA%��4:��T�l/=��@@�+S
�e�,U��&l�U�~�V��>�0�.;���Zh+0����oCx���a����!��w$��b�.�tz�>`l��Yf��K��>��H� ��i�I���n������[,������AP&�����k-K�����I���q��< ���T��f��.X�����C��+��V�P�,�/�)�����8L[��h�
�t5�Mv�@�U�A�P��� �{������-�ku�d0��p����f��z��8&V�19
��f�\�����\tU�=�u�#(��;�����N��jM�_��u�bj2Y1�1���J����)wH�U`&�V>3�4+���#��Z7�r��!���~y��\��m�4�g24��Q�����y:�C��$& ��5�"��
g 4��~�V��J��FM�n���)�rU�Q1M�_��5���,j��nm8]�v&����`��`����aI�o��'h��7#f��19���v��b��Wn�X�	�'o|��F�b3,�o��������e��z9������+p�2�4�<v�����&K
���j@����L�ra�v;C��J�;�"���|��q�V�"�62����z6����7X��Y���f�m#t��~H����������X����OzX'b���k����� ��f����{kUi4��;f+����w�'W�V�"�����
����9p��}���x��\�yc�h9�G���2��k��p *��E��x�
�0h��
���F�h16��@���4��������z��I����1�S�+D��F�����p	�*���/l%f���bP�&1�(�ihJ��d�/��V����e��W��y���R�����%�d'����%�����)�P���y/��x
,�(�Nf�i��x����}�~�XG�X{��U��*G�e`��+
�� fE���p2F �,��#�;��g�`w
��dV����$���E��~��>/������ZF<����	g�h�q����xr|�6�Y��n.�lhz\�\�;����r{����X.:J�B�>��X~��X����~/��}��k[��@3���U�����o!�����M�r2�\!��j6�
�{)X��7����f\E�3~������$�pzu�F�r�����&�%��g�>�F�-�y��V3��X �[�<��O�\����$�Q����`V�xq��8�#� ������PEf�%O�1'�sL��8|��b�S��������TVL�j�����@&��5\��_i��>����Cx�,$U���1Dc�2����e��_�m$�����q�>by�$�[sr���Az��!���/�Ex��,���_M�3�F���d���`5�G���%����pu��
;�hH�qN��n i��s���w��q���O::r��B{��9����&��*���dG+	��]R��29���0=�F�lB[�-x��������G�=tLQ@f��,�����T�-��Y�'�`��
���'@u�~�Hb#��FL��a�p	bPL��]��x<������v����{?������aE�z*�s*pu��~�r;y�I��
D��8D�t��K�o@��^p�R��r8�(@\��1���p�q�-A�@Vcu;��A(
��<�$�z���ox�`�Yx�����j���d�`�|�^,a. <� a���p���=��s��s�2B��(D���A���t��i R��~�L���������k�?l�f�Z��]���&�;_�K��8����U�����&}��E)�Q���h8���/�d�$���D����O*%5�U�Vp��m8����(^��;>��/|/\���Dj[{����
��DL��-X9�n�n�m)�LO.�p�w� �esp����u���v����{�y�)R!�J�&|��q�b�R0���G���Q>��N�X���hn����Lb'X�������e*6���ZU���N��o��O��T�d����O����.��r�b�M�)xE�t�)���WZ��N�C�?)?3�z�P���#�
��L������I���%OP��4��"X�B�?bN�]��
�����`Rx=���r��n��v������JI�d�i����/����)�5�F-u�lF����h�LoE��k�e�;��c�M�7j��=I�k�N3.3��i�\�����Y�q��p���q��hD�lz���6n���2xS��!������M� #�~�}s��n�3va�W�%Aa���!���������^c����Ox/=����>�b�h������ppq~|�n���wrT�}�2G?�?��t����-��~{
��x�W�b�$D^z��J@�L,N�$�x�?���~��F������_�3�Y�����q���`������9/ey'Z7�-`
;&Zg�1!���#���r���d
@8�d��R[<G���cIk������� s?$��0��l���L/�xC���gtY�Eu1�A��X��Tz!iENd�NN���{�M���2�7������JZ}���D7=�����t��Qf��{��x���>F��������IK"�|�<|���
�+��7$�����)B����0`�c�����Z��
>no�Ed.�6����$8� q�$]w��r��%��(���
�-kf\]�$N����k�l9;M�MG���+���tM�k�����g�����6���:�-�=�
]�����/.e�T����z�X,���>2,tC'���]������{���pD�����Q����%�s����[_�;5g��Ik/��o.u�Z�5��B�j�Q��?"��fd#5.�XXm����Y��ar��!�)�E���&7z9:��697���,��R�:�d�q�xC��Md�����4X���E-hJ���s@�V3AlH����S��w����o���<�lw4<�:rje�!�6����L�p��d�):E
������q�%�D+D���E��N�U&
k��^V��#�v�u(�s�]�j��;m��������J����w��s�k�&SC@��4cR����*����}���np���������Yz�u�����~���������a��n���[F�?���=V��GKTM�^���ce�wm�PA@YM?;����&�q�[�OX�C�v�q	3JcnU���J�LE�\�L����W�Wu�\N�1&���:�Fw-=��@��t�#v[VF���_$/����H'B�1����8�����xD��'��x��U�oYFw�v|�_��!��A��.�cBN�3��4_�����A�w��|�'!������N��n�6��L���O�����P�@,�H�/Z&b|�	h�+i���3�[���L��Gb�7����O��irr�Kz��<�vU�9
�R���r1��#��S�B��H����m�h���c��S�������a�fjq��!���YE�����7Ivh���h����rt��x�f<3�KN�;F�)�Rk<!�	�3
��������rruo)�D�?���y�jX'��O@�+�x&L�4����2�=#�K�Y���H����Q����\\��))	-�E�0������`����'l�h?v�O{�m���/&�arL�����g�_�YHtc�MU���7u�IvE���&�YS���	^���W�){M	�>@��`�����h���(0X�`�\p���i
oOMO�����6|3<�A���<�l������n�+�tzH%�=����}�;�����������E���nx��,||��D|F=�_�C2��������;���{���q i
��I�C�)<���~��78�s''�j��r��������=�����B�]�(����sx��x{��������w(��i������>�%�_��z��O�s,gG?��g����y�4���TH5�i���M9������
����l5�f[����j��|����^�yA/�����r���e���`���_N��~�53��O@#��8�B	�\[����)��c�d�\�y�i�`���
�\q9�Z��R�e���{��i���4������Jf,�������w�V��^^����eky��4~��f2�ra	~�l�\��_&��[P3�����1^Y��z�f�xw�?Rp���P�x�x��F�O��#ij��:88�h�^=��P�R���_�/�?���kV�f�#2VF|��
��p��K��tsv�?�}8�Q
N�6^�^x/�_�h=��t�Y�HC��^�I*����]�^��^4����M�������z���7����L>�P@���/oc�uO����`{���J�������|*�q����~��������!7z��.��5Q��e� ��R���|4����.b0����������5��>���?�80$�������}����q��(^^/���O�C�O�s�'��_��vTT�����2f%�+�4��9W������
�&������K��'��w��z�O�;��o?���R�sI�6���7��������z�1K@��w�z��L���]�
l�����3�IFO�L6rw�&������S2_�;8>=���	
z�@�TO�;-���DY�h���������O�������_���K��}�u����T�s�����e�������?xg���-����HgG�t~������[�����������lT�CK����j��3&�=sx���sU��_�����Rr�����E��N|��3>�9(���l��s�����m����/y�p�M��	�\���&1	~�����_ZWT���+�_Z7T����H���uK��;�'��{�Y���unYWS�U���
�S����I.��bR`p^1�R�0��7�p��w�/��"�e�]a�LE���.��������J�s����J������q_�t�y_��L_W�3n+Ww��*�e�]���%�*����uQ%>r^T�?�=e��@��J�H�-Ej�������So�r}�8���������x^��c����m2^�>����U�q/3�)��	s�l�p}b_��WY0C+�j�6�D����f�:�$��#�]�pt�����5vs/�+��J���')n��0���d1�F���d%Ro�[�"|��G����un�����	C'�i�r}fs3�wY��5%�w�%�$�NZ��Z���;�y@�Om91�^�-?MJ2l�L4OmO���d��L5���]Eh?����F��p�e9��fX�y
a���/���%������_��b(��a@���h�������+���w}�E�4�T�%�������^B{��./@�o�x�.`5s�����Y+�f�"�s ��Ox�$���]�+W�,^�!��6�"��',�;��&����y��'[�8;9>��D���{�s�����k6;:8FL�[.����\z��.��99=���3M��W_%G����5��6�F%�b��\_/���:����������Z�ov�v����mK�Mr�:1�J��,d�/4A_*F<�&$���^�<��@�I�3�}�?�yh�?��P��p,��oRA'��������.�������]ZK���H��DgdW��8)oL��k��w7�"�(#q�"��e(����D"=��c���z�gn����3���i�Y�e�i���jcfky;��S���4r�x��zSLZl����5hu���NB[�T�j���~��Y����Bt��S�E���G�-s�����o��j-��k�����N�p��V����	C�%�&���*��s��E�Ds.U\�Y�a�X�|���'�]�"��x�?t�&�%|�/��r��������������6���/P)�%�er@��4f]�k����_o7
�1�A�[~�l�+����0n�=wU4Bg��!��Q�/��7mp�77���LT5�iz�s:����|�i1��.#�����,U�'����t��	;3��1QJn���C�*c���S,�God��Y�*��DsL�~|���_�� ��OBu�� ��g���y�9�t�)a���<���.n<L�?��%s������fr�����w5	�>�8���,�4�qw_qk��7**��3�]�
:+�;��Ho�~j��~lm>N��nln>���<�F��V�kT7#v���F��V��|QL\��]���GL���Lr��@�dF�U����i$���dh+B;�1�#�[ER��5E�"U	��J-�b�
��F�E�E�_��u`S����� ����T���h����~�}����0�hl4s"m����^���z��03�#o�;�����l>]]�Q�[
}Q$X ;4�DI�<��>#��tx�p�$���q�J}����%�r�/S/����@������F��49��8>B��M��$F903�Q�V-gd%�H}B�.���g.�i�B1����h���^odd\��sr��)L��	���0@�"�.������I��1�M�{+V�~�L0�h!�
�_����Ul�:d��z�}����+"j]��h�!�����YS������=�wF$����Kv@?���=f��O�z�~l+��Vrf�3>�I0�(/�{���d�Ym�(���uN?u��z��],1\p��r8%��&��������HIy4!t(�HZ���o�p�<������Y�*��T��"#����&���t'y
�P�3� ^�;�QS�s����:LlR�G�J*=������R�,�L
����K����\�S*4��L\�ik�ar��H�y&=f�!&���W�K���d9�3���y�L�"@a�?\�g�*�c��&��M����f*���>Wq�����=��u��ee����1��J:1�7��E3/\�G��
���5{�����f���S03��c042�)\;��T���rF����\6\h���_����Wg�~!��b�T�L#RHS�����+AX�<���E�<\@��*���M��Vj��J[dGtL��X��;~��Lg56����xI����$�2��{M�4�RS�za2;.���X�]���]�aB��RY�I5�l����0�������8����LW����\+�q����k�J��T����x�k�o�w����_�"��
���g@���"^�@bp��R<�,���^���I���d'��L�u��Y�6�O�Q�7)�t���O
�<��������������U�����'S��h��T2r%�������g:����HH���:��p��Mi�U�b>��v�ov�?	;?70����KZ�2�'g��M1
���q��ta/�T�@<�^P��Y|g�sm�qni�o�6%e�:��J�Q�fQ+\����D���^�:�G�?YM����6�%*GS�>��f�`!�a�q�����dLvzJy��[�Un��J���_���o������D�F�o�*_.����!��5X���/)���&/�����"X�o�:�	�
"�8N�j��jn�Z�1[M��"�3�!��R!z�r�%	�
(��x6ZP:�����R2�s{j����
���3��U�${�N���g�{��GU�p�U�����<����m��/v������/son������R|��(Z-���*��-���������B��z�P���[�����~��0����-��j_b��o�/*�c2"F���(7�(�'��8[���R����Vo�'f�D������v����+���b���%�Q\�]5���[k�1jX�FL�[���6)f�V��i9X6'��,��os;���1E��o=���-I���J7�x���g3�U"'.BT��N���p�9�hPB�-����V�RS�.d	<�ZJRI����|�g�;��t%b3�B�R���Rb��k�{���E!C����@���jO>������{g���im�:�T!�x���Si��:�-c�����
����:�]c����qxe�>F��=�/N_|����
m��#�1��hy��N�MC�G���)` �nf t1y88:2��G���b ��Y�&��E7�/<�>"��=�w��0�y'�gS��uH��e�����{r|$�j�/��
�o������H�7N&���9z8��K������=_���G���?0a%�x+��(�����sAO�U�R�T��I���%��u�M���t�i<��.4�?��N�(��'>�k�����������Jm�a@R��Q1G�^�3��R��0�>Nfc-��%L�*��[�{}��a_J
�I���1��~M���~��PL�SC!&q�|d�=��S��z�Q���5:��?�2ZdLx��`n�-&���M`�	�����B�w��N���d����&$�?m5���v>�F��'c���jF���n��2�m1�C���T���zb+���K�Y�r��V���\wC4��[I�_..��<!5�`:�I�7��P����t�y�i=
��hJOM�7���P�����t��#7~�U��GN{�W'gj���|IiS�^�WI���g�hn�)�����(ox��$�Sf+��H����<�>�b���6��x6���u�u�����R��Q��7C����@IS��N�����S�h�������D�-(t�M<�������$���'���}�1�X����A� HG�������\�,-q|��I�&�8�/W��8���W�p����$�c��
{����'�q�4��M;{�������j���h��g;����f��gn�C����������sE&�����.Y���u���N��T��j��.d����+x)le��`�J��N�0�`�+��3��z���J}T�r�p'Vv�,�j��S�xNA������k���'�����q\������2���S��Y��l�eUG����D<�0,�������^�C�#^�����_Zv0�=�2�%{���L�.���4�h]�f]y@��6���j�o��p&��3�g}�o���'������K-�+����	����vdHF��F}�[�ci�t�W3v$5��\�~����� �L��*�Vm�Dzb ����.����k���h(�o4����k�7����e�I���V��>�c���!���E$�GK�x��G��yH����Jv���
u:�����k"�Pt��{
I������}��N�Z������3���.��m�cr��N���N�0Vl���n��i!���LOxV��RoX�����=G�K��+�U3\G�r��u�a(#�>u�H�^y������y�juM��~���r��\�l�1k����O�l\;j�Xy��@����#����}��&�T����.���^A_��r�M�����i��J�i�fCPM�.;!_@\F��8&��!f�s���/$&�WeuT��B��V�-�Z�������Jy[�RvO��U�W�Y��D��t����Y�c���F��c�z�t!7��3��z�x��j5��jL`?w�O�O���;�����=���}��t�����%%���RU)7�����W����R&�FK�.�����DZu����&��#��kE7��D�_�7�D�g��ItM�t4[��V��|���`?O�����::�CZ���BiidYp����Y��Q'���������r\�����k��8g�3�eVK��}j��/ax���<����w
_�"��='��=�b@�a�r<�U��m��y8K�x:�����3b��3��>�F�J����������j<�s����2����3���O����bv}u��R��9�) ��*-�7��vru�e���� ���6@U:{7������w��%�����w�x3���V9���T�*����8�WS�r��7�U������Xz��t ���a�f����OH=�T^������C.�V�D���hH����"�Z~[��p}&�=t.�Du��n�1�Y0g~Z��s$<Swoq�j�Y8��/��O0�g0
_��uO�IEw(�4$�Q���T@��9C����vu��*��R��,
�p=��X��������^����.�A��(����0ZDN,>��h��{���PL]��%���a�tm�S|@a��J��k{�h���c�w�l����Z��6LL�QX��D�|��g�#�������J������.�����|�-��o��
�[��{��������mvc��wr���_+7���
�9Q|�,����z*�!�;��$j����&�Ut��	����H�m
����R�
WAN����k��_3j9l3�gj{����LL��<�t�p�Z��U��������]�n����luc��x��������|����c+3v���],��Wd_��������'CR,�s��/��d�\�%9�V�S|lx��	���K���2G�V�Qa)��Z�.��'�bB1��F�5������-]��3�����$~������	�|9���.^��\YN�%�B%����d������J�/�~���E�j�xM�1�5�*O2�����!U{�|�����D����r/N����fCY!�y,��K�D����9'-�$�o4	
�?���}������n���=�}�=�����=���*J�R�����!�B�~�_yG������i�j�E��#=N�7�q��	o��@�CQ��9hV`�B�m��d�z~=�n�T�#��(�z�T� ����N'���Rw1���o����[.��-�����b_�D��ZU�����H��=����O�F#����&������IL�1�B��cS�B���D(����u	��
����cH�������
$�Z�l�TI���H[O��p�	��X^N'�����8�R��X�����E/x��4�P1��6�UB:k�@���7�J�g�|�Y������;ov���	1���	q0Bk�k6���
��vN�ki��*8�kL��*�c��.��	�������j
�p��j�V��p��U��t*h/�h$z�x^�K2������4�>�n
V���B�����jt%��g��Bt���{}���W��u�w��&��x��/��/��9I�7��I��=9|(&p��)���:�.���:<~,8���Qm�|�O[�;E
2'$��c
�	g��p>��
3Q��&8��A,<����k�]��'D��$���5���Q���d	���������&��J���+gBt����������`��o�v���4���PD��!__e\m�!-�M��7�~�'�3�6\��,�8��@�7/V�c�����c��+V����d��4���
*�+�p���b$1���H+Q���A�,|�����OLPz�����Q�����.�].�����I���(�����s���'A��������y/^�?�X8+cO���`��x{��Y�7��_��BO#o�(<:��X�>�I�����r��O�\�\��O��G�s��_����3���z~�����S*(����Ox�������`x���������u���~%F-�z�8J8A��S@J�@��joQ34*�pc� ]%�jwW}bh>W0�%����\������O���%i��-���R�/gc��l�=���,$:[������?a���?��7A�_<�G�N�v06=�/x�G�Tu�dc8���c[�e���d��<�	���=Y�����~�.�T~�.��q��D��p����x5w"P�1W�6�B��7�����S�`�' ��;�*�����h��^�h�o���?��a��
f_D��{@5R�q�&����5�'b�����b`�����r������@t8��c���+'O���j>�G�T��	n��<�����p����:�ql$�$��n&7����U���u���J�A����q�R�����c����"�UOr���pjMt)�5����r�t���T�Dn��������b�,���L!�����;y|����Pn���d1YYn}��{R�
����[��%�:�&$���6��lK����W� ~�fJd�=>�
9W�~.��=�)�K�7a�(6y��}���5�R��f�o���~��D1x��������8U�o��M��{rt0&G{.F������/�>$���3�b��s�������]�ZS�b�/���kg������eE�6��a�����n��z��,+ �x��:s��z�S������*t<�cJ�u"
�3m������;:`|�s�f�;s7���N)?j����/�����wv�;;9>��{�+�������kvNa�L7d��t0��z_�m+��@o�Tr�������1q�r /=��1Y���4��/���[<���P�-��2�.�?5_F�8���b��^D�F?��s�e���1��7��=�
J5��� ��Tth���f������T�~�*�"��v{!j�b{t�{��pr1������'�����V=�*S� 	��4a�#`V�Q�VC���3���2�@1�����F�������K�W�m+�Q"��}
��f2�����U+p�c��%�uDW�B5��S�U�����~�'n�����=�I��\�>�POb6.^���#��������������\�7��y�\R
�=\�����=�|���[TN@��*#gr���M?��N�e
d�4�@���Y��������Do�x��2��Y:`�	1��3��C�f���,��d��I5�a�{�(����T���,����2���ki���8�T��}x6��\"7���r�UN6r�l�OZ���gM���-&0�������Si��C��Z�H�hv�H���J%������Q��*��_���+�#�������������������a�wo�,����jg������W��5����P:��;������D��_A�[Y�
�B/&�aB������+�����9���p�� W�J����=7�T�t|����V��5	��e�\�S���C�\������+R�4]1U�%���J�~�&�2=�RZ���!S�\����Jr�S+Q6R;�� �������d�~�dz�5�c:��GC������x1�xE^�w
��4��"�x^�-�{�Mt�Gd�������.���S�v��S�bEi��/�MM��e�t��C������[���o��V�&`��)=��_�/t ��%����Yfc�	Y�u��n�1�+��
�|��}��������=���
v"���Sd�u�j�^��?�&�)�YRS"�t�A�g9�Mb?/��V.�2<�:Y���Gb�q���+�����?��\;C���k�tI JG���|���Gs�����`S�y`~t����@����Y����O�������&KS(�&�Dz&����fE��u-���>�����
�=�zaG�{d����
����jJ��M8CEZv�T����{������9����?������u���[����f)�EL�������E����~��
��J��X]�Hki�9B�F`'��7/�>Y�q:E�
9H����gQ4/=?%�x��#����������9�	>������P�/}���Sg���R�^_���S���<������G����N�.>w��|��?��#;�\�k�tn�=X����r$Zgi�2����9����3<�F�5�j9YN��<,=�RzO�$�b���"77a0T�l:[�$����,��>/d��������N�-���{]c�5��}Zy��	%�~����/���[.�lL�u�r����X:��x�p���n:f��o�cyMWK?��x�
M�p�wbj��.F�����31#E�b)/�k7����l�qJ� �V��>
��1�k[���%#T,����B&������JH+��sD[8U�����������q
H�a�I��4	�����E3���%��+S����_���g��-O���#(�@�����r����^��q��$������qv�rz������K!��n3WFg}c����8f�Kn��0�I<���8x�S��Fc��&-���!������m���z��4*i#���Ct����eU�o���;?z�����3�KC��0���n8�_`M�h������pS���n���E�t�
�6��W�v Zj��nj0+�+&���{b�����^h ��n.:A2�����{��
v_(�l}���M�f��wn��W:�I?���h�����g��6��/~���j�u�Ap��
�)����(#u�`V�5�=��������J�O�^���BA]����2�9���`���,�X��/�Y���.��K}��&t���!%�V
v��������,9i��N��+��!/Rb�!1��u}�����o�JF�����sl����`�jF��z�?Z�6�[k���&���X�xKSK5?��c���&���
Y
{�7�6���WT��B�y��Z=�NS���� �����B�5�>[O[��Skd�������N������.�!H�pyM��������r�GYV��V�'�����F�Ug��%��b<6���y���D�8c%oFv���� k�F��r�z�%�a\��T�*�>�y���yy�����_cB3W�1������zk�#���PL�V�G�5�J��k6��a�,�dbL���j#^���,+�F6�����=���M�H%
� �N����Vh�&�Z�j���&��Fr�@��[X�����|�8#9-��%yYW���|���u8��8qse:�q�nrV��"ZGF��2�i-Ac�%�
>�������Ds��mxA�.(�/��H���+>����!^��s�K���?����0�� U}z�z�|�4����e-����Q��jW�3L�	��M]bJS!
�����<�gS�}���:f��~d��G#SN�5[
��1�3�U���=�j�J�}T�4_U���������O�w��:;�����w�h5_�U�����K�0��b`y�O1KN��Mcf{k�����/z	�^�90�$�{�snsTNRv	h����;���s3��j�R��P���%L8�e^�S�Q�@_��1��+W���}B��9�����Y��{���Cu�p���&����������O�JSw��c������O��l������!a����� iDS8	tA��B6E���w��@�B�������m+��y1%a�2�l$�7=�_�����^sD���WL��j��lF!=���m:���1���o�O'#j4\DS���36��:����	T�Xk��HE^��#Jj�?(����D��P$@Bp�]IRu�Q$���}�����aE8 6�b�y:�A��`	|��%�J�h���3�r4��m(�qo���)b(�Y[��

�k
�����s1��<K��f2���O&3<�������	c��wcS�3�q���4�)���.��,����G}�16�j���w`�������p?���7������B������o�)Vz��lR��d?}V"#)	��t�s�dzcx7�'��q23r�i�}����\�^� �]Y�\�u/�����b���qI�<�y����.�J�^��	����jM��6�<�J8|{��	R���QU�������7[�_��`h�Et��|�I�q8Ec��4���y�����^��4[1w�F�t%����9y�]���`��=����6h9���k��G�����Z��N����l��m-��O�\O��,J-L>���u���=�����j���8�kY
��Q(�(
���V.G������E1��������Z����� ���j
Mu�ZX�U5e�G��~ZX��g�����r=e��2��Z�L��:���x;T,`d�<�4��� j2���v�*��i�n�d���*�&��� ������}J�c��,���H�%P�=1��%�=$kp�Y��V��������Xa+�NV�8
�,@�>	�&������~�)��|��<y���Q0�	��tw��%^�X��j��ZD[�4#F��=���}
����&���x����z,�f�_���K���5~G��*��
S��!�owGS���`��C*R���(-?��2%�'��s�d��Z����8�?:�2���A�s��g���F�c���_��N��|$��lK�
��w�~�\���������<�e_d�Nt��!���A��u-����O���gML7���[��3@`���H��E�#�NfCX�������P�q����(��:9}v�ILJ�x�=?������Q�Cx����F�4�bP1b8N�T��F����g_�&l�������G���6�N���Y��F�qmM.C�Q75k�c���T��`�pt�����������L�Q�E�-m��B(�E��f����p������lPR�
/�Q}	����gC���])]�e���O&k�,&��,�AD T�K.4{!|'[p������_�4x\a���Mt��6��,���b��_��X�O�x��e)]�X�|�\���F!Gm�z����hY�f���6��D5�1��i�eH/eh�F%���1T�BU0P��A2?�������G����<�F�]�����g��"!�`+�%��"e.��4���^l�i�F~����5�v|�>�����G���h�����7�|��P�#�?]�Q����9�]��w�+�Z�������d����f��-�7VL�j���)/tS`�H�!j��0u��G��D �E��!�ae�����9��t��N�ap}L�l���}����OON�d����l�T&�D���iC����`�P%)
EV�z+:z;W�1N&��.���+��{:�	z�����������m���&�8�Vt�
w�+^#�%�:���/T���&5�0��-����~�;�M	�������w.7����������qd��%j���w�����\+}[��i=F�z�n5{Rz�tG���	�jTBw��\��oj��Pd'b�C�(C�� �*��f��m��0p��7�J�.W�H�cm��W1K��s���+B`m��mQ�5H��x%5�Xp)��y
]�<�Uy�-���~[�i}o��z^�p�O6���S=Y+J:O�����m��� ��	�(�[�YI��~[��F����C�����|�e�sQ�F�ot��1���A��h>\���0D5��a����K!l����dov�����/�D3���W���$1�Q>/�5���&�g��pYJ>���;�; H����~$�K�60�;5��"�]sbd�_��}�{{�{o������D3��aC�3	����.AjX�����`v������F6[��:6�S�+�G�O����K��3Z-0#�������D���I�G\DM����HM��X=����8����o�.`v��0�����F%q`�Q��������[P������{PP����B��/p�\��Y�;�����B�_�S|��2eJ��f��$
=����9�B� ������[��Cwq��M���c�Ug�e_��araB�� ��xE���G�]�2�������������_9w����xY����K�h�7�p��57��������k���m��J����S������QD0�OJ��V��T=W!�Xb���|�bw������K�$]q�*1]W~�����:�:����O��U'I�-S�B�k
�@+�X��
w�$��k�y����8s~�he�����[�9�XE3���<&�������(��^�{a����!~.��@}r_�KN����RBL��w:	���WWqm�����#���
�h���^���������)3�����,(|Qwh�������H�G�xw��������v�;:/���m�D��3��H+!�b�A�����������=���{��S�3�M���>@'��O\�/a�e(����&JG�893�1�w�=��H3NcSN��x��q����R�I�����s���I*J��#��#@��������0�n	���s�y��]"^"�M���k�4�k�cj����H�C�n��x������u��bt���\�P�^�"���XQ�!;x�<�����@6'$Wv��-}����)Aw�����~K��:��%��{QSO�)�}��aSOr�m���9R�=<|�}��g(8y:��^�����>m�����9���&���^?��[�y�|F[����V���H�W������p�)��o{P�N����_�$C��
�I0��#�L���-f$~����O�����[��t�]��f��%4Xf5�M��9��q���y@��01g��4�4��:R;V��Z����M���n|�����4����]��,cB]���\�5��Y���tF.�m�������8h��(�v�B�EK��P����&S�>����$m�V��q����
H�"jqB���Z��pQv#�o$�n`6��E�M�h+1xD���F�����	X��7�C��h6�7:��{��V$���#�o&�<@t���dsq~���%�o<�?���0�}R����	���a���9a?�Y��s���_o�M�*I[O��P��	����}r�R��Y+tpK����$�����r�g��R��$��X3vFA��~�����d�/�X�q"9�
���AV�1G+����R��c���wvRY�a���D2S0=�9����x���x��?V�
�0[���z��Yv�m����X�N��H�������S��i�2#�|�:7�"�����M�����cW4��B�9����8�GB�2��t}z����U����V
�k;h��%�eaY���
�x:B>��@���<�$=h_9������{��S0���<�*~7�a���s��J!�������@�Hh��+v�?��38�v���r5����o[^����A����q����	'R&�g�:�������w�a���Hod���B�5��N�hZ!�g�/c�H����"��b���P�)�H��6��K9��x��$��1,*�����x)����S�\��J��"c
��O\IT-7�6��=E�����U'�/l;�]��G�dr)���VC�L��\����'��m���L`��~�h+
N�t{�v��p����E%��Or�5I������x���48	f����=��J��Jw�4�NGZ0����'���4�)���A*�N�8H�}�u���B��GE��&1�#Wo'�6���tiz������M83��s�)oV��j��������	 �w��*`u+�� �\B[���Ry����V������bt�;9~�����i����m"��C�GL5�Z�(�������<M}���n�������D|@�S���/��N3y��� x��*��O�BBm"U�^��t�|\S(5U
�G�OyS��K{�8���_�wn��;8��������p�o�>���T���uw��N�"�����B���HT���I�H
����UsO������o�O�h�]�?2��,c��p����S���X������JX��7�����v���X����-���
d��=������OGq[}k�k��E���s�o�!R�����X�x��kIl� \,�EFI�T��Br�8f#U��%P�fR~���f������?WK��R�9��������W����6�v����.�I�/;os���\p8)=�����n�x)�j�Z��>)|k�#��&-���h���U��!�����N���T�2Oo����Ep��j�.���!e9�����t*B�"�����s���h�)g�}����T�t��V)��Js�Jj�
��+�I�k�Dw�T&�c,��%J��X�0 ����>�VY�������<��I�%�����X��V]�jZ�
��8��5�C+K��J$X2V���!����\�����#�y���EL�	?a0!b-.i��i���ka��j��0ux���e|�:)�����r�c_�T�������q�x��C�S�+�2��	�FTY�& p�b�Q����*9��/L�9�T�(��>�=�{���=�q���{�?������wq��y�����+�2�~&�6[eRk��S�V)��J��BJ}�
�)7�)�����CX��w��w���o>��8?��<��*e����� �]���=T2z�	�ta����l�ng�+Hvt�\P�oU����������tpq�=>�H���n���je��p�����((_����N�n��N�9�
���#����}��HZ(���b?������s��b|�����od�;�$�r�U��
/O�R5�u�������}r�5�V1������F���2�X��r�f�z)����qo����=8?=����P��x4;���g��U0=��EW�����'��%�F�}��<:����$�(�u��xj��7����b�����MI��������;��7�'����|+5.����L.� (y^�]@������>t��4P��<��h�5��e���t*U��rR�]�t�S��1��|_����+���*j#c����}�����mpr.�1�\DG�=�2�\�����2�����u�[�uB}/�n�1tG��w!u�s����<E�
�r���m66+���s��Z�w3\�|��Fr��X���
W'p����HUw��B)��[qS�8�a�P�S�j���g���{p�^��pwv�z���%��=���J?N�,�a�R�����������coOz�x$;HU�L$X�'�?�����w$[��U����lu�n�c�����G���(���(|Rw�{������i�}O6��1���<jvX�����z�
����;�=�>�R!{�}8-�!p+�2��^&>��������(��]���3�?��W���M�P�#��b�-&K�/�l�����I}����~>?������,�@���}���oNy��J����g��8gp�X��D�4���.R�u��`W���S/�i8���0'��
�B#���9RHR&v:�I��T~y@���n�t|jM6��&�W�/�
]�c��x����.��>,�6X���j�<X��ccGw��F�������#mn��~��@3�Hn\��_�;�:J��������=4�HKldQ);N�����E�RG�Lm�x�w8��T����6��������q�V��ki#ZS��s�C�sx�$�r,Z}Z�	�(
��������z��}��a0��3�0N|���e�������$�No��Ga��&!�����,�-�����]7�?{���������l4�� ���N,�)X�z�&���f�p�
#U)���r�����gD�/yF��U<UlY����Ht#�
��6xu� ?`B�)y����n��~%/�Ah���=��`���!�~���t� ��d@u�8!y����yS}g\��d�R�������D�!��\��l~��!�+[p��.:gh���� ����Ln�Z�:~����O�X��;!_�����)�)4����M<}�*�+����%8�_��v�����h���0��>�D��^����e�������B}��e�pw	�A,[v��B>������m>����/��,.J��b�ym����x����m��of��`F��J����uV�H�L��Dx,.�r,�n%��Mk��m/,�6R�T����w�*��:5�(����.sX�W���2k�QV�:����H^T�Q��'Sl�yT���e%(!�R6BWg��H��'�+����1E�Ww1�`�	W�����Q��t�DO��*d%J��*�%J+X���
QA�|�5[�sn����oS6zF��8#���D��I�/����`tn5+��C�&�,��>,T�f��WA���n�XZ�bT-�0o-9�8*����H��������Uwc�U��BN|b�9-e�6���;iE+��Q,�Y8+X�M|l	��V�
~��g�;�C��H�S�>Y�(��o��V�����B:�����_���M���u��!\�p6���s)���M������x�G���9��o�wc�R��c�W7�gm���������i�n�"�B�G��������O��CZ�s�5�M_���4��%���;����?��O��F�]�&��)��� ��.����*��������S�A�r��/lr���J@�w������2��T���d����}y��hD@b
p��d�$����yN�u���:����%��0���	�?(L���sT������Eh�YI����@��Sj�~�Z~��7����������T����?�@��{��M��JE��# b�N�\�s1��,�j�j���rL`����	���-��.;i���d�������]�������\��6�M�����+d��_�F�SF���U�XR������/g4:�c��+��1�����~���M�3�������c�{�����|v.���l~���0��~H���zH��c�?^;���6
kE8����D^w�o�)�vo��h�����`c��g��S��������d���Is���=xf�����=����+��H4���jv>+���e�2�&mG�{��b���/q�CCd{����P����p�����p�bG��L���.�}�������U5t�A�i����A���r���i�^���c�j��u��8P���w�'.�c��X/�A����"�y�����5#��?Z3ISYQ���_�
�#���K���s�m��!�~Ej�e�|$x(����7��$�h<��x���
��$������f���\��� ���.�����������oi�|���\=@��U��G��Y���5m�B	#���O������q����_U�������`�x�-B&l\r���$%n�����l�de�ePLz��r~o����-g�w?uA��2A?�fJ"�"�
��	�E�X2.��@���h[9g1/���9f�U52L ����}J�6^	�(����c�
+�@)F	WC���T<����#��<z�	����'�$eq/�� ����+�k1vy�OK����-">�L�U�cO���v��i�_��~@����N����(j
(�J�@�+`��P�\z�����"<m���gd�kH�i�$]��g_�ck��X;d��F�t@1� �]�kwZ7W��$c��W(^��.{�f�vyu�6�@>Q��I��+��,X��\�L��������:���s��~;
��\��[�X8�F�^�u������cFG���������%���Z���]����C���F��:��"+�eZFI@������������Y.I����#Bo4�PZ��	(��P�s�����]3�����~qu��h�"�N�o:5��hR������G��:��?��B����:���#_s90Vd{�1�_@<$"��K���7��m���~x��,%�-0�D/����t���A;%��66��4�@x����bfj�� �9{,\^/���L�+�{�D�=����Mg��=|�2����Gt���R�d���<�b�����G���\5����r��i��|�[��}83?F����L�����*�BKj�E_b+5�gNFK��\�z�A��g!�zY�zm�=�vH��a���?
B�E��,���V2�
:���B�!G��'��A� �v������I@�u��/�������I����_��/�xZ�he��o��_�*��__�m�o�9�I"J�
�� �_{��l���\�����q��@������+E��kP	��������4]����=v�a�nLZG��jy����!K��p+�����<�}Q�� 4��gf-#��	� �&��)���",��$:�&`��A�H7��
�3���B���	��i�M�a7�</b�)���wI7oF��j�i�A ��!���,pX����4"5.pg��B2��<od. <d6��o���*�������������5ns��(�B�����v���u0
\����/Agsu�Ye����=|>��i	�{�N�����R��+�y�4�KFYN�i]��:%O��=A�z�_n�u��9�`~s�����$�K|,
��=�t����G^���z�h$^��yJjtB�3���G��C0uC�x�������-f��+�����P�k�$dG*e��\p�w������ep�
�(
>zcYE�g^��=�(�j���lDA J�������#H
"*��%E�2���eg~b��K�Z�
y^�V�@���
��Tn���.��D���*^PR87���.r��s>2$���FBA�A�\j5a�O��W��4T������%����J>�����H�f0{g1Wrgc��h�k��as��<���L�4d��7eu�����v���Lq�d����	���'KAj��%;��6��m���'�/B0�^�My����3��"@��y�Fw�M�k�5�^g�a'
��d��+�D�zph������qTS������9��h� �a��)���tX9E4v�a����i���Uk
��eD� l(�\��N��3M��u�[����v|l�$��R��w�����V�=��A��3�R��<��:-|!��,�m~	�m�j�ms���1��D��Bn:W,6Gz���3������x.����B�e1�#���
��Hn�
�.(`s�s���@����J������ aE8$��B*�T48P�8SLd�����Hp5o�H�������?Rll4c�A�4A���%
o�Q�N������.rB4��]4B�q�z2�Du��������1���DB\�8��r5 {��L+y9��*q$1Z��/0#3-]��7�1{ �A	Ob=])lz�sP�����^�Y����s�e�<��9>�iL����������_�N��c��X�(���,�.1���D��������=i�U?��*�f��j��Q���3����Y������N�p�XHp��y��n�%����to:��{9BT���s�C�W��x����6�������3���������NI��.��t�5
<$��\��I8d)!!�<O[z��2�P]�df^,������5������K������!����$��8@�����PeJYCU&���� ���
�SQ�Q�q������^�]\^�K}�62���Gca�����&���e�����tO,-��v
�N�o�b3�N[(�M�M�M�M�M�M����5��L���h<q�����{e�kw�kw�kw�kw�k��iSo�#Fm��������|Q�F2��U3`zV��T�q�E�X������IZ�63;�$&�#"�b�L�v�����X���Y�J2�P���)�Jg8�"��b�����Je��M.e��	�Z	��H��Rs����L���Qc��.>��E����4�,P9#�Y+�mjlF�\� ��/|~�}>�{l�3}���/�?/�`.iM�7��<n���UO�����wm,�>�2>Rqa���+���(
�\�Y\������Z***h�~B�
����0�X��a(SPX���	�h��2�`���c�U��Tl9��ia�
�����m�4��e1��E�
�T*��m�����8�-)R��J��)\G��LqY�Y���FkK�2S6�2�����
R]||��XY5�6\�5���	7�� �M~���M��2t�!��O�� �T�Z���2�o������d����X��t�+"����SP#�0*���o�m�5UV��]��t|����,%�� ��������q�xU��n<7����
<�������������q5Lt��P3�@�&���X�Q5���$dx�8z4w�L_�o����[�?F�H����E��������Y�u;	����Q�[�l}����
l�'9=t]o
���xb`5]G����a��d,r���j���!
#78Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#77)
Re: Command Triggers, patch v11

On 9 March 2012 21:38, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Hi,

Please find attached v15 of the patch, addressing all known issues apart
from the trigger function argument passing style. Expect a new patch
with that taken care of early next week.

 (The github branch too, should you be using that)

Thom Brown <thom@linux.com> writes:

CREATE OPERATOR CLASS now shows objectname as 'hash' instead of its
name where it's declared as "USING hash".  This isn't a problem with
ALTER/DROP OPERATOR CLASS.  Everything else above works as expected
now.

Ah yes that needed a special case, it's properly handled now, and
tested.

Confirmed.

When dropping domains, the name of the domain includes the schema name:

Fixed.

Confirmed.

Could we change this to "REINDEX DATABASE triggers are not supported"?
 This way it would be consistent with the "AFTER CREATE INDEX
CONCURRENTLY" warning.

Sure, done.

Confirmed, thanks.

REINDEX on a table seems to show no schema name but an object name for
specific triggers:

Was a typo, fixed.

Confirmed

When REINDEXing an index rather than a table, the table's details are
shown in the trigger.  Is this expected?:

Fixed.

Confirmed.

ALTER CAST is still listed and needs removing, not just from the
documentation but every place it's used your code too.  I can
currently create a trigger for it, but it's impossible for it to fire
since there's no such command.

Removed.

Confirmed that it's removed from the code. Just needs removing from
the docs too now. (doc/src/sgml/ref/create_command_trigger.sgml)

All these corrections I mentioned previously still needs to be made:

That's about the docs, I edited them accordingly to your comments.

Confirmed.

Thom Brown <thom@linux.com> writes:

All other command triggers don't fire on read-only standbys, and the
inconsistency doesn't seem right.  On the one hand all
CREATE/DROP/ALTER triggers aren't fired because of the "cannot execute
<command> in a read-only transaction" error message, but triggers do
occur before utility commands, which would otherwise display the same
message, and might not display it at all if the trigger causes an
error in its function call.  So it seems like they should either all
fire, or none of them should.  What are you thoughts?

The others trigger don't fire because an ERROR case is detected before
they have a chance to run, much like on a primary in some ERROR cases.

Yes, I've since discovered that I shouldn't have been treating them
equally due to the different type of error those sets of commands
generate. That's fine then.

Thom Brown <thom@linux.com> writes:

It was late last night and I forgot to get around to testing pg_dump,
which isn't working correctly:

Fixed.

Confirmed.

Also I notice that CREATE/ALTER/DROP COMMAND TRIGGER appears just
before CREATE/ALTER/DROP TRIGGER in the documentation.  This breaks
the alphabetical order and I wasn't expecting to find it there when
scanning down the page.  Could we move them into an alphabetic
position?

I don't see that problem in the source files, could you be more specific?

I can't see the problem in the source either, but when viewing
postgresql/doc/src/sgml/html/reference.html in my browser, CREATE
COMMAND TRIGGER appears between CREATE TRIGGER and CREATE TYPE. Not
sure why though.

Unless you're cleverly distracted me away from some issue that's yet
to be resolved, that appears to be nearly all my concerns addressed.
All that appears to remain is the trigger-variable stuff which you
said shall arrive early next week, and also the that odd doc issue I
mentioned in the paragraph above (although that could just be
something weird happening when I build it). Sounds like I have the
weekend off. :)

Thanks Dimitri.

--
Thom

#79Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#1)
Re: Command Triggers, patch v11

Hi,

I did a short review of what I found after merging master
(b4af1c25bbc636379efc5d2ffb9d420765705b8a) to what I currently fetched from
your repo (d63df64580114de4d83cfe8eb45eb630724b8b6f).

- I still find it strange not to fire on cascading actions
- I dislike the missing locking leading to strange errors uppon concurrent
changes. But then thats just about all the rest of commands/* is handling
it...
T1:
BEGIN;
ALTER COMMAND TRIGGER cmd_before SET DISABLE;

T2:
BEGIN;
ALTER COMMAND TRIGGER cmd_before SET DISABLE;

T1:
COMMIT;

T2:
ERROR: tuple concurrently updated

- I think list_command_triggers should do a heap_lock_tuple(LockTupleShared)
on the command trigger tuple. But then again just about nothing else does :(

- ExecBeforeOrInsteadOfCommandTriggers is referenced in
exec_command_triggers_internal comments

- InitCommandContext comments are outdated

- generally comments look a bit outdated

- shouldn't the command trigger stuff for ALTER TABLE be done in inside
AlterTable instead of utility.c?

- you have repetitions of the following pattern:
void
ExecBeforeCommandTriggers(CommandContext cmd)
{
/* that will execute under command trigger memory context */
if (cmd != NULL && cmd->before != NIL)
exec_command_triggers_internal(cmd, cmd->before, "BEFORE");

/* switch back to the command Memory Context now */
MemoryContextSwitchTo(cmd->oldmctx);
}

1. Either cmd != NULL does not need to be checked or you need to check it
before the MemoryContextSwitchTo
2. the switch to cmd->oldmctx made me very wary at first because I wasn't sure
its guaranteed to be non NULL

- why is there a special CommandTriggerContext if its not reset separately?
Should it be reset? I have to say that I dislike the api around this.

- an AFTER .. ALTER AGGREATE ... SET SCHEMA has the wrong schema. Probably the
same problem exists elsewhere. Or is that as-designed? Would be inconsistent
with the way object names are handled.

- what does that mean?
+       cmd.objectname = NULL;  /* composite object name */

- DropPropertyStmt seems to be an unused leftover?

Andres

#80Alvaro Herrera
alvherre@commandprompt.com
In reply to: Andres Freund (#79)
Re: Command Triggers, patch v11

Excerpts from Andres Freund's message of mar mar 13 08:22:26 -0300 2012:

- I think list_command_triggers should do a heap_lock_tuple(LockTupleShared)
on the command trigger tuple. But then again just about nothing else does :(

If you want to do something like that, I think it's probably more
appropriate to use LockDatabaseObject than heap_lock_tuple.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#81Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andres Freund (#79)
Re: Command Triggers, patch v11

Hi,

Andres Freund <andres@anarazel.de> writes:

I did a short review of what I found after merging master

Thanks!

- I still find it strange not to fire on cascading actions

We don't build statement for cascading so we don't fire command
triggers. The user view is that there was no drop command on the sub
objects, only on the main one.

I know it's not ideal, but that's a limit we have to bite for 9.2
unfortunately.

- I dislike the missing locking leading to strange errors uppon concurrent
changes. But then thats just about all the rest of commands/* is handling
it...

- I think list_command_triggers should do a heap_lock_tuple(LockTupleShared)
on the command trigger tuple. But then again just about nothing else does :(

As you say, about nothing else does. I think that's a work for another
patch.

- ExecBeforeOrInsteadOfCommandTriggers is referenced in
exec_command_triggers_internal comments
- InitCommandContext comments are outdated
- generally comments look a bit outdated

Fixed.

- shouldn't the command trigger stuff for ALTER TABLE be done in inside
AlterTable instead of utility.c?

Right, done.

- you have repetitions of the following pattern:
void
ExecBeforeCommandTriggers(CommandContext cmd)
{
/* that will execute under command trigger memory context */
if (cmd != NULL && cmd->before != NIL)
exec_command_triggers_internal(cmd, cmd->before, "BEFORE");

/* switch back to the command Memory Context now */
MemoryContextSwitchTo(cmd->oldmctx);
}

1. Either cmd != NULL does not need to be checked or you need to check it
before the MemoryContextSwitchTo

I've fixed that code.

2. the switch to cmd->oldmctx made me very wary at first because I wasn't sure
its guaranteed to be non NULL

- why is there a special CommandTriggerContext if its not reset separately?
Should it be reset? I have to say that I dislike the api around this.

Some call sites need to be able to call those functions a dynamic number
of times. I could add a reset boolean parameter that would mostly be
true in all call site and false in two of them (RemoveObjects,
RemoveRelations), and add a new function to just reset the memory
context then.

Or maybe you have a better idea about the ideal API here?

- an AFTER .. ALTER AGGREATE ... SET SCHEMA has the wrong schema. Probably the
same problem exists elsewhere. Or is that as-designed? Would be inconsistent
with the way object names are handled.

I'm surprised, here's an excerpt from the added regression tests:

alter function notfun(int) set schema cmd;
NOTICE: snitch: BEFORE any ALTER FUNCTION
NOTICE: snitch: BEFORE ALTER FUNCTION public notfun
NOTICE: snitch: AFTER ALTER FUNCTION cmd notfun
NOTICE: snitch: AFTER any ALTER FUNCTION

- what does that mean?
+       cmd.objectname = NULL;  /* composite object name */

User mapping and casts object names are composite, and I don't know how
to represent that in a single text structure.

- DropPropertyStmt seems to be an unused leftover?

Seems so, cleaned out.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#82Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#81)
Re: Command Triggers, patch v11

On Tuesday, March 13, 2012 09:07:32 PM Dimitri Fontaine wrote:

Hi,

Andres Freund <andres@anarazel.de> writes:

I did a short review of what I found after merging master

Thanks!

- I still find it strange not to fire on cascading actions

We don't build statement for cascading so we don't fire command
triggers. The user view is that there was no drop command on the sub
objects, only on the main one.

I know it's not ideal, but that's a limit we have to bite for 9.2
unfortunately.

Hm. Especially in partially replicated scenarios I think that will bite. But
then: There will be a 9.3 at some point ;)

- I dislike the missing locking leading to strange errors uppon
concurrent changes. But then thats just about all the rest of commands/*
is handling it...

- I think list_command_triggers should do a
heap_lock_tuple(LockTupleShared)

on the command trigger tuple. But then again just about nothing else
does :(

As you say, about nothing else does. I think that's a work for another
patch.

Not sure, that way the required work is getting bigger and bigger. But I can
live with that... I think the command trigger work will make better
concurrency safeness of DDL necessary.

2. the switch to cmd->oldmctx made me very wary at first because I wasn't
sure its guaranteed to be non NULL

- why is there a special CommandTriggerContext if its not reset
separately? Should it be reset? I have to say that I dislike the api
around this.

Some call sites need to be able to call those functions a dynamic number
of times. I could add a reset boolean parameter that would mostly be
true in all call site and false in two of them (RemoveObjects,
RemoveRelations), and add a new function to just reset the memory
context then.
Or maybe you have a better idea about the ideal API here?

I wonder if the answer is making the API more symmetric. Seems more future-
proof in combination to being cleaner.

//create a new memory context
InitCommandContext(cmd);

if(CommandFiresTriggers(cmd)){
//still in current memory context, after all not much memory should be
allocated here
cmd.foo = bar;
//switches memory context during function execution, resets it afterwards
ExecBeforeCommandTriggers(cmd);
}

if(CommandFiresTriggers(cmd)){
cmd.zap = blub;
ExecAfterCommandTriggers(cmd);
}

//drop the memory context
CleanupCommandContext(cmd);

I find the changing of memory context in CommandFires[After]Trigger + switch
back in Exec*CommandTrigger rather bad style and I don't really see the point
anyway.

- an AFTER .. ALTER AGGREATE ... SET SCHEMA has the wrong schema.
Probably the same problem exists elsewhere. Or is that as-designed?
Would be inconsistent with the way object names are handled.

I'm surprised, here's an excerpt from the added regression tests:

alter function notfun(int) set schema cmd;
NOTICE: snitch: BEFORE any ALTER FUNCTION
NOTICE: snitch: BEFORE ALTER FUNCTION public notfun
NOTICE: snitch: AFTER ALTER FUNCTION cmd notfun
NOTICE: snitch: AFTER any ALTER FUNCTION

I was not looking at ALTER FUNCTION but ALTER AGGREGATE. And I looked wrongly.
Sorry for that.

Generally, uppon rereading, I have to say that I am not very happy with the
decision that ANY triggers are fired from other places than the specific
triggers. That seams to be a rather dangerous/confusing route to me.
Especially because sometimes errors (permissions, duplicated names, etc) are
raised differently in ANY than in specific triggers now:

postgres=# ALTER AGGREGATE bar.array_agg_union3(anyarray) SET SCHEMA public;
NOTICE: when BEFORE, tag ALTER AGGREGATE, objectid <NULL>, schemaname <NULL>,
objectname <NULL>
ERROR: aggregate bar.array_agg_union3(anyarray) does not exist

postgres=# ALTER AGGREGATE public.array_agg_union3(anyarray) SET SCHEMA
public;
NOTICE: when BEFORE, tag ALTER AGGREGATE, objectid <NULL>, schemaname <NULL>,
objectname <NULL>
ERROR: function array_agg_union3(anyarray) is already in schema "public"

postgres=# ALTER AGGREGATE public.array_agg_union3(anyarray) SET SCHEMA bar;
NOTICE: when BEFORE, tag ALTER AGGREGATE, objectid <NULL>, schemaname <NULL>,
objectname <NULL>
NOTICE: when BEFORE, tag ALTER AGGREGATE, objectid 16415, schemaname public,
objectname array_agg_union3
NOTICE: when AFTER, tag ALTER AGGREGATE, objectid 16415, schemaname bar,
objectname array_agg_union3
NOTICE: when AFTER, tag ALTER AGGREGATE, objectid <NULL>, schemaname <NULL>,
objectname <NULL>

Andres

#83Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#82)
Re: Command Triggers, patch v11

On Tue, Mar 13, 2012 at 5:06 PM, Andres Freund <andres@anarazel.de> wrote:

Generally, uppon rereading, I have to say that I am not very happy with the
decision that ANY triggers are fired from other places than the specific
triggers. That seams to be a rather dangerous/confusing route to me.

I agree. I think that's a complete non-starter.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#84Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#83)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

On Tue, Mar 13, 2012 at 5:06 PM, Andres Freund <andres@anarazel.de> wrote:

Generally, uppon rereading, I have to say that I am not very happy with the
decision that ANY triggers are fired from other places than the specific
triggers. That seams to be a rather dangerous/confusing route to me.

I agree. I think that's a complete non-starter.

Ok, well, let me react in 2 ways here:

A. it's very easy to change and will simplify the code
B. it's been done this way for good reasons (at the time)

Specifically, I've been asked to implement the feature of blocking all
and any DDL activity on a machine in a single command, and we don't have
support for triggers on all commands (remember shared objects).

Now, as I've completed support for all interesting commands the
discrepancy between what's supported in the ANY case and in the specific
command case has reduced. If you're saying to nothing, that's good news.

Also, when calling the user's procedure from the same place in case of an
ANY command trigger or a specific one it's then possible to just hand
them over the exact same set of info (object id, name, schema name).

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#85Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#84)
Re: Command Triggers, patch v11

On Wed, Mar 14, 2012 at 4:27 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Also, when calling the user's procedure from the same place in case of an
ANY command trigger or a specific one it's then possible to just hand
them over the exact same set of info (object id, name, schema name).

Yes, I think that's an essential property of the system, here.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#86Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#85)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

Also, when calling the user's procedure from the same place in case of an
ANY command trigger or a specific one it's then possible to just hand
them over the exact same set of info (object id, name, schema name).

Yes, I think that's an essential property of the system, here.

Ok, I've implemented that. No patch attached because I need to merge
with master again and I'm out to sleep now, it sometimes ring when being
on-call…

Curious people might have a look at my github repository where the
command_triggers branch is updated:

https://github.com/dimitri/postgres/compare/daf69e1e...e3714cb9e6

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#87Thom Brown
thom@linux.com
In reply to: Dimitri Fontaine (#86)
Re: Command Triggers, patch v11

On 14 March 2012 21:33, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Ok, I've implemented that. No patch attached because I need to merge
with master again and I'm out to sleep now, it sometimes ring when being
on-call…

Curious people might have a look at my github repository where the
command_triggers branch is updated:

Will you also be committing the trigger function variable changes
shortly? I don't wish to test anything prior to this as that will
involve a complete re-test from my side anyway.

--
Thom

#88Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Thom Brown (#87)
Re: Command Triggers, patch v11

Thom Brown <thom@linux.com> writes:

Will you also be committing the trigger function variable changes
shortly? I don't wish to test anything prior to this as that will
involve a complete re-test from my side anyway.

It's on its way, I had to spend time elsewhere, sorry about that. With
some luck I can post a intermediate patch later this evening limited to
PLpgSQL support for your testing.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#89Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#51)
Re: Command Triggers, patch v11

Andres Freund <andres@anarazel.de> writes:

[ ctas-01.patch ]

I'm starting to look at this now. For a patch that's supposed to
de-complicate things, it seems pretty messy :-(

One thing I soon found is that it lacks support for EXPLAIN SELECT INTO.
That used to work, but now you get

regression=# explain select * into foo from tenk1;
ERROR: INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT
LINE 1: explain select * into foo from tenk1;
^

and while fixing the parse analysis for that is probably not too hard,
it would still fail to work nicely, since explain.c lacks support for
CreateTableAsStmt utility statements. I think we'd have to invent
something parallel to ExplainExecuteQuery to support this, and I really
doubt that it's worth the trouble. Does anyone have a problem with
desupporting the case?

Also, it seems to me that the patch is spending way too much effort on
trying to exactly duplicate the old error messages for various flavors
of "INTO not allowed here", and still not succeeding terribly well.
I'm inclined to just have a one-size-fits-all message in
transformSelectStmt, which will fire if intoClause hasn't been cleared
before we get there; any objections?

A couple of other cosmetic thoughts: I'm tempted to split the execution
support out into a new file, rather than bloat tablecmds.c any further;
and I'm wondering if the interface to EXECUTE INTO can't be simplified.
(It may have been a mistake to remove the IntoClause in ExecuteStmt.)

regards, tom lane

#90Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#89)
Re: Command Triggers, patch v11

On Friday, March 16, 2012 09:54:47 PM Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

[ ctas-01.patch ]

I'm starting to look at this now.

Great!

For a patch that's supposed to de-complicate things, it seems pretty messy

:-(
Yea. It started out simple but never stopped getting more complicated. Getting
rid of the duplication still seems to make sense to me though.

One thing I soon found is that it lacks support for EXPLAIN SELECT INTO.
That used to work, but now you get

regression=# explain select * into foo from tenk1;
ERROR: INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT
LINE 1: explain select * into foo from tenk1;
^

and while fixing the parse analysis for that is probably not too hard,
it would still fail to work nicely, since explain.c lacks support for
CreateTableAsStmt utility statements.I think we'd have to invent
something parallel to ExplainExecuteQuery to support this, and I really
doubt that it's worth the trouble. Does anyone have a problem with
desupporting the case?

I am all for removing it.

Also, it seems to me that the patch is spending way too much effort on
trying to exactly duplicate the old error messages for various flavors
of "INTO not allowed here", and still not succeeding terribly well.
I'm inclined to just have a one-size-fits-all message in
transformSelectStmt, which will fire if intoClause hasn't been cleared
before we get there; any objections?

I was/am decidedly unhappy about the whole error message stuff. Thats what
turned the patch from removing lines to making it bigger than before.
I tried to be compatible to make sure I understood what was happening. I am
fine with making it one simple error message.

A couple of other cosmetic thoughts: I'm tempted to split the execution
support out into a new file, rather than bloat tablecmds.c any further;
and I'm wondering if the interface to EXECUTE INTO can't be simplified.
(It may have been a mistake to remove the IntoClause in ExecuteStmt.)

Hm. I vote against keeping the IntoClause stuff in ExecuteStmt. Splitting
stuff from tablecmd.c seems sensible though.
One more thing I disliked quite a bit was the duplication of the EXECUTE
handling. Do you see a way to deduplicate that?

If you give me some hints about what to change I am happy to revise the patch
myself should you prefer that.

Andres

#91Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#90)
Re: Command Triggers, patch v11

Andres Freund <andres@anarazel.de> writes:

One more thing I disliked quite a bit was the duplication of the EXECUTE
handling. Do you see a way to deduplicate that?

Yeah, that's what's bugging me, too. I think a chunk of the problem is
that you're insisting on having control come back to CreateTableAs()
to perform the table creation. I'm thinking that if the table creation
were to be moved into the tuple receiver's startup routine, we could
avoid needing to get control back between ExecutorStartup and
ExecutorRun, and then all that would be required would be to call
ExecuteQuery with a different DestReceiver.

regards, tom lane

#92Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#91)
Re: Command Triggers, patch v11

On Friday, March 16, 2012 10:31:57 PM Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

One more thing I disliked quite a bit was the duplication of the EXECUTE
handling. Do you see a way to deduplicate that?

Yeah, that's what's bugging me, too. I think a chunk of the problem is
that you're insisting on having control come back to CreateTableAs()
to perform the table creation. I'm thinking that if the table creation
were to be moved into the tuple receiver's startup routine, we could
avoid needing to get control back between ExecutorStartup and
ExecutorRun, and then all that would be required would be to call
ExecuteQuery with a different DestReceiver.

Hm. I seriously dislike doing that in the receiver. I can't really point out
why though. Unsurprisingly I like getting the control back to CreateTableAs...

Andres

#93Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#92)
Re: Command Triggers, patch v11

Andres Freund <andres@anarazel.de> writes:

On Friday, March 16, 2012 10:31:57 PM Tom Lane wrote:

I'm thinking that if the table creation
were to be moved into the tuple receiver's startup routine, we could
avoid needing to get control back between ExecutorStartup and
ExecutorRun, and then all that would be required would be to call
ExecuteQuery with a different DestReceiver.

Hm. I seriously dislike doing that in the receiver. I can't really point out
why though. Unsurprisingly I like getting the control back to CreateTableAs...

I don't see the argument. Receiver startup functions are intended to do
exactly this type of thing. I'd be okay with leaving it in
CreateTableAs if it didn't contort the code to do so, but what we have
here is precisely that we've got to contort the interface with prepare.c
to do it that way.

(It also occurs to me that moving this work into the DestReceiver might
make things self-contained enough that we could continue to support
EXPLAIN SELECT INTO with not an undue amount of pain.)

Something else I just came across is that there are assorted places that
are aware that ExplainStmt contains a Query, eg setrefs.c, plancache.c,
and those have got to treat CreateTableAsStmt similarly. We could just
add more code in each of those places. I'm wondering though if it would
be a good idea to invent an abstraction layer, to wit a utility.c
function along the lines of "Query *UtilityContainsQuery(Node
*parsetree)", which would centralize the knowledge of exactly which
utility statements are like this and how to dig the Query out of them.
It's only marginally attractive unless there's a foreseeable reason
why we'd someday have more than two of them; but on the other hand,
just formalizing the concept that some utility statements are like
this might be a good thing. (Actually, as I type this I wonder whether
COPY (SELECT ...) isn't a member of this class too, and whether we don't
have bugs from the fact that it's not being treated like EXPLAIN.)
Thoughts?

regards, tom lane

#94Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#93)
Re: Command Triggers, patch v11

On Friday, March 16, 2012 10:52:55 PM Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On Friday, March 16, 2012 10:31:57 PM Tom Lane wrote:

I'm thinking that if the table creation
were to be moved into the tuple receiver's startup routine, we could
avoid needing to get control back between ExecutorStartup and
ExecutorRun, and then all that would be required would be to call
ExecuteQuery with a different DestReceiver.

Hm. I seriously dislike doing that in the receiver. I can't really point
out why though. Unsurprisingly I like getting the control back to
CreateTableAs...

I don't see the argument. Receiver startup functions are intended to do
exactly this type of thing. I'd be okay with leaving it in
CreateTableAs if it didn't contort the code to do so, but what we have
here is precisely that we've got to contort the interface with prepare.c
to do it that way.

Hm.

(It also occurs to me that moving this work into the DestReceiver might
make things self-contained enough that we could continue to support
EXPLAIN SELECT INTO with not an undue amount of pain.)

Something else I just came across is that there are assorted places that
are aware that ExplainStmt contains a Query, eg setrefs.c, plancache.c,
and those have got to treat CreateTableAsStmt similarly.

Hm. Is that so? As implemented in my version the planner just sees a plain
statement instead of a utility statement. Am I missing something?

I am not even sure why the planner ever needs to see ExplainStmts? Protocol
level prepares? Shouldn't those top level nodes be simply removed once?

We could just
add more code in each of those places. I'm wondering though if it would
be a good idea to invent an abstraction layer, to wit a utility.c
function along the lines of "Query *UtilityContainsQuery(Node
*parsetree)", which would centralize the knowledge of exactly which
utility statements are like this and how to dig the Query out of them.
It's only marginally attractive unless there's a foreseeable reason
why we'd someday have more than two of them; but on the other hand,
just formalizing the concept that some utility statements are like
this might be a good thing.

If its really necessary to do that I think that would be a good idea. Alone
the increased greppablility/readablility seems to be worth it.

(Actually, as I type this I wonder whether
COPY (SELECT ...) isn't a member of this class too, and whether we don't
have bugs from the fact that it's not being treated like EXPLAIN.)
Thoughts?

Hm. On a first glance the planner also never sees the content of a CopyStmt...

Andres

#95Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#94)
Re: Command Triggers, patch v11

Andres Freund <andres@anarazel.de> writes:

On Friday, March 16, 2012 10:52:55 PM Tom Lane wrote:

Something else I just came across is that there are assorted places that
are aware that ExplainStmt contains a Query, eg setrefs.c, plancache.c,
and those have got to treat CreateTableAsStmt similarly.

Hm. Is that so? As implemented in my version the planner just sees a plain
statement instead of a utility statement. Am I missing something?

Well, the work flow for EXPLAIN is:

parse analysis: recursively do parse analysis on contained query
plan: do nothing
execution: call planner on contained query, then optionally run it

and the reason for doing it that way is explained by
transformExplainStmt:

* EXPLAIN is like other utility statements in that we emit it as a
* CMD_UTILITY Query node; however, we must first transform the contained
* query. We used to postpone that until execution, but it's really necessary
* to do it during the normal parse analysis phase to ensure that side effects
* of parser hooks happen at the expected time.

ISTM that argument applies just as much to CREATE TABLE AS, especially
in view of the fact that we're restructuring the SELECT INTO case, in
which parse analysis of the SELECT certainly does happen early. It's
also not clear to me why it wouldn't apply to COPY (SELECT ...).

I'm not going to touch the COPY (SELECT ...) issue right now, but
somebody ought to go back and check up on the exact user-visible bugs
that motivated moving EXPLAIN's parse analysis processing. (I suspect
it had to do with plpgsql variable processing, but too lazy to go look
right now.) If there's a plausible use case where similar bugs could
be exhibited in COPY, we're going to have to restructure that too.

regards, tom lane

#96Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#95)
Re: Command Triggers, patch v11

While looking at this I also noticed that DECLARE CURSOR uses a
structure that's randomly different in yet a third way: we start
with a utility statement containing a query, and then flip that
upside down so that the SELECT Query contains a utility statement!

I have a vague feeling that I'm the one who's guilty of that hack, too.

I'm not sure that anybody cares about being able to fire command
triggers on DECLARE CURSOR, but just from a consistency standpoint it
would make sense for this to work more like EXPLAIN and CREATE TABLE AS.
So that convinces me that UtilityContainsQuery() would be a good thing,
and I'll add that to the patch. (Actually changing DECLARE CURSOR seems
like a separate patch, though.)

regards, tom lane

#97Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#96)
Re: Command Triggers, patch v11

On Saturday, March 17, 2012 06:45:27 PM Tom Lane wrote:

I'm not sure that anybody cares about being able to fire command
triggers on DECLARE CURSOR

I actually think it would make sense to explicitly not fire command triggers
there given that DECLARE CURSOR actually potentially is somewhat performance
relevant.

, but just from a consistency standpoint it
would make sense for this to work more like EXPLAIN and CREATE TABLE AS.
So that convinces me that UtilityContainsQuery() would be a good thing,
and I'll add that to the patch. (Actually changing DECLARE CURSOR seems
like a separate patch, though.)

Aggreed on that.

Andres

#98Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#96)
Re: Command Triggers, patch v11

I've found a couple more issues in the CTAS patch:

1. Previous versions delivered a "SELECT n" command tag for either
spelling of the command:

regression=# select * into t1 from int8_tbl;
SELECT 6
regression=# create table t2 as select * from int8_tbl;
SELECT 6

With the patch I get

regression=# select * into t1 from int8_tbl;
SELECT 0 0
regression=# create table t2 as select * from int8_tbl;
CREATE TABLE AS

The first of these is particularly unfortunate since it's outright lying
as to the number of rows processed.

I'm not sure what we should do instead. We have gotten push-back before
anytime we changed the command tag for an existing command (and in fact
it seems that we intentionally added the rowcount display in 9.0, which
means there are people out there who care about that functionality).
On the other hand, the traditional output for CREATE TABLE AS doesn't
seem to satisfy the principle of least astonishment. A third
consideration is that if we are pushing CREATE TABLE AS as the preferred
spelling, people will probably complain if it omits functionality that
SELECT INTO provides; so I'm not sure that "SELECT n" in one case and
"CREATE TABLE AS" in the other would be a good idea either. Any
opinions what to do here?

2. Historically, CREATE RULE has allowed a rule action to be SELECT INTO
(though not CREATE TABLE AS). Currently the patch is throwing an error
for that. This seems like something that might not be worth fixing,
though. It's fairly hard to conceive of a use-case for such a rule,
since it would work only once before starting to throw "table already
exists" errors. How much do we care about preserving backward
compatibility here?

regards, tom lane

#99Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#98)
Re: Command Triggers, patch v11

On Saturday, March 17, 2012 11:04:30 PM Tom Lane wrote:

I've found a couple more issues in the CTAS patch:

1. Previous versions delivered a "SELECT n" command tag for either
spelling of the command:

regression=# select * into t1 from int8_tbl;
SELECT 6
regression=# create table t2 as select * from int8_tbl;
SELECT 6

With the patch I get

regression=# select * into t1 from int8_tbl;
SELECT 0 0

hm. Stupid me.

regression=# create table t2 as select * from int8_tbl;
CREATE TABLE AS

The first of these is particularly unfortunate since it's outright lying
as to the number of rows processed.
I'm not sure what we should do instead. We have gotten push-back before
anytime we changed the command tag for an existing command (and in fact
it seems that we intentionally added the rowcount display in 9.0, which
means there are people out there who care about that functionality).
On the other hand, the traditional output for CREATE TABLE AS doesn't
seem to satisfy the principle of least astonishment. A third
consideration is that if we are pushing CREATE TABLE AS as the preferred
spelling, people will probably complain if it omits functionality that
SELECT INTO provides; so I'm not sure that "SELECT n" in one case and
"CREATE TABLE AS" in the other would be a good idea either. Any
opinions what to do here?

I would prefer both returning CREATE TABLE AS since thats what actually
happens. We already document that SELECT INTO is kinda deprecated: "The
PostgreSQL usage of SELECT INTO to represent table creation is historical. It
is best to use CREATE TABLE AS for this purpose in new code."
I have seen code that uses the command code for selecting the, app level,
logging. Its kinda hard to do that if a CREATE TABLE AS/SELECT INTO returns
SELECT.

Does CTAS ommit any functionality currently? I don't see any reason not to
support stuff there thats supported in SELECT INTO.

2. Historically, CREATE RULE has allowed a rule action to be SELECT INTO
(though not CREATE TABLE AS). Currently the patch is throwing an error
for that. This seems like something that might not be worth fixing,
though. It's fairly hard to conceive of a use-case for such a rule,
since it would work only once before starting to throw "table already
exists" errors. How much do we care about preserving backward
compatibility here?

I vote for not supporting that anymore. That being possible looks more like an
implementation detail to me.

Thanks for looking at this!

Andres

#100Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#99)
Re: Command Triggers, patch v11

BTW, I've been looking through how to do what I suggested earlier to get
rid of the coziness and code duplication between CreateTableAs and the
prepare.c code; namely, let CreateTableAs create a DestReceiver and then
call ExecuteQuery with that receiver. It appears that we still need at
least two bits of added complexity with that approach:

1. ExecuteQuery still has to know that's a CREATE TABLE AS operation so
that it can enforce that the prepared query is a SELECT. (BTW, maybe
this should be weakened to "something that returns tuples", in view of
RETURNING?)

2. Since ExecuteQuery goes through the Portal machinery, there's no way
for it to pass in eflags to control the table OIDs setup. This is
easily solved by adding an eflags parameter to PortalStart, which
doesn't seem too awful to me, since the typical caller would just pass
zero. (ExecuteQuery would also have to know about testing
interpretOidsOption to compute the eflags to use, unless we add an
eflags parameter to it, which might be the cleaner option.)

In short I'm thinking: add an eflags parameter to PortalStart, and add
both an eflags parameter and a "bool mustReturnTuples" parameter to
ExecuteQuery. This definitely seems cleaner than the current
duplication of code.

regards, tom lane

#101Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#100)
Re: Command Triggers, patch v11

Tom Lane <tgl@sss.pgh.pa.us> writes:

1. ExecuteQuery still has to know that's a CREATE TABLE AS operation so
that it can enforce that the prepared query is a SELECT. (BTW, maybe
this should be weakened to "something that returns tuples", in view of
RETURNING?)

That lights a bulb: what about rewriting CREATE TABLE AS as two
commands, internally:

1. CREATE TABLE …
2. WITH x ( <query here> ) INSERT INTO … SELECT * FROM x;

It seems like not solving much from a practical point of view though as
you need to be able to parse the output of the subquery before you can
do the first step, but on the other hand it might be that you already
have the pieces to just do that.

Well, I had to share that though, somehow.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#102Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#98)
Re: Command Triggers, patch v11

On lör, 2012-03-17 at 18:04 -0400, Tom Lane wrote:

I'm not sure what we should do instead. We have gotten push-back before
anytime we changed the command tag for an existing command (and in fact
it seems that we intentionally added the rowcount display in 9.0, which
means there are people out there who care about that functionality).
On the other hand, the traditional output for CREATE TABLE AS doesn't
seem to satisfy the principle of least astonishment. A third
consideration is that if we are pushing CREATE TABLE AS as the preferred
spelling, people will probably complain if it omits functionality that
SELECT INTO provides; so I'm not sure that "SELECT n" in one case and
"CREATE TABLE AS" in the other would be a good idea either. Any
opinions what to do here?

Another consideration is that the SQL command tags are defined by the
SQL standard. So if we were to change it, then it should be "CREATE
TABLE". I'm not convinced that it should be changed, though. (I recall
cross-checking our list against the SQL standard in the past, so there
might have been discussion on this already.)

#103Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#101)
Re: Command Triggers, patch v11

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

That lights a bulb: what about rewriting CREATE TABLE AS as two
commands, internally:

Given the compatibility constraints on issues like what command tag
to return, I think that would probably make our jobs harder not easier.

regards, tom lane

#104Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#102)
Re: Command Triggers, patch v11

Peter Eisentraut <peter_e@gmx.net> writes:

On lör, 2012-03-17 at 18:04 -0400, Tom Lane wrote:

I'm not sure what we should do instead. We have gotten push-back before
anytime we changed the command tag for an existing command (and in fact
it seems that we intentionally added the rowcount display in 9.0, which
means there are people out there who care about that functionality).
On the other hand, the traditional output for CREATE TABLE AS doesn't
seem to satisfy the principle of least astonishment. A third
consideration is that if we are pushing CREATE TABLE AS as the preferred
spelling, people will probably complain if it omits functionality that
SELECT INTO provides; so I'm not sure that "SELECT n" in one case and
"CREATE TABLE AS" in the other would be a good idea either. Any
opinions what to do here?

Another consideration is that the SQL command tags are defined by the
SQL standard. So if we were to change it, then it should be "CREATE
TABLE". I'm not convinced that it should be changed, though. (I recall
cross-checking our list against the SQL standard in the past, so there
might have been discussion on this already.)

If we were going to change the output at all, I would vote for "CREATE
TABLE nnnn" so as to preserve the rowcount functionality. Keep in mind
though that this would force client-side changes, for instance in
libpq's PQcmdTuples(). Fixing that one routine isn't so painful, but
what of other client-side libraries, not to mention applications?

regards, tom lane

#105Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#100)
Re: Command Triggers, patch v11

On Sunday, March 18, 2012 07:29:30 PM Tom Lane wrote:

BTW, I've been looking through how to do what I suggested earlier to get
rid of the coziness and code duplication between CreateTableAs and the
prepare.c code; namely, let CreateTableAs create a DestReceiver and then
call ExecuteQuery with that receiver. It appears that we still need at
least two bits of added complexity with that approach:

1. ExecuteQuery still has to know that's a CREATE TABLE AS operation so
that it can enforce that the prepared query is a SELECT. (BTW, maybe
this should be weakened to "something that returns tuples", in view of
RETURNING?)

I don't really see the use case but given the amount of work it probably takes
it seems reasonable to allow that.

2. Since ExecuteQuery goes through the Portal machinery, there's no way
for it to pass in eflags to control the table OIDs setup. This is
easily solved by adding an eflags parameter to PortalStart, which
doesn't seem too awful to me, since the typical caller would just pass
zero. (ExecuteQuery would also have to know about testing
interpretOidsOption to compute the eflags to use, unless we add an
eflags parameter to it, which might be the cleaner option.)

If we go down this route I think adding an eflag is the better choice. Thinking
of it - my patch already added that ;)

In short I'm thinking: add an eflags parameter to PortalStart, and add
both an eflags parameter and a "bool mustReturnTuples" parameter to
ExecuteQuery. This definitely seems cleaner than the current
duplication of code.

Hm. I am not *that* convinced anymore. It wasn't that much duplication in the
end...

Andres

#106Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#100)
Re: Command Triggers, patch v11

On Sun, Mar 18, 2012 at 2:29 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

1. ExecuteQuery still has to know that's a CREATE TABLE AS operation so
that it can enforce that the prepared query is a SELECT.  (BTW, maybe
this should be weakened to "something that returns tuples", in view of
RETURNING?)

+1 for "something that returns with tuples". CREATE TABLE ... AS
DELETE FROM ... WHERE ... RETURNING ... seems like a cool thing to
support.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#107Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#106)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

On Sun, Mar 18, 2012 at 2:29 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

1. ExecuteQuery still has to know that's a CREATE TABLE AS operation so
that it can enforce that the prepared query is a SELECT. (BTW, maybe
this should be weakened to "something that returns tuples", in view of
RETURNING?)

+1 for "something that returns with tuples". CREATE TABLE ... AS
DELETE FROM ... WHERE ... RETURNING ... seems like a cool thing to
support.

For the moment I've backed off that idea. The main definitional
question we'd have to resolve is whether we want to allow WITH NO DATA,
and if so what does that mean (should the DELETE execute, or not?).
I am also not certain that the RETURNING code paths would cope with
a WITH OIDS specification, and there are some other things that would
need fixed. It might be cool to do it sometime, but it's not going to
happen in this patch.

regards, tom lane

#108Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#107)
Re: Command Triggers, patch v11

On Mon, Mar 19, 2012 at 12:45 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Sun, Mar 18, 2012 at 2:29 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

1. ExecuteQuery still has to know that's a CREATE TABLE AS operation so
that it can enforce that the prepared query is a SELECT.  (BTW, maybe
this should be weakened to "something that returns tuples", in view of
RETURNING?)

+1 for "something that returns with tuples".   CREATE TABLE ... AS
DELETE FROM ... WHERE ... RETURNING ... seems like a cool thing to
support.

For the moment I've backed off that idea.  The main definitional
question we'd have to resolve is whether we want to allow WITH NO DATA,
and if so what does that mean (should the DELETE execute, or not?).
I am also not certain that the RETURNING code paths would cope with
a WITH OIDS specification, and there are some other things that would
need fixed.  It might be cool to do it sometime, but it's not going to
happen in this patch.

Fair enough. It would be nice to have, but it definitely does not
seem worth spending a lot of time on right now.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#109Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#104)
Re: Command Triggers, patch v11

On sön, 2012-03-18 at 21:16 -0400, Tom Lane wrote:

If we were going to change the output at all, I would vote for "CREATE
TABLE nnnn" so as to preserve the rowcount functionality. Keep in
mind though that this would force client-side changes, for instance in
libpq's PQcmdTuples(). Fixing that one routine isn't so painful, but
what of other client-side libraries, not to mention applications?

Doesn't seem worth it to me. At least, "SELECT nnnn" makes some sense:
nnnn rows were selected. "CREATE TABLE nnnn" means what? nnnn tables
were created?

What might make sense is to delegate this additional information to
separate fields in a future protocol revision.

#110Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#109)
Re: Command Triggers, patch v11

On Mon, Mar 19, 2012 at 12:53 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On sön, 2012-03-18 at 21:16 -0400, Tom Lane wrote:

If we were going to change the output at all, I would vote for "CREATE
TABLE nnnn" so as to preserve the rowcount functionality.  Keep in
mind though that this would force client-side changes, for instance in
libpq's PQcmdTuples().  Fixing that one routine isn't so painful, but
what of other client-side libraries, not to mention applications?

Doesn't seem worth it to me.  At least, "SELECT nnnn" makes some sense:
nnnn rows were selected.  "CREATE TABLE nnnn" means what?  nnnn tables
were created?

What might make sense is to delegate this additional information to
separate fields in a future protocol revision.

I think that we would not have bothered to add the row count to the
command tag output for SELECT unless it were useful. It seems to be
*more* useful for CTAS than for SELECT; after all, SELECT also returns
the actual rows.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#111Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#110)
Re: Command Triggers, patch v11

Robert Haas <robertmhaas@gmail.com> writes:

On Mon, Mar 19, 2012 at 12:53 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

Doesn't seem worth it to me. At least, "SELECT nnnn" makes some sense:
nnnn rows were selected. "CREATE TABLE nnnn" means what? nnnn tables
were created?

What might make sense is to delegate this additional information to
separate fields in a future protocol revision.

I think that we would not have bothered to add the row count to the
command tag output for SELECT unless it were useful. It seems to be
*more* useful for CTAS than for SELECT; after all, SELECT also returns
the actual rows.

I think we're all in agreement that we need to keep the rowcount
functionality. What seems to me to be in some doubt is whether to
continue to present the tag "SELECT nnnn" or to change it to something
like "CREATE TABLE nnnn". For the moment I've got the patch doing the
former. It would not be terribly hard to change it, but I'm not going
to break backward compatibility unless there's a pretty clear consensus
to do so.

BTW, I just came across another marginal-loss-of-functionality issue:
in previous versions you could PREPARE a SELECT INTO, but now you get

regression=# prepare foo as select * into bar from int8_tbl;
ERROR: utility statements cannot be prepared

Is anybody excited about that? If it is something we have to keep,
it seems like pretty nearly a deal-breaker for this patch, because
storing a CreateTableAsStmt containing an already-prepared plan would
complicate matters unreasonably. You can still get approximately
the same result with

prepare foo as select * from int8_tbl;
create table bar as execute foo;

which if anything is more useful since you didn't nail down the target
table name in the PREPARE, but nonetheless it's different.

regards, tom lane

#112Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#89)
Re: Command Triggers, patch v11

I wrote:

One thing I soon found is that it lacks support for EXPLAIN SELECT INTO.

While I'm not particularly excited about fixing PREPARE ... SELECT INTO
or CREATE RULE ... SELECT INTO, I've come to the conclusion that the
EXPLAIN case is a must-fix. Because not only is EXPLAIN SELECT INTO
broken, but so is EXPLAIN CREATE TABLE AS, and the latter functionality
is actually documented. So presumably somebody went out of their way
to make this work, at some point.

Since I've got the table-creation code moved into intorel_startup,
this doesn't look to be that painful, but it will require an API change
for ExplainOnePlan, which is slightly annoying because that's probably
in use by third-party plugins. We could either break them obviously
(by adding an explicit parameter) or break them subtly (by adding an
ExplainState field they might forget to initialize). The former seems
preferable.

regards, tom lane

#113Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#112)
Re: Command Triggers, patch v11

I've applied the CTAS patch after rather heavy editorialization. Don't
know what consequences that will have for Dimitri's patch.

regards, tom lane

#114Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#113)
Re: Command Triggers, patch v11

On Tuesday, March 20, 2012 02:39:56 AM Tom Lane wrote:

I've applied the CTAS patch after rather heavy editorialization. Don't
know what consequences that will have for Dimitri's patch.

Thanks for all the work you put into this! Looks cleaner now...

Thanks,

Andres

#115Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#113)
Re: Command Triggers, patch v11

Tom Lane <tgl@sss.pgh.pa.us> writes:

I've applied the CTAS patch after rather heavy editorialization. Don't
know what consequences that will have for Dimitri's patch.

It allows my patch to add support for CREATE TABLE AS and SELECT INTO,
I've been doing that and am on my way to sending a v18 now. The way you
worked out the command tag is exactly what I needed, so thanks a lot for
your work comitting this and paying attention :)

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support