WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Started by Andres Freundabout 9 years ago134 messages
#1Andres Freund
andres@anarazel.de

Hi Everyone,

TL;DR: Making things faster. Architectural evalation.

as some of you might be aware I've been working on making execution of
larger queries in postgresl faster. While working on "batched execution"
I came to the conclusion that, while necessary, isn't currently showing
a large benefit because expression evaluation and tuple deforming are
massive bottlenecks.

I'm posting a quite massive series of WIP patches here, to get some
feedback.

Tuple deforming is slow because of two reasons:

1) It's the first thing that accesses tuples, i.e. it'll often incur
cache misses. That's partially fundamental, but also partially can be
addressed, e.g. through changing the access order in heap as in [1]https://archives.postgresql.org/message-id/20161030073655.rfa6nvbyk4w2kkpk%40alap3.anarazel.de.
2) Tuple deforming has a lot of unpredicatable branches, because it has
to cope with various types of fields. We e.g. perform alignment in a
lot of unneeded cases, do null checks for NOT NULL columns et al.

I tried to address 2) by changing the C implementation. That brings some
measurable speedups, but it's not huge. A bigger speedup is making
slot_getattr, slot_getsomeattrs, slot_getallattrs very trivial wrappers;
but it's still not huge. Finally I turned to just-in-time (JIT)
compiling the code for tuple deforming. That doesn't save the cost of
1), but it gets rid of most of 2) (from ~15% to ~3% in TPCH-Q01). The
first part is done in 0008, the JITing in 0012.

Expression evaluation and projection is another major bottleneck.
1) Our recursive expression evaluation puts a *lot* of pressure on the
stack.
2) There's a lot of indirect function calls when recursing to other
expression nodes. These are hard to predict, because the same node
type (say ExecEvalAnd()) is used in different parts of an expression
tree, and invokes different sub-nodes.
3) The function calls to operators and other functions are hard to
predict, leading to a significant number of pipeline stalls.
4) There's a fair amount of pg_list.h list style iteration going on,
those are cache and pipeline inefficient.

After some experimenting I came to the conclusion that the recursive
processing is a fundamental impediment to making this faster. I've
converted (0006) expression processing and projection into an opcode
dispatch based interpreter. That yields, especially for complex
expressions and larger projections a significant speedup in itself. But
similarly to the deforming, expression evaluation remains a bottleneck
after that, primarily because there's still a lot of unpredictable jump
and calls, and because loads/stores have to be complex
(e.g. ExprContext->ecxt_innertuple->tts_values[i]/tts_isnull[i] for a
single scalar var evaluation). Using the opcode based representation
of expression evaluation (as it's nearly linear, and has done a lot of
the lookups ahead of time), it's actually quite easy to

*After JITing expression evaluation itself is more than ten times faster
than before*.

But unfortunately that doesn't mean that queries are ten times faster -
usually we'll hit bottlenecks elsewhere relatively soon. WRT to
expression evaluation, the biggest cost afterwards are the relatively
high overhead V1 function calls - register based parameter passing is a
lot faster.

After experimenting a bit with doing JITing manually (a lot of
eye-stabbing kind of fun), I chose to use LLVM.

An overview of the patch-queue so far:
0001 Make get_last_attnums more generic.

Boring prerequisite.

0002 More efficient AggState->pertrans iteration.

Relatively boring minor optimization, but it turns out to be a easily
hit bottleneck. Will commit independently.

0003 Avoid materializing SRFs in the FROM list.
0004 Allow ROWS FROM to return functions as single record column.
0005 Basic implementation of targetlist SRFs via ROWS FROM.
0006 Remove unused code related to targetlist SRFs.

These are basically just pre-requisites for the faster expression
evaluation, and discussed elsewhere [2]/messages/by-id/20160523005327.v2tr7obytitxcnna@alap3.anarazel.de. This implementation is *NOT*
going to survive, because we ended coming to the conclusion that using a
separate executor node to expand SRFs is a btter plan. But the new
expression evaluation code won't be able to handle SRFs...

0007 WIP: Optimize slot_deform_tuple() significantly.

This a) turns tuple deforming into an opcode based dispatch loop (using
computed goto on gcc/clang). b) moves a lot of the logic from
slot_deform_tuple() callsites into itself - that turns out to be more
efficient. I'm not entirely sure it's worth doing the opcode based
dispatch part, if we're going to also do the JIT bit - it's a fair
amount of code, and the speed difference only matters on large amounts
of rows.

0008 WIP: Faster expression processing and targetlist projection.

This, functionally nearly complete, patch turns expression evaluation
(and tuple deforming as a special case of that) into a "mini language"
which is interpreted using either a while(true) switch(opcode) or
computed goto to jump from opcode to opcode. It does so by moving a lot
more of the code for expression evaluation to initialization time and
building a linear series of steps to evaluate expressions, thereby
removing all recursion from expression processing.

This nearly entirely gets rid of the stack usage cost of expression
evaluation (we pretty much never recurse except for subplans). Being
able to remove, now redundant, calls to check_stack_depth() is a
noticeable benefit, it turns out that that check has a noticeable
performance impact (as it aparently forces to actually use the stack,
instead of just renumbering registers inside the CPU).

The new representation and evaluation is functionally nearly complete
(there's a single regression test failure, and I know why that is), but
the code needs a fair amount of polishing.

I do absolutely think that the fundamentals of this are the right way to
go, and I'm going to work hard on polishing the patch up. But this
isn't something that we can easily do in parts, and it's a huge ass
patch. So I'd like to have at least some more buyin before wasting even
more time on this.

0009 WIP: Add minimal keytest implementation.

More or less experimental patch that tries to implement simple
expression of the OpExpr(ScalarVar, Const) into a single expression
evaluation step. The benefits probably aren't big enough iff we do end
up doing JITing of expressions.

0010 WIP: Add configure infrastructure to enable LLVM.
0011 WIP: Beginning of a LLVM JIT infrastructure.

Very boring preliminary patches to add --with-llvm and some minimal
infrastructure to handle LLVM. If we go this way, JITed stuff needs to
be tied to resource owners, and we need some other centralized
infrastructure.

0012 Heavily-WIP: JITing of tuple deforming.

This, in a not-yet-that-nice manner, implements a JITed version of the
per-column stuff that slot_deform_tuple() does. It currently always
deforms all columns, which obviously would have to change. There's also
considerable additional performance improvements possible.

With this patch the per-column overhead (minus bitmap handling, which
0007 moved into a separate loop), drops from 10%+ into low single digits
for a number of queries. Afterwards the biggest cost is VARSIZE_ANY()
for varlena columns (which atm isn't inlined). That is, besides the
initial cache-miss when accessing tuple->t_hoff, which JITing can do
nothing about :(

This can be enabled/disabled using the new jit_tuple_deforming GUC. To
make this production ready in some form, we'd have to come up with a way
to determine when it's worth doing JITing. The easiest way would be to
do so after N slot_deform_tuple() calls or such, another way would be to
do it based on cost estimates.

0013 WIP: ExprEval: Make threaded dispatch use a separate field.

Boring preliminary patch. Increases memory usage a bit, needs to be
thought through more.

0014 Heavily-WIP: JITed expression evaluation.

This is the most-interesting bit performance wise. A few common types of
expressions are JITed. Scalar value accesses, function calls, boolean
expressions, aggregate references.

This can be enabled using the new jit_expressions GUC.

Even for the supported expression types I've taken some shortcuts
(e.g. strict functions aren't actually strict).

The performance benefits are quite noticeable. For TPCH ExecEvalExpr()
(which is where 0008 moved all of expression evaluation/projection) goes
from being the top profile entry, to barely noticeable, with the JITed
function usually not showing up in the top five entries anymore.

After the patch it becomes very clear that our function call
infrastructure is a serious bottlenecks. Passing all the arguments via
memory, and, even worse, forcing isnull/values to be on separate
cachelines, has significant performance implications. It also becomes
quite noticeable that nodeAgg's transition function invocation doesn't
go through ExecEvalExpr() but does that itself - which leads to constant
mispredictions if several transition values exist.

While the JIT code is relatively verbose, it turns out to not actually
be that hard to write after some startup pains. All the JITing of
expressions that exists so far was basically written in ~10 hours.

This also needs some heuristics about when JITing is
appropriate. Compiling an expression that's only executed once is never
going to be faster than doing the interpretation (it at least needs a
writable allocation for the code, and then a remap to make that code
read-only and executable). A trace based approach (everything executed
at least a thousand times) or cost based (all queries costing more than
100000 should be JITed) could make sense.

It's worthwhile to note that at the moment this is a per-query-execution
JIT, not something that can trivially be cached for prepared
statements. That'll need further infrastructure.

0015 Super-Heavily-WIP: LLVM perf integration.

This very very very preliminary patch (including some copy-pasted GPL
code!) creates /proc/perf-<pid>.map files, which allows perf to show
useful symbols for profile hits to JIT expressions. I plan to push this
towards LLVM, so this isn't something PG will have to do, but it's
helpful for evaluation.

I eventually plan to start separate threads about some of the parts in
here, but I think the overal picture needs some discussion first.

Q: Why LLVM and not a hand-rolled JIT?
A: Because hand-rolling a JIT is probably hard to scale to multiple
maintainers, and multiple platforms. I started down the path of doing
a hand-rolled x86 JIT, and that'd also be doable (faster compilation,
slower execution basically); but I doubt we'd end up having that on
different architectures on platforms. Not to speak of things like
proper debugger and profiler integration. I'm not entirely convinced
that that's the right path. It might also be a transitional step,
towards doing our completely own JIT. But I think it's a sensible
step.

Q: Why LLVM and not $jit-toolkit
A: Because all the other JIT stuff I looked at was either really
unportable (mostly x86 linux only), inconveniently licensed (like
e.g. gcc's jit library) or nearly unmaintained (luajit's stuff for
example). I might have missed something, but ISTM that atm the
choice is between hand-rolling and using LLVM.

Q: Does this actually inline functions from the backend?
A: No. That probably is something desirable in the future, but to me
that seems like it should be a separate step. The current one's big
enough. It's also further increases compilation times, so quite
possibly we only want to do so based on another set of heuristics.

Q: ?

Comments? Questions?

Regards,

Andres

[1]: https://archives.postgresql.org/message-id/20161030073655.rfa6nvbyk4w2kkpk%40alap3.anarazel.de
[2]: /messages/by-id/20160523005327.v2tr7obytitxcnna@alap3.anarazel.de

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

#2Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#1)
15 attachment(s)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 2016-12-05 19:49:55 -0800, Andres Freund wrote:

I'm posting a quite massive series of WIP patches here, to get some
feedback.

And here's the patches themselves - let's hope they're not too big
(after gzip'ing that is).

Andres

Attachments:

0001-Make-get_last_attnums-more-generic.patch.gzapplication/x-patch-gzipDownload
0002-More-efficient-AggState-pertrans-iteration.patch.gzapplication/x-patch-gzipDownload
0003-Avoid-materializing-SRFs-in-the-FROM-list.patch.gzapplication/x-patch-gzipDownload
0004-Allow-ROWS-FROM-to-return-functions-as-single-record.patch.gzapplication/x-patch-gzipDownload
0005-Basic-implementation-of-targetlist-SRFs-via-ROWS-FRO.patch.gzapplication/x-patch-gzipDownload
0006-Remove-unused-code-related-to-targetlist-SRFs.patch.gzapplication/x-patch-gzipDownload
��5FX0006-Remove-unused-code-related-to-targetlist-SRFs.patch�=ks�����_�����-�z?��L�n='�s,�=w:
DB�TI*�ON���]�"HQ�8I�\Oc� v��]��^����{�N��3n�iWX�ZO��~���u�V{��6��Yk�k�e#�b�.�����Q���.�s6p-_��k�b����G�r��[8UK�<z�C���Y��&��;��{^d-V��j���z��0��������gV������;��>�v�����Y����=r.B�B6���GG�J����oO�V^������O�TM&������<�r��p�3�����g�k����Ljny�%t?���C6PFK��+��$���B>u���l/��jfy��0�����`io�����5wR-���;�����|#��[�7k�D�mg�G��=����m���^����~|H:_�`���*�Bo��W����=w�����]3��a`��2�'.��"����{�UZDy��@?�`q�G@x��0�{���s����G0�h�y�����K;L�����`�>Q��y�={�	����t#����U�y�=}���	S
��6Q	�_�}dy�=@����x����i�����Cb`�ihl[����/�Y� ��=��Wp\��f�H������'�����*�����k���G;��8�vB����������J
\��2��=l�jp�������v����2��2�����I��E
���BNAO	�-�����n��#9��Q�j�����	6����E��� uf.�;V�5zm�>�~���Du����E���#�3V������"!��H�#���l*:����j���Ny���Z��ja�W��#�2
������l�Z�.;U�h�{&�	
�	Dh|W�P��!;�|*�Ri�yN�d7k�9?bXtv�.>� ����_d�C��g�n`(;9;��JX0����` 5�������6
�g��2�y��������Qo������$B��9��H�pI�fLps��C67}������j�����Jj�i���z�&�BM����U3����E�@^
}�Wk��B�_=�KKr�����=^�3����#���������2�����i�z9��d'bY�#��&�)1����\(�2H�F(3vr�����AA�v(����c�V��5�NMM
A����d�H����Fe��:��p\l(A�������CE���-IeR���JK���:�z���V�5kf�g������-I� �h��@��Pt�B��
|��2�H�m*Xc��}[����Q�/6C��R���J���:YR�h�*�Zby������/(d�6�uq�g.�T�Q��v-��_D�/�����_�"b�s��TP�5�������U��v��Y���/ �Q�|�J(�V��v���#��V�����Eb^����*�`J��4��t$�����\�>@*ow������O����h|���e��v���aN
��{�X�@"m�OO����X����w���s��h�x�����]�&�V�( gU���j$�z�N���$���N��`�(/288a�������nm��^�������o����}}��|����X�����c�%Cp���[�?�0%��6I#s����J�O�+eJy7���jx�(0( �xfi�65"�����M��������[�V!�����By7���o��@�<M�#z�!4P�2��l�!6���iNmw#[���ls��N��H5+����K�3f|'��g�;r�`$^A@jl=>��?�f���tO������fo���]��Ow�2��$3*� �=#������D�}/��rs!����AG�
=n�1M���W+��W��=�-�[4��g���:8�����2�
�����e�����=�O�#w��B���;W
�����P�b�tF<|�����"}��,0s�����7F�u�{�2�>��8�%�h�@��sH?Np�A}����w�D�5)�lwL4�C��>��(���bK�M�v������UdR��O��������e,
�yk0�X�Z>f����G�.��l'}�H
�=
#��P���4(M�p!���q(�p��,���n�T�(�bt2Fc��W6��?��V4��t���J�C�)��9�iM�r�O"e�~���"{����`3�gr��j2�D�`j���w��k������I��u��6r�_n6bgZ81��>����j��<j�.eG6Z�8%���3zv�:��.��v=�� ��hfw��;u�0t��@�,?Q�~��r�([��}XC��l�JQ�bqE
'}>��C�]CB<c�w��������������w�������^OF���g��ciR�����p+��e��mKx�m	O3-���%��}
���������R����_�B�s���F~�muA����;�>�q����qSg�'�K�X�U��7o�[��%�. ���v5�E��q1N��@�I Z�Y�Yf���T [aS�
!��2�
'�	/d�8C3V�!T�`�� �{�z>w�*��%������Q��u5�&��zd7H����W�JYI
����
��B����	�0i���g�
k�-1�M�&Ld!\������g������*C�����wM��doj�5n������'EP�P);��
���o�My%�H��%LG������������;13�mF��i���>�w2s8��:���sT�����1Q5�{���i���H<R_�B����|�[�����?z��@v���>����J��I�\��=F��Qw�R���K�A������_x6�wN�*���0r.AA�A4%�����I�%�Y���!1�K�D��}{xx"q9���D�';\�2���|�{�HEL�c�c!z����g[4��=��4$=Vh5i���L���&�������(MZ��g9)���g��4�?!������/��c��
f��Y�Sm��Zv!�J|���Hy�������
�@���u�%�Z���4���&K�#���W��[Yc%D0Q��+V�k�~���H_���-�����m
�&���0�EX4��O�a����� "��\U�@"�v%���!��Ap� �)b���������x�H���27^h���F���r�U.�
�RL��4Ey$�
}������>B���A���r]�W�23�;�Y�}�S0D�P�t���W�H����$�[�4�e^�#����>u0	�d�?��A_M������h��w�M����q�0@����V��fF��]��5�h�?aK�]��o5�1���4�B(}0f���LEL�����1��F�G���;~�����J����#�P#z�(���I���U?	W�������`z�7���j�����?'�������%1�rL�G����Q���d,��$�������N8|*�1F��#cK��`p�b��[C{V�������0I�����}#��Q�{r���lw(���D:�h���\�@�8O���:x���v�-�#����������4L`'��xr�w�Ev.�]�� 7��+^��O���R�y^Z��kY�������Fjr,	���lPd?.�8W�����:r��Z}�N�s���`=��3w�oi!����A�E��;����`sb����-��{ev����K������p��u��g�xx'![�D�
�N����m'd����Q+��F�+���J!6���c��B(��s�����x���=6�{w�K��2��1����;L�#�`K��$�W���������P��[�4j���G-��o��mrp�H���@�`���������|�!���s���q�s$&��, :��,��c���j���\-�'�nB��A8���(ghY����{z7�kp���,!��,���(�D��mG�D��b�A1��P����<�%����y�R)��"X���0���^�P)IS�3�1
h��c���Kxb]�����Dp���
���]�n\ylOPj,S���q�0$v���W/�UX����m:�*K~#l�w�g!��`�j(�G���%����f�-\�D[�Q	w��C���p4���g�T��}���(���i&�-�F?.�W��.Pw��!I*U��*I4��a��,��ES���,_z�@�<�T9�$���>�Y��\�D�������`����	H�m�E�W���%@���`��aQ�f��.�=���hT4������7���t�:��d<���^TY�C��}�nM�����G�/��0�G%)�����*�A��G�*�nxkg��I������/�o@�cL������;��G������[�b)q��N���r
?{�,�t�j�?���	�����I�XeWd�$+z]bE���+����Xl%��Fe�<{�q
/^$m����V����}Q@o�O�z w-��.�n����:Ag7�t����?N���
mL�����S<�����us#y��	S�*\�}�Kd���t�!�����N��N�����qR5R���h,�0��'��6��f��H���TG\���\����|���-�}6��x���j��Q��#�WRC�$�0����n��d0cY��>W�6�����h�lb���C�r���
4T����<�j��V�v�����w9�-��<Y�V���K��JF�U�L ���
d��W�~!5J�	@�<A����D�p0��.��^�I�b�s�e������7������B�%�����I�}�O0u�=s��esO�����P�W��AB���D�y���������N~�*����m<��Q�/x��mH�������*:�=r 9	�7������8J���<�r�����(��.�����!��e�8a"����YWE*2I`F�<�����h�z�KR�4)�>�����EwgO��j��w���l��"=��������|�������H��EL\l6�}�b��LL������p�s,�
I��8�BXN4��O�=F��h�q�<����*v���iS��)uLB�P�=t.W.�Sk��)�[��������=�$���E���jDm�h�d	���9f�������a,���X��}��)�8�m0�-v������	�X`�9�����27g}���Jx)
}�g�i�������~}������e�Z��������sx^D�ENo����di���~g�=��.�(M�vq����������N�5LOkO��~��5Y�!��P��0�u�
�����`,	�^yi"n9�p�������
�z���z��K��G0+��_�@QN�Cm��4A�;d960w�ZW����t�m�G��k4�t��4�	hy����.��*���:���
y$7�$28��5:kc�%5�G��`=��/�M���l\'�
Z%
=�W9WWU
��+����-�$_����(�k(	�h����=m_HH���*<�*�/�n4�a�u�R���/B�����j���h�mK�v_�}0��(�{�p����!GSfdx�E_yC�rz��R����MIgh����
��@�c�������e�svd�LF��!^�J�c"�g�.=�[m&w@E�f��V�����KT\jw�=����7 �R���{�9�E�q�;��Xy��y�]W��T����hu���,���nP�����FTca��
���8�o�B95/2"Ugt�'={����\��������;�����/x)C	X�b@��w�ts�����Q��4���u�0L�|&�������������Z�3�����!~�^���Ix�����m�lV����1�@\�+����g�j�� �'������������dx{��vt5� ��)��9x�
�d�o�~0Q�W��<e>d��SJ�%�ZKq���Vf��r���d��5?������������7�n�8F?S���{""a_����L��h%�y��sp���F��hN��~����� i�'�Nb��U��<����2��bj�������:K�L6���^^���{~�������0���Q+la
���U�W'�g�6K{��N�Y_��~A��/Pk��1=�fi����n�� ��y�����4k��[S��:9J���<���kwoY'��O7���7	A�0��2��1D0@�8
����`���o�GKg~�����~�[���}�������+��_F�
[D8atAb2����0�<���m���Y�jH�h�����B�� s@�W�{�E���$�����D���y&�{���b�LT�En.p-�w��m2D�����3��w+�,���\B����������e�����kj
`E�K>I�
r��=��,�����;j}�4�Xv�.�KU�1�-C^��2�9=qQK=�������
�����}��L�i�
���Q~2%�=M���H�iM��=��ip>�<�5T��o4���0H���`��j�W$~I�IL\2i�m��������@b��E��*�ZTB���A�@5m�����|U�=���"�0}(I�yd��Kbl��R�6&�YG$�~�����Np��!�'��]1�����]%o�Tb���T���	D���ix2m�{��6�,��D�J�~�|'��P;�������9�]?�m*z�����6��P��y����m��y��S�;��P�]�x?���I���S_@|
�^a�`��&����Z�B.��%��;���p��3���x��Pjk�EK��{fr���N�$WH�w��Tub�����h5�E�)_mk-�n1�>~����������]�O_�y�r�m�����*��}j��8U�}[t"OH���������������i�.��
�)�"bZ����{�!�:�IxcP]�u-m���� [�����I�����"�k!�� a�R�*,`�������H��<MR!H�c7�������/������:���1��	aho~;_�S�-��U~���4���w�|�����.��0RL7��>���&��l��$�;�%�$vD����%�]�����������R���Ws#�2��;uR�\N���G:*�V�8g��tK��� ��76'�DX��)����7�
�-���syt�]j����m�����nw-��I��gh�	zF.��Us	�R�����<i����+�"�#�m�Z=`�-���@EV���T��O���:i�����![k�^�D���EK��}*�Hc�2bE�C�]��>S�|�4G�(��s5k�m�1
P�5�6��I�d'�H�)Qx*�D�w1������5��,�s��
s�cL�
2o�S�R�I�S������$\�m�x�;�����k!P�6���c����������9��(��g�09	�MJ�����;"Q>����EC�`��}�_�I����4���p1��kB������Q
�.�/F�d���bj�8"T'xI�J}T���,�����z.���x��4l��\.����W 8)u�/����@��u�sjM��f�i�������O���:�ILm����3#���
��hSQs��Y�wC��i���P]<��sd���FSiL;�1?�U�-�>\&����Q�cY�*Jx��t��������7�m���s�����f�xI�W	���M����Um�8���������~�C'�*$�KBqrK
���5�E�ax����W$������I�f%[*.���,��D��<�+�G�2�����g�����x�
�\+"UD�H���I`�L��E;a��D����= 0"�$L7���_�>�T�)v�{G��d9;2�v!�OA�S����!X�n���^�,��"o
�����y�i
�OO5>��~�����x�+���gX5�Y~�D�8���(�|�|�e��0�F=�-�����788V�k��/����(L,����%rU�G0N����iA��������\T��b2���Sb�00�Me�Yfw�N��;M�3R�3GQ�$��2\.p��3�lZ�K��n;z�����f��E&U0�!�SGE�/��/9j��@�?���Q��5:K%H�]i*�%�r�D0ZG�������d{Q]sG�6��(��>��s�ij�0�l3L����#q��������}���:�Ci���2�D=��(`yx�t�|�|���Wo�p�S��w��B�u�
c�w!��l�KH�H���4�V%+��x_��;��?K�R�P�����c��
	P)�1�.P��I"���5c%�]���B����N��k��E���I�Gqo8IjVL��0�e����-m	��1!�
�YQz��b�S�1����xW2+���UDQ03�a}���)�2�z(
���\�;����/6���-&��,U�����rx�u�EF\F�<.N)U����I�Gq�n�Z�I�5��Z�X����<�%����ND}��'���EwJ0��	a�	_������4�5�	"��Y4��}=�_D�w���[$�Q6Az yU���#z�-z��H��#g���?n|I��$�D`��eY��U��td�3�R�Y���a��g�Fq���uq��A6	�$�g+7��<�d���RxPm,�ma.T�+�+W���4b	q$��V�����E��h�zp�<R��Bi���E����!|2�n�M��U�<��Y�����M��B�K����W�)��O���d� ?�����:z���>n��P�eE������>�7�<Y�?ma��91C��@,X�)��}���IzB�P1v2���8��y�"h���V4��j04���-����J�&��W�r��0�4���a�p�����j(���7��\�y��E��y5�#�td���Q��
�I]=�5�b��(������������VFy���)�,�����m\*{������`v�ac�C��%.>!9z�d��Z�4WZej����jlx�t�DL���w�@���-(��j�_S��xm�v��	+���2��[B7�L��2�+����6Ze�����["�G���
�#�(SQp�,V8�8����\?U:�����m�2�*k	v����Bc4�m��R;�t ��0xs������W{����|n����=S������:�����<�vz	�`���[L���j�L����|_}��e��"&�g$���*j�9(�.��g�'I��8�8Rq���0W#&�Y���K�������e��OPq�����R��LYb�w-��g����=�{��z%�ak
5�5M����T����d��Q����A�.p�Q�0G����R���}V9C5��������F��g�r�vl��n�m��dC3�����X|2uj�hW�j����o�Ez�%
 rh�E�z��N��s�+�!~�i:�WZ����h��i/�f<a��je�d�g0a 1�_,r��_�ysgg�����J�jJ:��=��B�r�\ZX�[Yd�"�\gW
������2��]�Z��|YOVV"[z�jaNp�;�O�$��d�"F��K��5�u'|A@���-�W6�e���r��P&wf��>g��=�����@%d��g�|��>����X��@��b���P�����d���<�N_AAV�i����j��&%��\�F���q�K��Gl'��f�
#] �����:x� �H~*
^
��8(����t����6wo���]
�zm�V~w�G���_������-�����_����W���{�J��
��c�V%tu��iV���I>�8b�$��I�i�sb{p�M�
M�%���	?��
������
�y��[���X�"�!:�H[��J[������������o��y9�����3G`�.
k��#4��+�p`�$�5r��<�������[\�����j� �W���/s�T��
nLzj�����t�B�ht"���o�r��S���`db�	�����f��
��9[q��Q{�In��rxf�p��/>�C�BQ_gIy)q���E6E��j�*����=
�����fs�?���>,yf���"��2R��D�kf�S�n��i�������l��9�1)�B>u6Q?DZ�J�~�$�����+H)����e�$�&�'T�y��� �lS�,f�,n1K>�!spe(�5�~��x�f��>mWV��C�F���p��Q�d�������s
��@������������b�KJ�@)0�"��a��=�X5�C?P&�a�g\]�C�+�# �8���2O�� N���@����Y��]�
#\e���z�P�l��$N�W�*�����;��5��/#����B�Y���G>o�`�q��5������X_$�[�B� ��m������n�	�B�"���1_��0Ir�� ����U��!��T�M���y��G�x��h\���jQ�{�����I�W"��������Q���+�-�j����@q��0ZX8�{���GHEJC
�H������DQ������y\��%?iW6�1�MC��m����M�&���{�/���n��5`,*�B�����R�������p=�~���y����1������x��>�F�����K�O�����Oi����)KZ�s]T&�(Q�5�I���9��B,:�n"X;��J
����2�
4e���~��'R�t�!Ft�����E?zd;�y^^�r��J!��Vk/	��4�*om�=�Pu�N�1�����Z>~�=��s!�"��#�ks|@y��#���b���iL�yD��Q�E�M�k5�nx}j�9N��Q�����6������0�~��p�������u����?����M��d���,��L�w��-�H���8�f����Y9�af�tx��+����Y�G�N�A�������Y<��^�������v�����#3�5���(&���d�������'�g�y8P~��P�x��2�(����t����p<SA��)������4��=�������*���$�"���L
:��1��ErJ�G>���
��m~��x)��Wc�#6�,tN$e����������u����[��N�8�X���1�.�;���.{�C%�k�Ct���3���d��R5a*�4�}VJ��C�����r6N E-�Ag4��P��u���f{��Vq�?d���S>�9v��%����/�y__�{���{����WFs��7����8����U��L��-�]G�wUC���l������KEg*�<y�eU�����M���$q�c��Yw(3��xM�>���{�$�^�[���������k~I������:�����p�e�=��Vi���vB=��3<9��n���|��������v0���
��cI3���$	�o+c�C'��hT���2l��jFU�V�tv���/Lz��G:�a�P14����8�q�S��*^d���ag���o�����M��c�O:<�t����?O�^��o�&��K�*,�0���G���V��1�
9��K�?��w��7Z�`��%�8��~�J�r���v��`�a��?/��nH��U9�cmrM�{�������������"�a�Y��3�*0��pz?R�w?�������!M`��`c<H���N�D�l����[�K��4�L2�7$�GU�k^�V����o�&�,��7]gI�}�%�6��
o�I�����������\~d_	�Q>�JY�X�� ��2�@�I����������������w�����V�UT^���up����r@[��?����a;]f�{�o
�����=�1�<w�����
-?�q��Zk�������z\j��kAq��L�L>��T[�u��g��[u$��h���O�
>~[rK,9��"d���0��/_K9y�d���TI�4��>��Y`z�9b�dF�O�����q�9s����D8�L�!� �Ht�GoaY}@?��?v?���B`���p���!$,�	��P��j�����^%�T���0b��0�N���fq�gF���He|���F�+�Y���:^��h_D�a��4Nhm<��k6�����N������=E���f��>�	����A%�ch;M�1���Z�=S���i��i�H��Qw��N�w��2ju�$%{c�6�������~�M���r�Fz�RG)*�D��5B�P6x<��������#s���cQ���L��i��fv2[���_H�y����,��^y+2�'�Ue�^SfI����B�s��7�P�YX����V���b��PZ~�J���35��N|�2���j��-����lw���I��N��������7�x���+kI-�@FJ9�.�w./�-����Z�Q
m�n�b�D@��W��G�X���*F�J�f7H�������-yK��Y�^M}\��p_�!j��H'#;}.e�/jk�_[������_uf�E
P��o^��q��
�Zz����9#6�t;���s��RRA�J�������x�_��3����v*��P
����������-��q��_�N��[���L�UA��@"-���w&'\����I��V��I��H0t|<�-?��8�M�!_����k<��$�mvWrh��G��=@�2\m������*�>�b�������o�7=H�L:�m�n�!�-��HZ��[��\����3��'`����^��ft���X��y�DU��o��=��\k���&�Syp�Z�	{�0�����v&W���G��f����G�"a�O��c��FC���1�u8������I�mX3�\�C*�c4��
B�l�DXq;��x��d���y\C��9�����H�����'K�}���6s	�(��t��sW�0����u5�V� �^{�e�a1���t���i������lZ
�����s�+����8/��~<vz�cW#G1��0������G|�S��u)��X�����2���$	w��7{����nk%L����4Hq���;�q��T���x(�����,jy�[j���G�����K����zB���-�T	��&����:[���S�#�]E�^��(���h3�m����0gU�c�����4N~����������� Qo���+����^E�?8 �!��Ew��x'fVjB��~Z��D3�+ok��~�Q��=�Xy�[�,R���w�Dw�S���35����wl����u�Y�nA���,a0�/�>(x�{r2_m4�Q�J>�F:�b�=����
��V�$�O�#K[�e�1F��v�v�q�>��[���%�$���.��8��G��W	�^��<iv�-�yQ�d�lT�sE���o���Q�����zw0NY��t	b��jY��[�~��~-?�����P4|�!l�|>G���E�������5�_V�m?Z�f�1���^�������1v�v

����e;^�=���r�JG���0&��H��2���l�|
?h0p�$�&?���������y�m<��C�}�oUM�5����X�E�Ic����e����f�Qf}���x�_4�O�����?%~
?>
�����������Gc��2\_��kU�m�J����P��n?��u�->z�y���M�E�������`o��������%uh<�gT���^ )Up� �n?����h����z���`�3���K�q�
w]�y,����8�3}Jj�E
d�K����#��`(~��nk�_�����/���D�������c���:��/x�u�/�aS��@/=�G�w�����a�A�L�����)��Cf�Fi/�?����0���b��=F���63_D�Kp�HH������?�����7����/��7h��t�MAy��|��T�'u�4������V{%A��Ag@A�������j �3��CX����Y�\��o�j�z�D�Z	�t��(a�$<U��,������Z
�]�&�����]������~Q]�u��!�����&����J�C��E~mi���i��iQ#�"q!��HUlq�����OTE�k����4�7���2z��P�(���0�Ojj��Yo�a�#���������%CM=�����
b3�^���sc-�X���5���i���Vf�OX)�������^p�7����d������t�R��.�~��y�>�:^������F9��7$l��8|W�L�EI�\��r|����j��oG���jH���4wq3��^�n['v���6�����~y.��L`eJ/5�P�O�=��B�F����W@}�wb�������;P
��u����kX1�E-����B��5�3���c�fY#���������8�C�qq����Cr��u���?�*x�N�4\���n��%>������������6^�N��v/��W���"
��;*�R�~������[��O�/���~^�������k\�,����@ ��3A@�I��� ����%/��j������h�GY
~�����H1�c�KGx�X���K���ng|�}+�C,
V3��A����i�d��B�i����E~Z��v���`�n��Zm����'���@����������Z����H�z�)���c06�)~�+T_��)�w��V����T�*>jx��e'�	���.g�g�v���8��e�k��
���c�c+Gvg8�INHvn/+��,|���-���Te��&U���x��K.�)=G�������������_��*�.<xn�~�7bQ��M�6���e�����f�.���79��K����OL��I��B<��A���-<�����������R����`e�<�o��&�W���j���S) %f�4����#@���~��<� D!i�����[F�]>���?��/
�5�o��Ik�+��=hq�8j	�#0����i���j�8�!��vcp�P��i��N�}$��)�����~��B��O�L8��YPdK�z�KC*��4��S�}u�����4����$���H>��\����sr�Us�����le���2�_7����8My�������8sc�k�f��f7rGP�,O��,�n)�ma����#��~�c�6�m�/k(0xO��T��|{A���:/���p�a�EFa�I��8�s��J���M����h���
���t��)���K������c��������4���UR��u���A����*���4&/PR��BZ�w�%]�����K����*�l�X�:�|�����w�v>���k�#���Eg?rq�I���;��+e
���IX�D�����0�c1&����������)M�[�������ay�.\]0��\�d��R	~r�����c�xg	o�

I�D_I���2��yZ��$=4��e��CA<��`Y�x�<���[#���L4
4u�}n=i/���X�2��pI��ew�G���$�z��V
��>���${�a�h�~���pm������Iov+h����"�1Q�����Az�����T�F5k��}���6K8�5�������X�	@:xz��@�
2�E����E��w���N�����{����|xoy�TsoZs���
@��j��j]\�2
tC����T��������%��G[]~�)�	��G����8
��Jf�Z���CTg�&��3��e�#�2.FkM���0}Q���"�-uYF�~��y�$�����aid�9O8.4d�!]���6`����� �i2���E��
�#�L�a'-�8�+q���&h��a�)�����:y��H=���������*���c�;���h0�/4�#l��|��
`�������P����a�����>��DO��3��3�V�!Z}��%�XJ��B������/5���v,�]q�)���\�����@�//��+_s���*������W��,��h���L��!��?%j )s	��:bQ�Z�-~�S����Y!�(V�!����f���<R��������u��%�o�I�.�eQ�sRF�yB\I^
��}���0%�o�h
�r��^��z�������Te���������1
�������:U��U~����*PLi
����Bx�^dq��V$��^Ge$����H=��5'��'��F	�������.�KO^�!�h�GGya�/l�]lE�v��D��cpRj� D�E�(P
�uln�}�]q��`�>��`<}��mo��RG�Y]'�K����eR���0�dS�:F#���C���5��C%lH�8D+J�E�hBG��C�������wTo��p�7�(���ka8��|Q�B�w���r��o`=����0�
��L��
�~�4}�~-���o@�A��U~�"+77�b!3@"�La��
�V��h%$$�h�T�(ad~;_J���Z5���XE#��=�.������L��KDP4^l��qC����\�p����;�7a���a������A9�7u�r�eXC�����}�1�����#i��wE"�X� )�#��p�c���H��s���4/K���l�OQ����9k���o����(#&].����,W�r��Q�bH��Ndf�x����<���C����U5�	��bZ�l����/�R�t�5�e�
����E]�#���,a����N����a��M4U�P�N�����\�g�J�kS����
���^��]�����*��q|��N6Nf�K+���=.��i������P{����I�>KX����7b�>�������I���������������@��v��5��+F����0�h��R����@���P`2��oQ���_��?C%'��Xj�)��T�����#6r���>7P1��y�K����Am?�<�$�����C���M�*D��$�@
�\��h~��%l�A��6��b��6��M'[S����e���IZ]��f��BwX��U���$l��eF���	�T��_��0��njr^Y���2�b�5�WXI�i��?�ZktOC���K���k��{�C-���v����^��������^���������o=:==��=D��|�?4��XP�/��QL
��(ha�E&�������!�c��1��������J���:�������9E`��0d��g[a����R!�{e*�O�h]�����OPK��/g\�aD�3����
�Fo�c��:"O�vTy����������n���:��$�M2��}�}��c�2�����)�	���-`&�������B��8�nE���A�Oz�:�����G�$�j���V�Yi����
������n�r1�r�
:]��FA[�E*oJ@��
<�\p��c�
Q��;@�>}&J�r�#�O1�Qz��T��������D%\��d44]P+H��lE~%Zgmc�"�#��;�������y�bA�R���G������5��L��k��S�Q��8/�>�R�z����`���O��7%�W�JL�}�]N��y�����u	sP��^w�"��0*T�UzD�\�������~��76��M�A�������h�+\W%���v����CO#�h����i�
u$s^��a2
7����`f�������&�4]�Q&���=��V���]6n�F��P��jF�d�����S��W�h���M_�9�a�����
�T�`��,�:�`�w�|�I�7.x�G��a�/��R�Q*�N���H�<J�]���
2|_7��kK�.�����iG��`d���C����U1\���l<l���E�5�v��q�OF��T�B�x@�a�<����zN���mx��<5h�<��M�S���[*���j���J���!�����!fXs�L�$���t.��$����l��;t�'�����kT�}���8��>�n����������o�{���zvzjF9=�P����V��������4�����~�k�"Z��m�3k��6e�y	�O��4&/
�(w�(�T��������&�/�/I�h�{�����v���������c����
fX����T����hT�$��%-:n
�S9�#��!o9���`������������BO���h7r�M�B���p�~�`���\���b7�H9����|�$�`vK��|�<���O��>�eF�8���`��sA1�>�Q���j�pdu��,�Vo������^�u��^��v���r�����j/�4��?���%���}~�G�5F8�Ma�&+�x����C�.d��r]r<��r�[�Q��Ns�[�c�gd�C���S�Z)�����	��"/}����I)�>WNb))gD��g$PE^rC-��s�+%�,Ci���t���� �� *��!:q��A��r�h���� �����E��R?�<���s�
�|X-�����x'E��L��g�������x��:��J�������M���H�(qu5%������f!�Q��"&LDV,t"��U/H�Pbl%��v�&sI���D�V]�S
�����-�}%�3r��)Nv������c�����&��;#:)�\������;/��cu�=�%K�E�O�:i_��ny���>%��[��D���H�UN���������X�h��2�<$Z*�+i���b`�iXE��U��{�U�YD
����7���o�������k��V���|*�'�����"����������a�Y����\�����iO���E����1���?�A2�+��\S=�T�0
��~\
@MD��hi��*H�v��w��3���0���~0�[�q�=�������a����i��r�l�9��{|���M~������?��?N�;�����NG)��%�W�P��	��]6���n�5���'��'��:t���~�����GM����]	~:D�
P0�m�Q��z�R�j�������Aev�M��[�Y��O��LQ�A�xM�j�6@_�����M��`��}��;
��b�_�A��
��~o�oW�w���6&�|@�x��1��@���_Q�`�
'��������x0���p0l���b��z������mM�{L��?����|�����dalG���h�����!g��{��&���j�Lt��m��Y
�;�@��T*\�I%�]p�j�����{j�gAM����'$�{�� p��f�������p�.��Z�\=�i�+X����5��gm/�VB��O������9�4&����<�<��8	6����0�ji����d>���_����������^�-kRU�j�0�ga8����p44n��X��2�[8E���T����+y�7�6�)N�Dn%f.0����"�l����oI�������g�V���]�{%N�V+�4�@����e��	��I���"#=f�e��e;2��a#��q�0#!��
�
�\��+I�����%����3P
T=E��1�����-���@5��6���'��NDw$���p���mB�2'���|6
��"��4X������
V��]��(��r���w/._M)���p�x��m�5��Bk���T�W�����9�Fx��,Xt��
%�E0�w�`T�Q�X�����6���m�/�F5�Q������#~�����B���~D���hd�����w�M��*���0���Vq�y��h��zQ	u�`�%����g:=f�{�.9;�j�2
���R�p���0�����H�O�=[�"����l���u��{�:��=N����0����%�EU�	�kXN��u���!�~�+2v{&�ux��ei����d!����.����5~��k{wp��� ����oE���y[�vhDU*���$7Q*��b�
�����.����
�Ui�u�DJ3N:��
l���k��]m7���E@���U:��@��/u�g�S�s���T�
������w"������}��1yImo�0����t� O�<JGn��	C��v�l�d�|@��m�6;��������q)�=����<�3{	�������
�.u�o������&��/D����G�^�y��k��Q��c��8�������Z�tR"�9����\���j��#��U��nC���X]m�%����9t����n,��;����a������yO��j�,�*�V�-����er|V�k�Lh�I������r�����
q9M�����\��\�B8�a���E���B������"�%#`��9P&�@=�j��o���2(��_�hM��$^�R��������gR�qBR����z�����$��4�e��;=�o=mq�0lk�R@P�y)1Hk����������qC�R��$�������=��nUS�5ZH��lP!{�u���kH�`�����]��xT��@CR�Q)��<�
����N�V\��`��$��8�^���Nm���ZY#������b���O���nR������������"�A"	�Y��=��KE/�.!(�'���k.Ay��??����^��w�?9���B�I9�ZMj�����t�!�D�T���]5�Z��f���zK��������+���dE��3��9���-w��G~���>t��=�
����YW<���V}�9�1[�|��c�����S���I���H�d���00��RM�,E,"��B�.�2'5�����F�����������bIQ.Y��+mi����qj����N�����������������0!� 'Y4�����)N7�n��AZ�=t�`�������W{`�w�1�����g//������`��������g�N��B��������-^7x�r����>XM�@�s�v)L5U>���b����Y�A0vzUe����ts.'Cvi��\+j�	������
ir3��Q��S�x��i�:��,���y�tf�y������'i�b�G
<��0��mn��=7[���v��/�5rY�$��p�s.�l�s��g����eyx3c�F#I>S��`���n��'�?�Oe0��B�!T%�U���k�E�p��[��4��~�����hkxrj���8�)|����rC����3�����a��n��Um��d1��_Do2�]y���k��x�JY�j��J��~.3x�����V��I�`�_��a��V�.&�?���z����-��t�=F��}����[%5j��+���*R7����b�).m�����`�WF���������(c��!]���;)�*M���b�������+�1����p�'��^{y����w�o���d�A��i�C�|@�A|�����&�
��]����je'����`<�y���V��U�N�p�5v[�_�HkK:.O�*I������v�/��WbO`�m?���k�<�4��]�)�no��F"���R-�1)��������L��E�vb�i�T����J�@"������J��
^Xh�����^�`D+��>Q�� ��|����B��h���|9�6��Y��o(Bx��e{�X���h	}�~�FT�9���������z'��M��7�p�9t^��2�"6���9�0����8q����qOV'�8����D*
�ph���1��O��Jx�0K�F����4�D����V�]*�T�!�Z�;������_U��_�0��v%��2/��d6�
�V��o�]�U����;@����`&�0����J�7n��5�a��+�[�q9����j�c
�;2
�_�@"f������pz.G��9���[&��62,Y�����M����J��~Fa07�~Z["C��������'��7��+7����CZt`z��������^o����h����}�#�5��I?���/�%�of�mWDx�"�3A�����:��|�\
'L��]��s�x6Ry�mD�(�J��G������8�E�S��SK���oB��[��&���*��{ik���-U�>l��2�ly���>�=d�Qiuz���o��fU�a��s4��]��v��^���������
5������w�����w��0��%mt�i�h�����cq��1�V���[��*s�=)����
N#���#A��8�ah/������
�I(r@�����x�S;�}M1���������/�_�������o�{~�;dw;h3L}��r��LQ��fF��q����2��/w�[/����^���y�eo-��t��~�;UQyC�[B2�vCQ ��Y{>���Q��
G��
g�C*�1WP��a*Ra!�K��j�.�%���6,���XBT��c���O�����]��x�M:�R3�9�����
���q�.�8����7k��e4�}�	P�dm��w*�)ag�!^�7B�>��Dg����� 
��X�]�{�&�>�g���c��+�EJ�u�T�o����pv�.�j�Vu�~
xC�o�i%�?~{2MZ�A�k����3���h`�jI<
�6)�/�EM�����B�B��d�����\k��������L�"
�3}l8���s������z9|��eG���I��������Kyw_��`B�������(y����YQ�l�-*��a.�i�+��2��7����-�u��PF�`	�rxKU����=����n��:�2*���K��m�����[��b�� e���*�d�eU�P�\/��,^�7J��R�N���
-}�r��������P�)L���t�p���u�r��o1�I%.+���N�/XZU���6,eHU��n�f��`�il����yX�'V�u���8�l�8�K�<�3��g��HiI�t���Z��q�w�1�c�syd
���Zq��A�p�do�#{���IN����G�:��uL��2�V��MT��B��M�%L+��+�������]�5����K�=can�������M�j�\@�"�<�L��2q�*�U{���O�y����,����3S�m��<�)���t4���
Cj?��^���|�;!�d�!��c�,��%L�'�yc'�3�
rd�B�i��/�D����}�������q�w>��<�a�S���h����T���dv�Y�"���w�[q�r�%>��7�B���c���3^X�t���T�wK~�\��	{�O$E6AsB��"L[+��9JX������(�k+=��W�BQ���t~��nkG8h��t�b�z&I�
����wg���<��]~������%�u���z��]v�]�����}� &�&���=������6�����nWE\���P�K���E��g�V�?�~�oW�7h;�"tC�rJ�v���b��?��0C����
H��F� ��/��a{�jM��A0���Pv�uC��J��5t��p/��G�d�@�eH���21]x��L����pKCk~�4��63�3T�����S�Qfz���d��>;Q��`��n�S����W���.�R#��xt�@>J-Q��:��ZZ������������L�|���G	C&2b��NI�SB�s����/�"r
=���$>��Mv����p�Z��	��<H�xR�H^��|r��'V�cY`���e�������|��|����^^����J�D�F���(?�c*�%�b�%R���vMy���v�I���p��p����u�_x�(������al�c��\^�a��p��n�:��=��1TUE��7��5VvPb9��������e^�����*-K�W�|���4�V��y�k�1a*2�sy�T'fM�������"�	�����X�dnk9�1�Q�EN�m�0������XO���c�(�	/�` �+,~n^��tr���u��������T��t0����}�dax��Y����<���d/96ua�U�������5�S�����98�8%,�AE�������k;
Fz�1��NF6�;��"@(
���ju�].\������hW�����	~�	<���k��,%���H�| �E
#��n������s*��X�U���E���9��O��k�|�GI�$&|�Nc�kG�a0��$�J��j>
�n\��3�T��fsv;���Y6��<���U�h0��� |:-��j�/���p4	4G�n�_�Tej��R���k������UISH�?��J�=45�����p��H\M�t'���#X`�������zF�����S
�G�a@��T��A���ZU��A/��'U�d�`��4�R}�
$��b��Tr�Y������'�� hp�]��o�S�'��
�?~Z+�_��#���$/M~���hU:���D*.�����)a�q�|��d���6��������=�5����z��Oz=�[�cwX�w�%�	nMPK|��d��<T����T9�B~�h����
V{k��\'��h-&=,�:�Z�e�A&�m��Jd�>�L�+[�B+��j������M'�z�@�d�b��>�t�v�F��-�~��Z��~�t��9�����-��������
^�f���TX�����?a�`�DWa����P�����%��
?�x������?������`��x
������������R����X�S
�mU/�Bd�����Y5����q�����SI�l}|svuU�V3�����I���;~�;��~EX��;�vSv�������G=�7������WHt?9Mx�����|���E���d|'�I��2��3��>�-�Cw[��O���>Q��+�!Fd�-}�7n��{��81$�fI%4�W��z�T5'G�6(e�����+?QD�]n��~��G��x'�3?�7��~���}v�_@:%PDg_�{���.��*���r'�of�=��~��[�L+�bp��a�E`�q+�b��R�$6���IF%�VHe0�w��\��;���X�?����A5�QzA&�`�
��
�m���V�]B�u
#F����0���K��`��'����Rl
z�f�Y"�����o��;�p�J����L���%�����������8�|�V}b��Oj�"?�;�����px����mw(F�3�X��J=q��o���s����cXl��<W���2y9_�
;��b�WK�Neld������{��\Ui.���!�|^M��H��+?��jZE�UzLki�+$�%�\�rXJ\)��sG����2^:�n:Y?z����������o~�����C�����l+����T=���-Ui����l����$�mC���3Wf�_,0�D�����ix�8��n`�H���CS1?H�������h4lt8�^*�Zs�0I�0r��
���p�����;�'[�y/��9
w:���<	L��w�wX��>pbP�tq�k��V�EA�W1)
h��<�\��aw�v��P�-V����b'C����?������Z����a��S��:�2�5v�B��ou����"fi8�����x�x_z���������3���*���z�E�t��!~��V��dR�c��$�1�����{D8)�G�k&�!��]�P�!2�F��0��R� �:]��������g����[����'�4�b��Q����-h�1%s*S+�d@:l;��e��M����&��'�=�+[T���6�@��_|�h�F��[��-x���hs������a7���mQU������4.����u��a8�u'�V0��C�7�l_HyH��j�����4���,h�F���n��,J����w-��;	�-<�x��V��m���������+j�N�������
���^6�'%�V8\��
����C�5��C�����]��$<OV����4e5M�-�$)���}��&����0*{
�Y����1>�\�On��w��87�q
������vR��S��t�����t'�
P�`�l��W]4�7�?{�b���
��%Y(U��g*g�����h}n��xY��`U��r���4L��~A�
��A���{rB��f}3k���eUx����5���r
�.�N|w.�lv�&�s����_q7z���)rg��jx��-��]�9����IEz��P�u�o��s�;�T�^�H�H%�
��j"Z[�u��#��������>`�J_�?�1��H��w���7���*�@��Z��V"g��<U:�*�,2��
t�t4�xl�,���:���&�i����K.�T���x��'�J���<���czF�BxB�M���\c�����P���qv)�5:�H�S7��o��7}��I�������6>/�7�_�_z_�J�s�6t�xvK@ale��q��z2�����X,.�����HJ��H��Nf��I���zD����V���m���x���v�6��&<����ra>N,(2�3�����M��x�N��:�����������j.��n���p�s�s��f<��	l:���-��f�]R�!~���0��Ya���pa��c0�+�4�%���
�Yx,��D�D���n?FT��-�L��s���*� ��	��y0�r�~����&��������T��3�E�P����
�h��C��D#�%f�vuME"�X��M�� 	�� ����z�tF���	��P�nO�p���4�a�p���U�DN�%��g�p�w"��f�3�
L�q3�a�4j�SY�^���d����a}~�z�����;�Q���k�y�PT�����F�)��R�y|��f|`���s�e��U���SR�d$Vx���4��=�N��U����Dt��n4L �����e�
��6�l#Dp\lG�_��d��u�Bu
f�) ���R�N
 �^eI�tt�����0J��/�@���0��6�����rN�-�[���������F�����\���\�H���o~��w�3���_x�����s�M8l�O�����������k`#�)*O1K�p��O�A0C���+�L,����+b&p������}�fJ��6����)��v��bS���D,��N���n~=�6�����������/�/.^���:r��t8�y<���=�'�b������(��'�R��X%�c�����IxK0�/v����3����x�V�����k����} �-m+ S�3�V="%NC|����7���_��x�������i��6Hh�Z����)bb�����bT��<D�s�s�����F�-c�EK�����[�Q��s11�s��o�(!2L�	�McE��Iobp�%@PN���E��w�#2��,���`�0)�����f\$��4����a������t�v=���_a��r7��],�Q(#��0A��P���k�G�z�05����J��G��W �����r���E|qYD������m��w�����3B���lY�*l��l�=����0w 0����oC����_�#	)B"�b[l}�e�"h,��@��n�F)������c���dL���}H��k4l�
�������5 u�
zhd<XWs��
����4(�O^
H������,0V��r���=��71��|�����=Hv���]��$���.��FJ���F3�z�������@i8�]��@i4�k���*�t&�;��P�D<�4�����?�	gdW��1�n#"'Th/�G}
����B�d�op`���^���w��7�����T+
�oV��-c}��X���K��V�X�1��U�+�Y�@F��Z��r���f�2���xo����� ��E�XBf��F�UD�K�����Jn"��@
�}��<�	%b�R'n5B�E o��OU��-�W���\@���F(�	_���b�Y9M��G�x�F�>�����_~w.�_�q�@���E�*���gzGe@��0Gg��H���i�+(�9��o�"[m�$ �@�+����n�-&N�.���(��`+�X���eJwl7�'
��k$��������"n
��|�=�������G��]�q�X����H�HW?bve�Vp�[K���$Z*[�i#�C����!�|�B�R������<#�Vq� ����,�	f�`BK�$�<����_ �m��v�5�8&!%���n�@J1ef��#���;����	m�9�cS�tr�I�m�?y`��1]������(������)"����V�sa�c@�t+���7���0��� �::(����J=�>���#���k\�=�9��Z�G7i_�(V���"����HJ��Yij9��0VC)�TI��C%��}�=o%��S��~O��7����6I)$UBv����4��C����2�(�5�M��3>����;Q��k�.%�k���#'���W�p�'�iD9���X�A��G*����Stw�dY-(FD2��P��H���,�Zp�I�6+�x(���x%��lb��b����F����%$ivM�����0Z\��!~�������	Q�Z���2���H�ifM�
�%�/�yf��~Rs����)������2�Mt2}�\92���df���8z���^~���6����$�a���ns�
�~����$��R�x��Zm
��X����E��u*�-��Y;"���	M�:� ��6G|��al�b
�����	��j?7
����i��J��I�liI�<0!�0�?%�������\��34+/������d(�����A�0��%hjB�@_%��$�O�F� �z���7x*�#�34SWn�C$L��T_�5��M����A-�����5cF ���.�����|�Y�������>�2�I����L��������[�����qX\8\��S�)��_��N�g��@�\��������V�<{��{I:�'�j��>�;�_����� ?�������
���=�H�d�B���D]I���D)#�����ZJ�
���JET��!�Xz5NN������R��,���`�����B������1�ZP���=�(�{���i��Pz���'N����\Q�V��B[B����Zs�������:����(IC��[��[������FiE1r��`��GSak.��\�
��L:���'�wV�=��WQ�_�����cFuD��J\.�O����Rz��g��%#���)E�����tjK�
�f��W����x�j���'�m�g��
(s��L�71"�8���+ps/�K����B�Q,`��r�1	����*��>������0�@Y�V+�kV���*��;�����|�b} �S#)44P��9�D��� ��b�9.�yw��{K����,��
�8�,4������D���YT����8��!�?��];6n2�-��sS���I��|������
����!�y����8Sv2��0���mD�=�P��X{(��Y��a;�>j��r���^��Az��!�j@@�9���JD� Q�U�k��	8�Rn'������Y|���H�����:��dL���gO�"te`��$��cw�������e8�,�#j�u��sBrQKR@j������hm��7�z��(hx�;�z��c����6NR\��I=�Sn5�G	����e�w�G�%�wTa����g��$��������1C��	�����o��]n/)rBA����xz�����L�\�s�X����E R��0	����:�
(���������GY�Eh��������^�z�����!9L?R����b��,�������V6�V=�����IQz�t�*?DZaO���3t����68�HD+B�$����[XL����[��}I��_�~�����(p���{HH1�DJ����b����V{>�,�q0()\<�A1�j�1�!���H,�R��)�D�>��A:]<��pV�&�� :�F;.���q2CD�??�����C��@v�r��y[��P��N��#U�������Wy���Z�T����cH����i�0!q\rZ(@���`���{��������P�F��H^+���q8�x��[����'���}��-aj_S��7_�1���Nr��U�7J
xy�&�����d������Smt�7���:E�����H/�^�r�?��%GMoU.��h��-��������|���TaL�Rv���J&p�s��M�<w�������U���MI����>����V�Uyyj���T�;�W�I(������K�������{�q�j�%S!2M8�'Llo
+0�k�����h=_���e�_Z�k%-T�ao1������ju���9p]6����VRdG<�$�+A��G�7>p��m<��X�e���P.Xv���U�
*@������@��C�-��r���U#���9`�S��P9�U�k��8�jO�]���s�{�z{��C�G}�^Wa���q����r��
���k.��xV�0�E��c�{*!I?���	�K�L��m#�s��n_�W�oCCl����
[q�^��_)��l$��/��Or����Xkh*d(�����b�f!#)�M�R�����4e�k5��OjJ���_.��i��Bf�v*�����p�Q,������|\j����}nD��eS���z������E�X	�l�@1�g##.����:��;���o�����P��:L� �b��}��(�>2�t�����q���=4����*�"��#�sM,��:���Q#J�����}W�� 3��ND��oAyC��<*+�u����{��tx��Q*��s��?�0�>RGV�cK�-C3�U��v���9p|���u'�ENL~�8Y��mC�t3�����Z�*����&�+-�����tk%���a@�D23��/03���Y(�s�4�0V���1p�t�_�#����o�L�����qR0�er5r�'%�ds�o�#���Va��W��])�����:"V��rk9���kCwKf�&�tl�2L�v�A�9��W����nZ�����	��*lJi�%���8�F�]���h��}��x4��o�l@��m�
h���O���O����I
K���#@g���v��GQ��Yh�Y4�		����������.�m<mR�U����	��Mr|D{�
�,/�=r�|�1|J������c����@�b?I�������pb'�L��+r���R<R^8I*��;Y�jH�@c,�Sr�W!�����62N=|t�#�g�v��i/����pU"��}8�$h���VH����� I�{���d*�btN d��t����I���T36.u�n!�"�h�c������2t�dr��Hc�	b����6��>���.�p��a��D��=5�vN"�5�b��/f#�$�+��F��</��6��P��b6h��Y�j�������^��������y����?P_�
��)Sn!�=�0\��\������������e�`� *���i��p��������hU�*����o����U����9E�F+��^����~��nw[��`��6���ic���	�����O�]��/��<'�0�������^R���B�-0Or��$x��P
����J�d�fe���&F
~�����92������|�q�X~��[�*C,sa��2*���89��!Z_�_���+&O`�v,����4�E����.��v�8z�mu:���G�?�|��+
0007-WIP-Optimize-slot_deform_tuple-significantly.patch.gzapplication/x-patch-gzipDownload
0008-WIP-Faster-expression-processing-and-targetlist-proj.patch.gzapplication/x-patch-gzipDownload
��5FX0008-WIP-Faster-expression-processing-and-targetlist-proj.patch�<ks�����_����������f��J�6�|,��������P��G7����.@�OIIz�N��H�`�O�s������jh}���;�:��v�a�������F���+�a�c�)�c�g]E�T�����1=������F��k��ir�e���K-���N�
�%7�3d������2R��"��F���rsq7y��Q�3�����<f�5?���w@���C;�5��Y3h�����m�A��@������3�[�����	�$�N�C
!��3
(1wg�&g��s�j9�*@�o�Y�A3��b��������q�����t7�0�|�:U�1xs��&���r|#�$�b9V���������m]�W���hN`?����c�#R+�J��Y�4�w�Fl83��c@��P�5�Bk����av��t�	Np���j��lV��:�g����k^����C�`�����������o�c�u�
|`�n��"��]�-����{�~�)p5�:�W�������m�W��O�G(E2��Ca��\�5����'���'��_������1B
4��E-u!y�z
B�oit�a�kH 
sHB�]�}:��l�gy�{1�LA�3-�UUe�������$�;����Uv�	ip�_!X�4Ro������ZZ��c�{XvV:���HX��u�v����Y�V�����%$�0�$�~jX%r�F
���f-�����A����5����#��!_c!�z�{/40�^�{G���s�]Nk�F%��R�79�8�w7!�d��"�����q��RrzIa��t�!H���	��kZ��;�~�����
��������8��-�C;�Ln���C���L��po���x��8Ti|dL��G�����k��2���Q�� P��%��#����{��I�t����%o����c[3���n��8��>�����a�����=��m�%��@24c����M��I�YNCQS��\+��P�M��x��d�j9RR�6��@��8�&���!�i{3����.�P8Q;�wk��-��^�	�A�a`�3c�9knB���0m�9��y���c]MnsQ��C��q�� �����(C0�G��jl��5�kb��)A�~
T��L�������:h�t������]�7�Nk�
���~�=k���A�C^�H��K�,K���~��c ��p�UVagL���A��A+��������8K����u�{J� S�|R6� �C=�4����[K�E�:������d�����c�W�m��:�RTh8G��b)4+@�[R=�RH""�Z�A��3va���c���
�������f�)��y�Z�v�E
�#��}�I�N��B��;
	+5'���}��o���UM���)�;����a��
����:7�������]C7��v�����p(ke�GY�W(��,=��0���-�A��k��$�1���mf[o9��:gNh�2[�p��C=�� ������������7��&���� ���Aj��Y��y��P�������i��_0
�qd�vH�$&���gZ�b6�����]w87Q�h�4�m^f���|"(�D�>}��F�T
�������)�3��M�h����:\�^.yY*y��B��
�]�6q��g�e��h��[n����G�����e		�V`�$�-+'N�<��:�`��,ZFdJ���$;W$*�,���//4�������T�����#��[[����Z�/v�
�g%+9���/;NC��������*�2N���d�,���������?��^�����F���_(%� <���,y���"L�1�%�������d����W��%K����1\��d�I����{w!�'H}"�k�x�)7�K����m7�V����v�1�\GB=h�IGp-��)\���	���E��R���������������^��-�����>���z�
!�T<nS7}T�_���X�p*p����
V!P���3&�O	�	�P��UP,Y�D��?BE���������d��/"������cX�!U�%�W���'zlTb1OHy�v�.�������D'1i$8z��pe��!4���s�i����fKC�#���Y�s1�A��c0P��)��]���S�����zC��{��aD0�u�
����;-�c��D�0F`Y�\�JZ���
t������_M��b-1���4�J�}^'j���D#'���d��	�
i����-��n�a�0��h�`29w ����5F 8��7����\Ht�f��kVJ�,��@�n��A�9,aT�4yCRm`H��`Y�8�)'�Y��&�{��V����
U�`CU�n���j�(/����r
�`��(���A����n�l�E������m��^�4W��>^V�@3��C���9t�4�	�K�=o���?�p���p�G�����:5R{��B��!�n����X<�t�8�NG��������QZd�����&������
\���&�8-�v"w����b���������u�=@��9����{Z��2���itU=��� T�����S��)2�;K�Z�V�~�@��9�nO�����dD>u��p�E���	��*��xH.:[�����'�H? �$F�t����Z�����:���'O�@z�@(=���J�������~�{��%	' 6 ��{��%-��������+����]FD`�n�TL�CT�A�([v�	�4D�}�����3��gr�$�~��~����gO-5~�o��D�3�����ry%��*w��bb4]8$�DoK��2����[N���"X���y�i��}|��+�2$��8\m��5 P �����",G������-?�'���=��� �*���X<yD�����S*���z
o��W����['�0t�|d�OkQ�
E�0�	�q���b�D7X�uj$�'��q'��[�{� ���et��%��rK��������������7�vS� -���������Q�7d�4�&����"�x�*�p������j	�c9��6��1Lg��U��3��v���	��=ed#�� �P4��P�����x��o0��9�/�#���5cS�}P�w�$
y���@��.�3{��K�N[m����S�[�n�,���p��cb+Wa ���	3'���,�/	: �X)�yC��k[�v���>a����K�!"�}���AOi�:=�k�N�8%vI�:0�I0�@�b����X�?F�_�q]���c[`1��f���c��
���Y<��v�\����C���(_���:�628x"�#�C����c�o|X��� ?��-�Gu��Z���_�����[%zH��:�������S���a���{B�vD�x�!g<���T���y�K��g|},7Ncd����F�����$4s<!r(h���"/Y��1�� Z6��r�B��{{I��f�(�!���E�1��Z�<�7������s���H���g���)O�.���^��N�@�^=��amm_�p����p�>����\n(�H�4���A�"}�C!�7`�H>����|��D: Q�
�A����e5k�`�+'hC��}H��c0db�
p������3��O��6���<���#5��=��K\E���[?��Y����������
bI�I ����I�!��~���? ������M���w��k�e�yq^��B����������^��L�W�o$N@!��JGi��9]��w������Q��A@�I��L�X,�]��m�O~��_Nh�[<�+w�0m2p-[��:�X�X��Mk�����)���xx��@�#�lT�lZ�"�UL��'���	���y�v��\�5]F�)L1�3�3�p�����T�{O��h�%����QJ���E���4���4NS�J��g��
�_��Moo�����R�q��O�f��[�4��������I`�[]{$�������}�k�,B1n2���.D���?��[��'����>��^M2�����l��zj����f�n9����9N��&�c���;v��7*�*d4����{�XN{GQ��b����@E%���o2LA\����ym_O#F�v@�	�n�5h��"~DnR�r�R*"�P�s�����H8j�^������F&P9|"G.'1}\��������:r����A,5��������YM�YVl�K�Ph
N��aN��z��� :Imt�}]U{�Vk�j��������� )�\��'�Pt'����6�'�|o����Rb���8U�x�i�F��J�����8V�Y��k�����������E�VH���k�(���1�����"��=��g��py	�� �++h1�#�;����$���x/E,|��}WR��om������0�C�� ��Q�=�����5��B�7q�*&�I������-�����2��~�<l������v�]^O�,����C`��nz�
��0K�h7���o]Dv�yq})���%A��Rb�co E[D�I���F�O��)��d{�Z(_P�v��@�eQ=u����	e���v����8��v��Z=)�q��I�z�wg�*�M����?��yB���O0���*�Y�"2�}e�M�9�h���P�F�S����!����=G���HM��Kz6��1�/��x@A��f?P�&��������e�f2ME�b��O!A�&�/tR;6B�xTM��T��IX5�t�^�Ry�tVT��*��E���_�6�	q�Pb�*%��$�1���%��D;J�Qb�I4�����R�j�%{Hj"���7��(�~���k�����T3����G��n�}c��VK�T������ay:y�����O��~��u�����
�[�K
��j�������� uj?j���]]�1��?�a�H�bk�\��{����(����c����C����_�M�2t��Q��{�����s�um�H_5A��:B�=��JtD��������)��,?!>���U�����Ey�V�>����<F�u����'1�x�{x\���)�������e��K��nm��ju��F�H'qo��]7�	v�m���d7�S;�_=�:��G�������y�B3��^�
��%D�2ar���gq ��x;S�s�5[]�[��r�65R���� �E�J���2�[<hjn�,����w������>5��uw�����^��N�b6+��4\���&!
��[����Uva9����QT��������"����ZH-�G4�*�MO��vW\}����A����~��>����E&[��gm���
����^����&m�����������rz����n ��\�����n;	������7k�d ��i���H+M��i�����8<��"�N���
�0���Qgi�_�������n��f�3���}�nn��; W\9sw����z���bZs�-�3�]~�5>���fGY������xu?�����c=��,Z�>��0�^�o)���V���9�E�q��$�{iM6J��g���`+����������.�����~��������y����pK��0�����0�Fr��iC����!K~>���U\,��\�brq�/40�Na'
/�|�X�^\�ey_������)#�|}=������������\�hu��E��2��.�]������	P��>/w��In/�%���g���%�j�7����}���T���\�������v�q#����_��YqH��-�m%��Hr�;����8�dgq�������&-k����z(��|H��9gf���w�P�*���������l���wp��>�EKN��4�����������_�w�/�G�HP�����������x?����9�?��*������~G�4���9<ml�X����G�o����o~:>��YD=�=�?qk�rvp�0�z�����a����PEn!���d�������������}��S3�?�T���{�W�_9��<8z������������}u&���14��q|�2R�?��N�_�3���x���NwO^����j��H�>�TG���B���z�I�!�-69���;~�sp��|
��|�Y�o��<����a:���'��_�H��������������[�!������M��G�����_Gba�S���I6$E
�I��0���b�B�����D;2���P��+������\M�LB�.������E��x��A�WT\y���zM��0>�������P��d�l�`������h�?�&�K2��
��LR1Y��]�k�E��;7�8��j���������s�����v���dt�nNO��y:�6c�3zm\�2��O�b�m5!�:!���=-��AU�$�����P����L�&\��K�x��L��@D!I$b��
�����m��n�3�������!�(7$�D�M�-
�������v���Y%�W.<������$2�q(ZwV����d��+�q��X	�Cd#@X��X],�;;hMI6� %r�Fz������l�.��h���=���_8��H_�h��������d�028U����I����#5��4�b���������&}Ep�$�	G��{���'"��z@������d�����,F�Iq�sZ*U���C�+�RW
=����b��Jr���=H�UH����_h6�}�{1�rwa��f���n'�@��`O���0rl���y�)L��x���}c8�A]��'�M{+�7�X��a���j��%�\�O8��������b-1��
d����]��5�>{f������42l���~��-iV$OS�� j^]f�;�V
{���W���b��E�a_�������}>�Pg��^��>��l
�h4��d�����jE��OV����f*�����\^0��I�M^�@a��?6��$/X��=d�q�^�v������������]���G
���3�.lv�r�a�	T��(��� ?�����hC1#�p#��(fd%n��A��]�~��}r�3����Pe�
S�cXg@L�4���7�2YL}�N�fJ�����(���X1�5{�t�a!��gm����?~(;��^����Su{E��*�)�(�K`	��f�~�;s����o!��������;m�.�:C)���Q=�������� �����e�����[��}2�'�����������f��}�y�F�ZZ�{}c����<+7�y6�;�
�T��u����[N�@v���!���M�$��[�����qb�)@���������i��Mp�XV6��D"@��0��a���K�
5��qV�s�
S��7M8n�����������O�x��3w�NR/��)�s���������8r����;������t<�i���3r
�,f���/Z�/���RuC�+�6���6�G{rX�N�D�����C��}(�=�1���]v��8��P������������������%nxi����O���,;��J�L�8�RisSi���D0�m���i����i��wU��CO��*�:	\�uc��_:�L�Y�6��H�NRW���CTK�f� �b�	����5���~l���	�q�����Zc�g��t[�TO
���L��(Y+D�H��8������k�$��%*��OH[8B�&��DpH��qY4Lh���S5�.q��_�4zi�}�9���i+��/�1��0����E��M��&����b4��K�
��[i�k�����V�;6)6�
��)�Hj�l�f����g8���?Zc�����8���}X��@��S�@}��M}�5w�X.�M7���9������F�KW�f�-sL����!�n�����B�z\���z|���.:����.z:
n�
�4Z��D��g�I�����G�{:��b�* �wFXw�����M���]�7�G����u^�����X�K4v:A�"���ih`D-r�?z�0�.~���-x��Cj�#�z���^�xX\Ml'px%�,���;�A��=����<�(JWp�*S�B��-Fa�u�[F�n��-��(�)��u��H�B�w��[���:7�<�l�u�r��F�����&
�@����bh����i�a|����_m��Aw���8�^�'�;��)�������H5�w�����G��]F���t2���&n��-�8	�vP��x��v]`r����F;y�la_$�����t��
gl�������p��I�.��A
��gF�&ct���)0������W����.��_�<�t��������=�5��
y����F��ax��4�8]/��/��������������^��Z_���W�Rd��	�Q���$�QY�&~1Q"��D��op�M��n<s���)^'p���}h��������I���lAc���R�lR���"e���S(�o c$&f ��'�C�Pr�uN��`�	}(�J&g���V2�{���$l�%^�U�	���Nc���2	��f�Y�\]�+t�T$�R_?�����S��pv�F����#/'r�.��*�?	�y���Lm�g���EU��x/]�t�����mK&J�4�sD,��
�������_��F�[�dC�:�e�P�y�����	�Q�b����6�`�!���2Q����B�����k&�q�P�p��EC�u��7���o��k�vA]:i�y{��
����Wy=CvK�e�w�%������yOb	��sP���r�Ok�X�7K/6%��Y�f�p_��j56���y�������;�O��
�p�����u�����w������X��}���1��V��21��p������1�
�f�"��M5�\12���N< L�qlaIU��Z?SBO[��>�u�Z
������W-_%���Pn���d����CP���+�y��yA�W��Yr�W�����V}�������>�mV����
T���=4L��):5�ef�����.�K�kd�n�������z^
�b:J������[
l&�T"T��T�n�U=�S�w�pR���'�u;y�Fh����aY��'�C`6B`N#�@�s��������Y�2A��	����z����W����z�,Z;�x�k����|��/���x�FN)s�^��F.��;�,X�=��
�2<0��)��f�����C��us�����'�v4�tm����@��k��7<�#���h%����Y!�l2�&������.ft��V�.�<���T��Y��{�!�3l�B	�DW���Jg�`�����fT����frZ w��Y:0BJ�sLM����>�z���d\��'���
�^��E�{7�����B����2��������]�dH�����n@��xz�����d���(�2��_�H1y�@��&���OX�()	Z��=&3!.�:�&I�h�"�Z��=J):D�?t|
�U����-�4��t<'�g�c&���f�8����
o��7�"�)3*��G��z}��`u�"8=	R�R��R���@�uHN�G�<uT)O���<oa�P�UPD�H)�,�]�@��9d�����L�3�8#�`�+�K��X�B=�GN0&�/�i���u����zh�
�C��~V��kf ����4�S�aGh�W����D`�mM�e;�{��>*�:���|�����%�p�����q���J$TP���*L�e@�P�N*�����	f�v���!k�\����B��a��A�,�.��e����.�QM��'9D&����2��� p�cE�������8���($�x
���Kq��{/j��s��L��O^���8����E������*���
����)���K�TI��{��7����V��@}et.8�Igb�)\�an��8C�,�7�,������<�����C$k��@��u�`�h\�	��r��F�p�����~o�DG[�Taf#
���u
-�beBj0$�0��	M������P�Qc��v�WB��Lp�k�����=��*���O~��K~C%�@�-^�S�k�%�������
�6���c�
���`���N�]��zl���'�F�t�`�
�������lQ:������f��u�����Fc�.U������&��j�l�������O�	�+�sQ�!4W������N����Z�
/��,�<rL_��i��
����S��j�:��i������o�An� �"Ww���`��=H���C	�l�o$<���6c2C�!�'~N7�g�30P�0�+ln�l�P�-N+l�X�X.F��r1�X������Vc���Za�z�g:��Ygx��1��c�0`~��y���#����RB��{�1�n�gCz�
�}6��'$h���
��'g�����,V-����P�?�]�zc{��eaLT�E����iI�S�H�-E��}����G'w5w/����.]-�9����.���*�y�-��J�1��V�~�Qq!)$���I����	:�%�G��O���h�I������v�������u�;�=�������:�Gc5������X�Vnl����zz�/bW'"��K�����������$Y��6���C�5�s����q��i���4�_���*���'k��i�}!�iX�4��-��X. 4b?����u�O���:(���p]d�����M4������J��HU �f��3������1�-�]�V�[� pM�����:q+�3n�Qz��2�����@D��S�
U��B�`�H�j���!�@N�J�����-':��x�����A�%OcbRF���0TK'��O��.�3��,�r�0B�i�M�;�HrF��	D�P_,�Du�j�k�}d���k��d��sk��%������L�&�����7?2�95+��q�����7���D�B��~������}_$B�6����YEb�{���� �W���s�`����)��HE���'����P%D��}Nq�������IP
kP����p���"jhU�!���.����KP!1�f��8�4:�(���'�W�d3����7H�b�2�����h}�!�o\��XYE�!`�+�_L8��e��F��Z�vc;�k�������1���!�:���F��/C����Ys����EM4�����uu^����N��9��YcsU���@h�l����6�6���+-f�*���0��A�������b�o�H�i������������b�G�J���w�p��dUuyF-�]gC2)�S��@�Il���40�(f��4�d�7�c��P~�3GS�W�U�Z~��0��t��J
f��t���{%:�|#�G��H�qV�dk(�>���#�4����6.��0	�Uy�(�6�[�-��L�=A<Y[�l��U�K������N�SiD��Vi4VZ�x��0j*�x5���k��&d����\C��E�%l����
�3���/~H�%�P����~�ZX3���>���>��(�Qp��-R������7�����v�v1�����$�E���&r5�q_�b�P��G�����[oE�u�o	:_���EcHJ������P����X6���z�C��1F�������@��M&��!�dh$�����th~�����Q�z_}=����P����a�t�z����Ma���dq��������.��L�Q�3�����Mz��1��S�m.vI8oT86t�&�
Q����P8O$��Q$��������D�c��&����27��.������!iln2�4]��[��G�YY��/������	�b20��y6BE:��������;��7��0��Q�9��;�M���x�}��gi���n��0���yzp��O�k��Sg�Ih�Ib��rC�M���)faI�*q��3�Z�k����v��* ��O1<1b���p��S1��q��
�����k������_�c��S|���uL+��u�2�mm���N��R��p|Q�p����p�A����������~�1v1?�*��������[�R��~����Xg|��Kh<���M�dEdZ�[�M
�&���8��3�D����'������qcl�'�!SP����t�{k��V���30��6\?wcg����#�%F����<�`t)��M��{����=�����Uz�%Q��>P	��X8������;T���@��V&�`��G�?��vg�e������0D���h
��zA`'��V=H%Y#�a��M�d���:���;�\���9.o
u�f�Q��f�AH���lZ���;J-gw�K���h���>v��8��Jp�i=�^�f�M=� kP��]�N
����$��9������7���t�`�6�������u�����%��$IF{��C
�9��G��x@�(�Bd�([�6[��
��+U�1W�I�u��P-1���{��:�Rj������]����1
�*�m�&�!��������kL
�u��DI'~"�[�Y�Bb��O�N������������V�$8~h��t	����,T���T[���Q�������2
|]�kmb'��p��k�(},���w�Sh���@��.��=6��g1�$��
M��E��(���P0E�^��F>������O�\'U��H��2nS�������(�,��y�p�Wj�h�LV3r�2��j$�����X_���I� sCe� B���;uiUw')��Ir����������ui�8��:��%Qi�H�&�Wc��<�qe��$W����n)~��yL\�8�\���{w����v?��5����h��N����P�?��G�q��{|�w|��V\d����
�(A���z6)���E�����d�!~Qc��	��F�	|��(�iE/wiB��$p ��������������/��1Ht��X=n��������DN�c����^1�%r`p��@ G9W�FC��f��bt��Z�w'�S�����Q�FHE��
8�)����	4�8�����=Wf,�U0�XG��n���)F�G; �(C���+�� #!�HF�2��5��3f#2V����Qk!��$��A�������/����z���fc8��k��6T
c��a-��$���_L�bQX5����j�/G�{eF�841��m�gHkF�`�7�O}�]�)��	����O61u.`o�l!�{yp�r�l�����
T��6h��	b�V6��A�O���?���3�;��+i/jY�e8�=�5\L0�aQ���R?�#�D�7�7jC8Zl���	p)8�dK%k�.
��p���2A�$�����k{�a<���+���[Gg_�F*���5���(��]\����"y(���%�f�������6������N0�I���3
*��B�)VtO�n����/@�K�;�?��8]��
\�Q:gq0�Jd���&�:�w-��yE��l����2����Q�*R�u�����#qM9;�8yyb��~M?��|%c%�H"b�e�>����9�Z���&���|�~fj�\�
&������/�������\^M����T�g^�5�V���qq(�N ��=z��I����5�Cj��1HH%���|[�?-X��T_�q�A���=��m�����JK~�7�e3,�����Zn�~MY�W;X�n��o�/�7L�4�.���p��?M��Jj_1<�|��XmJ�j�1�fS��G��"��:2H���%����V`VQ+�+1��E�x*n����"�������0��'�W'��������w����Iu:t�d�\�sN9���Ja�A�y>u���..���`���\�z�[-OM�w��/�e��U;L�,p�����cV�s�"%��`N�;�'�MmB-r����1{%��������X�@O���k�bZ��i��	��e"R<I����%����]"fQ'�L�>��e��,����q$G���7`�������u���bXV�+���o�{���QwN��������|s<VT'��4�bqmFbY}s`5A\P�-`��j�8/�m�}>��6c���VqP��V�����AG
��ek ����������e_�-7�w���?��v3�j���M�4�&�1+�����Z�%3i�5!�����O!��8�_�*�:J��$����>&��d��3��.YI_���*k@�]L�hR��o�K4�k��_Y6L��0������;km����$:;�f�N_J��)������E�*����i�����e�6D=��A�3�*Zf����xJ\Y{��TM1��ho���^��m8{������T0\>�&����T��)�8����<��eL'��4sD����h'c	�j��M8S���q��;��R�`R�&zo�������G/w~�]\<���<Ig7{5�����Ej�a��U1
�b�F��z�8sd��e��������"43kM?j�:�3�0v����l��7�Wm�����bU��*u��������?��[W���Zf�!}������������}k!�J��yH��C��Y�o��9��}]�V��������������q���7����p������Y���L��
a��;����x �'fq��=jKq`�����:�l�S�����R�ym|������Jf�B���3�*���i��\�!��RZ
l#���k����N�������1�W?i6�� C�G��P2��8>�g� ��v|���X���'���Ej�cA����4�m4�����TgbA���$���:h�������K�	�� j�Q5�
��-�\~��mN�\�4�(k�HYF\�� ��\k!g�\,����&�0��'�Av��A�i-�]{:��=�f�������hB�aaS&�PstW�����N�![���w�F?�k�k��
�,)K�gUR���S����)��[��W.�b��L�����`�����86��2��y�,
�%�'�4���NP�*G�J3������PLl2@����m�����J�Ij�>�y�>��aJ������z�m:�3F�z ��='*;���U�����Y�����q����bq�W.��f��i7�V�]*��+_?�K��O4*4�F�E���������g�
)�y�R�GAny��bBj6���MI�W���l�y$���$P�K�}{>��$���<�>}���zZ�W'�?b4����|wo/�y��-��b��~�0X|m}:��]} �l��^>�#�+���+h-���t�b�&�8��P�C��$�^aD����z!��&��(��/2�']%Z�%��}�{&��V�>^��m�u��S���c��8u�wF=����3Ab[�o\���(��������
�+���u�]�X�i���]�zG�������w�zp��u]KA��o�gW�Xs�@M+*n�<�8��?�~5?G���s��Y��V���B�
�:zz�#`�7���,��������lT��3��ZLf�����f��{���������%fR�J�����>:����t���U$�����X���d�j�r����oL����m|���}H:��3��c��������b>�q����8Dz���@���}�w:���F��}f]�/>
�`:�$����d�l�U�U����K>I�.��������U�Dfy�i��e�6/���mD��|��z'��Y:�Fy?�\�c&��9+����i�s�f�����"[>c5��<eq���LF�cm��Z+��|��Vk����8/���z�,����ZW���q�^���oQ2�f�K�?�	�&3��s�����a�SD)8�� �27�"�x���"3�>`4���]A^�&���	)�@����e�2�=>�3tc
Nxu
g/�?��PE<�^X�E������JK��6��H�Ei}/W�~e	�)�����H�szz��Q�m���+ba��u����G��9;���Y���!���[@�<���<U$��
���lh����V��:I���&��n1y�8a���,�^H	q�[�Iqk�7�!��>M\	e���dAJ�+>"���B���^����Y_�M�|�kY�����pj��=N�+��Q$<I���'��t�������<�2��f�j#���aM{X�D�<�W���}t�D3��&������<~U���6��M1���,����/�����bU_!���M������/���u%}�]��Bwc=���9u�J�F�kUo��%��L�f[�K_�6t�*s�E�
���W(�
V��bUy��:�QROx�w� ��Y�GA�jQ�M�t&}X�)U\1��B�DT3�U�y��39������c��g��j���Y��!c��6y&���6�}��l��[�M���Fr5�S�G���H�F�p�u��;[�_~��uA�G)�g��������1s�}Vf���ey�
\��)��Y��J�S��U��s���y��e
M�ec��?��!��
�Q�G�cg��
����b�
e>����jV��|�8��z��_�����<�oB�Aj���n6������Q[ ���H�beY^%�br�e��EG�KQ��>��\�h��H����`b���j>�)�����M�
���V
����1]"�9_f����e��`��)�!:��]��E��i�$"��u���1!��l������HkiM����F��2�m���1r3��	��T�V)��&�K��I��e:�a|��t�&������MW*�;G�@8���(B,��B�����,��j�%���q���"5�H��@�L�������D"T���D"^&�����X�d����'��WM=��Y/H*�z�Z�`:$���x�7C��5]1Q�����8/��V����|Y�����^����{�L ���d 1\�
�k(�&:��1n$$��ceN�������K��	/ �bD�Q�=~
����i���* ��m�������7mNR�[j~���|�����|lL!
l$�\���d����W.5`�0V?��F4�_^FcQ����M��wN�FU'O��B�m�*d��d-w��k��Z�����%~������������R�u>�I�����
`�P ��Z�Z�r��Ci|�>�$�����BO�Pc����A��a�?d%�9F.]���5V��Zoo#~qQ����,�����E�c����m�E�
�\8������?����QR���'U^��\v������G���A��2;3[r��+�4.���N��d�[nb���nAn���f����~��������D���R��A��*��������d����bX��@�Qs����;F��b�qaQ��PIN^����w����E������N��_�'���U�}�����E��V�O�_�:8���0G�YX�����IQC�Yq��2�B�b`,���C��s��if$����#��A,5;B������Z.o�� 7C�:��'��*@!�1yxe"���w�wE���5��g�we��hW�bo	-�>��/�)ad\�*a��l*Kq�%7�}�9}��F��I�
��LL�G�\
 >��
Y����:�[���ka���M��h�0a~xD�'�AT�V��Q�n!.|��0��������D9���]k{q1�G3�=�����|e�2E��a���
����"v��W��u�l�wm�������7��A�l�N$c�m
��t�����P�'��IC*���j.-G�]4Fy�*C�nLtl_�3c^u�$^�����:��X5�������7H'�f,�D�AP)#A���.q�S����g���\v)�j�W/����RW����7�����]?�����9����GP�����L,��c7���J��I!G���.V�ts<�mK��	O"fov�$g�p�'+� �����xn%��Wk����xu�I�������rcw�dM�Y�6��q�t���R���[��2�5l�������b�d�!��v�~�o�����3;�����z��f�+q���Ut�Xv�^��_C�80������"�{T��b�0�o��t��rT����h)�w���V�l}���y�H�&	�h���'
p�F��q2�O��&O�~Tw�zS����qz�z`k��M������t��b/�}��%�]a���J���xjt�c���2N&�:��yU� 8����_�Y�e��1<�����N�Yc#����?7�\�nlA�Cl��1���K�'.����<p~�_;D��+��o���2�1�����`�j���{���e���he{�*��K���D�Lw=`[������
?�@���-�C-��G�`��������������L�m�
��w�t����	VJP6��d��&)h?/�s�
5���~� N���x��i>�5C
Z�'�������/YZ\#����N%w�1?a��|�	�G���r�������I�Q��
�b�9��;d [��E6�/T�X������/mF�����g�����������Gg�����7��t��/�&����8�9:p�&�~��_9��qX��Z��[\s�us�A�q!#h`L�g�)��h=F�Z�g���#W�,��i.��;�Ls�����.�Qi��Z3�Vm��I���:���(�L uZ@BT��fwM�rZ^����>���}��^�:>9sQ���vI�=��5	��f3lI$�"���)f���F����(�Q������������9�"@PAC���`��?E����a�G�|�i�Ti�uR"��
\�4��V �����Ud��*j�b�|��`�,��t�Y��j'��I5��'��7c�A#(���*4R;��.��FR�����_P�H�A}>�T.h�����y�C5|��k'i�{���{�iE����������P���/B���v�.���N�NL�I�lX����~�i�����o:[��A�pXs:*fU��D2�%	�yF����6����9+�8n��L	��U�Z	���� X��l�z}
��O'�;{�{=�(\�^�������� ��8|o���������(i����pB�+���r�
� R(:�.������%����/����L�������� ����47-��������������=�)������Y��������s%�-����O�k���� �
w�>��������a�
a_j^�^O��e�)�>[�4�
�HH��Oww��� ����@#B>"Z�*��g���dV�i�����G��������J��KJr
e>,���,Q+n���86#���<H|�*g�
tT��gT��*3����m^��2��f�!����8�Fw|����8�x��<r��q�v���r0��>g�4���y�M��&�>�_8	8Wyu�Uz-l�Kn
�"�d���b��r��H;	��BS_�oq�^��?��.%��;��,T�`8Jlo���;�h�{y@L-E�=�
�bB�I�x	�0�x���BoJ�x(�8������h������y{�5U?����������_�\�)A{o��d${V��������Yo��=B�������(KK�}��TV�pL�A�����B(PH�a!��D@R���X��R��
���I���[���'Kz�L@n=�h�E{�!������qLdZ�<�rp�fi{W-���E��6�X����@���^BiH�����"�n�B]�����1Z�{@
`L>�$/����/yu��<����*�����}�D����7��t��.��vN�G��n���I����_�D���X��:n�[�>�,��[�����iB��v�SU~~��S���{B*���)���5���AV1f^<�bn��]����l������')
I�(C�����~>]�����{���F�\lt�uVb����a��!�+z�9�����.��mmb
�o6W-��4�U�K��M��=9.A?o�U'!�.����THlwS!rpR����fc�ZM2�W"R�/�����<��A=6�?���Y(,�����}��s�5R@�E�s�����_)_����W���>��������,�h������
����NA/���E��~4������i,f%���
�~8>>���)�b���I}z^�h�����R#ZF��H!�5���;8%m��bv=k�qr�fI\#���s���M%hOP�i���O�G���g�K����A;������p��N^/,G�/��b��ti'K+�>�����p�~�����p��v0�w��O�0�;8=;8������c��C���}�<���''�Gg�/bg�h4��YI��tA1%��9�X���LH�"��eA�Xq$���*64Sc���:m�s5��!�:�O�8��'5���J������/���q�z��#�,	k+c[�� us'���SJ�Q��3�<1������u |Z��0���Q��@�n���N��S~h��4r����?�:	~V*L8���7le�"�������S��}.f:��%������?N���)�"������g������I��y�q��r����f������w�Z����%F:0�"�9sV�8`���:��z���g��Q��axz��t�b��� ���/T����,^�z~�
�n�g�����<�L��������(��n�'�.����k����L"��^I-��\]���We�qp�y��6�;����j��K�O����F�u��g��g[,���e�2��}�&j<�mxS��h�(�b���,js�,,���~���������D�s�Z�F��6�7���565hs�M5��������5��or�
�����\o&�z9�5��M[���	B5�x����-�v����)�G ������|i�j�?#�V9��n��6������N�*`[~������W�l6����D�#���j��K���:�j�?#�|D���M+��aA5�2�]`�	��M3���38��t���1���i:d��s|�?����dt}0!��Qk!�����j������caE�A�?9B���Q{n�3��G�`3�s�����btn��u�����\���fX�a?]���K�������~]O ]$�}W���u��SR�]W��r#wd� x��9��$D3�u�. T"d�(	�]�D=��$6����#T3�J�[��r��k������r��'��'
��v��74�?6|�Nh���;c�����o�����{
�N;�]��^���A*}����o���2�\�
+��r^n�`��=��q*Hj�wR����05C`�������F_^�]����#w�����[-.K�V���J=�����"�	����[a���ab����~[�I�������7UL�����CX�6���w�w���s	��`�����a��`=�>0>F����;�D{V�9�������6`
�U�r���D��5�`r��p��bQ��O<c��|��1��.@���{PE�pm"9�+g�x����9�lg��2B	���/V���T�_��2�B�c�M���r,��������?�����Y]����[��X��h�0��a��n@Go����z���T��U&�g��N���$��*0�p������	�G1��ga��g��(i����SN�+�wyja����`>h�jJ��[eN��<`��2� W�p�������M�-z4A��!.����l�p�l��.���}��7wjLF(H��//��%9���PWE9����GA*qt8E����2��,K�q���-���`@A��):'(�������Eq�O�es��(�%��u��~�CDm�^984T����!�K�����j�q�P��6��+��!P�!�)$�S^]f!�������5m�1>�����W��^:���]|���j��8�������HGe����-��JL"�_���2�O�S6��8B�Rf�2����LD"�e�Y�A
cs�DY��O�[��JX������9���P�TX
r0`�:�U�+Z�x���5p��V��np3��%-�	�3o��"eU?[�W�e�C��m�b=������m�b�������y;�������tm��wS��o������a�|��~�x���V0	����yp�!7��d�
*��-������}�'4���������n�@�Ls�u���8iU�8�D���S��7�����wLr�3������C��1d�K�7�PM*�S#���9O�
���oV7�}�^����(hb2J�iM�&i���V$�5����cx����m�y��?���b����h���`�p7?	ADc�d��pA���*�7%���$�,��|�6P�������n[���H[����^O����n��S�Z�O���k��i�T���D�H6cY��������!I��R��
)^Y1���C�z��e�Z��Sz�vR���OG�VfkP$��s����N�o���Nd�#�����w7sl]dU�7��;��<��l'���[�0�P�.���$g�kUr9
r���y����59O��e�����cf��-�n�������w��<R>o�e�O�����b����~}U7eW�3X�Tp��;�)Y�A]<qC}���xj�gI�X��e��Q�\C1
�S���+�si"�/�`����r��x�!���$Bm����Sz�G^a�f)0�i@$���)��
\����h��&(Re�8��^r^x8h��i�
�y?����Wy�����g�k2��T����5��_����tM&A��BZ'�/D�����S�Hm�rXP������(Y}"!�.���l���EQf��D��t7������E��I�J��A��C.?5mw�%��>� h�	�����Y�cxA��'zHr�y04�@dS�
�B<V����{�J���c@���@�\�\��4��
2t�b�
��q���w��K*3�<������M�E4��zQ�]��7?���{;�W5���X����|^��q�V�<���:�����w4R��	�F���IPAb�rF������[������<tX=�enY\q���������y
�_�r��W��������uno���������P
����"Xt�7�t5P4\�������c��`�Rq0������������&���O5OVSf����I�MNE��G*��ll��S��-Z#��.O�hrR��4�N!��t���Q����BZ&-��A_~�F���~�!�WR0�����8t��*)C�Z����U��j�r�E:�_���W���u{~�r���yC�^;�������6�r��;��^�����[�K*��M�E����(���.x�_s�*�����c�e�5��z�d�w6�H����r0L0�
�8�J� �"#�4��M�'V
Y��X{39�@��
Iz<�(
|�0�J7��%u��������r+����0�M��������e{�������,W�+I@,8g�,�'A��$�I6���%h��M��z��I6��aS�p��j^����b&l���:1�������$��y�P�,�H�lC�
c�h�k/�����^~����`=Bi��*+1RbzR\:)�^4����S�-���H�h`�Z�m�����(G�����vY��FwB��h�wDO��(-	.��[@��yG��,k%0���b�t�p�DR�|zDyn19	9zG��eL��o���X�z:;@�T��>�H�s3U�����a�"{��w�9������@T�@o��(�4f���P�b�6�������6����O�?�����7K��^��
e����&^�����Hr���4z�9���VF[�NK%��QQNr~7�k$���kY�o2a��bNB�C��������z�Z���O0�1����>�a�� N##�NP�x����Y�(C��R�
���,Z��S�r%�2Cm�|(?�d&^��S);3�o`u��IV
[�$�i�5( I�6�E�b�6��nJ��	��v��@�������)����W2�,���Q>�� &	��Dj���1���1'��q1x�/;�a7&�7��jZ�P(3��
_�����A�\���)��:�"��+4�)�:k�Y��_�y���L����5�
������c�]�S��K6�L��L�7�36���C��I��2��b���gXf�A��"�s����t���I���SO�����7W�� ��A�����������f�Q��$N��h[���������{��M��=�l���
#\#y�@��-�P�����y��������S�E�=$��g�
mXh�|�a���y�p=9��$��.z�-���Lr�t�M���r�vPfy����x/'��0�Qf0��������Z�Q���wCn[�DM����*�frzj]�-��k����_u�[�.�W;V�=���s&��I��(�3�����'�����K:�hh7)@������3z=vj,_�}K)�&^�k����"�F�PhS���O_���� U��V���3�������-������L5��Ym&s��:� �\<eq���es[=D^f��q��C�pz�$�~��:��r�y;�h���6�;{�|�f7�|v���M��'G�����|��'�1��r�@a}Q��Z������n����Q�$��%�@���G��'��9P��.e���'�C�;s�#(�d��A�3F}f�)=
�����f���m=�Vj'v#Mw����������n4k&H��p��p��mrh�p ��~ ���[�m������'��1j�Q����9�A5S����K���m�3����s[�� i}Z�����4�)�4S�a�M�M���b�C
�+�-W_PY�i��&<�4��+5� �ir�\���DD�'H������������������q=S`QxNC���E���CHZJ3���e-�arC�t��iW���uJ��#Z�a�7���N���=���s;��H�W����6������
��%T�Sk>���+�n��j��%1k:���Z������oLRy�R
Y9�.��yr��`�K�,�������\�c��� f;{g��?�b��-���542���K���w�,gg�x�����\u2�g;/_-�UX4-@������EV������aL�`�d�[tX��x�@Q���K�4���b6����}^����e���3���"���O:��a8F��?*��p��I~��G-k=���RO�OO��>��+��p�f'v��=<������a
w������_�|��5q������k��
��
�O����y9p%wp��.6n�s�V*���tJ�-�1�& m��������z�S�x]��>O��	�l���v�������7���%���D�u:S�D^o��@�q������r Y����4�����7V��V��I���������EZ=�/���	�������#���Y	��r�/�KZ�f���Y��W���t|���81��J��DN�m�5-&hN�&�����T �m�����;�����XC6LA�����9*�1.��9����	�E�����L�<���@�}]1�S!�+I��)#G�IKN��b��a�2����E��V�?R_e^�1N�m���
@T�����pzg�(��=���1�9e���5z��gl���vXp0hQw����6UY�����!+3��`<��j���%������R��J�)�����<�Nb�fGf�Re�
h�&Kv��Yir�{,���Lr6g=
kY:A�0�\�.5�L3��XO'�%0�
S��UQ�a���P�g��OQ.�T�
�b:��%���� �����y%�X�FN>����j=��S|�@E@�d��<�e��i��Z��$���
�������]�h����������Y�A��k`� +]T��`�����}���"��_S.����h�c����N��iKZ�@��n�����}~���/��d�h��p���\��^S8�� W	������/w~�;x���::�|�`�Udn\`	�i6K�f9S�.b�9��3�����9�O����G���dh��k.\h�b5��*��bJ����IT�dv�����q���o�x*%�������Z�K`0�n�������jd?��UM���U�T�
���Q���pCVO���G�����F#���9�<��m������5�3�&!�L���8E�L�'���x ���$q���O�I����+�^���$���Z��0��J h]P�{+�|)��~e�)�d6�O���,��|�g�8�H��z�$�"���N8 �R���(~)�T�PE���+w!.A�Ms�o�+c��e?�{������������|����P���w�N�+��|F�
{-�C�U= ��t���c�n�J������y�N���7�F�?Y1�r9Bn)������TP�Zo����6[Vl$h
\��2�h��~��h �i2,�q����D���-q�8������y�Eb�3�b���A�{: *R��-��?��a���D5_H��I��Yf���\�0�@zZ�i����0u�@=x�lY�k�D��y�5�g���'�W-�j�X�']f��|�u��$S���2�)jF�����������+�<}���8�����\�f9: 3��m�,�G�����W���O+c�D��myz���Dw�D{��i@<�G�h6�oX{�
'�#`�����F�:4�&i%�H�z�.��\���A.��SpG�2��UL2X��E���[����#��xSU�Qb��c������y�\�	Y��:��:9�c��<8����������a�k������L�*i}:@c�>I����Vhp�q�.����Nb������;����t@�P"�c ��2;������SA�N����bp��h���_��4:��X
�������-I�	��,(R����)|��OL�d!��"(:�h^�'�;m�������D�z��G���Xn�*KU�d�qN��;����Y"�d���	���@��S���d��F����G7m��g/w^�Z!msz��f��|��������p���O��������}�<:�bE���g!�������ccp5����W��E�����=z�x�_����4�Uf���,ZY5�d�m��%F�!_;
C]!{i���P��(7����W��C�'Ae��B������
��)jd��&s���IFfN)���
c.^���Z���N��9��ess��-��moL����q�I����ae{�I]�o�|s����[1��b�p��i�ihL�i�
���OrVe�(�����,B�������
���/W�����������P����O~�����98�	O�iK�>�5r��
�7��m�8-�qHOD|v�v�sx�::Vs�v�E&s��.�;n��m�V�8U����q�Y��x
�o��^��S�#|�%���(�9�N�e��.�l�2���-�po�6�b��J���9A������ow�&�NA_O��o5E��1F���,Z��v�4sO�Q�0N�IX(�������r{v���@Ky@�PO�	5���*���2�$A%�`�q^L�����\�y��rH4Z���+�.��ND���[S�{�������g��]}l��D�,5�q�bA��O>]n�d���$���-�����$-��N�$�b��Ie����D�����P��ef�QF��lU�����,����{�

�e�|�Yg>h����lV������C�iV`&~����\�4����,�+�����5��2�8����#���*��nS�Xw0����f��x���@)h2���9(�6���*6����iE�iV�sq2,8���6Es��f���7R�������UVZZI���!%)z}�/�i��Q�{H>����2�6��[v�;��_��P���eI�%���&�8�� 
�a��Q����?EtQJw��|hHq�F.)Z��/J�-6aU� ��CFZ������{���f�@d?".����+9�=d�P��NGL"��%)���)�6�y��5�d�L��("MDx��sy���V�E��Nb�c�J�%�l�(b��K�J~D|~>f�1�]"��hD�wT�����E��$�+��;C���
��.����S�3G9fp��4}Z�n[:����c�G�������CjhQ��`!h}��������_��@!Wf�����Z��~��{�%A�qVH���c�/S��������AhI��&I<�k��C��-O^1�u������fb	�����G;&���D���a��y���Lt��JGb��������q^{�qS�����"�(�T��{���9Sc���&6���z`ye�����8���w��T�Lx�VR��G,
/*����<��)	u<E�/����Y�s�{�e��;|��
6T����Q_T��A�}��Q"��7R�
�K��G�
��Q$kVs�J-�
��9�x+8�4[�X�A1��n�!����{8m.�S � �����Za�W�N3�XE���(�:D�k���;����h��Y1��Y����h��p��������YC���d�q�>3���'�;�i��Eo&^��U�������#zS� �:Q/�l5Mm�B%����1�F���|=Z���5����������q\m`	��f%[���>t���Tx����rj���P���R���YSG�^�-����Ae������G��/PCOM1\$�2��E9�L��F�~PP���T�j�W��{�(j t�V'�/.���,!YsP����9��3L7r��C���^�*{`��'��8������a!��lT\�@r>q��m�Cr3����1�g���5Db�no�syX�����;k��0/������w��t��qz���x�M����_���qs|v��p�����MOZnO���5�?��$�����i�!��}w�]��1Df
4���~y����/���!����sZv\��`�(�VA"�}��F�Av����
�coM�[�W&�
�H&�*��a6���?����Q�l"6<lT��9�qL��������ZPs����cq��F������r�=A�]���=d��*�I�V��z|��2k�?|x8�8r����yn�0.n�;Iw���&�5DM�-�=�
n<�����r�p��r���
!�61�&���e���D��c�`5I�^������2'������1��$���s�������W��@��
��(�a/�_Tc�6���l���g�U+���+?EY�X�����H��BiY0
�Vm�U�a��bam��U#�:�W��*"5��bq���-hRc��2X6��N����K���|���C��+�e��'1����D��.�K��>�# 
j&���1[E��X���6��P��m4^��PM����P����,3�8���ll�%�6�>��m��f������w�j�3���(6�[�k�u��z���g�Jh�a4RLe Se�*�_�e��7a���N�N���B�9����.D/���W��~����[6UL�\L��c4d���h�����tOvd�����g��z����S��a�.t���_����}�+��"��q$:";��#���g�C�cf��r����ht�A^�R�����ZRNo_7����y�}��U��_�����>�C��{��)�����K�}(����"<�S�n��)M�,�qxWkM�
��b`�!��OF��Qz��5����FT���&��f��!�h�ic#p/ �!#`�g)d��X�a�P����Yv����l������;!�x��Y�x��@���QH���G���b��.�����]��Z�W�7�������3���d�M�'�?,���$��P�����R��
a���IX�@��AB���t��v4���]�u^�F����;��`����DX���x�H1��NBdD���#R��n���C28����u��W3|��8�7X�����m5��G)�)������Xd��Tb3��'>\��q��-���fi��]��m���������-���S_{�+�`����)�r�~���0��m�o~lon�bN��6J.��{YW6����7������+��?���7x���/�u������gh���t #�H<����Ll�/��ik-�RC���\�t5M�\�_�Y�4��U�fW��.X�rw�^�Ocu�����?v��s1�D-%x/LDt�����T�����G6 �=�&{��gbm7�e��p�$H�?�%����W�=77���e1��X���S��A�_�3\�<@�j}��{|��=Y�;)Pk0�15����8�]�E���w:����.V_Kb�xY��$��+4�Y��3=��Gy!X�=�&Y�X�_f���6�
z�w������c��Q1�����eL-m�~t����O)��Y���G/�=�M�H��lk)�JAZ����<���=0�oJwz����P}?BP�poR���>�p�L���������ig����1��pTt�[�m?>�b��=�;������Q�UK����A��`��	Li-0�����U�����$��n�,�x��G*�����Q���,��\h���
 ��\7�x��L�XR���$Up��3/�`��-c��W���(�81j�D+PL���x%��H).�����{�q��7	����y��Z��F-$�X���T�����G�����3�e�&�>�+�l�r"����tW�p>�����$vNDA��!�'����d��\A��"q�<�O�����$
NyMq&t��A��x)�t����,'e�����}dF�N^��+5#<���k����T�(1��$��([����>=�\!�6���zP���R�L��_F��������Sb�������:HS�
��f�������f��0t�'��q�b����'kP�Q3�:z��!����!3����2.�@��M<x���
l-��HF� ��K��x�b>����G���������B�4��A~qf5^kc�+�'��,'Y:���]�>Q\0��IL������y���V�j]��9�����E����g�pw8jn��\��y3�����k��������v��Q�W$.3�d9kMb#?�k���r�s�D�����4�!7���@8#wg�����=�1�7������TiDe�-���C�w"Sl�ek�3�=�W��&�9��e�QtO��E������	�������q/X�������BR��v���0�c|o�
��3��/��m�#���s)F)�"�2�t^��j�k��"�)��(�7A����N��\��1Y1wR�=>�;�h��.��_E@�`z����Z6��u+�o�e�#�r�h{��������S,�4&_���Jg���3�O�����~�!�?��e?��[�"(����euH4tW�i�}g�-�zH�����z����N$��F@7j����B����Zo8y)�������F%��vW�y�G	~�88>�9;8>�9=�1*�>�\�����T�m!���]��<��H|�����b�}Cz�[��<��M��88Y��45`1e�]�0�[�Vr�����y����8��������i���o�%�QdevT_y{��l��!� #KGY����G��kl��HP��7��GV�]i���cV�]�-H*����D�����t�a��=4B�CY�����r����sY
���%�Sxp�����������3M�\F����_�)0�$� V���&PPu��{tp�m#�<%��e��,�~���37��G�3F����t�����o����������L�OI��f����&1�G������`��Og�5�_��,���'��k�&�T����VjgOg�����:�������@�����m��Q[�kZ6�����t�M*�*��K��a*���Y�8�=Y���
<��;��4��T��v�M(�O���b�e��/�B'������O���?:^7P=�����U������������X���;�
QiG���X���
,���[��2�Y���n���#��l��Sm�eA�����xz���=<�v�[n:!,6�����)D���������e��xx��n�p^?�.8�+T�m�j��vd�h�3��x�sr��q��=6-���@�{��Y��2~h��3��]R?~4�}R\M������&�<�6�N(8<U�l����m��E���'�p�+����`2��$s�[�����8��A�"���/����MM���`.h�j8�H?M�*k�Q����LU:IdR*2w��|u���Ea�,���9xd2����yU�����M�tq��T�����J�i���48�5�>^�O""��n#������[cVh���6��X�����/�U�����#������y�x(����g�����_�����
����fw����"XYq�U+�/m���e��0
;]����B�5@����^N.XYL��_�����{j��*('ax$;��������E1c� �Yv����J�}�r�sx�>��[CM	H[���Jn����n�;��f�c7�u���_����n��{�w���q�u��^uw��z�w������F))�z��O���Z|[��q�H�� �Z�����;y���_���cY��@���e�8�JR�/���#���8�y6��������*����Oqu79Kt���j�F���J=E���f[�~':J�������Sfi��J�Q�`�]�	����H�����=��L-������K���_���n�c�:�o�-����(]��w��td���I���
7+���g$��*�����.hgp�b��;[[�O�7�3u{f���E�{o�+`��I�{����qU��������qF�����8@����~r�J�{�`����������fks3|���������'O����{h�����=z�����'���~��Q��[	|��JH��*YV-���M��
��
�����(��������2d?I��h�{e2�����a���^wC�{m6u&����[�%
��VA�4a<�����u_gh%$>��$�NF?�L'@��A��$��r6�l%Y�)c_W.1�B��(ZBF�����7R!�<ls	�px�F��
��!f��v�7�z
/�Q��m�������.��@z��E�������|����Ew>��%������y��yF1�l?�9�d�jz�������m��
,��t��|��:(_~�M��SsP����G�����p��92�#��U����k�k�������W���nn>�������xEJOK�G��'~���o;_'��_��i8��IFV�-��y����5�0y���/^�,��/�%w���SOZ��TA�������l��]�3���X�����<9W��L�0�YL�$Xq����61��c�si�����6��W_<�]��2tz&B�l6�,3�}�6
���`N���>g��qaQ�j�E����h�?�h��8D ���/�v�>��|����r�����n�L����$���b����PX����xT@B��OSUIp�f�������{�ntH�$GH>�4]��CK�c���\���n�5�
��z�d���l�c�Y���-����o���y>�����\���/)���>�<�P�t��z������L�&c��_v��@,��+�c],�x������t*��%�A�!��
8p�1Z�m���������%�;��p�J>?\29���������r6|�G!b��]+�[8���G�CYb8p����0+�ql c���et���� ������m�$��
*�
a����[#3�[_�������M��M>��n�������c����$�Pfcc�����[���!�Aa����b��wk�1��#���A�G�l����Vj�cquh|
P�<I9e]�L�$�u�7�v5V���xA��7[O:[[�o��:���K������y���P�]���=�g��D&�?C
x�0��dZ�������ae>�no��@���
���A�Wv]3A�����b��c��h>��s�������`�	�=�/e����\)69:i�@�M&=���>�� �K��B��g��e�D�S�y|���p~��y��d���d[��v9�d��N�`s���7��m�-�'��Y�V��]?k�3%*qe�����8�b�k�|����|�ds�������'�o��ZQ�����R�%QB"����o�2���`��'j�n��{R`HA>'������s6 r�K��l�/1�V�(�g�Q�Yy���_%:������~;������O�l}�������B�qT��Y���f�bJ�=|,��$d��-I&^Orz�]��]�sQN��w�I����/@/����6do������.�J�'L��d�q��;�V8���f�������ON�_����w���L��(�x�����v
�J��p��9�N�#f��d�M*�����bL�;�� Z��
��2�;��xL��>��_�QN�^������ n<U$�������5^��CK����3pN�r��o��5��k�{�Q���[ErHpgx�^�j
����L�Y���iE�31�&p�0������'��s�Km�&N	��������s������e~�sT�uxTBT2>�����b�N�tt�N��('�����Vc����* c�_J�XR�l��C`��{���
l�7��������$�~>N�|����o�$��4��S������2���d��%{��I���S�|��.�����vR@b�[��.��+�������vJ�n��c\bq�FX` 6�t?�TLV�'���qN�����zX�f&�1���/aO��S�����}�
����;������?�V���D�,^�%J�#��i��'_%��*��� �&��7>��7��#��R����x-���yS~Sc	7��g
�`2������E��j�F-;��2�n_s��EF�m���fJ��y��
p(&
�
3�Gz�Fq�h�rj���������bEa���j�Xo���7f�qT����[��|�q+��3�����1ii�M6a���5����� ��!LZ��wp��{���|8T�!��&��06$������
����Gg�����w�Qq���Vp����=0-����Id&>���\���iV~���r�l���8���U�G��8*uD������L
3)fl��pe�mKv^�,{7���M�3��C��������l��h���`G�m��d/�`L��I~�:c�gY5k�L�|�f�]������k	'��(�3-�5�^�m�z��`Y���0�]�I�E
�Iq�����>��m�G��-�G�����Xw�z����g�u3��g��v�����Ym�_���8@�0�������aB_�nX���e�0��Z���A�����l���^�����&l��~vV��7|���ai�7��B��d�+�Wepm��
_q��u�Z���XwD�N�Qz���_��;5�������-���w<�f<�����Za���L�qw<l����'�V�4T�A���W�.���{�0��:��$��4��;�g����)��~(�@���}V)���o���#�����i���5A�R�m��,�p�D�����FQ~��� 8�S(��e%���8:_��G&�������ev-���8���h�
��yJ+� Nj��<
������7��;4����������H��*9���Yr~��'������$iL�������6��f�SRHmr��%BW���.E{�Hs�Y:�l���\��� ��BT#�kz��@T*3�c��r���j��3���'u����}U�����j���1�K�s�R&r��	��`+i��~@@��p>��QG��EXD!Y��]w����B�y�����|��t>�;���q���$��pJYL���L����C
�	?�M0�n��Q���g��`�����2�(��=S�{J�a� 4z1��gjTy-"��gB/�Y
H���8>#��Y��K>q����u�PhN��aK�����mz��{
H=���j|st����]%�������_�L�;ze����L)t.>�Q/6����#���G�-����}���=��<~W2�v���������&��#L}
x������I�h�h��u�&�V��O_���g���"�a��u������S�
�	�]�c���\oI�!��%
��yF��b��4����Y	�������
`�QN��a(A<�h'sc��s��v���d�SI\Z�f���ca��9�\����i�|
����"Vk.g�~C��CSot��������)>��c4e����ul����c������/N��hQh���{���'v 
�I#t��p�Vc�K���On
&H�{��M�����.&�
�x�g�k���]��?��1I���������	����_T*i�L�T�JH��/w~�;x�����1i�0	��[����2�B���wo�O��E���8�Hc?>9V[U,���`�8��7��N���
b�g��/pL��p$s���Mj��Q���
[�������X��1�8`Aw�=-�z�v��jZ��R��f|7�
��&?y�}�;�3
�2�k�_��x���wx�������������D
�,d�G��&+�����6%	��	D��w��M����P����<y�lu�\�zn�������O���s��*)��hn��xc��g���������+���_r����?��G*(.�;��.ZQwk-\\��-��6�Lg~���hg$������B*��V	��.�1�7&��8����L� ��[�]�u`o>��a|()��b�0�2~~u��`����#�*���r������sl|�V?��A�;��������'��QpB�����o|D��h���������m�Am@�u��F����im<�������@.T�|B�+A�B7��1��0�]�D/.]�M0��N����V�h
�p���<�0����92��"�Zn��p��C�Q�'�����svM�1�� �%�IrJp�1�JS��j[�|f��`�X����=�
�fU6Bk������-&��MBa�m��gi��$�\��y1���_KA`���<#M g]`�r�%��[�G������I�q+r���8>G�jb5n��~F
��g�)�3�SfnYef45�;���h����V����M�>��bI)�P�
�T�?M��kr,fr#r���G���?%}��87
��N��[�1��j{gug���7��xZ���"O��w�!@��)��Uz������)e�!�h�0���MFD��\�o��0�`��c�2d�iPB�	��NK2Bg������&�����5���:u|��i�������������LJv����{�C����JYaP���q�~t�����^h��$0�+rs/\.�z*o-$��wc�8������rC7T���T�\1jH�n]�A3�!�n
*N��P
L����0�T�?U���]
�e@����
B*�]��V���H�,��|��)�����@�H�+�h�C��z������I��#%z�U���I�����%9
�I�M�)�m���c��0"/�I����'���#�RB�8-F����/0�M��^�\N�@�I�9h�:��d�����z��� �
�Q�H������%��'��W�]��
��u�y�{���)���"����z%���v��
p:������V�KGmn�����A\���D�.F:��n_Uf�D�����9>��SgS��M��Q�����d �;���_r��;�������� ��Q����]��F�x�5�w��{���o�o����{����k���e�X����k��Y~�A�(�0?u
j�r�"Y�P������p�s:�2y�\�y����u.]��5.a��d�-o	���*+c���_��Kw~�����Gz4�)��y��Q�%E����
B���F�"�'���$��I�����7����&K�R*b��B�R�����	�	�������E��e�NY��4?�q{����]�����v5�b��)�q��`��t��G'	l��V;���v}�/�mR_�����\����-(�-���A��N������b�J�UJ"�����i����<m��D����
V]��1�H���{�i5~�O����*s����QC����8��w�"�����~���G�S�����~��XC���[�@_o%�-:H"KId���
(e*�t��A%���}�BF1f|���_yQpT�CAo�&s���->�9��;D�+�����YPn5�����?�����u��?+b`���AA��{3LM�g/FY�,��b�G6� r�*�T�d�����}2*+��\��A3Q`JH�K���J���gY�/�����0��6��l�Z����UucM�%��qf%`�#�����_s�����L�+�]�N��q�	��6$��l%�?s������^��w�e>�gc	�n�t(3�E���	��gNB^�1�}�iF�6���T�9�5),���
���Oz?��<��2G�0/<0�$+��Y/4����z��e}������>[>�D.��5�F��G{���`xX�3@M��`.��]R�
n�0�2��-=�)Q�!�������O+Ef,"�����~-,&��$���i�isi��p��`,P��|�0�n&�^s|���%I�j�v����b^I����XSu.&`�t��R>�@>$dEI�l���D�2/�}�����rGc�P�!
(b^��1v
S+���@���2����q_dXMp� ��5d��e*�*p�s�VJ����^���1�Kk$�����-�=��b����oY�EH:�����lM	]��a��E��@(p����X���f����(�o�,BY�;(���y����rW�~��b��C�	�qs�U��@�pY�
�'���z��H���l,k��S|L���E��q.���p�1�����b(3�B�s~�M��N0��;�����a��m6���xq1G�t���ib$����A��W#.���y{&�
u#�B�����������|G9���T��
�@4�g��=mX��lV��:VA�����
!��^������pi���b�.�Q�=6��P�!������W�f>X�%g��/F�1�E���RbR��f 3%l�>��A��t�~'z{�J�t$��w��C;B>����{Ot>��F�+i�&{��j[Q������������������;g�?E,����x����a���������/AMh��V|�8l<�g�/��}_�$����n
@mcu���\0���� y��U�F���r����ad9����H�[��F(��^�t
S�"Ph*���K�:��8�fq�������cr�wz�?��X����9�hm�>t���r�8H���J}�-�#!�oy��
���.�GR�����D[f�'&�2bbz��
=�6s��#���S�D�g9U.%��@�������E���o8������s\k�N�h��n��8�%�&jc#;w�:�3�����j��u�����}�����
�:>��dC���Sk'-���mZ�<�
uyAu��5N��4!�~��s������8��w�o�o��y���)K�T�V���A���c�?��r�,��*�����a��(��C%s���1����/�>w�c�0rFB�#�)s��Q�E��:�(���)��T�aT������UU���[fC	���@���Z�n���s��>���a0�iX�
�����{��N�F��]`���X���
����A����h�,RP_��}�H���/���	�K6"��k�m��������6n�R�z��nf�����U�8<�u��Ey��C�-�6�M��
R �]��"�:��`[w�@�����KV����Y������t�����U����t��	s??���Y@U���Az!c�>�FaJ���)a�����l���j���^����FY��ZOv#���2� �*Cd���"�(���%1�i6�V���@��0��a�A$��
|.Al������%/��W�����J�j���~��������8�w����R��@Ip����i�P�r�k?�>�+k�^�������n X�2�'O�K��{7����|L�=|jG,!�s�1��';Pm�;|rY�#sq�N(���4]O�}��>����f�'lJ*Y\D8�v>������Jm����"��%�Y���:�@E��OXU�'�t���k�E���*���rp����z0v�haOb#�fbP�)��3�y���
��]�P}
4�
����`P����t#JK��>�_���88����������T��{atpd\5�w,=y��/�_��+�GU���1&�?�y��N�C(�0w����M��$.�g/��K���������/(�6
X8���_����h$N�
q��^���[>��V�������_��
��a"�����v��R����]��]r�@[������i����I��J7M�e��h��d~D8�����=M�3���-�w�:+$�n�>�
��:Dd������/��V~�����~/7�p�������|1�+1h�O���cu?K������*���2��&�hN�����~2
�!J�{C4�~f\��Ju������������R���7rnwv`��������E��L>�0w��8����g`K�����5�	�(Q��$�P��S J;I��<�2�R���OCkR�Zm���=���'�!��A-�������:�{�������1:�l���t��P�d�'�����cS�����Yd�x}��(:���](�!�����o$���Z�(���B��l�_����F����\n�<M8��o'A�-z�>�r��2��hlT���n��ILB8�9��r,�4�@�G�Wm���(3�]F��Jyv�J�������J�VT���U�y��U�e��%v�o�U���	z���/�+,�a� �R)�����S��R��	�B�+i����1j*-�R"X^C��H�&�K4x�_����e��Ir�����Ny�yQ����Mhh'����������I�����&0���D�72@4��-������s'g��;k�A4��mt�V]!O�1:����f�V�H�L�	��5�G��4�����97�O��+[p�
��`��O�d���'p�KvVW|��9�u�0�u��?4�$).p.=]�u�^3��������<-I�i�0�/%��H�-.b�d�7�.0(�;�>�Ig-�]������'�2�L�C)���I}��C^��t������c�f�E�[R�.��2���&����<��
X���Gl���]yb�
%`����������Z^]��T�����O3�����k�>KbW�0�)���G�g��#D�1;[� �f�"��w���~����m����o�W�u��'�M�|�<$O9W�E����E�7�]����^p3��E -,G��{���F���j���V{�Z���H^���:���"��qP���K�������0�[�*��h�:�;�t$<D ����e�E"=J��I��I�"N��E\q�!5�sTe��4�5�������'����W�'g*����A�]��������$m�������)���&F�5��5�������������m���\}�N-��ibQ�Bp!'c��E�d>��!P�`.�	��&���7�b�[�>��,������I��������4).]���d�����h�,yqr��5}Y]�0P�<>��J2�68�	������6H��d~q�cE'D��W�	�O��A���Y��)��j��8s�������*������-b��q�0��(���T��[�|b�8�%grJ��#��}\Q�M���������S;Q�U��h���%M�2$&XCX���/46�IZY�y:x� WZN��G������Bq��~o�;�s�5`b���������@�H��Q�'
D�.b��b:K����HbZ��^d��
����a|!��o��~��M���Q������LY_d����0��U�Rk5c��Df��-��1�hS(�Mh��`� o.��f�vU�v�PK��.���V������p��u.~mq}�F�_���e=�7�������n����P�$���/I��N���������_�(~1���
���m�eQUd���v��eH��O������Bu!��?��/�ZPV9t�>F�yY�UQ]O���e���kU��������7�
�e2A4��Z�H�(���Ha;���ih	`H	M"�*;�n���s�M;D�c�����@9�qU�r��� ���R���[_���(����7EI�&�&Z�
�f� O�9�g�7���S7��~��/���,��jWo�����we=�
u���y����"��x��[��rSz]B�c��lU��q	���f���2�
�l`F����-����Ia�	Jn�"���f�C5���YDaS��g��{��C�]��#H�qk���O�����Wz>8����0��@������{S	�Jd�;��� �
=���#;��8
y��hi������g<���D���9�h�fF��L�X$��~h������^�E��$�~��Ok�,��3�z���	.qH�@����	5�mX�=	:�O��&Rr�QlO��!�}�� x�XW�"�0*�b��3�UI��9#�	M�F���"L)oi�g�C6�mSa��j>6�Z��$�[�1��;'Y
�����a�-�{�`wy�p�%Z\�N���dty�}��UW���r���0^�.S�u���L��#Ux���"�/�7��/6���n�j�\�
�}�Z�#g=��<H�Al!����������<fd�x~88{���/�~�
[�&�&{�w�$O�K��I�G��l+�<�'�1,#$<<:�{��h8]-��YfG�������x}�G>�6�C���� �|h���F�e��WZ1�%���5z�`�An{���6��I�*�M�V��I1�P��B���-�:��Z���lC�i�W�4,��:���$.���C�C���D��@��{Z"P���Pe*$� 4����r�\��"������ig%r>�n��>�����s�&���pd���#'l�"Q����n����$c������4�]p���>��e?��[r��`�U9�WH?�����wL�6T=�z@Q�m=y�-�;a#��� 0pw�/�5(�X?~T��'�2��;D��E�mf:�2�N�bZ�X�S�V�����Dhz�aR����#�\��	�6�1w��Oh�F����t��������U]�+	�L�x�N�+{�(��Sh^�f��+)��vG���{��I`Xo:�Y7y�8�Y+��q�LB������R���J�3���0�B`�xV���{��+�Y�����0�
��A�3j��V��f��YD?�������YY�����R� ��������7����-�2'5HBoB�%�H�d��OA���D-W�tuY��s_���F��df�����3�X(���&�����)�������o��P����bJ+�WK�l���G�����
�c���!�d�k��������	��W��S�����\�i�v&�.q��h���]%rf4�mT
�f#��t��+�@���R3�D2siF�l#�������"V@�,��Lv�*�
���� �8�Ls'�l���T�m(6A���������bi�@��U��f�J����2�WD�����%t������P3y�B/��o��K^F��}�=>�`3��+����H�����Q`V���=���;�����;���%a��A��[�w��DDe�!.-U/�!Q3�O>�>�+6\np2���L�&Z��+Z4:6�����{��f�M3�oE�����[����Q��B��:�p����D�'���K���Q��-c*k���p�PV���r��L�������=��G�R�)}��*u$��d���ik�
	!y���Xc�������J#�����I�p&)fd����G��W����Y��������Q���de�������?b2� ������}���v�9���S��������
tO".��a���Mw������� ���� ��=��:�K@����C��C�#g�C��
��w��R<���K�>�7�y�H��#{@q�L��9����(�Y�'�����k���K��K�=�����6-��Q}��RjM-��:��yw�qAC���s�?E�'��V�N��m]f���|�g���1�l�T[@�Ff�)��!4�kX�B�F��1[��X��Y�c����:����:�x�P�U��9Q:s�e�Lm7�C�'�*n8�t�S��[���Y,_r����S�l����.x�l.A��I�[.LH�<�Gc%�R���T���i����<��o8�k�����'w�{q�WL�0�F�d: �I���C�Q��i��)P[(z�wi&�����E�2?W�I�$��2"B$p�$��;3i�3��S��1�$'���'{���J�U��G9��N�L����p����yxf�������`��������jN��W+t��?>�S�����Q�y+vx����Id�-����b0�P��&�HO��m�'��!�U�5r�KX:��:>���gE-Z��G�
40Y<�s~A)m�@��}�,��6��p&��w~��g�	�~#	��+E53��7)�i�>'y��r!���������{Q
�B����Q�-l\���.��)�T�����Z�;�#f�WOz��'y�I�����y0��Qn1Z����q���������q�~h�Cz@m������sw�{��zMg�z��:�Z���h��|��t}8�,�����*O���L���'�Gn*�v���i���XdCj������<�n��.tR�AB����o�[[�'�~N����h�M~.����l�Z&3�'ggA�����<�,�)�N�����W9�(����)h5k%�	?�"�gp�
���q�+P��FpC����fN�:-s�J|=�>��i�Yw4���lD58��s����ntJ7�����s�K
�N�#i�md�
�G�K��Y>��@#���� t�g�����z��P"��V���]��Rf�9���%H�)��5�W bX�Og~�>����inu�S��I�"#�|�F'�����k�B�AY�������K
�����L�N����1O�q��4U���^BI����F�|
T�����rv����B��I��9:��S��OCU}��b���r)|��W�q�� �{����x�v�"s�Iv�f4�>�&��WNY���������:���l���i!�,��J5����K��L�7=��v�y�aM�/�D�O�
+d�I��������v���*����go�)����2�S���^���ZN8N�P�������lXc����v��U\������:��ig!�CU�}OQ�`���Y��\��������!�J\r������;#�r��}�T���'��tk������c���w���H-(42vd{��+�f�'��CE>�����,K�1�c<��Q�-V�Ex�X2��
P��[�Qb��D�)@��#5��b��?�8Iqk>����u�P�,�R��y���s��`�w�D�@�m��w������%�z���
�b�K�`���!���F���hJ����ot�V�A6>�$@T4	5�Y��=��M�D�~ �:\0����iy����0�
!O?�o�J=y��j��$-�����)�3�K����V�k�?����G���|\>����e�K�)��
���_lE��"��M�c#��%����[(.~�7S�E����7�a�a�<����
@��M
z���T���?����+��w2����$8��QF������_�^�������{�?�0jo9����_�z������8#�����8
�&�o�oS�)a�1��%7�j�@xq��Jqt����s����o��&F~10b�Z���+R�^�l��V��S��A�H��X��us{��O�7yhi�/I�������4�B��������oct(��;���t�f4`���
	[)�D�b�ol��m
�`�/'(�����@VZP�L��k6��d�`��F���-�l�rY}$@������k���s������P�����"e�7�����U�����m�����x �)3�$<�NB
?����'d����58y~M��?�_�\BB�{���Y��+>6aDsZ��g��E�s��rK��A�E���5.0��'E�4,��B��}P�U�\�r�9�����I�e�
��a5?7>��#���a�O�#tj_�������X��j0B1~�����3��0|�
�mcC&�\�
���o"�DVF�W��L�T;�L��D�@��nQfy�&����@�kRQ"wn<?<��0h���	���s�� @z-��fG�a�k�����2|�|.���1����xt���O���U�����P-q�|b~����;<�Xj��8�	's��jR���z�K<E����'���[������z
�(�u���	��/&��[�����t��myz���xiw�D{Y��;��
|���q�h�=�;�^�h� ��f����Q�S�[��!y�	��zwQ���T1��7q1�y�w�w7�j=2N\����`,��<a^\��?�����G�`tx�����������$GaxM��YMQMi��U��t�&K|n����B������w�����/i@��[&E&�0bC���V@���
�t��m���>��R�dY������t���?�>�[it�Eq�!����'�B�]&j�
`���"EJ�*'S�D�����B��EPt���"��I�������hN�w�������W��X_;�,7���*Nz�d-;a�}���5K��1���L�Q��~���oB�C����c������V�/E���C�Y��#�xq��u#�����x�v0������7��Z!�������ccp5����W���)�[�n�n�_1<G[�*3�
je�0^Y	=���m��ZswP�'�������g�#E�o@���L�FY|�/j&+��I���p���z�$��p�]���E6A3Y|	T�a��k�Di�kA�u�x��t������V�`o��\�,<�D�9�7� ���L�:��*,������G��S��VE�4���.���� .���������C�?M��
���/W��)����}[qR�����'?�����������%�<�1	����7�6�'[�� 8)���}����������]qa����X�7���y��(�yQ������@����L���`�����l5�S�2,��w@��i�OT;�umK��0%0��brVh�/���0���r����Nb������c��
��/�\����"(�#z�B��G�p��'��K'�$,���)�h�������?��t�C�����MN������n��+��~�]�������T�Nso�N;"��t6�=�I6�0{�����^/�E��%_���4�N��g5u��	0��L�Q����cx������h�;�0#���5�3���)3	F�(����b4M-�_����s� $����#�e��$�����1�*	hVT��B�9e���%`d�(��
���e���A�����Q�����h�k��?����K��.u���B7d-�gY�2���qNq�0t��}�t[�����l���	=A%;H��Q�g~(k�(��9�-S�����$���������s3�8���R6�Q�Y���\6V� m���dI�-oN�j+b�T3ufC�o'y��fSn3����*p���}���aH�����H-�>�ZJ��X�[�>�5��2$��p�.��d
�h�&���h��s�JnX���<�?��]?�pg��r��B+�����Vv��}���S�@^���6�}�xe6Ns���n6��W��;�7������o$����Y��F�I�����w+������vN�{�g�d�F�p������m�����+t��������<����e��B$���|R������O��qvnH�BK���n,L��'Z6��fu��kS�#������#����E}Zh��b�E��9I�������m�
��������6�zv���c�/5�n�& 6t^�����>�p��qa�=�c��|�2}�����i�?j;��d����-����9�Pf����7z���M�,�������t<���%�RA���:�sr^o%�I/��|��/%*Q|�E	E%cE�������]cA��M���|����Y�	v�i��I^���q�)��Z�.�����E�:I:(��r0���u�������;�N%�l<�n��uRQ��U�X�������r]�������
1�4/
����C��&y���sz&��m��+����d�lQ/5%���>��!y$�PA??,�G��z;��.N70�_H���iD�T/h'2��j$�Z6]�Q������W�/A�@�I:���fb��+�O�����	]��:l�!$R|t�X��@]T�H6��?���a6���"��j�j���X���_����>9�?:C]~(���3����]�.��q������%��P�;
�=���������Cc9/	��q1Xy�g;/_-�
U���}<=���	Q��gux��s�-�����E?�;B~i3(����O�Q����IXf�5�?��a��IG����9��Z��:2{-������.�t������N��g����������������w�A�j���?�������~�)�{�����_�0H�_]�����$��E�w�8A��pUK�Y-Xp�����O��^�j��/W�yy�{|��q��q����n[s?-.��Y�������J��g�-*��5���1���y�2��<l�\�nlf����W.�FB�1g��N����3��ycCK���z�����nDo#Oge>�����|hA�Q�\q�����'�1��������2���6�O
�s
��2~�Y��-nIe�}��b���������������w�����#H"��?�	G���
���@���w�z�<�����vt��&�{�bO�g��/���M����q����1������wRE�D(P�/49�Ce�Fb�������Li�3���}�.~�����WL�Z��A2������w2��!����nUFmDv���wu��22���A��M���S��h$���o>��q��G��������>�����l@Dq�2D�:{%$|�l+yp���U< V��4�$��jap$XWT�_�������&'B��JA�H�����!�?X�-S���� V�a��*4^�1	�n�"6e]����(�B�iz��6`�B�\�7��t���
�H�\Bw�1��{e�89>>[�0Y���b�g4���O����Q1Y�����d|����Ay�����tHM	n��u	��2/�j�wjJ�[S��(f�h,k�}w�Y�D�������,E�D��d@Za�����?�I��6�
�����9LV���������k�f.������.�o����g��bT^�E� ��@��w�D;k4���������>���`hc]�S
���I�����*��&���h�6�]�Y�*���C���M]Md���k�������Z_����b{�2�(�6%�����~A%f��1���ZAX�	}u�v��lW0�����+��Y�B���j�4�-~O��v�f+I��D�8|���^��;����d���_���-�@b{� ��iz�b��@U��K��t�����<��%M��Vg��ba�]�f��&"&A����������p��U��6���b��dMFZT.UZ����[^f2���F�;�C�"�����>o�f�xAr�U��>�I}�''�����3�qa�����2���\����j��L�����
�Rj�S�X�l��G��(Vr$��)������x-��!|�s��F�v��@�Q���G�)MQ�BbyO���x���l)Dqx��W�OJ��VP��b"E@���
�\�*�d�]��G�g��E[�7�49{��X�/���')���`�u�"��q-@y�2����A�D�^&�j]P� Z����t�����9���"����F���@������������}.��8����S����tCqh(�w�eBd��#�B�}���U�`W��2�iEV�t���8-�59�G����Q�t��������U�5����y1�����_'����c�<l'E$�17��X��3��*�z�i\������>����n���B.Z�\����8���>cw�%)O�]m�hS����/��3�9e�����1�A`F����;�������w���q�����w��f�o�7cQ��%��%���|G���$~����e��,���gQ�N0YK�+v�^��;���`u�Bd.�|�S���	�O� �@`x�������,B��
���hdk�{6J�P�S�7�q��"sZc>���z����3���a�@r#�$�g��4��(��d���r3^�y��,B���1�������������6�H7���J��.g��Yqjj1���7����]yb�"s�;~-�����p�?p~�FV�,V
��C��Z�?�"c/4��p�z9�
+�}m'���f�W�f��=gL����K�o�	fI}f��wD�����d���XKsK��;`K��'�.W��r�Ei�,^��,�������6K�}��e��F��^������]�����'��]Z��Y5��W���1���Zx��oH��9s��b�@g�
'N�!�}9�� �;��,O\2� 4�U��[S)T��0���$���������X���;-�����]�k�xE!�(��V�����O�{���?����a����5mx���\1?�K�Z�Y��@e�iwVt�z�1"����w���-<��v8��WfC���p�
�z��s���������A'4'��81N~��eo�����d�����B�y�����|p|�svp|�)'���id(�RNU��c%��$�e�m���������}vF�Q�������g�� H
�=Qn�6�U1Q^��z��i�)/!���
�r�HK��x<��,SI����%��E����lL/�Z�<��ss�A8i�q�m��I��Z�!�p V�~��6l
���l|p6%��
B8/�6��l�k|�m��F��`"Hz�g�g<���lQ9�)01M��x���<���~z	��G��]�F�Fw�����d��5���?����o���0{�	fd����W�o��Z�T���K�~v� C�P+R#,����&
A���H��.&a[1p�#�eK������i>j��xJq�������B�3��:�

�0��Y?*��r�E�K=�Y�h}
�$���(��50��ci`��-��l��V����n���N��y�f�����o���	����s�-����`��Q���{�������y���K��y��O=��vJ��3\�I�K���~�v�t:�j���'�1`E�g��@s�������L���������M�}n��kS�`���������������F������M\��x�'�u��U�>S>KR��)6�r��1���n>�<)1��g��"�0��M0D:��J��t��}�!�)�����l�Q!��d)r�C��'��.?d?�	��01CNxsy<�����;y��&`28�L&�j�������hC��?��	KTiFUN[@���H���9�[��y��nO���k�3���I���\�T����x�
"O����r�����$����_%���/<#��JTmXz��9-)��
�������/����h��a$;�T��flQ�w��3��uh��\�UY`y�~���jf���Q�ql"�b8|��T���,2��0�������y,O�@��y��w���/�V��gf���B�yr����/*���&g:�L�~�����n��-�g���QTX��������n�1@��DE����YG�-1E��Y��&�GO���Z��`R	8����y�'�&��5������{����������V���E��X���lI�gx�����3�1j�4I�,����j2Qn-���N/`�7���L��VA�77�<1�fr��D����l���k���V:F�oe��Wq������p=�G�z��U���5cu/���
)�M�Y��#oD4��Rs����PkIl�3J���4(!���������T����%���
�IH��G:����G�&MF[bruVl�tm,��LQ�4S_gf������Y���'��'����G�yA����C�a���������F
��O
N�'Zh�
,g�s*=��	�-�,9����B<Y��3�a"7
<f>��g&��s�`1l��)����}�����JQ����f>T�<�]e������������L���~�`�����D,�����i� �,I��$�N��R��^r�4���N�2�y���(��|��O�<j<&�_]��7�GH�DD��X�5p�_�W�?�������������t�MI��{���(=�Fgd�a�U���������^x]��@�wX4�.`?T�l������Q����5dK�-���[A�I%K���lh#��T���g���Vwr����5^�|���q�c��9�n�b"yd�zS��15�NW�vn�7G)6���^���Is�|Bm-$p���������`���n9q��T�z���c���8y�����W����l����[;�$���I0l+�-�%�_�?`ENK���i�~$&��.k�����tV�]��Iop�|t�;}��|��01����x�]��IZ^w]��������"r��K�:��B��j���U�X�����S:����O@�:�~&�L�����!V{�%{��HI�����#W�>���1c[B��5�������'�T������������f���I�G,��{'�g�O�8u$�PuRU������R^W.1�r����E���?���Y��4+�9��`n�!�mSX������=�F����u0�5>�����-$;����_�w_���,F+��c���C6�8�+�j��zd��*w����a&�]�FE�����1����k�+���,��(��R�Yb�����8E���E��*P���U��X�#W���Ybj��������`�H�\X;2:L��V����1Q�@A�IMdP��U�E!|���7����8`3U|����W@�Fy?��xJ|d�D��3}Q�5^�h����z�����U�z�0`���s�`�(������F��=���Xva��H�|
~ce�-V��q��M�bS����t���3N���i��?
�8����T��N�t��1�����{<��qm��i��������������%g��5���o
����P�V]�}�7��L��E���S��<U��������.pk<��J�HA*
�VJ��BT�>/������dL��f.@b����_�����t2�<�C���)C�M`����H{
g���{��a�8����c�A���������4���P�L�#d�!����b�6>}�������Sv�!o�o~�?�������?�0�
������6Jd����NS���A6��z{"N1�+�p�&��
�$�%��7bc���b�A�-����a�Xu�/f�G�����tk�~��@�d��~Q���\��V�>&U�������Mn]�\Q�!����\����r�]�)a:F8��Y���*�I�!��j���|�S�R����w���:B�dy"���Od.":RE�uHj��H	������5��+o���N���Pg���%� �t���D�a
���SS\��X��Qy'���
���pfn
���Qr
�B���~^R_&m��cChEB�#�=F�����-���E���f�u��d�����-�9G�_O�>]
�~C���|w���l��e�S�D����7�P�h�5���sqQf�N�&������m<�����g2���I�#|"��F����zr�Kz��pd�;&��L��p�a1��w-}�f��&�/zw��/�op�9���@�l+/��x��#���vz��X���e`�n�O�����-�z�>����48�B�n��hZM����H�:���D���&?y�s��(��m/�3y�����d@���m1��<'��y)y
��	"�<X	�	�@�HO-%p�ieV�����2��"'��
� ^XI��Rfp2J�������\�!<`;�3z���r���,�C�������2y2ri�(9�K��$������E^��8{�E����`���I*F�G�A��o]�\�cf4��eSG�������]&�2:�\\P��j~��+F&m�Nz^1n���@z�7���`��[q��
�-`����k�P����1��S���gU���I�TV��8�09��zL@C�!:g
�	K��Q�N�Y�������A6�]�Gu��YzA�:�����sZ�S��-����@��q�pV`X��";>��h,�&�}h������y���.O�+h�6��������g�sh��9��+��\/t{�/�*������VW��|��8�Y��13�-����*���z��y�M������}<JJ|\�I��@�pAR��y@2r�9t����v|�W��q���[-�C�=�����j4�������2����n���y"SF^og�'G+��5���{+-pW�������"pD�mD�����#Em���.w=	�u���nz�~�S��H����#U�g���w(�4Wh���A�#�:���AH��b��%kh��=�Mic�u����}�9n*Q\v�����<���'/�X��-��l��8pP�Q�;��c)������ �PURE�N���TL���m�v_$/�i�o��������7R3��5���5����!-���P->�����O��%��@���+�p�X�O�_�:8��(�����.�qb$�U`
,�&�����/2����z�c�G(�#��]A��Q�"^��N�G��F7~����I�����%�1�G�\�f��u��^O1Wu{�9��R��U���_
+�'v�����I\F���i3=pcG�d� ��,u�����*@�L�f��9�R��\�$�����LJ����8����<	�ed�l����@����}q14����������U��nu6Z�&^�W�/�'�6j1
�lz�����6�����-x��q:����5�;��&~�
�I�<GK�~:G�r����-N,P��
�������u��:|I{��|��qa��o����??����A2z�(�H��
,��~.�vW��.��kO������B�g��}����E6��5���|��b&-�tl�t��w��O�Y	 CQ^n�x����&h�	��cT\�|�x������cI���.&;�5B���	�;(�b�uI��Og)��LT��4���Df��3l����un��:���t~
����U��:�Y'4����
���
�	�7an�U�/�����"��?b[Q�*������h�(��Y���#�������;��i������>�@����]�O��S����Lr�
���������}V�]�<�/�~��'f*����?���L(���)h-��+)L����
O�{�����V!��\�RM�"����e^Um��5��U6v���>o���^����{���m�N�8@z�� ���%�P-G��a�����������=7�e���V{��`�2�-�$�n2<������/�N�Z}�/zE�~2��}(�=s|r7��A��;�����	� ���-���
&�Kt0��o@���������DM�J���_��������W�t���QM����lz2�����8%EI��L�SS�`X�� �f��.33H�yeV%�!��}2�\����#$C����e�O�S����^�����X���U����v+2�*��v��Q��S��&qoy�Es�E����iV�!���Mu�@�s=���P�`#���S�q�������9�ts���N�Y[����W����ca����e�2|�mR���>i���%������s^�����Wa�Lv��R���{����#�]q^,��r1� �=���s|�
<6z+k���8�c^�N���;m�6E�^�hyqC������"7����_q�"s>�OC��*t�5����b�R�!R��D^�����=\���%-��9>i�!�s<��I��5�5�`��<@y��b�z'W���m/Fq��������_ V��
$|�koA�;z�p���.�P9�p%�t-}F|��	���aG��)1_+��?8�y��`�G�Y�g�'=��z? k�\�t�p%Nf�	
g�T�M�����H�k������@#Et���[&FC��5G]O�_� �,��L���pL�'-��������$$�	G[���z1�g �9�K����V���#K��;i��^��m���A���qM�W�3�S��|DI����S>7�PW0SF���.�3t��f5�0�6rH��*�����V�ID��M���`�W�=�e�9B���=�WA�T�@�����.���xq���J�H3���8^8F���qIN���F44��AS�/F� _:bd��-GqC*�0�������U�����s|�8d��+�0�Y��8���*�G�.�����d_83���S�V5}��F�Qh_�u�*����e�b)����
�
�!��i����eK���v��v�7������m���l�*c�5���f��0�)��M���Y`n������:��}�lLZ���a�N�C�^���x`����J��������������(�W�p��BA9��H��d��Bz��������PnBt��#jG������`<�2HX>e�t�h&|zL^����S�:�:KM�B�HDF�����,l��2��������l�[��`�U+�a'���Npq�~�=eo:�Xgh&�y�!U�m�CSK�9U
!b��f'�~��S���De�zvi�N���1��#���0����D*X���_��o�;�HG����x�l;��UE�O��H�Q�$�U>�g�)n�!kW) UI.��I��/��S�^�z�����_6�(�d�O�G�~�����i���9��~�SD��+W@��8�*.V?���"u���<#H��i���Rq�o�,�Z-LR[�?b4n�N�v��a��!8I{Q�����9Ww�x�c$I L�ki�IB�������za��<���pd�.]�Ro=T;���w�i���=9�W]�����&(L�V��@p���F:����u�)�n�+�h[.N�!������+��+	�U��
	��=�aRL3"�y#�b:)�&�|c�}{�����0�N#yD.
Y��/
`Px�f�1�K�^�Y�@t��`*o���L��`*��t���,+��q�i�_�$U��rm@zS�vb��QO����c�����.1��):�Q��(����N�'8�|�-�fa_��_����u}�zTS#5?w����"�e�~�Q6�)��|��L�����
���g&���3F��ILI���)9����R��L�^b�f����	=�~���w����8���p��'(�����eJ5PA1%'�1$o����Z�'�1������\���$l�x	��3�.R(�'4�_���c"�
��8~��&��RS`�>������8'�1��)pb���>(�]Q���K��]�-R�"Ow����U�{����+Z��T�#�2��r[��C	c�vY������e>y���!���<��q�� �.������rUn��
e�]���?���~��	���0�!���x�@���5�EX#��L��s�F<p^���%��R�sK
����R
���P��#W���;�?��v_��	�� ���xJ�X{�G��G{�/���z/^�6%��s�F|=���;mKD�d�8\��
wc�����}�=���.�-i;P��EwW��oN��}�[Y��/'i�;�(*&P��J	������q�-@����%���OM��L�]����5�k ����H��+�q��,HOZ����m��H�i��f���@�����e�O����vh����J-Nl��?��:�������b��n��&baA3��W�F�Z�N�����tR����<<7����	lx�|���"�=omz+[���[#^SR��aX��5��Z��0���n����G#<S~�jJ1@�uV�� �lt�y?�T%�;�Gs�u��Qd�i>��$@�m�~�XB�De�}��Z��)7KR����O��"���(��7@���:f����9#�N�<W�A|��p-��G���*f�������W7��#y���B�����"��~�M��1ip�2e$�Q���{�07�|�!V"���3����'����?u��=Hz�%��9J6{Ir�]�7Q���%�Gh��������z7]�xZ/��@K*0c��D��9^�����q�
��B��������K(u�lj��#�*()(5��'��k����M�J�e1����i��o�����p��t����1<�DReP�e����zr��=����dj>�Q�1�l�Z�]�r�{S��&�J���)i���N�6����XxH���b>JK���sG��L(�5F�����@�cMQ�^b�e�%�T6c����.�f�O�������)��
��������
1���RK��ze����E�dQ�/��`���lF}��["4Fv�N�����(��$�	���)�y�ci��{�]d�I0����0~�c�����h�c9���������aw�?g_�X��i�wR5`7f��	la�zyo�j���l��:
�q�2�Na~�qO&�Amr�������jJ�l<�]�0��|�4�5�X&<s4��b��t�C]P�
�&]������NZ9Z�M~�W����z�7��.BP��bp3�} 6y�I��
�R"=^���L���yf���Y�(J��s��l��.N�.������d8��	�)6Q�����{2�JN�vh� q��d���1�%w�Q�d�7�o�	{��9����&�&����M�1����p���}v�z�����i����i������+��h�;p��?�+��jc1�GQb{�f6��\$p��������Nv��y�����6�t:/1SSG,���G�P�q0#l�
��?36l���i@J�FdC.�	����2�A�tc���M_���r\��d����x���2�67P%	�S��?��
x�d;��(8/x��yJ���bd
6u	�7��#sdK�i���	7-��W,�Ja���5e���E�����.\��(���d~��|~q���f����I{��s8��l��o������qdH����^���������h	f��9���<����"����m���|�o8s:����|w��IM��������3���9K(�&�`z���h�H����>c�jVL5�rY�������?�����l�G%�6����	��dh�}�	�1���m'��01��4�n�Z�x2�_���.��Ho��)���1���!u��P����y��VM;��1��	;L�����0cgPq�V�L���������I���:n�����Q��do];���X���)�&��J���tO�|h[n�P;�?�JV3<6��6?S�%&����T2�\�qt�^�az�T��Y1@>��%��������B"M���
�Z)>Lp@�1V�n�OL�^���������E�n���_,6�.�� /9Y
L���+���Z��$��c"��I�y�����������z+y���7������K:i+>��_6�&M�����9��_0	U��:O���C��
[��xc4N�� 8��"Bc��K(�:�b%�a�m$o+�g%���
��	���N2�4����r4�1]-F`-�I�9ffX?`��ut�Mz���H�����NG'S$��$���P��Wx_p�����'�����wy�f�&!C���������|��oY�x������9-��M~K3I�"i��]������=�������5�&&�y61���Z��	����2���C�b�b
x�Kc\qlI�Y2��Y�!���j�?N�CC�\s�Y�����m�'g����]�l��*���D�55�{J�����k�uuu���ZA�_������
�<�I��#�a�I7����q�W��[x�)�����%��0'�T��v�/��Q�F��O�	�c8<-�n.��?,5E��@���"����0+"iiW����bV@Y
��(���S��@-��h��d��;K��C3������W@4���q6�jT�Dz6��b���&M��%e@�������Nn�I�,�s.'3//��(vE�GIk��B�������5�V
��b����D-u0�<�U^]Z1_V����~(���3i�������T��\JJf��6�%`o�������o��X�2�=C`Sm��-l��[�����d��44l�o�Z�F��@�m����{�u|�T���]����:�y`FG�8��5mI� ���.r�o{�Y��4�u�O�����lX������XT�ge
�4�E��'GL��f�����8�V�Q�������L2f���i���}���")u -6��(
9=F�p�"��U�4>�qP�Q;Y�6����/�/�'��I9�x���_�o<���|������wU�QQL+y�d��_���cT<���tS<�3�y�/���fT9+>�G��^Go[�n�w�� K���'OH�@x��PoL�Bx�����J��/��������F.����M��#c��F��t\�5�%8��0KD�%��N������M�~z[4�d���D�dss�&#�>�@Oz0C�v(���Q��������x-8�t�7������T��g������XM������K��D
{<V��6I������X+)q-�b
�/������ �/'�_v��Xx���((*����4�)GQ�i�kV�'�%>����#?��e������O[��<���]�(�!�k�������~�Za!F��q|oQ8h���1��� ���0
~�-�����7���A�:��C-�N�����V�HK��C������l�W�0�<�3��,���M�j�5se����:������P��GR��,��%r�u�(u�.����
�m��DQ�����|
����Y^3-�E"�o��p���p�T��4����Lp$/����[C6K���2�;c���5�^��.E��������{h�t��z\����B���lTR��^����|Y�{�J��?}���������_��M����d�������*���u���G+�������w�N�?�z�b��`LG���^"�
Xp�c�r�
r�g�^�O��(3XC6��o�{�T��)�h�S��P����74g�g����y���#�)*���z�&��*�_�G��@�k�%����2��&!m5���Y���:�;�������Q���`$O����9=U�,���5��������,U�1Q�*�������' �lR~���f��Q�t�M5R��mg����G��Wfk�f�_�@$:<��g#&�o$L����8�
���UOs(�4	Ptb,������S��U�2]�����fFb���D�����(�&g�C��q�=��J�+���3�[��}�N��8�p8u��8��|���;��aG�21��N�O�#�~E:Ea�OW>�,��c�� _Se�v�������F�I3���3�]#)���U;3b���x�r0'�4��[����_
x���*35/m�����P���.��l��>�$B�@|���-p{lB��K�|k]���nFMD��MV�D����jS�������q�-������6�����viDQ=}�D����]}�j�)���X����{��.����q\	&O�V�jz\hRK�Q��5k�-�1�Y-lq�2:���D�D'�iP$G��&�DA���r���E�@�?L>wa8rc��@hu�����J���q��
�#���,���|�=,�i�O$QE��6M�n�����+h�k�Q�3LbcY�w������r#�&c�2'O��0V���i�t�������e8 �*��,����k��yei-wc�9^J�����z�����M�T^04wE�=�9�k2���G��Dk�Z}��N�de$1.�QzNs6��D���'��mD����9F�t%��h�3������=3;{k���:�3��'E�H����{�6%�k��[D��2F#�,04gyV�� F�	�{4����J����_�2��������Z�P������?�<	{+����~lBL~�54Q�mU/`_"��nvH�S@����~����L��50)��wP!���M=��Uu���O��, ��%[+}�4��q��'��aB�����9�uRm��}�X�Z:��$P��8L
������n
ag���v������=,^�����M]���F�_����������#�a7-V/#�<����%�[�b0�{|9������#����^�?����oT?�t�����24��z���|�!��?F�t�F��e��'��eF?f�@������/��~1GVE����wh��jgn���5���j�$-������t�A�)G��o~"J�V��	��������<�A�����1�T��t�{V>�vZ��$�&�tE:������m���/��l=1*~�D����Td��G
=[�]�^Y�(�cb�j����Vj���s���x������pu�$�>��Sm��E_�\�������f��� Z�/�n=���,�������H���9����W������\%R�~��+�����Iz��E�]�h�^�G�P#�#F��C����v~>8�Q|����!��,�S�C22��$�(���Sg�=�j?����U�9�6���k4#%*�PY��cm1�LKXc6��b��Z� ��|����w��&��{�(g���1�d�!�<�R�BP�O������1�Oy���J���o�����|0�I����������������]������S���
�*J�"Y,O��+Va"��J���w��//���eD���4�-t���m1@�v��]����*��q ��`3���Y;�����v�	�`���!�.!k-�%�T�O��z�g�}�4�F*���N���a1{�UBu���U+]�I��������j���xk�	�����^e%��..��3t��2���RV�e�`�������P���������u��\���L$�<�p=�.|>��(T�sI�pv���u�lX�."�gh�v�Q:T0�{��b)�;U��!�Cd"{
�����D82���*��c��f�i�v�ul�UO5g9�A,:�������Z������&|�����7�n[�>���9��g��x�������8�	����X�5����H�g�c���[��P��mc����� 'x��1]�P>k�����xd��0���FJ4��n�LI���Gy1g}�Jd��X�m�n+t�X�]�!����OY:����
�����l
�z:8��|�������7_��D��\�O�����
q��|p���x����U�^�7��
��V�=�)&�Z)��Y��e�>�����
��k��Wyy���E��*�2�Y$T$&�4 ���_&��Nw����f� �_�q�O%�&��g��_#d��bw��N{�k�/��2zY�x\�kiD�;	������o:[_���z��l�^�LT\��%Z�W��"����C��k$z]~M��
�L�zC������,�Q�J��`]��.�����{��������v�]"�p�,�=l%�6�y������o�������?f_���xm�����"��Fj��!C�F�R���y��y�p�nQw^�����:���_|���/�~����/?������ze�5}-�[S�n�oI\�|��:���O�	�,�]����a���_U�k���q��Z�i�k�7���se���fT,��j���4�c�)
��1a���(�vl9��*
�~��WO�����/����}����_|��"��.�]�2�~�l�����R����sUrc����J��t�K/�ab�:��P^}�9L��'�t7�Z��\������/�$>����L
��n���0k����uD�����e5���te�+�A��������I������77�'[_g�o�=_��+�q!�ks�����A�&��� &����Vxl_e%�$�����N�4�<Wn���F6�!�8�~��%���#z��m��_�%������	����>���~PB��].��P���l����+��5vPk�uZe�m��<�v������4�[�����\��s����b(��8A���p��JYN���O���n�"��	���0f$�����?H�����30�H�����&���;c*,&�a_��Q�R�1�	��1v�x�U��BET&R����V!O��}�����!��'�l��Ts�0�����g��3#�p>�M`��`�o������
^���_�8��F3�������b��v����������6�$�������g��>��'(����H-I����@4�����Q�	���W]}� )���Q��]�U���������V��x r����O�o��37����	#3�M1KBM�a��=Z���E2��m��3��$�*X��
&u�`z�	�h�n���|��������7��L�oa����	�8$�9VGc�;L\Km�qP��E��-*N��xu��*��zz���Cce�������]D�W�iH�N�����&xN�@�5Wk���l+������H�%�JAc��	�9�G�����v����'s
B.�F��n\T[�t����Br�%�G���-��-�Y�C4+���6w��xmv�xg��<�m����M����M�q������m�����T��&�������3h��>��J�Y`���8K[Q�8b�����q#[�u�M8�v{�j��p4�F�Q�*��m&�B�������C�(Hx�%�c�e�����r�a4�����a�z���aD-��@sX����#U=�Z�T�1j�d�c7|-�"M��p���S-�|������^�1\
���������,�H�	tdg	o�GF:(�����a���F�5.��������.��S�R��������aD�mi&���:5��{���%�+�t�����l8��~�)��8[r������5%r���[�{�x��������2�\�����'� 7`@n������|��V�m�A%��D���*�`~�1�)�'�v��!R���{���V��{�������:@-B��P��Ao�o�_�}\�H�njM�FH�@�:@Fe��~2k��W�+E=���t�ID@�V�t�>��%�L�z{�nHi-������(���$"��,��Z��F^@���'�l:8��Eh��k��a7>$�
,��?e��X�?�p�I]/.��WW'��W�������d{~��'C8�&�(�r	�����lz�}dOSs�w��������>^���oKU�@����DE���������������!��I���'�I�6nLz�����[���E��!�
��S�/.S��/���!�������VSDJ��n}�Cs����ho�������1b�r!���������Vk��d����]�T@m�Q)F$�AU�UW�m���
���!D ���T�D�����vR�~��Ev�Sn�u�7����+�2%��t�J��3D�Y�� �`���v����2Y��/�b���ovd����T�O�_M[����f���	�ygG�����nf�\B�K7����hwp��N[�~0h��S��*��"u�H�t(������[��l��!"G��'&F��JnS���
qbd;�n�
i:b�N�t�9��Wk��M�����|�+O+���R��1t������'��~�c7t�.������V����u����S��0V�}���5G��k��V�D������"��8�
�v�f��n,�����(1��R[Z����1]F��(���n�%��y�-N�{�g����7A2o
���(Y)��`�����I.%��
��(h��u?��Io0m����,h;�K)��r���,�����_d&D�Y��9��fC��^���[k[����C�����K���
�q/��Q�}
B���{(����f�l�M��+LJA���Bidb�!)u(>R�Z�^�,�p�6H7��$���^d1����U��;8?=s�^�wE�(���!��RK��{,�w8�`Bq't�.�� ��(��U����~�����d������50�J�]���~���qr�kR����C?^���N����f ���?�ON�����HF�+���`_9�i�2���*=9@�D����7UN
=v6P�%���mW��n���~���ViW3�G�$b1���V��Flv�����D�Y�� ���'�W(F��|"�G#c��������j���\~	A_I�>��I;��������6���o�$���*��H�)��P=�a�v�����v����N��-~���Z{���m����TgP���n����W�<����	���^)�<�U���3'h2��YE2�(4KJd���%�q:.���^`��]�1������"[n���H0�^2q{�� ��@�U)J������B��4����:l*p�8[16�&�z�-
:�M�.=0E���7v�/}�:���@���Ne��P���H����O��q}�%�'HZ}��q�O�h���E UT%>�L��n����g�1j����dW_�4�-��tqv�!�{��T�P�@��g���cE4 �l�v��G�1h�_>}:�R2]��A���xW����Ir�AH�XV�{K���E.�3&X�zt���0�G�)p1g8'w����G\eNO��I+�����=�L��<��/�^��QMj{�i�s��*�c|�?���P
@[�j�'�B��#[uX��1j���F�������1h��}X���f��&g�iZ����$�7&LA�?��6�� ��`�&�F�������V�]��
�$�/tU>}(�-,vr.��rpf3�6�~�^�>������{��w*��"d��p��T8?F�G���viA���9c��=�`�R'���$i�G�>gM��j�������f�g@�^��Mi���O��o���Zm�Aw�f7�x��u�����w�����V1��]���`��Zg�w���d��0a��"L�E��iVm8F�'<B���~����?���&��p������{�T��W��e��%�*���mu����[�
{�2~[��h�X ��c��B)��	e������Q��
1�Y�$Ldn��L���2h��QF�e�n��/L���g�gmP@��a���e�
���9���H�\HB_��������vVP�!�K?�c�6���(���9�a#g�����!������5��k5CC�#k?��\K�N��S�*��p���[�w?�������??�YM;�1�OD�sq
@��
�3���]���_N^����/�)�I��,mb����S��Z�@j
��ys�;	�v�G�E��a��r&�\���G��Z<�(�_�F�i�R����u�^�����
_��w3w����ArHVoq9
�\����)t_�i�����g����~��Ta���A/h���F�6�t�q�9������d�f�>k*�SX���6������PY�**ia����(��^9-6�|��dK*�,�`'Dx����p�Ja(zW����
V?�r^�l����p@���{��|���cF���1��]O'��,�J�Y8�_s��d���-l���{O�F�#�L)�&��#E��d�^�����X����������-�<����J�F�+�������u�.�c<cig3h�Q���&m�aD���L�&{L��-3-�5�eTn���������������Q)(�+0�~������Q�����A�'��n�����-z�����q��F�
��puK�`KU
E����bC?�e
�N>o`���w-%��5_
)K��D�����
a5��c(����m%�#���X�P�z����b�tS$�UR<�ML�?{��n	�4��)Q$���~����8Gz#���w��	->w�J3�:��t��S������)�K6ybH����_�a.���S������pq��X+L��qJ�NR�

{��y�=�dcL!{��f�����h�8q����Y	p6V��q:����,���	�4�������f����?��dN�f�=fJ��pq������������o��O�v[��q���$Xi�5�)�bKe���l����
Ln1W5�\�at�p�~��%na�n��;��,�n:���V��u�� +-SN�����K�Y��H�����G�Q�|�9�<������
�[^���b*�/���dW[%�y-zy�����p'[n���,A:o�4k����f�y�sk`W��.\���o�v������8�<�u{b5i��H�@�����63u{���!���~�LA,�2s��rNA]��4���c�����!Uv�m��D���4=dS(��zSJe7���A��o�k���9����h��i����(o�$�A��6B:8�e�%�M���~|�O^�DZ���3LG��d��Uk�
��7L�'�g
F`�M��W%TL����G�9������&�"g����O��#�&r.����K�k2���I����!XQ)��N=�cGS~S�Q�����m���R����,j1+���r��fswJ��FBw�J��]��OB1�!m���{��U1'����}:��~������Y�����j�K�)��*��=�6�@��������%B��w}`
y����n����(6�2�
`Po�:�qP��F��_o�z�]�
k�����x��?Z�Nb�G��I���dH���Q�����y1OV��:����(���s<6V���o�K�����o�Xs��2m6�do���:�}n���O�F�C|�it����4���-��Md���J���
�S/3��-K���'cPW>���Oa�Syl��A7h�f�6h��=8����q�maVNY��h��9}:��\������P�����'�S�x �D�r������������`�;*�QgJ�������VW�o;�UHEJ��&�v���k�N/�O������b�m����`�P�Y�������������z�Q��iUZ��I�[���~�o��������6Z���j@���d���dz�=_��Y�r��K��9y1"������������iI�U}�����+��ar�a������b?����L��a������L��l��X��
�K� *P�v�/��4J%�f�<��I9�������@hb=���,V�����.���dS�����pv���.2�yt�M������
l�	o3�����@�Pi�0�"cQ�5IE��TI��u�]+oL}<Q`���)�������C��_e�J�'%��)�6�4�����n4��#�?I�.�B�V�pXu�����O�OY��h��$�� ����������z���X�8�y�t�n�����BY�h�_
J1
w�Sec�"��k�bQ�YW��`q��-k�b������9�a��P���y*�{@L���s��\��AU=���� �1�������so�+��Q��k�-]����X����o�T��G�V��&B�SS�e���9)R2g��G�q)gq�O��$;Q��}j��5���F����U��A2�N]�9�S�����������J[B��g�2�x{��Q���L�S�:gn���B
����9M�,P��������m�B��~�"(�,K�h�����,�����{�7���Xp�[�����[_7��f����� ��L������c`0>.��T�7��o�l��G!��n�D�t��1�q��B2?3����<	f��kfR��s?�����>{�t�J�g�)I���`i�W9kT�0u�Ie����Y	�����KE�e�P|�f#�1��-���2REP��0C�2d��r�2����� 7W����x�c��s�m�bK�����G3x��f�^.;���r�2CVRMe�L�:H��4�?f�|��[TF.k�o1�����M��~ED��]P���N��~���������eN���o��h~���x�w�\1�2�,�\�7t������������c,qZ�!&p�m����A�-�0��'yF]i��.�i�P�%��T��SM��0���9����p~S�ZL�*��I�2���������f����D���^���,8_��a����q��U��a�E2zh�G;�#�t���FdL�
H�	&�- ���7����tM��R$�X�8uZ�{����Y�������1LPr-�1�$M�t��F�4����0�����a�"R�-)'�$.����������)�)�>�� �y8�[J)�#��)q$nY�baI�,����%p
��%#f5S�k"��!c��y��mn�����!f�&irH�pz��W�T���-�1���67��@�x�����2������8����#���{������
QGn5G�4A���S!K����,�>�����(���
�#$o�q%�!�&J����6���zR���O���+��Xo�
�C���j�1�;`�Um6y�(c�y8q���A���1+��t�LIAG0^T�$���c�-<G�b�%��@����zG��3g�Z&5������	�!��c�}m,	#��I�4*��\�GV��d�URO�^����m����6���-MNf����!�]n��?�������C��BY�fUR�J
�&�m���[���*dE�`�=*0�t��*���V�7
���68�$�����C�����G�.
������VH;\6�3�N���?�����������:JV
�g��u�8,�Q_g�{�*%}���+��A�zikC]�/M>��|
��+����a\��C�V�����BBX�_�P%����$��	����j`����v����,;�f������R�K��+2Q�����v��=�"4e�W�d�U59AI+�FW�����`�F�8����
�4��t�r��]`�p�C����|����y��~�%����4]�0�8�jN��W�� �� a����I��5�y�. ���m����<z����?���n�!(�H��!A? ARl4���]��o��)�c��m�cw����A�C���2��z���j���m�G�A�����
q���]Z���YX3�qT�e�0���0S���������uW^7���h�����f4�j�I�����vZ����F��iM�������S��v$sl������f���-�$}�U(�<|����L��
�#Ua-�S�Qj8c"VE�[%�~���%���
�2�<#���������F�:��S��8S�0��9mD�b�?��*��G�� ��L8�3����t��oV��cG�\K���}dr��Y���vT����t	���M[��B��b�2���V!��p�a��7�n-O?o
$��w���-vj�����Y %m��c4X������R�k��s���hY6w���)/�X��������o~��2:8`�U��'GT��%#(4_����G�Q���J��T_�:�mn������{(@w��i�����c
��f �-V�����x��8D�\��rqHA�xI�����|��`C��3��VG��,�����#iM����y��Hb��I���J���M��m�;_��_}��=��@������ �2/����H��Pjmg�1�\�.v�\�Ps����
�~d\�zo�@f.�F���q�VsO�*8�<
��6���y��T��g�'�l�-\�v�:{Z�"���p>�{�4��7�N#df������k�������\HO�_Z>��t��������&mim�FU���U
�� �����R�Od�]���������~0h�~���vz�N����(�;����NYu�����8�$�����R�"�W��U�I[Q`c4"� 4����VB��Dr�����>�\�����j8��I�e_;if�,�ObX�o:D�_����Yf*){��' �J)�yw�j
�Z���X���I�y����\��.8�!����u����B����w��UM�I������Y��O���o�U��Vo�=�:?�gA����~n���lf@V1�{h��f��O���t0j����tW��
n�����e��������e���n�|��B�~�kj���~3�fs��@A��w�TM�>l����\��E�	X�*�f�RwL���C�@��x��=eRv��)����+��O��]�H]�����Y;��b����N����>91�=���<:%-�4�d-�	d������){u�? ������1KczH�:�%CL�-����s���4��TwkAh�9W������t���>���2�uQ*s<��%%<�uRst��UE�G6S�*/�����Q?�[#��S�9�7���,+r�J�@�]��Og���=�m������_d'~�Y�=�����$B�$C�9%����~�7��j�v��k���~11�A��p^)	������r��a�X�Wev�&	��w~a�.�o���+���C�mQ�qL�JR�Pi��������KP�����Q�R���@�t����Q���S����V��,`W�bj���L�q�;AgT��F�f����7������b�r�J�ku�9�?���2$'�a��w�w����W�I@����Qy� ��64��2��N�����]��2�MQ}�`��kt"����)k�d�8������{�=�*�O�G��U��X�g�;�<��8&��>]����uV�Yy+Z����e��"�����sA'�Y}7�^��s��9��9��Gs4�@�K�[�#F��kJo0d�T�����>�gV��56�wZ�������h�o�{�`��������%���X1�M�I���,�q@�<���7C�GN���s1I^NF�+�84%����� ���2L�B����YC]�!�L#|I���6����Z������t�hx5��%�(�T������z��g���t�����	}��
��k0����,I�3�*���dkB%��`;D�[����1���_�K��l���tL�W'���W��;���D�Y]���x����Jj���ep}��;8�9I���s������`��^�]�yzJ���#��h�����q�!�W�wG����C��%���I���4���W�I���������<����!a�����M0�J���pJ�fbC��������G��L�E^��4eD�.p�E��}�
�����Gl�zCx�-X��0M�~�3m#�/{c��C�V�v�>��<�}u�����ZDW�:��j��RUC��&Y0�e�L�<��"������������EJ���q����n3���T�R��	fJ�,;�A��f�'|=E��k��S0?�k��ZrU~�����0�m6����9'�(B�!�4N�b�q��l��27�_.��� ��5h�]��D����
Csh������S��>���]��|���_�	��BM1��.
kh�.��r��S����"���Cc��2��n�8�<�+�����q���k������	6�Q��;/��d~������c���%����@�L��9�OF�n;h�j�z�������[X�|�)���UT��%�X�!v_c1�"&��>����-����)�e_$�3����r����o+~��h������[�E�Wq�S�,q��-:�u�
aB`�6��(�&�a�����6`�����w'���	��Ie=^�7�2�p�g1�E�d����0�U|������?f..������	������������������'���K#�EG�2Y/����j��_*�k��w�I�>�!�2
��[�}p��q���/���N���,��T�J�2S�7�W�|#)�\�"V�\Ge�/���d[�����~W����X?���u���5u���*���V��fi�]!Z�5%n��]��.P�����'���y�����z���$������t���������T=������gB�"�����h�3����n�F]L���*5�@z��4:�sT���=S��k��m]����[(��*�	"�X�1�_3=�zX<�{W�x����H>���}�v��t0�?���F����v^����������l6���#��S�|���M��������'�	Q�s�����x=���S�r�a��1]<\�������m������9��^^�SUT�����_����yqu����������K8�_��;����=�4��)��p~zq�I�������a]6$�(�����>��^�����\���W_{�����k��
|���7�|���(5��y�Q��8�<G�I7�������4$�; �h��
�{z��L������������7j�N����m���sRa>�������pu����C�m���g�]_x�?�:;}�jeH\4Q/s�ZB�J(�z���5��k5�����8'�&H��7�"M0�y����`�B8���r�z���������?\|�^���*�?���:�:����{����8;�?xm������5�EtL��
f����(���(H�~k��P]C������>�P���<�as���A8-�^B[5�xV9"8(y���>����8�H��Q9`jp%W�;��iAC@M����
�P�b�$Z����R$�.B4�R��	!���r�e�������z��].�(�b\��xE5��<��Q��b�-g����v<�a�o��$�+�t�9�n��>D]�W~���!��(�'i������W�~v�Y/����9;;}�����+��\�Z�j�����i(�>�V�+8�����?_��:�9|AI������}�-^D:x`:��&� 5@Rzg!C(1����@�N���)�r��/���Csak4��14�8�or^A�}��(�7������8D|F����X���cG�`�����`]�k�%��|��F9�otk���Z�bA���*U����o^S+�3��m4.�;tB���V5�'���PT0�@j>*���h���U����%�	�H�c@�Bf��V�y>3����N��'(���o�L\��@X�,'�Y���G#W`��m+fFcu$;�Lo
�)@�]��>�wmRbW�4�9����(Xz&�E8�
9�����E,�X���%����UgD�\_�4?�h7d��x>c#kF�1k���z��]*�����e*��6e7�
�-����f���J���*"�����*�1�������dR�{!6l�	�S(Y��lx"<*~��k�+��G���0Z' �%�`R�F�L
;R3mr�sG�;�{�)�Wh�E���sU� rmL�-�l���{Q�!�\`��H`E�e0��D"�/�����	����W)P5�}�m�%�war$�X��V�#jQ����eB�kld�
V=L��Z���; ��-�h2%J��4Va��m�����&�g����Y���7K|��<����e8TV��������h;���yel([m<�TZ�k�����3?AYo��'r +��hQ�){5����
��x���hs�L�)���e#+=�c�o��n��C�~"�����B�������vc0���~s��{�~N��-�6����)�,�p�����h�0$�������o���s�fq��U30����kKY�}p�.����m�w�.���[��3��A����5��_�
&�Qo<n�r��p��-�����^,�����>���s�)���n�����=���J'I]]ad{����@��'z�Cm�G�f x�J�����1��25����E�[~:�|$(20��p��8�sa�
���3�iTx�OJ%������/�I3&�.�%�7����W1�3�
�W���P,)�U~�A����Q@�1�t`[�p���$1\���5��j`U���t��S
 D�%��Es��c��D������To�T�v�
o�}�H�u�����C����0���!���h����*��#�%v�n/U�j�$)��x�
n������[���5Hn[X����)�������3�����{d?!�T�&�a�����.��N����MTY���_Hq`t�u������c�u������'_���`�C�L?\��	��n������Jx?\\�:���Z�^M�t�'.D���O
p���n��ku��/����0�F�����p�Y�5��1�\������x
��L��!���s`�~�%\�����Z��a���"==s�����/�������q�������C�W
�)���,.M+����"�Z��^��
�J�����kf�P+t=��*���^��7��5LM���+i8vg�
�X��M���q��`�N���~d?Jq
��Y��cw�����:9�;����H������W��^��F������'�&j�c��z5��5�����v��\yS���N� ��B�~w�A��C#�g�h^�+���!{��Jz��WB5�t���Hg����Ig�2�<�,�D��KV
��Dy=��zj������[���o�l�S
@���X �1B��,0������rzH�RU�w���bu�I!��tn	).6R��E�������W��
��M�)�*A���B7���(�x,�Q+�|�G�j�a2a�I������l�R�����$���SJi��l�\�'��S������d�-u\�����c�R���wt���4���tYc0�d�ww�o9�*F*��&���20O�6N�eF�t���:�)=�Z���MD���
J���(�*��[*6�;����-|�`�z1C�-��Y�B1-�<��<�� %�{e8���'����H�|��NJ;r���"�����E�X)��j������?�s�D]��	����* ��d=FD���U#�$�Q1Z�FS�����������,����v��&N@}H��e(�}�"1����1����+��;���,�	Q���X�[t����2�u�*����_�M�<(8	����s>��<������:W��Z�E�����o�eu%Bb8 ~Yr6 �u"V��#-]L� �&/��H������5&6�]�>��h������_p��0�����4���
���r�0U����7�aB�:�5�]_���'��^�|�d}�s�5���}��t�����Y�h����<9|��WeL�����M���?������ko�s+K'�?d�R.��_fe�����@K%Io�\v�������Bp�
����C�t���N;>i���0��9^�����xMq�����_H���=��sU8�^�"-Z
�x����gFm�<�
��kJ��D�����g��d���Dm��w�
�h������a��"<q+\7���Q�6��J��_%��-�N4�r])�J1�_n�
N����5e!�e����4�J�� s��'?�A������2�l�����uZF�8�L4M�S5<u�U�������V,��!�9�2/��f^�����Eo����-��j�z����Z~\/>���6���x^)R������m+O������5w���m+�W�VvT��#�c��i {�m+U���U�V��uA���u�����<�����j[���m�'�j��~j��C����m���i��������<��V�<�
�R����������-,�Em[�Cmkm�y�[x�X�l�����'��V������~6=���g+Y�le�~������g+�~v�~��8Z�e0�HZ���m���a�##��!�"Go6��X{������ ��V���yjZ�m�$��E���@��t
G�� f�s(�����i�=�J�7'���B�����I��U��;��FS~_T!�K���N�.���SX��<��@��Gxs��j���C��	�x�r4	��N\
(�3�0r��d�{���'W��L	�d�Rj�W[JMk+����Y�RP,B���,��N�;��t��Q��oAR�[d�QB���d0�i�1��E�t�$�Z�r��<:�'8�t��|KV����C�ZE��c��]���Oxfw�#�(����<�����(-1�4w��lE��f�`fmG�Vkj����LU�1�?5����!��Cd��8V��`9���(X��B�|�����P����<��;RR���,"��	�B����������p�Ol|Ia�������a�W-����T�$��	a�7��K��L���6m�a�W��N�Wat&��d
{��t��YsMHc�z3�x{����()f��a�~�Z�Y�O��r�&E&h����
���o~����G�N
d�u`�qy	N6a&K��{lJ��[VY�����5�bi)dV_�F�-� s
G~_�l2V�+�=�U�����*������M�������0�[�~0�����c/��[���!x�M�D*������{�2xf:t����J�f6��`���a��	LEA����I��ql�h�uv�)��T��	~ofLe���65���td�I��T1��6pI��4�����=���]1s��y&�z���ul��
��z��&8AX���!IO����O�iL�K�ZX��I"<m[;mJ��0��v�.�������LtLZ|A�O�y���%��(��8�`,!a1<���F�L2:�N�����|��M���ML6G\����W
�����E$K�����x����Z�1��6SO����23+��% ��JLq�@��L��Ut�������S�*�:����M��e�M���37�$^]hA_������bW'W�������$�F���&��3G�VR�,���A�V!����c��y��?��{q�<�ob���d��������D1�:�������l4��
�N����N���L&h{:�cb�K|�/>��5�+�� �����g�'��&Af�VP��zJ�9��9���������/jJ�ui�wA��}X�@�y��&?�CI�R�W������n������9�
�FP<�'�l������R����lI���X�|����n�[��Q�#���Mr��t�#]^����*l���A���hN��t���	i�Q�4�sj%ZH��BE2HQSD��.�1��f���J���W��1M}��k$QL���*��9kYhJ��=������S���?su
������f����y1������,���&�.�'�l���i��D�����R���X��5�v��t��(&;�Tg�2�X,(H@�{��S!
��u��U��$�;�>�Y�����67��'���M�tAE$M���<Z��NxU��T��Y�.���w�A���vG9�wbY�Z99�����Y0p�����{-��X�G���+�|Z�\������p�s���3�]�
x^�^�>+������6{�}S���J:JLi���7:���t�G3�8�
"6��CL��`W�1�x|yy�_�1�A��`��~S�^m$���X�{�~��oV��4��Yr�F��������?�W2C$����l�xZ���l~)���m��������L����!>��zh��$�H `�	Q6�0fK�.m��\��1~]����5�1�]�p�}�t�T��8�/,L���*��x��ql���(���O�z� ���?���l�t���E�K��I�F�v���:.(�� h��,(hjQ"�E�*�il
���{5�'�)Wy�������"cF�%)b�(W:��5A)p����4� ���x&c��J(=����dp�-�n9
*g����_�S�\$m��I�F�3���>��0�ma�k�et�xy���y2�d�nK�Z&6��Es��`���'!��L�s�s���h!�����P�1%o�iF��w8Gs����3nr��0�[��O1iv�L�|9�!0E���6��l��a-�q�Kt�z��b�B���� +�B�%��"1T�L�)@*CP�g��1�f+����+F�KB��N��F��^���
Wk�M�Zwf��Q9G��_��9��Z|��
�=m���Uo�R3�8���-U�G������VA����P�k�,��>���o�������������������a���:�����s@�4�Yj��`�:�%S_Tl�k�^��n�h��Q�We_@$��q���`B���\��:��|3#/T�!=i�l-|L���"��T�{��A�a����!��?�2�B�x�q!E2�Tg6�I&�����;
���bb���i���d�+U��i�)���f���u�<LO�T~��&K��#N�T���%���v�y���.�����G<����xB���$S&���n��h���;��>9'���6v~�!Y�����#�EN���\>���)?���Q�#.<�F�<"�x����O��d=�R� �L�QB�<�R�g�mQf�=J�L�W�w#�&7y+iGIB�{�k���d@�ii���zpm9������a���x�;0���P��v�n��R�3�bL�g��c�B�Z��&�@9�<����W�K��(��O0��'����4��t�s��-/Xf9�W�,5`X���!gDc����2u������j�M\�����Y7���8 M,s=h}U��FB��u�x��Bbs���-����C2���d!�0E4����_�R������u�W�	�ZZ��IV�y=I��[�����'-Q�"���o�_F2��R����-�8d�5�s���{1i�Y��5BNz�hvq�T�f.��|�����-�g)�daU��}��>~uvru����	'q�1���e����Q
��<yrLp�M�������o%s��]KS�u2�8PJ�1s�l���P����m4aH�M�-fNy������y`�7<�x�������<���l�����%�/�0(��c ���~P���JT���(;�P��)���;��JKg���VV[E~"�����u����.@6
8v�.��k�@Sm����2�ae��Q�T����`�[�IC������1��GZ]g=�*��K���C����n�Q�
�r�L���h�l?��}�&q(��sYT���\n�X;2rk7��E1fI����y�ZQ<	���z�I'L���H��0>=_������usYtz�y�/��EPR�w�Q�p��x�u.}��-d�[d��h���x��OoA	B��������I��W�>}��@�?���W���=-o?�.��5�����1^��j�EY��Se���S�c�
'8����\k��:a�����eR�bR�.&�Cx�:}�`#��R��Q��c�"�m���)1�����@�
't!=�3��$�-�	�i��80������g�u�Y�fD��p�p�k]�
����=-�loe(��%��n�9]��������G:G��3��~�83��9��r��q�xqz.������voF�A����u"������5�4zfq����/
	�
��x�<:���zm�L�,��?!�_��U�������-���!>�~�/�k�	�.ESz`]�v�b��uUH����>�W]8[�i���_9z0����(n����c��k�%p�'�:��b
�y��2f>7��R���I����R
�G�{� �n�A�YZ�L0��:R�(Z�*_��Q���
f��������p����p�<������O����9����v8R������e������^���RBpf�� <�Rb��,�_�}��f�P�j������+�}��|g���,�=��e�H���rh�>�G%,=�V���\��{�%�,"�S��(B��ED���l�Q�������2)>�7:��?���qsR��m�=h����6�lZ�-i�I���Y�����e���o�����C7���'�JqW�k��l�Y�J�V���'7N��o����|�b�KV�2��Z��]��_��V*�A���`�������Uqu6O�xT�t)��J�d7��e�#�'�x��\�n��z�O���`xw2u+����Y�z��P�
��!��;��L=-G���p��s�����N>� /��r�-o�>����u6������q�7i���Z��F�f������Y	�E��)�Y�pV>����(�H9���9�@��R"�x�?��T	�G��w���W'���K�B��\�`F�C������{���K�K�k'c0]m�^�1��� �1�Nth���~9}���e*���0<�zJ����a}��Nj9z0��J���S�����l�/Q��������c��hbz<��{��5���g�r����
0009-WIP-Add-minimal-keytest-implementation.patch.gzapplication/x-patch-gzipDownload
0010-WIP-Add-configure-infrastructure-to-enable-LLVM.patch.gzapplication/x-patch-gzipDownload
0011-WIP-Beginning-of-a-LLVM-JIT-infrastructure.patch.gzapplication/x-patch-gzipDownload
0012-Heavily-WIP-JITing-of-tuple-deforming.patch.gzapplication/x-patch-gzipDownload
0013-WIP-ExprEval-Make-threaded-dispatch-use-a-separate-f.patch.gzapplication/x-patch-gzipDownload
0014-Heavily-WIP-JITed-expression-evaluation.patch.gzapplication/x-patch-gzipDownload
0015-Heavily-WIP-LLVM-perf-integration.patch.gzapplication/x-patch-gzipDownload
#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#1)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Andres Freund <andres@anarazel.de> writes:

I'm posting a quite massive series of WIP patches here, to get some
feedback.

I guess the $64 question that has to be addressed here is whether we're
prepared to accept LLVM as a run-time dependency. There are some reasons
why we might not be:

* The sheer mass of the dependency. What's the installed footprint of
LLVM, versus a Postgres server? How hard is it to install from source?

* How will we answer people who say they can't accept having a compiler
installed on their production boxes for security reasons?

* Are there any currently-interesting platforms that LLVM doesn't work
for? (I'm worried about RISC-V as much as legacy systems.)

I concur with your feeling that hand-rolled JIT is right out. But
I'm not sure that whatever performance gain we might get in this
direction is worth the costs.

regards, tom lane

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

#4Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#3)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Tue, Dec 6, 2016 at 1:56 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@anarazel.de> writes:

I'm posting a quite massive series of WIP patches here, to get some
feedback.

I guess the $64 question that has to be addressed here is whether we're
prepared to accept LLVM as a run-time dependency. There are some reasons
why we might not be:

* The sheer mass of the dependency. What's the installed footprint of
LLVM, versus a Postgres server? How hard is it to install from source?

* How will we answer people who say they can't accept having a compiler
installed on their production boxes for security reasons?

* Are there any currently-interesting platforms that LLVM doesn't work
for? (I'm worried about RISC-V as much as legacy systems.)

I think anything that requires LLVM -- or, for that matter, anything
that does JIT by any means -- has got to be optional. But I don't
think --with-llvm as a compile option is inherently problematic.
Also, I think this is probably a direction we need to go. I've heard
at least one and maybe several PGCon presentations about people JITing
tuple deformation and getting big speedups, and I'd like to finally
hear one from somebody who intends to integrate that into PostgreSQL.

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

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

#5Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#3)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Hi,

On 2016-12-06 13:56:28 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

I'm posting a quite massive series of WIP patches here, to get some
feedback.

I guess the $64 question that has to be addressed here is whether we're
prepared to accept LLVM as a run-time dependency. There are some reasons
why we might not be:

Indeed. It'd only be a soft dependency obviously.

* The sheer mass of the dependency. What's the installed footprint of
LLVM, versus a Postgres server? How hard is it to install from source?

Worked for me first try, but I'm perhaps not the best person to judge.
It does take a while to compile though (~20min on my laptop).

* How will we answer people who say they can't accept having a compiler
installed on their production boxes for security reasons?

I think they'll just not enable --with-llvm in that case, and get
inferior performance. Note that installing llvm does not imply
installing a full blown C compiler (although I think that's largely
moot, you can get pretty much the same things done with just compiling
LLVM IR).

* Are there any currently-interesting platforms that LLVM doesn't work
for? (I'm worried about RISC-V as much as legacy systems.)

LLVM itself I don't think is a problem, it seems to target a wide range
of platforms. The platforms that don't support JIT compiling might be a
bit larger, since that involves more than just generating code.

I concur with your feeling that hand-rolled JIT is right out. But
I'm not sure that whatever performance gain we might get in this
direction is worth the costs.

Well, I'm not impartial, but I don't think we do our users a service by
leaving significant speedups untackled, and after spending a *LOT* of
time on this, I don't see much other choice than JITing. Note that
nearly everything performance sensitive is moving towards doing JITing
in some form or another.

Greetings,

Andres Freund

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

#6Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#4)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 2016-12-06 14:04:09 -0500, Robert Haas wrote:

I've heard at least one and maybe several PGCon presentations about
people JITing tuple deformation and getting big speedups, and I'd like
to finally hear one from somebody who intends to integrate that into
PostgreSQL.

I certainly want to.

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

#7Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#5)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 2016-12-06 11:10:59 -0800, Andres Freund wrote:

* Are there any currently-interesting platforms that LLVM doesn't work
for? (I'm worried about RISC-V as much as legacy systems.)

LLVM itself I don't think is a problem, it seems to target a wide range
of platforms. The platforms that don't support JIT compiling might be a
bit larger, since that involves more than just generating code.

The os specific part is handling the executable format. The JIT we'd be
using (MCJIT) has support for ELF, MachO, and COFF. The architecture
specific bits seem to be there for x86, arm (small endian, be), aarch64
(arm 64 bits be/le again), mips, ppc64.

Somebody is working on RISC-V support for llvm (i.e. it appears to be
working, but is not merged) - but given it's not integrated into gcc
either, I'm not seing that being an argument.

Greetings,

Andres Freund

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

#8Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#5)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Tue, Dec 6, 2016 at 2:10 PM, Andres Freund <andres@anarazel.de> wrote:

* The sheer mass of the dependency. What's the installed footprint of
LLVM, versus a Postgres server? How hard is it to install from source?

Worked for me first try, but I'm perhaps not the best person to judge.
It does take a while to compile though (~20min on my laptop).

Presumably this is going to need to be something that a user can get
via yum install <blah> or apt-get install <blah> on common systems.

I wonder how feasible it would be to make this a run-time dependency
rather than a compile option. That's probably overcomplicating
things, but...

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

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

#9Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#8)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 2016-12-06 15:13:21 -0500, Robert Haas wrote:

Presumably this is going to need to be something that a user can get
via yum install <blah> or apt-get install <blah> on common systems.

Right. apt-get install llvm-dev (or llvm-3.9-dev or such if you want to
install a specific version), does the trick here.

It's a bit easier to develop with a hand compiled version, because then
LLVM adds a bootloads of asserts to its IR builder, which catches a fair
amount of mistakes. Nothing you'd run in production though (just like
you don't use a cassert build...).

I wonder how feasible it would be to make this a run-time dependency
rather than a compile option. That's probably overcomplicating
things, but...

I don't think that's feasible at all unfortunately - the compiler IR
(which then is JITed by LLVM) is generated via another C API. We could
rebuild that one, but that'd be a lot of work.

Andres

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

#10Nico Williams
nico@cryptonector.com
In reply to: Tom Lane (#3)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Tue, Dec 06, 2016 at 01:56:28PM -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

I'm posting a quite massive series of WIP patches here, to get some
feedback.

I guess the $64 question that has to be addressed here is whether we're
prepared to accept LLVM as a run-time dependency. There are some reasons
why we might not be:

* The sheer mass of the dependency. What's the installed footprint of
LLVM, versus a Postgres server? How hard is it to install from source?

As long as it's optional, does this matter?

A bigger concern might be interface stability. IIRC the LLVM C/C++
interfaces are not very stable, but bitcode is.

* How will we answer people who say they can't accept having a compiler
installed on their production boxes for security reasons?

You don't need the front-ends (e.g., clang) installed in order to JIT.

* Are there any currently-interesting platforms that LLVM doesn't work
for? (I'm worried about RISC-V as much as legacy systems.)

The *BSDs support more platforms than LLVM does, that's for sure.
(NetBSD supports four more, IIRC, including ia64.) But the patches make
LLVM optional anyways, so this should be a non-issue.

I concur with your feeling that hand-rolled JIT is right out. But

Yeah, that way lies maintenance madness.

I'm not sure that whatever performance gain we might get in this
direction is worth the costs.

Byte-/bit-coding query plans then JITting them is very likely to improve
performance significantly. Whether you want the maintenance overhead is
another story.

Sometimes byte-coding + interpretation yields a significant improvement
by reducing cache pressure on the icache and the size of the program to
be interpreted. Having the option to JIT or not JIT might be useful.

Nico
--

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

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#5)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Andres Freund <andres@anarazel.de> writes:

On 2016-12-06 13:56:28 -0500, Tom Lane wrote:

I guess the $64 question that has to be addressed here is whether we're
prepared to accept LLVM as a run-time dependency. There are some reasons
why we might not be:

Indeed. It'd only be a soft dependency obviously.

Oh, so we'd need to maintain both the LLVM and the traditional expression
execution code? That seems like a bit of a pain, but maybe we can live
with it.

* How will we answer people who say they can't accept having a compiler
installed on their production boxes for security reasons?

I think they'll just not enable --with-llvm in that case, and get
inferior performance. Note that installing llvm does not imply
installing a full blown C compiler (although I think that's largely
moot, you can get pretty much the same things done with just compiling
LLVM IR).

I'm not entirely thrilled with the idea of this being a configure-time
decision, because that forces packagers to decide for their entire
audience whether it's okay to depend on LLVM. That would be an untenable
position to put e.g. Red Hat's packagers in: either they screw the people
who want performance or they screw the people who want security.

I think it'd be all right if we can build this so that the direct
dependency on LLVM is confined to a separately-packageable extension.
That way, a packager can produce a core postgresql-server package
that does not require LLVM, plus a postgresql-llvm package that does,
and the "no compiler please" crowd simply doesn't install the latter
package.

The alternative would be to produce two independent builds of the
server, which I suppose might be acceptable but it sure seems like
a kluge, or at least something that simply wouldn't get done by
most vendors.

regards, tom lane

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

#12Andres Freund
andres@anarazel.de
In reply to: Nico Williams (#10)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Hi,

On 2016-12-06 14:19:21 -0600, Nico Williams wrote:

A bigger concern might be interface stability. IIRC the LLVM C/C++
interfaces are not very stable, but bitcode is.

The C API is a lot more stable than the C++ bit, that's the primary
reason I ended up using it, despite the C++ docs being better.

I concur with your feeling that hand-rolled JIT is right out. But

Yeah, that way lies maintenance madness.

I'm not quite that sure about that. I had a lot of fun doing some
hand-rolled x86 JITing. Not that is a ward against me being mad. But
more seriously: Manually doing a JIT gives you a lot faster compilation
times, which makes JIT applicable in a lot more situations.

I'm not sure that whatever performance gain we might get in this
direction is worth the costs.

Byte-/bit-coding query plans then JITting them is very likely to improve
performance significantly.

Note that what I'm proposing is a far cry away from that - this converts
two (peformance wise two, size wise one) significant subsystems, but far
from all the executors to be JIT able. I think there's some more low
hanging fruits (particularly aggregate transition functions), but
converting everything seems to hit the wrong spot in the
benefit/effort/maintainability triangle.

- Andres

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

#13Nico Williams
nico@cryptonector.com
In reply to: Andres Freund (#12)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Tue, Dec 06, 2016 at 12:27:51PM -0800, Andres Freund wrote:

On 2016-12-06 14:19:21 -0600, Nico Williams wrote:

A bigger concern might be interface stability. IIRC the LLVM C/C++
interfaces are not very stable, but bitcode is.

The C API is a lot more stable than the C++ bit, that's the primary
reason I ended up using it, despite the C++ docs being better.

Ah.

I concur with your feeling that hand-rolled JIT is right out. But

Yeah, that way lies maintenance madness.

I'm not quite that sure about that. I had a lot of fun doing some
hand-rolled x86 JITing. Not that is a ward against me being mad. But
more seriously: Manually doing a JIT gives you a lot faster compilation
times, which makes JIT applicable in a lot more situations.

What I meant is that each time there are new ISA extensions, or
differences in how relevant/significant different implementations of the
same ISA implement certain instructions, and/or every time you want to
add a new architecture... someone has to do a lot of very low-level
work.

I'm not sure that whatever performance gain we might get in this
direction is worth the costs.

Byte-/bit-coding query plans then JITting them is very likely to improve
performance significantly.

Note that what I'm proposing is a far cry away from that - this converts
two (peformance wise two, size wise one) significant subsystems, but far
from all the executors to be JIT able. I think there's some more low

Yes, I know.

hanging fruits (particularly aggregate transition functions), but
converting everything seems to hit the wrong spot in the
benefit/effort/maintainability triangle.

Maybe? At least with the infrastructure in place for it someone might
try it and see.

Nico
--

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

#14Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#11)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 2016-12-06 15:25:44 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2016-12-06 13:56:28 -0500, Tom Lane wrote:

I guess the $64 question that has to be addressed here is whether we're
prepared to accept LLVM as a run-time dependency. There are some reasons
why we might not be:

Indeed. It'd only be a soft dependency obviously.

Oh, so we'd need to maintain both the LLVM and the traditional expression
execution code? That seems like a bit of a pain, but maybe we can live
with it.

Yea, that's why I converted the "traditional" expression evaluation into
a different format first - that way the duplication is a lot
lower. E.g. scalar var eval looks like:

EEO_CASE(EEO_INNER_VAR):
{
int attnum = op->d.var.attnum;

Assert(op->d.var.attnum >= 0);
*op->resnull = innerslot->tts_isnull[attnum];
*op->resvalue = innerslot->tts_values[attnum];
EEO_DISPATCH(op);
}

in normal evaluation and like

case EEO_INNER_VAR:
{
LLVMValueRef value, isnull;
LLVMValueRef v_attnum;

v_attnum = LLVMConstInt(LLVMInt32Type(), op->d.var.attnum, false);
value = LLVMBuildLoad(builder, LLVMBuildGEP(builder, v_innervalues, &v_attnum, 1, ""), "");
isnull = LLVMBuildLoad(builder, LLVMBuildGEP(builder, v_innernulls, &v_attnum, 1, ""), "");
LLVMBuildStore(builder, value, v_resvaluep);
LLVMBuildStore(builder, isnull, v_resnullp);

LLVMBuildBr(builder, opblocks[i + 1]);
break;
}
for JITed evaluation.

I'm not entirely thrilled with the idea of this being a configure-time
decision, because that forces packagers to decide for their entire
audience whether it's okay to depend on LLVM. That would be an untenable
position to put e.g. Red Hat's packagers in: either they screw the people
who want performance or they screw the people who want security.

Hm. I've a bit of a hard time buying the security argument here. Having
LLVM (not clang!) installed doesn't really change the picture that
much. In either case you can install binaries, and you're very likely
already using some program that does JIT internally. And postgres itself
gives you plenty of ways to execute arbitrary code as superuser.

The argument for not install a c compiler seems to be that it makes it
less convenient to build an executable. I doubt that having a C(++)
library for code generation is convenient enough to change the picture
there.

I think it'd be all right if we can build this so that the direct
dependency on LLVM is confined to a separately-packageable extension.
That way, a packager can produce a core postgresql-server package
that does not require LLVM, plus a postgresql-llvm package that does,
and the "no compiler please" crowd simply doesn't install the latter
package.

That should be possible, but I'm not sure it's worth the effort. The JIT
infrastructure will need resowner integration and such. We can obviously
split things so that part is independent of LLVM, but I'm unconvinced
that the benefit is large enough.

The alternative would be to produce two independent builds of the
server, which I suppose might be acceptable but it sure seems like
a kluge, or at least something that simply wouldn't get done by
most vendors.

Hm. We could make that a make target ourselves ;)

Regards,

Andres

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

#15Andres Freund
andres@anarazel.de
In reply to: Nico Williams (#13)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 2016-12-06 14:35:43 -0600, Nico Williams wrote:

On Tue, Dec 06, 2016 at 12:27:51PM -0800, Andres Freund wrote:

On 2016-12-06 14:19:21 -0600, Nico Williams wrote:

I concur with your feeling that hand-rolled JIT is right out. But

Yeah, that way lies maintenance madness.

I'm not quite that sure about that. I had a lot of fun doing some
hand-rolled x86 JITing. Not that is a ward against me being mad. But
more seriously: Manually doing a JIT gives you a lot faster compilation
times, which makes JIT applicable in a lot more situations.

What I meant is that each time there are new ISA extensions, or
differences in how relevant/significant different implementations of the
same ISA implement certain instructions, and/or every time you want to
add a new architecture... someone has to do a lot of very low-level
work.

Yea, that's why I didn't pursue this path further. I *personally* think
it'd be perfectly fine to only support JITing on linux x86_64 and
aarch64 for now. And those I'd be willing to work on. But since I know
that's not project policy...

- Andres

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

#16Nico Williams
nico@cryptonector.com
In reply to: Andres Freund (#14)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Tue, Dec 06, 2016 at 12:36:41PM -0800, Andres Freund wrote:

On 2016-12-06 15:25:44 -0500, Tom Lane wrote:

I'm not entirely thrilled with the idea of this being a configure-time
decision, because that forces packagers to decide for their entire
audience whether it's okay to depend on LLVM. That would be an untenable
position to put e.g. Red Hat's packagers in: either they screw the people
who want performance or they screw the people who want security.

There's no security issue. The dependency is on LLVM libraries, not
LLVM front-ends (e.g., clang(1)).

I don't think there's a real issue as to distros/packagers/OS vendors.
They already have to package LLVM, and they already package LLVM
libraries separately from LLVM front-ends.

The argument for not install a c compiler seems to be that it makes it
less convenient to build an executable. I doubt that having a C(++)
library for code generation is convenient enough to change the picture
there.

The security argument goes back to the days of the Morris worm, which
depended on having developer tools (specifically in that case, ld(1),
the link-editor). But JIT via LLVM won't give hackers a way to generate
or link arbitrary object code.

Nico
--

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

#17Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#1)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Mon, Dec 5, 2016 at 7:49 PM, Andres Freund <andres@anarazel.de> wrote:

I tried to address 2) by changing the C implementation. That brings some
measurable speedups, but it's not huge. A bigger speedup is making
slot_getattr, slot_getsomeattrs, slot_getallattrs very trivial wrappers;
but it's still not huge. Finally I turned to just-in-time (JIT)
compiling the code for tuple deforming. That doesn't save the cost of
1), but it gets rid of most of 2) (from ~15% to ~3% in TPCH-Q01). The
first part is done in 0008, the JITing in 0012.

A more complete motivating example would be nice. For example, it
would be nice to see the overall speedup for some particular TPC-H
query.

--
Peter Geoghegan

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

#18Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#17)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 2016-12-06 13:27:14 -0800, Peter Geoghegan wrote:

On Mon, Dec 5, 2016 at 7:49 PM, Andres Freund <andres@anarazel.de> wrote:

I tried to address 2) by changing the C implementation. That brings some
measurable speedups, but it's not huge. A bigger speedup is making
slot_getattr, slot_getsomeattrs, slot_getallattrs very trivial wrappers;
but it's still not huge. Finally I turned to just-in-time (JIT)
compiling the code for tuple deforming. That doesn't save the cost of
1), but it gets rid of most of 2) (from ~15% to ~3% in TPCH-Q01). The
first part is done in 0008, the JITing in 0012.

A more complete motivating example would be nice. For example, it
would be nice to see the overall speedup for some particular TPC-H
query.

Well, it's a bit WIP-y for that - not all TPCH queries run JITed yet, as
I've not done that for enough expression types... And you run quickly
into other bottlenecks.

But here we go for TPCH (scale 10) Q01:
master:
Time: 33885.381 ms
16.29% postgres postgres [.] slot_getattr
12.85% postgres postgres [.] ExecMakeFunctionResultNoSets
10.85% postgres postgres [.] advance_aggregates
6.91% postgres postgres [.] slot_deform_tuple
6.70% postgres postgres [.] advance_transition_function
4.59% postgres postgres [.] ExecProject
4.25% postgres postgres [.] float8_accum
3.69% postgres postgres [.] tuplehash_insert
2.39% postgres postgres [.] float8pl
2.20% postgres postgres [.] bpchareq
2.03% postgres postgres [.] check_stack_depth

profile:

(note that all expression evaluated things are distributed among many
functions)

dev (no jiting):
Time: 30343.532 ms

profile:
16.57% postgres postgres [.] slot_deform_tuple
13.39% postgres postgres [.] ExecEvalExpr
8.64% postgres postgres [.] advance_aggregates
8.58% postgres postgres [.] advance_transition_function
5.83% postgres postgres [.] float8_accum
5.14% postgres postgres [.] tuplehash_insert
3.89% postgres postgres [.] float8pl
3.60% postgres postgres [.] slot_getattr
2.66% postgres postgres [.] bpchareq
2.56% postgres postgres [.] heap_getnext

dev (jiting):
SET jit_tuple_deforming = on;
SET jit_expressions = true;

Time: 24439.803 ms

profile:
11.11% postgres postgres [.] slot_deform_tuple
10.87% postgres postgres [.] advance_aggregates
9.74% postgres postgres [.] advance_transition_function
6.53% postgres postgres [.] float8_accum
5.25% postgres postgres [.] tuplehash_insert
4.31% postgres perf-10698.map [.] deform0
3.68% postgres perf-10698.map [.] evalexpr6
3.53% postgres postgres [.] slot_getattr
3.41% postgres postgres [.] float8pl
2.84% postgres postgres [.] bpchareq

(note how expression eval when from 13.39% to roughly 4%)

The slot_deform_cost here is primarily cache misses. If you do the
"memory order" iteration, it drops significantly.

The JIT generated code still leaves a lot on the table, i.e. this is
definitely not the best we can do. We also deform half the tuple twice,
because I've not yet added support for starting to deform in the middle
of a tuple.

Independent of new expression evaluation and/or JITing, if you make
advance_aggregates and advance_transition_function inline functions (or
you do profiling accounting for children), you'll notice that ExecAgg()
+ advance_aggregates + advance_transition_function themselves take up
about 20% cpu-time. That's *not* including the hashtable management,
the actual transition functions, and such themselves.

If you have queries where tuple deforming is a bigger proportion of the
load, or where expression evalution (including projection) is a larger
part (any NULLs e.g.) you can get a lot bigger wins, even without
actually optimizing the generated code (which I've not yet done).

Just btw: float8_accum really should use an internal aggregation type
instead of using postgres array...

Andres

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

#19Craig Ringer
craig@2ndquadrant.com
In reply to: Robert Haas (#8)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 7 December 2016 at 04:13, Robert Haas <robertmhaas@gmail.com> wrote:

I wonder how feasible it would be to make this a run-time dependency
rather than a compile option.

Or something that's compiled with the server, but produces a separate
.so that's the only thing that links to LLVM. So packagers can avoid a
dependency on LLVM for postgres.

I suspect it wouldn't be worth the complexity, the added indirection
necessary, etc. If you're using packages then pulling in LLVM isn't a
big deal. If you're not, then don't use --with-llvm .

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

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

#20Craig Ringer
craig@2ndquadrant.com
In reply to: Craig Ringer (#19)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 7 December 2016 at 14:39, Craig Ringer <craig@2ndquadrant.com> wrote:

On 7 December 2016 at 04:13, Robert Haas <robertmhaas@gmail.com> wrote:

I wonder how feasible it would be to make this a run-time dependency
rather than a compile option.

Or something that's compiled with the server, but produces a separate
.so that's the only thing that links to LLVM. So packagers can avoid a
dependency on LLVM for postgres.

Ahem, next time I'll finish the thread first. Nevermind.

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

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

#21Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#1)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Tue, Dec 6, 2016 at 9:19 AM, Andres Freund <andres@anarazel.de> wrote:

0009 WIP: Add minimal keytest implementation.

More or less experimental patch that tries to implement simple
expression of the OpExpr(ScalarVar, Const) into a single expression
evaluation step. The benefits probably aren't big enough iff we do end
up doing JITing of expressions.

Seems like we are try to achieve same thing with 'heap scan key push
down patch'[1] as well. But I think with this patch you are covering
OpExpr(ScalarVar, Const) for all the cases, wherein with [1] we are
currently only doing it for seqscan and we are trying to make that
generic for other node as well.

So do you see any advantage of continuing [1] ? Is there something
extra we can achieve with [1] what we can not get with this patch ?

/messages/by-id/CAFiTN-takT6Z4s3tGDwyC9bhYf+1gumpvW5bo_fpeNUy+rL-kg@mail.gmail.com

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

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

#22CK Tan
cktan@vitessedata.com
In reply to: Andres Freund (#18)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Andres,

dev (no jiting):
Time: 30343.532 ms

dev (jiting):
SET jit_tuple_deforming = on;
SET jit_expressions = true;

Time: 24439.803 ms

FYI, ~20% improvement for TPCH Q1 is consistent with what we find when we
only jit expression.

Cheers,
-cktan

#23Andres Freund
andres@anarazel.de
In reply to: CK Tan (#22)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Hi,

On 2016-12-12 18:11:13 -0800, CK Tan wrote:

Andres,

dev (no jiting):
Time: 30343.532 ms

dev (jiting):
SET jit_tuple_deforming = on;
SET jit_expressions = true;

Time: 24439.803 ms

FYI, ~20% improvement for TPCH Q1 is consistent with what we find when we
only jit expression.

For Q1 I think the bigger win is JITing the transition function
invocation in advance_aggregates/transition_function - that's IIRC where
the biggest bottleneck lies.

If you have any details about your JITing experience that you're willing
to talk about ...

Regards,

Andres

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

#24CK Tan
cktan@vitessedata.com
In reply to: Andres Freund (#23)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Mon, Dec 12, 2016 at 6:14 PM, Andres Freund <andres@anarazel.de> wrote:

For Q1 I think the bigger win is JITing the transition function
invocation in advance_aggregates/transition_function - that's IIRC where
the biggest bottleneck lies.

Yeah, we bundle the agg core into our expr work... no point otherwise since
we do
it for OLAP.

As for experience, I think you have found out for yourself. There is a lot
that
can be done and heuristics are involved in many places to decide whether
to jit fully, partially, or not at all. But it looks like you have a solid
basis now
to proceed and explore the beyond :-)

Send me private email if you have a particular question.

Regards,
-cktan

#25Bruce Momjian
bruce@momjian.us
In reply to: Andres Freund (#5)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On Tue, Dec 6, 2016 at 11:10:59AM -0800, Andres Freund wrote:

I concur with your feeling that hand-rolled JIT is right out. But
I'm not sure that whatever performance gain we might get in this
direction is worth the costs.

Well, I'm not impartial, but I don't think we do our users a service by
leaving significant speedups untackled, and after spending a *LOT* of
time on this, I don't see much other choice than JITing. Note that
nearly everything performance sensitive is moving towards doing JITing
in some form or another.

Agreed, we don't really have a choice. After all the optimizations we
have done to so many subsystems, our executor is relatively slow and is
a major drag on our system, specifically for long-running queries. The
base problem with the executor are the state machines at so many levels,
and we really can't optimize that while keeping a reasonable maintenance
burden.

This is where JIT and LLVM help. I outlined two external projects that
were researching this in this blog entry:

http://momjian.us/main/blogs/pgblog/2016.html#April_1_2016

I am excited to now be seeing WIP code.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

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

#26Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#1)
7 attachment(s)
WIP: Faster Expression Processing v4

Hi,

Here's an updated version of this patchset (now that SRFs are
implemented "properly"). This is a large patchset, with later patches
being more experimental than earlier ones.

0001: Add expression dependencies on composite type / whole row components.
This patch later allows to remove some execution time check (in
ExecEvalScalarVar / ExecEvalFieldSelect), which'd be inconvenient to
do when JITed.

0002: Make get_last_attnums more generic.
What it says.

0003: autoconf test for computed goto support.
The faster expression evaluation implementation uses computed gotos
when available. So we need to check whether the current compiler
supports those.

0004: WIP: Faster expression processing and targetlist projection.
The most important patch of the series. Changes expression evaluation
from being tree-walk based as done in execQual.c, into being a series
of instructions. Then adds an opcode dispatch based interpreter for
that mini language. That allows to later add different
implementations. Projection is now a special case for expression
evaluation.

0005: WIP: Add configure infrastructure to enable LLVM.
0006: WIP: Beginning of a LLVM JIT infrastructure.
Needs work.

0007: WIP: JITed expression evaluation.
This finally performs runtime compilation of expressions.

I think up to 0004 things are getting pretty close to being ready.
There's some minor FIXMEs that need to addressed (primarily around
CaseTestExpr/CoerceToDomainValue when invoked outside of plans), and
some more comment work.

The biggest remaining piece of work in the whole series is 0006. Right
compiled expressions can be leaked, which is obviously not ok. So I need
to write ResourceOwner integration.

0007 covers nearly all types of expressions /queries (function usage
isn't supported right now, and there's two missing calls to
MakeExpandedObjectReadOnly). There's a lot more that could be done for
efficiency (generating smarter LLVM IR basically), but we already see
quite significant performance wins.

I have later patches that
- centralizes tuple deforming logic into slot_deform_logic from
slot_getattr, slot_getsomeatts,slot_getallattrs and such
- add JITing of tuple deforming
- add JITing of aggregation transitions
this works by converting transition functions into expression opcodes,
and then adding JIT support for those new instructions
(EEO_AGG_FILTER / STRICT_INPUT_CHECK / INIT_TRANS / STRICT_TRANS_CHECK
/ PLAIN_TRANS / ORDERED_TRANS_DATUM / ORDERED_TRANS_TUPLE)

but the above 7 patches already are large enough imo.

For now 0004 is I think the patch that really could use a look by
somebody but me - it's quite a radical departure from how things
currently look in master, and it's a fairly large patch. I don't really
see a good way to break it down into useful parts :( (the projection
changes are the only thing that I can see usefully being done
separately).

Regards,

Andres

Attachments:

0001-Add-expression-dependencies-on-composite-type-whole-.patch.gzapplication/x-patch-gzipDownload
0002-Make-get_last_attnums-more-generic.patch.gzapplication/x-patch-gzipDownload
0003-Add-autoconf-test-for-computed-goto-support.patch.gzapplication/x-patch-gzipDownload
0004-WIP-Faster-expression-processing-and-targetlist-proj.patch.gzapplication/x-patch-gzipDownload
���X0004-WIP-Faster-expression-processing-and-targetlist-proj.patch�<ms�F���_�O���lI���\3q�u��>�m����Y�+�5E�|������.�%EJr��M��6I�������b#���io���Ao<����oO�1���iw8�����Z����[�f�1�v����������3v�����@�������K����!��-^�_�H<C:-6d�����M�������I�[�����=c?_���}���I��+����{��HL�_���������A�,��BD�Fu|���S��-���Y�]�^�D�wc�����%��-��������4��b�}�6������(��A!w;��2�h�Bx2����%�k��{a����KEG���9w�?�7-f�[���C�� �^�n����BjN\�0v1��>[�(��`6���,xj?*_�:�����/���o^����T��v��,���<Y�a����~��t,&�����c@
�������������~�(�H^�����{
<C2�HkD��U@bF%R�sd�"�XU���
�.<!w7�m�^������1�j	�����������hU�H�@$��%c��q�e-�wtk�I�ni��$��N���WlsoT��?�A���A�r;\ 7u��mOt	-��6�:��
��i�]q����6gsH��M�#���v���wk��4*��C��E��
�u7E����.J�p��'Z��w��C�QR��n��H��a��>B���_����^�-w^�uo�)�hK�iO�������C
�x]���	C��x����C�~�5y���9$R=�s7	'�!��Kb�C`��?B�������mg��C�M��G`/E������=-]K��VD�9al���7"��� ���n9zL�a������~�m$4�{�~�E�������{�{�=��#8F�$�gr�����!����i!���)F�z�����W�4��fy["2� vE\���<Cz��/&;�'�,n-�I�Y��<�5������(Y����c��U���p\��.|K���q4$L��k�p���V�2�2����t����m���]���z�����v�8U�p��}
����B�-6��$������q�4:fW�G�&D��(���z���0��ZO��=�J��R����v{�@�srH�bU'/�
���kN�	�t�&�����T�0V:��:�����|���7h��+<R�����~�U�����aG(l�:��#f
\��E�}eC�{���-��g���H�&D�C�h�$)n��^ckZ��i���z�vD=�aD��Y�-D�����if������)4+A[��|^?.R��Xc�A�'G��u}�n�����X$�G�c�a.��y���v�%
>�}�>��@�A/����5G����h�!�!t���!a�������{��0J�F�^��p�����[��Ol;/o�iIi�����N�5u�G��Y �W����`�$�Am����.s�{�n@�����`�
&V����P���@���qg�7���3�N�e��5D���P�:t�����MR*�����-��a�?�`��3���R%$&��Q�x�R6��Y�}���Q�h�4��-la�	��5��l%{4���_)��D������Io�GSd,�=���d4���W��-sE���pB�&�����q��d����|a5m���Z�g�:s0r����%N	�oED
��$�I~�h��|-�D�\��0��|7�L�~�u�v��)�4w[�"�u�\�@rEcx��x�H���RYA��%'�����40��?���	@�P�a�pp�Y��y�P.8h����)4���_�u,�.T�i�	j�V���"L�1ZK��a)}�������K�,�^U,����L���DoA��EpC���$tCo���h�n~� 8��G��_4��~��1\��O3M:�I�t�0%��	���E��V��������={w��������[��C��e�m?�V�K�QM�5�&�R����AJ��^���"�[ ;�a��s�&B�)�0c<V,~��aP���@~$���T���&B������[���T�#�]�t�����b�I�N[������P�b�t�&�G����^���S���1kX���2�&���8�C!�chLh>���2�%j��I[-���x�'�z��}������7�i��2�#Hc�5�����;.{��'b�I����:W���6��������h���M���9^L2��$G�!i����-��n)`���Z��L!<��lq��0�����	%j�g�i�k�*���>��J��@Dq�1��0e�F�����TcY����'�Y��2�������������F���?����0Nq	h��%�Wb���C��iW�g��[6[e���G��.��Y��q�|y��.�������OeW5t�4�
�3=/���?�pM'�pM'����:�S�I�D�o1�nK?L��L�zc�	�*�����x'm�KKl[�q�a��y{�};���-\j��19'�^�?�������\�n�?�J2�%���x4�t���iw:��L:��D�%�M�ak��&��4�u�Fld6%}��	�s9k[�c����n�����Oa��K!�_ 3d�<M�leR
��r&e��k��
�%�3'+ ��y�(vCo����V�U>��01���{��;����C�������/
��CwL��.�@n^�jF;��U��b�I���C�#�	���D�5	E8=/�7���^�	�}@*���6
x��UK�v��� �{�-�7�y�����pnNwH�6���B�MG(=������q���������@$ss 6 ������r��}�*'�D��X�;*g��X�����<E?U>�����h�����q���
^>�y������k�c��^�� ;��js�������*����$e�%i��UJO[q��L`'��k�*K�Bq'9���Q�,/��D	;Ni��9Nr�>����F>�p7��E����oh�7q���q�s5`'L|��(�����rH�s||�	TU�I�U�ojL'�aO��Ng���i���
���OA����P�6t�G}	�>����?���H�[��\��M��8��+?�$�����Z32�A&��"m[�W|�����J����2�!��i��+0����FCjr�� q�Tf4
p���hV^��@��5]>F\��p�:f��y�������A��"�
�������j�P9�X|�������H��n-.�WF`�n���*^B�,��M����Ft��M��
P4�8����Y�y��!�P���&Ve� ����u"���\pT�X)�Y�A�r��M��0�zE�<h j����gX_a)�=���h�0�:�3���M�)���U���2#�}t����]�"����#ZY����Ng����g��"���m<&I�Wb�x���8���.�%�t<�{B�aWd��8C����W��D�+F+����.g.�CY�����~��>����@����l0��.��������??@o3B;�6��xB:+/h�eB\�C	l��3:��8����u��t);�r�h��&r(h��M��H,�V�`yfC6��F�Bi�����|���q"$�������PD��� ���-��&;&*&ug>��t��\_[,�\��h����Z��[��md��Q��P��G"�p`�a��K�J�@��]Qk����f�:'�(�FB���k�
��@�6���>�G��d.�-��t�6��v��F����S^��6�^X����YZ�<�[�R�9�q��)���x����7_��V�����9���RZZ��$�hH��L�q	6���NR|�;
_V,��0��������Y��uYj^�#� "�%���w�go-���L�W�o$N�@���
���+<:������F��6���7�2�=�]�Hm�:{����9����qT�39�vm���������9�I3#2?�tf������iD�c��\n����
����[��B��r?���8�ZZ��A�74,'%KJ��.��SLk��S�L��5����<>�a)�;����*`i�X��Bz��o�6[��3�e���t�(#A]5�����F�tB�D����j��y� ���A���j|rFc&�eb�v4�Oc�2~���"�����,��ht��T�=7�/�V4B��#&�d��aLi��0&�8�j���[:
����W&��miW<��k�R�'ypr���Xi�8����q~ssu�J�j
x2�9�����~��z{zwqu�����E�����_�����/0���`z�������2k�T����1�[�i�-'d_~���������$��V�H�^���9�0;��\�w��i�t<�u��z	�{�X�����Z�����:�P�V-�1/)��G~�,d�C[�R?���i/T��+Z5h���sU��UW�������%��-r��1�[p5>��A��7�
�Skd���������Y'�Dp`��6%�H�e�"��O		I����������I{��H�d�;��Jh���}k�����8�A�����GF��b���;P��~tPb�]4�)����^`eJ���F�#L�SzRFU���`��t��|4�Y��
,Kt��!)����P)���C��=T��w�������8Wa�P|���������Q_��+$��/���TV�lN����g���7M}[��r[����Y�CZ����6���6�_'�*&�t`�������x*���8�o�Xc��J�_�4"�E���H?e	}E�6_�.F!T<+�U�P$@��"��q��E��o�2U��4����<����tT7�?�-�3���G`caC��b�7�����O�Oo.��y����oZ��"���`���K�n������T���$U\
��
����R�e��3���W��\���Q!m�b�s�e��"��
�g���+��R���#��d���<$��J�����8fpJ��o/g*�b����Am�j�T�������������9���;���YF�7�Q�Mg�no�P~�j-�_�
c�v��)C�CH���G@��9���L}������4�������M���}�������OWN��_j�V��Q4X[%o�S�����w?������H�=�2]W��m���
���D*b�TOYR+���#���xH8�t��"H:��g�50 �u�|�,OL
�)c�<��g���!����J��hZ�A<���"G%;��4���WA`zj����H�-;w�J���)�^�1�J��_�c���?�M�I�wb������dZ+�f�V��EG��|Y?>9�u����`����5	�����kte�^��'��6L'A�e�{z�)n>��}��*m����g��1�n4���$z����\����H`T��)������c"�LDD*=TQ5-7�!���A��F<�q��T����0��X=X��B[c����c�i�I}dL3��4��`)��0]������&A�|%GGb�����Z��<��(���B��p	�����_���[�5![���P]������5��.��c������ RK��O6s'\",�x�A�YG���o+������YBM�������H]ID�:S`#C��w�)�����$���re��Nz<&�*�U(a�Y������	��K�DX>�-��$��M��f�`�c,#�))��D5�s����T9�)�J+����M4,�DB�����xl�Km���b�}RZ'^~i�X�9�b�"�i)r#B�Y�DK�f~M�����D�gL��#!�J	A��`���Q�\Y�.� �d@�����b��.6V1+�a�{����q-�����2)[�����Ht����Jr��9Y\	�hH�H�j�����\�H�������Xs��g���!�%�!�Y�((�kN0��&�VX�Y��&�+�*F��������"���vg1 iv�b9#�s�1�I���;��[m�B������`����7h�_�k)������m?�!&I<-ds�g�x|�� 6���l�Gt��b6�������G/3���Ho�Qk��v������G;_�����N�r�@|`�Gq���NL����@)��r������D^����=X	�{��:-��`�w|v��`�5r3.����;���{�_�'oj���7���/�!�3~�D�H�1�a���bz���N#2J��-k��.�X_z�c��/.��j�{i��&x��YZz:�P��$-���i(F�w����j/o�p;����'������`�%���E��K�oK%]�p|������^�5�b\`�CL�z;I&��/��G��d~����3�c���$�1��Y����G:���t�;�f�h+���5x�E�A)�"���?4'���D����������7������{�(� 3��
.
���*�f���*��*�]����5�,�,�(��>�t����#}�g�J�]k�<��o�q��Oq^7��0���<�4}��Dv�<�9I.�f��l����,JQ��N��;MI�G"*�8F�A|0-v��>�5�9��9l;��)sg���x���3s���F��N��iA,+�
_�2����p}Ne���n�X�D:q:@���:p�s<u����>�{�a�Et���"������i}�%�$��
phU�H�#�@8��W��W���v�M�Dr�FWl�EU����b%��f��������������x.N���Q��5	pu3}!��E����|��pnf���oD@�ah��j$����kk".�l�����d�5�No���K:m���[��|���K;���G�:D�Po��;i}���)H�$}�D���mg���l��z�	�h��#�qE�������� f��������L��P3�,��B�Q��=
��R?�TH�tw:lhXy�Ki�Z36R�7!���i6EY�,��)�2�
��fm|���g@�Zf��2�@��m�1����������,�������4����kF+.��vL��0u������Fg��&�`�H�gs��//DG	�(���p���(:����0���e
�
�GL��)���1�M�?���#����s��R��	b0���Pan>���^ +m���=06b+���\��=�Z���:3�({�m�Y.^�^���}�U���������%lk;t.}��o������Hty��^�?���\���L����������Pupy��Q�\�������Vat(��G3F��7���X��? -��������!B��UFz�-�]Wz�
�9
��k��T��$�����x��l~�����_���p�"�F�0��"4��L�t���9�JEk�T�'N�b��p�;�X�U����_��;��Dxh ��$B%B1K��E�IG$	QB.��z�y���'�2��0���l��'C 3�b*�9����2�����g���MgY�����#�����0N���7��=�v�/zCFti�NG���?��+v���H5�9`3��n/��38�����[��>G��0�m�x�����S�5�9��-��J��H�,��<u���Xc�����|�>Q0i]wccQl1�����;�.������������q�+�t�O���W'N��E���%k:��������������#do�����}�M�%�|i�����Az��f��d_4�e4g����M��A�����^����r�@���=��K������$F�5@�I��en��3v8�f�I;�����T���-��o��D�'�mM���!7X��C��)@�T�bJ���V]$d[CHl��Oqn���h9\����k���������Y�]���4'�4
;�RH��Hz��7������C����xOP�*�$!��=��m�1�0�]���W6F��$[u�F�SC!��8~I"�%St�u��+=����T��	d3�57\P�d�)jK�u��#DP,5���C�4�5����qo��_f���!X:}B�V�!�C��$�b:3����1�eU�}FheLhny���r����ID"��=��o���o��������>����d�������Qi���y|�-��g3�S�(T���j���'�.L�����T����06y��@#(C���Sx�;�����y�o���t1���<��p�����xG���X�!0T��zn�i��73a:�f��;V�UR�D��&�8�%)�����"�t��nw�_��v��y��.m����|�(2_��$C�kS��L��]d�8P�E�5�*�P�tT=��H�
b^����CF�=�������y=��n0�`�������_:S\
N����F�F;�>u2�`Cj��<���t���C�1�Yd���t��M����7��-w�9;��X����]<�w���)�I�F	�2X����h�c�D`N#Ck4t���������b��������f
�W����&p�d���3	F|`�h���H��l�f
�\��^>�IY�(=-4��������>�,L�=�Ok��� ��cc��(���>�N�v��P�������G�1�0�mC��8�O�[3+��C%[f?ZE�&�XW�'Eg��D(���RS��;O7�J�x�������
W��Eu�E'm���e���c�&.��P@�qv-���4C�Z}������<A�9�;��2�4�dW��`7u-$�jN���6��*^b_g��-��;�G�&;;zYBN��K��A��dv�H���Ys�l^Iy��i���zFtt�*��K��`&��EP~!�Z���Ds�q2I
	m��8N��0�$�b���]��s��qL��-~���`lm��Y`�=�c�Ay�uW+;A�u���%���������I�|�xV�T�W@A8��1;&Z
(��$i����6<�����u��1���o�bU���}���'�,�| >M�d$���2�[5�c�olr����x(��\��3^����8�vS�hA!���3h0�S6�����3EF��H��J_Gj���(m#i��3c�{C���XC���Q�e�:����  ������~����GF%j�+�����nl���c�3��;'�)���<O7,kh���8J�8J����!��x�*V�?�!�8J
Zl�QvKe�X��xe�Xq�%�XU
��cE(�����:M�J�g�8B���i�^��Uc�����~�
7���xX�q�$�\C>NU��O9<�j�py
;�V�!,���x�������@��z-��T� ��&�1��s�Z��������c����	�@���'�K�{�Lpl{�:�KU�1qt��s�bNq���"�#���d~M�[�������#��r��q:zHE�UVU�6�.��A����F^���N�7�'ks��� ��$�caJ@���v����7��q��3$�Af�dG�WlF�\�eo[[uV�N2Pz����
�*l�$�5��zSC��<�"C�5q��?�.a��4g5�A
5�(H��Df"�+�������b������jAB���B���C�p��vA�� :���v�2����
�wvD�n��.Z��op�n�v�����TN,3/�j0��8E��Tq���,���aX%~k|���Nl+�
���\,��H�F��V�=���*�,��$
Y(�3>v����N�q~��>��Y|���5�l�������������������+1�Q��]Ng�8:G�tr�����$���%GUfr��>b���w''z.���-:�y�����Y�������B��O�/L���'�0�����
7��m},D�w�N5����:/j���3�(n�nK�Lb��>I	�cMHG����R�H�l)���48tu���Eu%l��I�c�?uk	B�W����qg�o\��2���j6�r�fA�������2�h:��.	�S��kz`��}5�;���������S:�!
9�4/4AuP,���BzhX��	��bk��^��X�0��� �
 ��U�_��h����o"(����������K�u���t�QJ�����m�IuF�t���2�Gz�QR�C���s��Dev����&��f�SQE��1���)���	L�����4������)��Lq�2�Q2����6pW�E^����F��P��%TU�Pp���H�AZ��d)R��6"Y�sh~.��,a~S�J��[*�W	���6w_����a�Y�k�e�j�
~��'��/���U�x�{j47���H6D���a����(y�u���XN���	u'�E���u����M��
->���1"�v��?�{'��bAw���e�&j����<���4y��~K�'z���N�zj <�~����7n���:H4vC�M�������U�&P�s�'U�&P5�V�T��{y�.�Y��q�X�>r	"B�U=2BO(�/��U�"��R~��~��6I0�m%�CI�MO��+�A�d�~�:��.VO*�����a
xT�/HL����o�tT�"4������A������6#���i��h����5*u2dX���q7:�w��(�.�8��.��P�p��G�S>��8������b
�Ov9M����tG�D��6mEZ����v������@�,�����jg�Q���������m�p�q%�����|�w�����r�y�����-��bQ����O-g!�������r03�R���s4`T���2���O2�[�A��,0:f����V���~��@��Hl����[�Ud@^��c��������g6�b�}����#1��C(�E�u������������6�Q���'����W�Ipg�����q�kS����t�'�W/�PI	�f@����������
_�R(�l�h���+���[H��4�>�Nu��^V�w�
���m	�V��mtw�C6���A�q�u��#\����u�����@;Wi�}��������{�#0�[�
�Bd����o� 1����G{�>����:Q���@Sm���m�H���u����
��1��*-��N����ifc4r����k���K����C�*��2Y�i�D��_^^�X����_��r�����,U����1����k�Q������������m�4�&g��gNV��d�:�!g�������b:��E�3Q��
���T�&q>N���kT���"�Z�S:��B0���Br��C;�>yU�� K�j�j�p������k������5�JP!�Y��Z�'<�]rg7�Cm�[����5�Y���o^"�ZK����w��I*+����Vg�����*	.��YV�����i^������>���M`�����
���5�.���5��
�?�Xi=��/�Ye�����PQ`���z�����f�Z��;�u�
�
���/����u�����-Mx:#���2���u�r�����L�����z�X�e�j����t�Q�m����d��������������?������&�7(�����9��/���HF�-�E�bQ��K����\��-��u�r ��^�q����.����S[R��M�Q��/�hig�>��
K�c�W��2��Z�Qh�J�A���K����*5W�,���iJ�N��c#�����B�q����������������_V�����\�����6�cIk�w�j��
�;���f�'�A<��9��R�H��u�YL�M�c=V�K7����Mcs*������U�0��i��X6�VS�x�����u5���G��au �������h��"W�3��C�D#,�����]F����P�mv�	&!=�1�C��.�	�JK$d�f���U�?X;�T���8ce(W�$=�x�8ey������D���oHT�;cu(}�
���k��B�\7h�Z���un�"r*V��7�5���9��65Md�W(r!
�wA�}�8��O�z//N��g��A��}���Mu'�5G��}�(�7���V�b�:�����54�{o�B�Yo�`\(�1��QA���`�������Q�w��z�V{^��I=Q&��o����a��p����
�������_��L��Q���>k+Z�^�R��D�:�F8mI�bn�P���M|N83��&�
 ��C6.��� ��p��V���W]�s2`�	��x3HvVb����Su�r��7<�C�~�]�r�=f�K�u���Q4��[2�NE���}��r��� �U��������8Rny)9}��������%��6�*���UcJ��	�B�\}�h�{z�[�*
�As=��
6�t�v���T#lI=D3����O��M���@;����{_`��S�k�����h����J�}��B��f_z�KsU_� X_��O{����_�����������?�@a����i���:�7��J�h�0�#�q����5��O�,��x��y,��g��k5:��,n�>���s,�	���Ge6�5~�:�����;�����K�H��'J���9�10������(�0��\y���aJ�(��d(�E���h+:��R���f�wI�@��0d���H�����o�������Z�i�fM���[��y�?�
�{/�'G5�gU=,�-
n�MM�f��������;�d�������V���:�������L��.]�����������1|��q�	nh8nE���:�R�C��Ey�&4B�uEa���
5	4�����5����A�3�N��$�Ni1a���l��?��`��BiA�=��p�GO9):�p�t�	�("U;`PSBQ.HG��
3�9�a���������U�F���?QJ����\I�����vO{���m��<ql�17�q�����sAF�U)������x�X �R5����'�MQ��c��}��:�8�*H�^��O4)�N�j7���� �	�c������]�w"�T^=��P�0������5]�t�'�sl�4���4K��w52x����Y�S��j���0�WY\
��1eR�G����m��5k� �_K���#n�$����y�$�2�\*UK���I�o��b~��9����qz����!�4��k���|�"����=*��S�K~��y��f\��]����OG��4�3��660�iq��d��9�	��Pt����N�z�<�Y��b��o�	�Q�R�e�4�6��y�k�j��|��y@�nYk�=�9���d����EQRkTe��=8{�L�}�X8;�vV����q�+�>W���"�>���i4�%p����������1��lu�J���U:]�V�����TS�)�AV�1�� ���0�6neMp�J�w�X��[�U:]��
?_�Uw�E�D�b�,���bK�+����������9y$��c<Y�����ee��{����-�'L,�Z��J,��PV��:wM�FUvA�x���{x��G=�;|�s��qD�)���-���)��9
Z�����w-:H"k#�;���q�5bK���G�����$��J��E��a+�d)^<U�Q����3��(J�q(@0!m��Q������������8��`�}��^6�=��d�������HH|&�����C��uun(.�_h��f	h��S�8�������R�� ��m�u�_K�H�M�P�5��4���U��(Y�T����D(���fz����Y�E�A*�s@��ds��*��C�9���<������E�M����������q�ut�|%�5�+j���uS�5�3�~5�����US��GY���b�OJ��	�=��W��t��H�L���N�����}��t��E�Hf��t�����i������96b�����A�����#�F��ju-5������5�\N�5{���n�Bv$�d���
:�8x����i���
���m�][C[�����5:��-�nAX��,����6W�R�
8�H��*����{Mp�>YP�g���
u��u'@w�=����7�X�D7f��7�|��R���Q�L���.�h��N*!S��4�y~�0�$��N�{t��gS��.���1��9�;�|���l�\0N��}�;%oA���uh�����M��4�J�9U8�-�f������jL{������o�58���I����k��p���K���J.����g#S�ff��p�/
f4���9�z�9w��(s0Z������O���V�r�}o!��&����5�"��n�}�K9�NQAq��k��x%VG�@��|��`���bu�)E]�d���3q�wC{3���%�W��l�RW"��F�cz��v��Wd�4h���I��}�x��KU���D5R6	!�h}~Ki�r�4�!����b�_o�jmi�����j��(1����	&9T�-���*����6v�&�f��Ef��J���_?1�\��o�%�5�0��@9s��W��Z���
]�g�6�g�JU���4E��VZ�R���V��ZDs��d�A��e������vo0��]���}������JG�'�cF�iH��9�x�����'����*�y���11i��:�E>���\]����N��~����gDJ<��&>��bJZ��*��4�D�l�c7�x��J�P��`��nB�8LT%)����������
r��l�q���8���R���i��i2�������%�w"3M��m`��0��DROs�u�Y@!�����uxK�F�!�Ks�t4�`6���-�]���f�9��>���XeWC���G����Qog�5y�d����n�K��_��gD�j6a�
�������#T�c�� ���|��Wr�%��9�������Y�hax����$C_��O����D�-����=�2Vz�z�����"=-��Y��Q�A���[�s�j���=�E��N�v��~l{7����������i����0�;'?-6}���g�a�'������MBB�9#�����F��t�%C�����������;\
���!�!�F�D��u��k
8M������#1&v���v�;��.J�:8>��92������x�NP/i�Mjy�����o�)��$Q�Eq^�2t��M�Y��N1�����Te�i�	?�Z��v�����?�[��"�����dOh�\QKq1z���Y�i�T�$wH�6"u]���G?����L�����/�Inj�����rCn�pN�=2�H���}�)C���6�
�!��Mp/oSC��$|�U���%�lg�%�9+M�mo�6�A^�7?����o��Q��^�����E����6��T��@�}��A-l�Xk�y�ij"��g-L~��ri�<-' 4\���
����i�Co��/������Kf=`��t��fv�~SY����
�}p��H4.o��p�n�_~J��>����$�-gi�Rjd3�����[L--�$M��"�^���T��b�o��Pp-<�����e��;���t\z��p��������o�VI�,O'X4=
U������KX5����9���R��us��\(Z�H��WP��!4��I>IEb����%o���~k�Ib)��F����4����:�������
]�.�O�d�&�b�[��e$�r@�T�F|I�+��!�a���ND4���RA�\��3����aL�y�S�py��F�	���$�U����^����k��p�Z����2s��C����`�]47wsc,� ����y}n���VNO����a���:rcT�l�������n��������P��
�6t��U c��G5&�#�gS\�
�	��|�2G�p+Y+�����{��!yn���T�h��|�-��Y<N�����][����o*�����N�����#jF����Jn,B�R��x���\V/J�����KBt=#�4�B���`��wj�=��J��F��-\k>��|fX ���d\h��R���E�)���jJ��T�n0��S�X�w.D!�c���
y�H(7v��\���O�r������Q:=��V��}mM��$~�R��f5c���do�km����s�R��������f�S�<5i��C�aT�9��I�^&RM�����+Y�B�i�=��R�����5�{�{?�{G/��E�E����O�Gm�X�����$���Q3���{�{��)���A�803$��y�`D�G��%�J/���p�x�L�/29���e=������z�+'Y��q�V���I��n������\��������Kw�4���Z��#�(���lV�i��!X��/C��������B�!CC1����B�o�������/
�-�.����]Y��t��C�X�'�OpXzmWT���b��{�F;*�`G��1AH	�6�}zg�yR�,��#�p	��>�	�e���g������)3���t�/��G�AO������z���Q��ZmEQ��,@\My�S3��*Tzf1�t���y���\�/������=��i��T{�
�\Lw��Vr j���5�*L�)��zT�������tG��uMV �����`������&����n��R!�3E��Hj�����Dg[a���2A���@����4-���8	1��.��7=8u������S��rQ�����>��g�^��i-����.��
.9��i�9�����\kW;�Tj,�e����AO+��ym�����V���W�P�r;:��j���V%���Sit'��8�^���7J/.�\<i��xi:H..�a
�����iy��k��6/���/�Z4��c%�>����5��,����Z�J�������/��}�{x�F��|�L���|�����?�_���|�|�k��|�}�B�.2�k1�����IN�s��S.�@�8}m
�>�_x2O8#��Y-'�
dE������s*84?T"P�Xb��_Ss�h�gC����=��i���~J�U�����j`*	p���[�J�M$����~��XU4�H���Nf��F����<1V���g�(*<���I�LN��}&h��5�0�|u9:�H���fI~��U#�;����6�E���xVp�X���������,O��lQ�'?JG�s����?=9;�9��:Y[b��t�t����!X���[���Q��M
MZD�lz�� �_�Z�M<5���	�������#T��;[]������0������V�=5���W{���(�d�
�9O��l~�f8��p^�A�->B����F���&�i�����yU��3Hn������)�r
���!���S�p�
��f6�1��!�f�i�����K������O�����o�����3~s;GhC������P��%V���I> �~�V�!��[�#J���Ka�����a�J����GX�{p|6Y�V1
r���d�5�����JS�����g;/�LS�p�(�PZ/�~���l�=�r��B�K�� �Fe�M`'�����Xe1)
�}�����f�w%v���Q���v-y1�"�I����d���N�lQ/���A��2���o8�}B	,X�4�e4��I^T��)*0]'�B��4
��ur(��2�����YV�O�W����^������/��������_>��K�wg�e�U����H��%��������������Q��"�B�t!6��n9��2�������U>pGB5���,�j��1k'��,z\�L��t�CG�]o���I2�nZ�M�_��=x�+�N�`��Kh�����Ma�������@|�ht��WJFG�S�K/O�#Y�b)O/��j���������x8����i����E��������������)���^� �]�����e�R���_I~0j�;������so��Y�f�hg{��6�{��3�;�n��y�����������C�$8�Y<�O���*����q����,�)�u���V��d~�j�)Cu;�^MG �IQ�a��v:T�	Q�&W$`�C����"`@@[p�E�M����Y���
J��<�o�PrG�1Fb0��g�Y�I:#4��v���p0�1�Sd�X�"�l����T�E6�O������s�_���oV�1����������:j~��Y��5��y<nmJ1�i3�������/(E���U�E����������1�]��2���8����`;F��+�GfiG��.��g5����K���,���R��]������)��/�*M�FBqnBpB�*O�{K�rZ^�v�^���`���/�'g����imB�]�FG�#=�"N�a���x8LfY�jjp�I�������2����e�� �.��I\gGNf��Y7������9�H3o�kW>&`�S����c�Z}�������xk��Yz���(��v�:��
�"���zM����qS��N>I����fP�P_����![��m�lZ�.c���/��mv��@����\�w�caP��8�|@�M�����Ho�G,��O�G@w�����H�K#�4�r�cn��t���:St>�,~���y����LTIa��c[e|e�0���$�	�3�I�>.�T�V���!z�^���	Xm2F����H��A����5�w}���X��t1��a3��S�m�)N������;BSP�<8>��v���~<��&Zx?�����)��t�T��Ri��;-������:[{Z��-�E��tV�{��kN
;^kN���5��=����:
^a1�<e������R�C7(����J���V[L{����55d����Zi�z������J��!��/�l������G+Q ��v�MX+Ns�;��S�b�G��~8��z}�W��Ha����	�I�u"�y�cb������`�����-H:����Dc��o������V6��%�.���n�T{�T���tF��WNU
(�;U�|VH`
�0���-��"����M���)����	f�R�1�0�11&h�p3���T6�dg���p_�7Q�6��S\)�PJufx(�E��;���XOr*�!�B�x�S�k<q��I�8����f���l�s��C��"m��#kRa	7��#���BG��!�D�"5��*���Z/��0���������G^����&N�w�L�Z�]=J������Z�6lU��8=���U�9O�:w_{��	A]
��:�
��;�a�(���,�����t��B6|�Bre|A��O�h���]5�a���[�r@��������G�t���s��=Y�X����kh���$�gF��o=9�'c5��dN��Mm�T��������7��Dp�Ls<!�O�%y��0!���J����B��W�%t�Z�z9��H�-�r'��h;�����'�u�@I���3��U�^�@{������u����er��E������]��#����/�bLfk�P��)Hh����ef�ZkB��w�!�����_��VF]��S�,oG��EG�?���}{y���d�hpxptp6������
S�{�^���v�N�{�����6��!���9����<���6�������"T	U�z��?���G�@<�qV@�O�J�P�F����B
%�{*���$���)+0X�}��{|����o�G
�F�Ir?����9�
c�?���"�^�a��|����W�~��Rz�P?+w�������`N����.HgY�l��sd�X��2l�36�j��pJ6�mt�Clwv����$���"��Lf�^��)�����PYR�P����v�88~�����Z�y�����H��\)�D�P��o�I�>���
:����M��U�$�������=WO�,H�6(q�Go��Y�]��2�%��g!M�Q�g���S�:5�b���c�������Q�))BS��j,;}�������ZLpH�����W����U����������g��4.]]hZ%�|�w���������!������r�����`����@0�?s���5�r���ObS6��:
!���h�|F]�(��&�Q�5��@Oi�a�zJ��dFN���J~���2��I�9�&�c�d#����]��OR%1�.Y��g������I�wH��En��A����%�R�Yz ������w��g���*�9W���X�@K�����>-�)O4���nN���x�7.O�d{�p4�B����5z{�"���oL��N����>%�@^����.�v����F�4C�O�J02������5��}������Y�rp���GY��8���u|CZ@�)O1�?���3����26�.b�I
���� ��au���Z���x��q�sv�PE��I��M�pO�����Ih�T��q	������<�j�I���U�m���N�C�.��j���]�r�a��wt�iW]��Z	�l�*��.uHo\��4T$m�As9�%��{zz����R�*V��/��?�4�C\1����v�t�]+�y(v����C�x��'!������|O�SYe��Su0�@�L������]���0w�k��&6��R��P��k�!����h�+�����/�?ao�c��ts�
���3�<�$}�.UE�Y%a$��J��"C��xf�����v����{�.YD����������-	&�A�����ml�2�������mN��������}��$9��n��������Q'r�TJR?��������������m-��k�E�!Tf���:M�Xy��L=P#�c(I��^W?,����x���,�r���R��m��UZ�c��7���o/��w��������������h���������W���V���;�~���������N���E�AM�4T���3������o�^�a]�����6^`b$��> �4���Q:���2����?�u�lIImf���Gj+v^E�%�3��C�[&[��f�].���L�����j�Y��
��=O������l��E5�:���+F�Y���:��������6�!.�%�XJz�2���u�;��Wcg�A�^��s��u"��v�	
hs�����k��o�L�0t�]P��2�vf)��,��)Pu�	��'8g�����l���h[�76o3��N����������|��7�A!��sbV�sX�sX��U/�y��p#�]����ir]��E���||��mo���G��~���C�6��+��_~5:c���y=��<��[<���v���'��P�Y�8���u�-��m�x�������n�zIk��v������G;_w�E1�����F?��s :��U2�f�1��g������D'�%y��.��4�J�"�����x�Q���N�:<����8������%�[@������W:���C��~��l�}�E���|1��9�����bJ�����c8�y����~���
�n]�dvI��Ko=�����e]��;eO��>��{t��gi��$-��h��{�<4F��W�1p���8��p1�?D�+�.��%����qqSP���[�Nf��7��O�N��+F�}U$���e�iK����`�3Tl�w�7$?�#�Cn����	U$Nr�gCRd���H r#{��
v_�@<�q���`��Z^���?����S�_��*��0��{&��8q����a��_Z�3����*c�=��hkK�����T�����K��i������x�k�������[���=�?8}�5W�����t��M��f��)}�����Ig>����0R��N�S7�H[��\���d)%^�S%��NTw������F�`F��O��F6{��)�Y3E~���{�1����?�\�����y�Iv�i;�J�����l&N�\����H��
::
��i5��pS�����RV��������
�i����a���:}����t�.�l�3YZ����q��c���H���p���s�0)4.���F���P5LN.�e��X��YuS�c����f����KN���=����M��D�0�r��SG�����h$��f������^LO+�#{�q���9�����
���{hYz��0�V�D�XS����u����8`�,R�'
�����Lu/)t��%����{�����*R-jJ�%!y	f9F��+K����R����
^
������D�9q�q�SzV��*�.EZ�_�!O���=	�xA�����Cy_���_�����e�\1�f���NeY;�Sm���_OkA~Gc�_Q���C���{.����9�s��5�0���N/N����_�O�}.-��(�<���n��5�3�maR]/i���.�qc���7�
����?^��;���8k~������b�=�����^��6��T��:Y�H��.�������=�������{'U�xvp\=N��/*g�T�i7KT���e��Exz�����6�����\��Bz�����^�����>t��{�?9jhzp�nnbl�����U������3@������Ja
~g��W���'k~[{B�D�5o	8�wRI���~����R���tTN���~�u������*P�����D^��p���R��N��b���|��4�D;��s�CvQb�M��h�����Ev��p��q�!��m������q�[2�i�g���gk�� -{]��p��|���	/fb� �7q�.������2��>m�fh��z�y2|+i($���^���NW��m��\'mE�Q
<����j��c���Je���JZX�F`�������k��%I�5��oX0�$��|^0�!�M����n�����V��&	F-�#X���yx��.SV\���;��/���h���G����E�{<����l����rp���6?�v�k�u1������DJ���Pt?��_��e�k �b<2=�����S����C�����l/�J�Y1O�>#�3�<yu������z�!���!yi�I�������FLkc�����E�z&P�������/�!M�cE��Y��������&-}#U��o�a<@���l�������v�=������ws�o���:����[	_J��'����t�������~�+m���{�yY���=�<%8��� �kh��|�}SB��9���Y�rL���H��$��	���wjY�G�*�a��@��\���1���	[l����P;;z����Y`+��HAN���Vm��5&�^gU�G���H�$����d��+�k6W�"�
dX3���~bd��N~��c�����(��e�$�G������Sr}k�P�}i�9>"�e�hlh����wD�F�9*��?"n|��1���E����M��-Z�����7r��f7�Mk��~�A�ti7&���;�Z��Y�d�����
^�E���W�����p��F.5v$D���J��*2R�F�s�0q��	�?0��(Z�&;�vR�W�O~S���r��1�����$f�@e�Ps��*KN����[ng�����j45~��^C%'��^��^[�e�L,��\�(;b`A_9��Q>��":���{�v�����N>P0�&����e`�I87�u��N��-p����������}��~y���������2�	,�������?��s��@���t�}����o��B�4S�a��dm�/�n�1w����}r`S�?�o�{�|���'�}��;�F_�����7U(<��Q��f;����C�.u^y0��.���?��[�^r���Yc���O��{��2��]�����.��G3>5G�$�|"zC����4F����b8E� ��U��M!"�Z�u!p,�`Q������B���eM���Q?��������[������>]����+���pu�����W��;���L\w�w��>�<�_�uU�F�E��	?�d@�����7]
M��U�,�b2\�g>���S��2��e���a�u����Y}}����z�����)��J�#d[�Db`%����.���(t�Mc���<<9��m�fm_K��0H��p(�n��~1�*8���x�%j�"��y�n"N��%�r�i�O���r���Q2���B����ji�����a�pYn�3�)��yJl��EX)7������f]�M�M�<���4`�yZ�B�v�?6%�����k4�o�R<�&pR*X��:(�����a��z���i5+�[�O�Y����]e�8������; 5�&�����J���E���]��]������&k�i+��l���mbB�UUU��{[%���I��X�J�
��0���-��������]d�����G��T+6����u@X&];J�D����V}?G�_}Vy�k�s�SJ�RE�������"�� � %��;�,�6,s
*������-�$U�a0��r��,��B
"H��d����������0K���f���	W�~�k��TZ����[��[S9��?���7�Z`�zb�3�'�`�������5���y0m��O� �1��[��_�B�)G����������-I�O�8R��)�����,#K��v~�N�����8�4�S*�R��16���5]<���,h����z,K�<����R�b*s�8��\T�A+���\��Z*:�sC;H�8������
���J�!�^=;��=2���Fs	��+	�]6����:���N����k���������_����l����St������(-��7������6���z9t<��;P�U���4�YV<�K����6��[���|{2�������Q�avm>�������J�(��
��qwg0����*J}��)&��}���,|:*�+(F���e�[���IK7�nq�XsF�I>���J+�I�5�<��Ze<�^�h�K~R5�d�^����c�Fk�X���0��S�p�'�����s'�����L�p��}rq�����{�\^i����LpM���X>j4�nJAX5���]eC�X������K.t��b�+,+K�KS�[�X��A	�J�Fkj��ln+�P��������s���#����2B�{O������1���2B�g�8]�DT�h2:J���H��=GSV�t+Ch���\@�)��Y����P�����X�t	�\�P�\
�����LY��PV���W��
��b2-�U�uu��MK�	J��VS_������mN%�r�ei��Nf�hZ#���O�+I�C)(]Y��x���Q��Y����=H�V��B��q�LG�oP��'N5������������vZ(8�
�y�������?[�������=���5����ko*�B��~m���owt'�������%'�����C%�d8_O_�4A'���N+S�'/�|r�s� 3��y��sU��=�Z�t�h�l������2�_�b�d�y�t]U����=e��r%��7	������2o�?(����
�
{fK
���E��X����&p��:t�"�[6/a]���e�������cJN�#���:"\��X�����B�[�
>,���u
���)���?�w!]�r<Z��e�Lr [���o��8��iZ\G�v3�
�MH���_+Jg�4-��=�q?Qhh�=.�r"	���dz�G�����q�T������[zGs����pm���;�����M2����~���S�Bc����h�S1 �F*����S'Z��R.Qh����BX#�T�#S�/<��>���~�2T����[�p1��&�-�����0���7I's�;���t�]c��^��l�55�X������4z�9���N�^��r���"��������{��WM�|WJ�19�������	�<�#�J��lA�����Us��a����-���u�
��K
�

�����[�;��
�R���r�A1]�PcK�?	 �Z>34����|#���S�o:�|2^j�����i������U���(��2����7%�L%��s��O�S-��B��.{�}��i��7��������bz`��,������I���AV��_�3K`��D�Z������j�*�Q\�(��}�I
K����>`|��)���i�s8�����u�;���"�FQ�=<��
Q~�����|���\��(�q�S�|E�h��hGK�\i��d�o�C�E�$jIYs�2��La-C���+!��%�h??a�TF�)@����X��|�Z[�F�����0�+�pxl{
M/��d~#�f��(E����2ZLAXL�s.�*��%�-H��$��U^�t���V��'��6�X���;9���,/�$����M
�w�vQ���]a��o�=rR\�6i<��~
B��E;e	��Lb��9��Qe1-c�!��M'���y��k<�����v}���a�t�����fC��� �7�
\_��^������D����43�j1i�f�M�����!5Z�1)[�=D&u;�rz'�d��H��MV��T��++Z�b��i^�9��u����@hL�DC�j
8`���%��FA
��4Y�1�p�������o����=O��l\D�	�L�f�&+���Rc��
m�oSe��b`%X��PP:��?���TS?�^�s��������>�l)[+A`���#����8<�Y:I��x���r���'sz�{��iF��iZT2�����+O���lyL�E�d�[t�5R��(��R���f=�����k���i����<�8���a0�YXe��pv�>&kw�/���w��{�sAO$/2)������A���O�`�{�S���������G<��`a��]����������&n=}������k��W�-k���nx��dw$ ��/D���>:�;�����@���/d(��LA��i<f�r\h��^dy�^N���
�i�KpTR�{��KX�9���kJ���#o�|qB��t3�/�l4W��C\`{t8G��}�5y7��	:��'�(�q��U}r�A����D\%��.������y��+��}Pn{�����0�1|�����p/z�g�Nz����������~��{�c���m s�R{8a��e�=2��
\����=�N���=��u
S�	"VF�@�cG��N��L���E�Gs4�)��S���[Wt�i�5��Qj�������:���xF5o�,Yk*u9$��g���nF�nn|fj8�c����8��d��� ���1�(PZ�b�����3oe#����:�9JF���u��Q���pY��� ��c8x�.����Nh�:���z��7O��mR%�|A@�<?�p����i����x}�t�BT�������Z���Pf�:�S|�t�MD%���U�n�([����)%�u����7��I��bL+{Nm;~oB0���wA�$��~C�������2�[`\�KJ��R�&sM	��
$�f:N�7�T�G�g]R���7
9�]'����U<\d�@T��/.n������/J����#�8���d���q�3v�+�������E����Z��(s�FG�Y��b�d`����T����#�M�>�tM��2K!0��������%��Dfjfd�*�������GcfO�G����%���I��|@y�K'HR�G\�2���j�Jf�9�V�<ETEE����|

z��\����)p�(�f�dT�EEBdz0Y�I[I��B�cm*f��(��[R��<{J�\R�$�xd�]1m2w��e�Dm�W�I2N��D	��]�W�rV�+C�Q/;k%����f�;M����AV����*'C���\gf�Vf^��$��	3�0����;>�<s��e�
���|�}��!��0�Q:)"L�j��_�v�?8��>�N��"07~a��)p����)�XAv������k�S���'��(wN�=�=^��o0�$F#�$9�0�d�e�-�]���+��K�?b������;�(�w���Ho�>2��^j�H����=����L�����:L�A���z����:=_��u@��\DQ������sy[�����L)�X64�Lu�<MB*��6��8E�&�u��m2����t"�8�&�'���BWn�zw���&�<��:����5��rj��BM%�D���=��CVtC�d���s0�a:O&��T�f`N��
�
��/o���2I��S�%�0��`��_�oPc���#�w���;LD�mm�s���<���S�1��P��>G�T�2|�1�I�kvq���6�;pJ����+�� �L�z8��V;�a��P!��Iv!���������o����`C�{�o�7[V�H�����1�W�2�h �qt�g�(��o"9c����	��"���y}"9��J1~$�zM�i: N(���w��7'�����Fj��LJ��#���s�����,���1f8�����}JL�{@��-��4��L:��b ��eW����i�d�Q�8']6?�%P���y�.#��3�������y�`?f�[4I0�f:���y��0�����(�����I�g}���O��!��4_nB����Cwgo�`/��5�G��M��
k������-��i��d�%���a�sz��^��Q�\��2�E���T0�`vibb���;�o��zi��{�r0V�=��K���0~V��I��:<8:8�~������������P�e*������
W�����4�D�h�M'@�4��oNF&n1b���KT�J��ol��3f��=��*(���u�0�18rf4����W��+���8����y��/b�P$d�!%nbg
����$;�����h��~E���:+��.���`��w���r��Up�1�&�oZ��t�Y"�����&{H6���Ra�~q9�_���1xyv��A�M�A������������
~sz��)���v�����3���<���������}�<>��].���(����l���Yn�_��w)����;��*G�\D�I��N��n��E#�����T:�C�i3(�����%��_ C71���Q�b���D�!�Cf�L����x�]��7F�\b7�S��:M�W0F1U�a��k�n��:kA{$:�"����q{{��_����'ko$��&�R�/RXh<�AGU��K!z{���)�5��{��HCH�@cMCY��brU� �I���E�3���E���We9����np��5��o�Y�Ps����$*��S�����������x2O[�������,P�zc<zJ���op!^�E��.�NE>;�7���3'oWl�#+�9�%u���r�D[�+X����u���2����TZ����}�{���!��J^$U�r0�wr/�/��n`[`���K�eu�����|�X�aq	9eN�'�3��co�T����)���������0�E;7�Y8��l�k'���R.'3/^�wq���v-������j��
r���������K������ [1'��u�,��Qm�h8��?�6��x��#=:�����]�������X�3Z�D�j�'�'�26���n���C]��v`(��<���M�2��h����R{�t:�DgH������!'�zd���-���
����1�}�e�(�q:r�g��d���Q����>�8X�
�c���7��/+e�SP��g�b)}X�q��RzFc�qp���cD�NZ�)���$���MRcQ60��MJ���������'���x8�2�m�����"%B��L�m>EO���
�)�B[�A���"�F-W���!���:�	�����Q��wHa�����"�@�Am�u����}T���e�U�x��q����z�{��c��hd�z��o�S#F���e.��)*�,�-�
��U��<O������0�#��������6�O�d�g�1�����je��0�p}F^cpPa�WFI1g�e��Q��T��&1>'�+f�����,��"q�$l�'����r�}.0 �	��*�:����N��#�7�����M.d{��/)1����C1���?����+��������)��w��/�=.SC�R�����	��@�xB�~�g�I�5F����
:5���^�#����&,IM��$	Y:�/}k2~9�������J�����
��f%���Vy�A=�3I���m��`Q=����x4*��<4��p�����fP����\�%`$����_*p��
����
��"\��{�
�l
��@��jl��#����6����VY�
mh�[f��8n��:���T?8��bM�V��ao�e��bl��%�qB>:8��::����n[��Ee���Y�<�JJ��g�^	�1
�=I�����r$�Tc+�9*��nK���J������y�|i��K�eJ�R-f�{[�
�,/��f������+v������~�g��������z���sO�������><D*5\e��E�`�X��ul1Eki��N�T
G���y0�E
D����Ce3!W��(���Z�nm�������sl���0�����,�r���Cr���n��2�,�n
{�mm�������B|n�B��������JM���d����2��R�;]A�������Y�������Sb���<?&��]S���}�i�m}�������
 ��
@�B����m�/o0?8�o�}��(�(�*�P���av������h�C��L����'+'�����;^����|�Q($Y{����kWcv��2@	|�b����X/��@�b�C�e��d�h;"�(����s�QT���3<j���sg�4��b#��(sF:r9�IV��; �D�e$B�4D<���o|��90����.Cy�R�r��d
���K�_D�����29�((�3��wq��th�����y�������Y7�OG�!�:(L0���N�<8N�6�qugGu3a�[�(Yps|�wVmqf���-M9�'�y�����6��_:�z���C2IN���v5c
���P"�h����8B��k�KB�����8���B�r�vj�@I���*#��g�Y.�a7���lu�&c$�����d��<C�*��u,�9��G��'���>�_`��)|����Qi]
-� d��JX�5B�u�B>T@H�Up'hb1�i��L�����Jb�~B�3�6���D%Mb�h���R���q^K����]j��$~_�e�[��(L�i��A8�����-��;����}�w]����W ���h+��pp��	H��
�uAe�UN���Q���ef�*z�{!��{�l@H9J��`�x���:?����y6xOL*���7.DJ7�W��J���(�_U��
n�"���6��Es7��>NQ�!F�u�e:�u�.
Ba�}4U8ZT��%)�X|Y�/�U����V��4?�F�Sk$kG�)����������$��Cs��
��n_Q����	��SE����$���>�����t�����c��5���k/�J4tq����(�?2����T��t���mVT6`��2��6x�e��.I��3��G�����7��t�Kn�y�9��2�[z���2&m��������@o���g��	�l�@�+�_;��_���@�6��kzt"y[��B��
Un��
�)�K��P����~`���J�+��0�]#����yM[������t���;hlQ�/�we�����w���y��g�vN}�����7��d�� C63�� c����25.�$�$���FzKE7�$�����W:� y��#b:R�W��
�k9�6S���~����?-�q<$��v�[�^��N�� u�p�q���>�G������J�?O�l�[o�m�O_LH	�8�����^@��SL���l'�g��_2_����	����������!���j�JGO����m��8��u��w��Yz�f������[�O�t�8H���?��C3@CC38�`Bh�����F�
��[r���� KV���q�������AS����,���)X2e"��b�����XT3��-����������m5�:p�����?�r�
����M�#5�H-�G���{�^�'2�����r�&nN�F�w����(a���j�B8��J/S������k'f�ZB��s*�y�<q���s5Q@���2��p���&�z$=����P�)�g
R����~}��U���q���^���Vu�����N�:>u��a����|����
�*��bM��;e�34Lu
=F� �O4�[�i���z��>�Q�������u_����$�4,M6x�������
��A�nAr��a�i�������������B\MQC �o��y�R�
2�!>�8��(���I����x��,�����$A��H?L
�-^S���R����b�S�KFz�:a�L�3\bv8��Io���=�7	�(��S�`O�<��9����$��N�#T����D$^V��|���$�v}��y���}{��g�c�%9dd�0�����%�,�Q���G��['x����c��q6���)"iB_FC��dt����O��YV������7'��kd������)��:����&�z^6��,��a���p�J���iL�"���,��6���Y;��B��$-^�L[���0���������$����h\��43r!g�P<u���}]Q��1����e��d���<��93����:n�����7i\��������}�=��C�#�h�����U4_c��p�(S$��=�QK�_'�X�H-��a�gEAVs/����D�B_��SO-�W���C����e�
S����"g5p�8��0����J�j2���9�����8w�%9W����X��"���l��/U������mJ�H��it����m���d�H�p�,}����7}z�A9�R�
Mw�0������o��l�6�s�+`�oc��=�&!�P>8|BR�yB����Emq���~������	[U���NS��BC^p����<�@g���������8E����J�e{2R��dH�����<�G����d��-%���tF7kWG����,d�h7��BIJJ����=d��N^�0j�>#<��0>�����<G�oHB_d+���aHg�o/a��yPE3��t.�/�B�c��	������n�T|��/n����[�mS���B�J�Ac��Oz�y�B����	z��+�sw�OB��-+�(��s���K>������"r�S��#���Q�l$����g�y5B�x�Q�
��4)�/��c:����VWia��K��$���
�>Q\�q��IfL�����d�sS:���=����5�kg�K�X��?�Y��a��N���!�:���J�]������y��3Q���8�rp=���mW\���!�3����/~�L��r�15}����	_�FW����)���w���<��d�FT����mm���L�]�H\�p��g��6>���@�VBE�F���S16bT��_�z�������~�<Q�46�D_!z��3A;�|��{ua
�����3�;�[
�e!J���%H�j�lzen
j�{
rzSx�n���5���:R]`��	��;��5�T��h�i<�����3eY����2�^>�0!�uM6��_p��
N�!^t�s��O��#�i�I��3.���S���GWT�U��0���L4�$�B���N�u�vH�����y�'-�N$k�F�,nT
��_j(sM�(�-p�bX���������M����������A��n2�&������T(��ST��#�����������8��}Q��iaG��� ��VuL���(�I����KV�>g�����(m7�*�0�A!aIC���6?-�g�v[a>
�����<o���y�8��?g�(�����.O��
�<���1npX"/WG
� B�����x;S�����[&��n�aEWR7\�M��0OC���"z���Z�.V����r�gk��eL�:����������������r0M��_��P���*�u����	��������%~�*�3S�K����2	����v�/�����Z��������(�Q�����N�<�R����;i��ei����rA���a<���y��j���n����N��M<jXc�=5_9;{:�b�&�����l+*����}xgS1�v��G[��N$n�������LEi��
����!#�p�c2����
<�\���;��4��f�u(�O����`�����W�B'��������w�7<PH���s)���<�����xV��@���b��F�}� 0\�w4*�����"O�,v/x�m�m�M4�~���KA@����M�x,�ys�m��V<��
��)D�C�����\��qy<<n�H8���
�|�J��i5��/wON{�z1�cj�h� �_%��7)�N�x�4xh4����*b��.�8�&xsu���B�c���G�j ��m�*�x��C�ozW��d^zK����nvNU�8���*�ttMJ�3b�Ksi�!��<"�,����GYO� L�I�^4_x�hPE2�)�^�A������Y;P�7�}]bj�&��h��T5�4��K���>^�O""��n-������[cVh����/+��#���^@�`�I���Y_xJ�y2x(nO�gB[ 2g�����eW#X��E�Jd	���� ��h����8�i���P��	�����KV$�P��?����=�����rz+3��_�����Y6g�VBf]v����J�}�;9�=<��>��[AM��\��s��Y%���wr+�3�n���������`��p����r�u��^uw�����f�cOQ�RRZF��������,���<Q�	�rJgp������\Pe�y���Bw��$	��K��T��~����1�S�T�m��&_`g�9)�^���X�v�_|z�K8�n�T�4���z8��C1��#r���r�?3� ��������,�K�)�D$y�m��5v.������H�N��x>��y��{�-���h. 
3'Q�N��oos=��s�iY�7�y�3�����Np��8^�-5k�����W�C=Of���;S�����1������N������lG�b+G�f����J�.�������8n7�(��8�r��Qr�?��a����Kx�����v�$���'���d��N��)�>��"0w�0����������S�R��J�r&=P����p����&G
��/Kl����7�~BO>�Y�)��+LtJ��
��R���q�_��rhf�9��������s���X %�_4�9�^9�fT
�)��!g��X�Fpc���aJy�z:�����s��G�Te�iI�=�vp�2L���&_�X�5HxO��Y���w�e}�D���t�$������t����h����;�3P	0Z�Wg�G��9+�F�jF�H�����8���$�v@s��b .�!<Y�����W��1���B�)f�h�26
�xC��P�DSK�o��o��ls�.g�(`�����]���o�����C8TIEZ����������?���,�6B��V)9vR��$�dW�Cp~�x�m�h�����S�����IsJ���TQ&���a��2��g�@�����k�'���#�89�I�K�=Y��y�����Q������"��AU��sL�����S��XO�������z��l����w��%v�+����/�-+K��1�P�Nf��������+;S�!L�<$���q���$�-���*6�@�@�i��t��h��t���/2��Aw��N�k���)��F��,�/�LG��\�0�Z���l����;T��D����G2�],��|��Rd�����"@��]tGx�q��na^��7���������D�����R����0g���:m#x����N�$bD��46���3�X$8���xc��!m��P�>���k'yr�@)�B��[<O���N����N�)U_���WI�U�b��X8|#Mas%':�>�\���KB��B 	
�|���9��S�F�������G#:�'�����|>/8��>g�fP��~"�~���=i`���]�m���"�S�Y��k���#.wC�"X�{0/��d����S�ql}0��h�dS��u��[�'��[T��)�\v��6v� DET��~%����9���MW�*�+	�gV�s�'F.��=�p�3H��:���+������]��k����hs^etn��e���/�OGK�-o��(�AZj�b���J���/�#:���">0�m;v{�Z����t��h��(G�&��Lo�~�������._e��F�pL�b4�9.�RR����
�rW��?�l�g�.�FrF�����re�s	����m�}�+�{���X��o!�,�y����������)X1�}�x76J�$t��nb�,���~��6"�O��q�����W�+�Vl����~�#�eK0[��Zi��������l�Z{Y��-�7��_c{����4�rd\b�
<+'�c�u���YP�K��R�~�8"�E���y�lY2�hZ�M��\no���%�a�2Po�wU��*a����o�E����$q�XM�[����c ���	�*��q�:!��!*H�\���K!H�|mkQ]r�?h�}ud6aW��`Sp\��s�L%
��XG���G��%�p����
��[z#���^P������,�r�08a[�9���d��UY���6O{���3���5oG/N�G��G����M{�#y�I���_��&�U�*$"�n���n�l�������e��g���@D��'1t���E��3��q�H�?K�QkWdT��)��D�,���nH��`�+oj�J��%�;��G��7��S#���������)RE��XI�&b����l�#p���!��,W
g,���3�x��GI�)g�\p
xK@H�����1�Z�Yd#B������:�����+�F�:���/������t�Q����kn~?�Egr�����8��g����VN
�|>�@��v����6�+���@*L����y�����1���_��|�}�fW��G��I��&5����)����X��uy�����{{�����tVQ0G�2�F�eSA��o�T�~����E�'���I�D�G�j��S�-�g�r},��$��Tn��1�I���Z2���$�z��@�*�-����S����\��{1B�}����%��S�tX&i�{���f���N�+�"+�"T�Fk�eE?�("+8W#Rz��J���`��T��(s������{����A�d��L
T�A��hY��yc�1��v�0���7�GJh�#�\��F	>�r9v^�i��\P#��0��i����)��R/R��^�K�R���_�@Kw���y���S�:g1�"�p�3�&���c�`z��|�]O�r��B
�����������e��D,��G[�[pa�g��W[�P��[{����jE��Z����{�O5�����q:�� &�c#v
��KiN�66���7�FX)��;�z"�"U��9�B�D�N�m�{���'^�d����V���Pc/,��3d�����y8�����zO��a���"e� #��<�0�h�a������$I')��F��]�3$*dF5��S��c��U�$���t��|�5��� �[�����
����9��"X�W�5d�MeF�kc�7�&������=�	�"Do���N�93��\��(���Z���OJ�UZ qRv�N��8Q:	���*�"9��QN�2��'�E� �S���d���O�q��]����J-������Jn���	!j�5�TpV�,�D����*������{(�G��%�{X���8�d:z��+����8�n��e-�q������������������/����(�y���/�������u���+����uw�m��������K��S���Kc��g�^t/R�5y�i:8�Ys�Q�����;h�3����5����	nJ���� �#jP���mmI����'��Yp�8�HB:H�G�m�3��o�����?vq���^����_}��v��
wy6�����l��Yd�b�>z�aN#
P���/����*��xB��������D�W��~^��_tv>��|�yg���W�O��^lEVs� 6����_}"F���P���>u�8��*	|xX
��lv�l"�����UD�d6N�����R��F(di�~���e� ���0+�9 |m������W[4[b����t*gs9��{B����3AJ`"}q*��wT���V��,�U�.B5h���F?����_�|�������o��u��@������
��� ��]Z8��+K�qDjF�MO�r�T��8^qa�o�n��i���d0%H�1#r`!,�������G�����y&���K�5O�����m�	&����	�� x�#��F_(^�Y
�n+��������-�9O�Xd����|�������6&�F['��\�yQ���PU�p��"x��z���;�k��wz��lO�;���+�l����e����qI$�a<������s�����P�tp��;*�!*�=;�KX��Z 
�<\b<}j�X�����P& +���������V��:��������^��������?�h��#}�r�����1�I���R��tWqQ$l���?C�U�N�g ���vS�u�p�y:������H%�(�.T��!)mi���U�p�tPQ0/��)��<'`���4�8gz���I�	��u"=���C�.r6U��^H834;�!���l&�<�5�����d)��(+���m�����Z��$�fe�,g>���*m��T!�5iH`+l���n������x��D:�%E:n!"�_~����o��7�����(~�m<ZQ�����t��%Q="yxP�w��{,�������'�����������q~���r���������$��t�/���N��S^����o�����pv���_�	�7?��s8K��U2�f$�Q�����@BvI�r���<���=�s�O�X��c��Y���������L[rp|�;y����N����{��|C}h)�N��_=�`���w��EM���M����Vq��(�LpB��{����
�i&���yH's��c|$�U�������%�9�A"N����������2�$���`�!�+`"gtL��W���k��k����c\g���?�y�;C���l^�����Q���w�v��lfH��u���Yq�'�f��R[���EL��-W�
X7�I�%5���<��*"<"P:,���I�l�E��������f?D�>a���������5�Gu�xo`�\qK6�Ib�������vA�y����0�D_>�|���;�����������G�l���I���$�E�:*fJN�������Q���BA��2�s�SZ���<&�Ii!NWp�"D0&�>LvT��
���	�\Qs��Kt��-#|��~/�/a5����b��&�g�EL���7��xR$I_=��<��Q���G��o�*1o�J��j���D���a[T�f]K�pt 1G������KN�����c����5���4g/'DtPy�f��&�)�����T�ah�(����?�k���k���:#^#xu��#�<�!=A���R��
�v���:�;����/�rq+����k�IK[m�%k]kXh����h�\q�@��'���������A��1�O������ty����Oo�k���r��N5�c��t~�g�Q�����o>�,#�R���x������Vi�z����,<�\�n�2�������|>t����}����:r*��.���E���i1�,�BG���pt�\J�JJ�i����U��2+i�a���3�_d��)>)��g�|�����MEa|@nV����p|0�c�z��*1������U?�F��*�><R#]���,�?X��)�����V�Z�[��W��V^B� ��p��[�}�f����qe�mK^�<�YU���L��tT�<���������2�{�2�2���#��dE�#.`L��;���t����:�
w�=��S#�,"Vv��wFpW���=�QE��e����D��,�kd������<z���(��o[��3�$�N��]J}�9�	���>��\]8K[z��6���qK2��0o���S|ypQ�������/��������8�siI�L|��k;�c�r/K�ar��S�������Zo(B�F��d�+��
�������W�1tyd@[���XwD�N�1��Sb�*�T@zW��S�[��2`���pt��8t��~�4z��f�er���E��}'<�����*?�a/��I�Q��}���I�bh1F�?AvAl��k�������a!�a�1c]d(;��Y���H5����'���d���O�H�����-������(��(�1��T�R�q�8�:�v.�����4Hw���c
0%���4�z'�e����L.���+fjP��3]�e;_��yt~��w��a$����%$�)@�Q�����2�%��
3�t)��+|������m���'��!���k�P�����j�2����X��3qFU�k��L3�LI
#�j��X<).h��b�r�a�N���>�LT���������a�L�
��t���!eh]�o��E�b�M��h��O��~w<��	�`�V&�q=C���������]�1#������L"S(l����gC}bS\�37����PRU,��D��DE\���q|B�
� O�T��r�-��������tk!d����E�~�=��gHD�_�.�\m��@��lm&�M"�LfYS�eTR/
�@*	�LI*	�����&�P�2��9�A#8�fgyv���4�7ZB11y�����F<cc1G#<�=����������}��`�:��{1�v����3���,1���{��]�K�[T�!Z{��
/F���M�����3�c���o<0���q�L�0�egb� ;&���F���������X"�V��lw���t%EG0������M�~����(p��+��=?��8�F�/Ng�/.���L9r���7���Uv����u?���^]���r�x��Yd�j�4B��2�� �H�
p����w�c�&b����������)vcRe��C���L�	~@�mY��8�uw�`���7�&IT��*�m������_�v�?8��mZ~g���g�S<T���OV�RK��X�����<�<CW"I�!G7�l+�����
��_��|��r�f!�������	��2�y�ru�M�����[<9�����: Z��cS��hI&�w'r�BA�z��to�Pw<��� 2��b,=����J���J�����'�G������A���^o���6���s�Mr���>o���[���@/�����Z�6��!�y�m�`�n7',����E�k����rJ��<[ Q6I�sLzQP�F��g��X������&6v�O�.���wp(&��H<p*+����3�_��?�/lq��y�
C����"#��Q8�C��zj��}!�����h����&�g�D[�X����q>�b�"�T`v�����=<���8c��wP�^i�l&��m�5$H��{�y�
�CI)�Be���_8���	����|�W��C�AN1\:�c\�>k�d���KW��*��?�|@��N(��#��[��3Z9��}J+�Ta[sPki�����mk���W��wB�D�uj�-�$�Q���-��+�����J�
��5������4���<�#�6���g����g���ht���BhMy[h��{dj���9OA���3=e��2}�a��t�'�4��l�I�L�X�d#3<��H�E2����FD4l1�=�L04\.;����Se�6��Ul6>e�J/����)Q���y����Y�.���i����0���(�^O��M�g�J�X�#��"�H�(@�������P5�W��'����^j(������H��7�f0�6���<^�O��#p�I��<X��,P�w�a0����*����t!�����p��i���A?N#�5�t�:��n����i��	��Uk��t7	Sr�����v�8u�(C���R��7jR4�b����6G'����Y�s|��S��;U�#[v�ush�%������r��b)�0�����,�0���W���+^�6@|�	m���������0�\,����u�D�:��E�7������s���Mp�����aklAy������.������n�&����PG��N���]
�e@
���
B:<����F4��H�,��|��)������M���z�h�3��t��E��s�8�rWUL���4�4�T�q@�����B��_�8���`bYO�Gd<�H�;�(%��S�b2R/Ym��n�c:7y�P����j+��lhFo�����AI6�G%z=j#V���o��_�w	(�[RV����*��c�J�TP�m�������=��7gV J&38�$y�5�_<n�wO��XX4�	C��b��1�N���F��<��:l��(F��p7�cB�`

�zV�]i\�1(�+Y�4���;�������!
��B�,���\��������y�����/���b=
7�\e��.��������Y~�A� ��yj�r�"Z�PT��Wma!guNd����F���\��ek\�2Eo��RY�\YUV���{�|���|���c�#�~L��r@��l�z����n�k�nq��U�"��,|�MI�)F$����[�)|�RkZ�o����B�Rd��bI�'��S'���*�&R<ca�g4?���=Td���Y��:�]�Y�1� n�l�M����}t��_�hgp�_��	�������}.&A�5�*�������N�����KE�J�*E�IW��v���3���_���2���U��m�9w���������c�-��H,�Z�R�Q�e�%��s/�.XO=uB������g���B��7�s�|o�KJ�����$����{����L��n�%� ���P�)d+������.~�E�Q�����&\��� ����Cd��N����V�%_����^	zw�����s�gE�~�aP����c,`�9k#�9�h`�<��4��);z�b�L�����ge�����(0%��u
�8U]Y��a�;L	�E|�F-|������&����8�0�y�1{���`J�(��o'�dj�X	���p��0���P���C����^������&�z���>����dk�S
�[_
Zx����F8,/����q��v�R���������S
�>�U)h+�y�������vg,"+o:�$)U7��Ju��q��?	W/y������N�O��k�o�g��B�E<@����a�@q)� rmK'v�%d_K:�,���d�c�q"!.)��t� ��+�c�!M��r�ZT�
Y���"�j��E���KkC^�|���)��t$z5v��lL,���#"IQ�[���Q�	�oR�k��B_hz#_S�	yq�S�:2(#6�!��+�_3����1���#�����&����T�
 ?LJ.���3^QxBu�Tw�|6�����>�`D.��
�H�-�d"kO'34&SAv��&��Op�	��� �[y�V�:g+/������`��8�g���V#y���N��`��F� �p�����q�#4��LQ��LT��_�����<���|G9���V2.��\��*N�����E��;�O)k�xy���e�*���#��d���:��#u]��	�@f�PW��v~5z{�J�t$��w������c��Gn6�Pu��kM`/��e;�jnc��G����0|���u�e�q��������(n}�[W�Z*][�}�����zY�?t>�-�dwV���bA�:&%W����0F����y�������y���a7��K�:R�`�U�~`Q�^/N�Uh�������(��X�����5�� N���j�!"�wqgJg�k��+���-'�W��f�����^��.PO_��Ff�5�-���k x���Z���?��*�bdv6��)>�6�[���C�z�z�5D��C����������3�c�
�$��C~Hd��Q�
z�����6Ei����8��z}�W$����	�-���O��Q� T"�d���z18��de����������,3���`{�[%�q��`�/�1n>��%�3/����7X.���P������
�+ :�<-N���6v�n���Y����Z��$d��L���*s�Rx?��M(_��@���e��se*��i���D �5�cL�`o/)P��D ���Am�[� ������(C��h6�UG�)P� �r�m1�;*��*mA�V��t��c�Y7�k�R3��
�R��?�/�=9�)������30���>���V�������s�F�F��4c����a��5#�	�
>���������8����}pA�f����g��KG������#u��%q6��5�g!r�F�t�X7���?����KI�9���3������6��|^����A�
��	�q!.N������=0a	��`w��R=�$���]���u[����[5SW|�A���"�5��%����<����������=����R(E=,�	��	l�'����11Q�WU�/1����'������*ew6���l��x>q�w�~��[�U������p&$#huh_����TG�n/���I��jWJ%"")�
��
#�p �]fP=w���{����:E���o\�#�2�+_9k�I�|4XGg1�)����p[�������h��%��n��)y���z�Ji��Pm�Wk&<O���1�����������Q�{��`����cn)��c[������:Ev2�0����d���l|�w���]t����x� Nk,�p�T'�zJ*2����/k��+L
����i����c��Q�'�������<*7���j�.�����v�\�BI�^	�m���F�uN�F����
��������Q�E�-�;}P�5#�e���^9�SPn��+WR'������9B�jgP����������7�^wnT��1��������
G��d�����u����l��c�U��D�@��k
��GF����j������B�[�Au[ly�>e����4�W��t%V�[6���>���h0��j���,�(���N|
z+ey�7���|b{��?wj�y�l6���4�G�Bw-^��ne�)��o�)D��/mO�����'S"����������o�>���Q9�K[����bj�|�������^1��3'q�PI�b�M+�N����I
3�,�,O��b�i�#�%�\�z �TH���@��d.`x���]�����s��\lL�Q�Y����Xl_	}����a��F�K���U�ZlW�6_��	����_�cH�<�r���V���2�]\k��+����kj������|��h.d��!�2��A�	�f9@oH�:������*()#����#��o��)��������`��f��.Uw�B"�3���rn[�>�g��A���[K+h�R���o������$����Z��8q����%���F�q.���A���2�����2TZ6%�:�L�����L@�6Pa��R��� ���@G�
��
���".K���T�X3!U��Fk��(3A��N�}�$���<����U����8�!s�[o;7���k>��{^�w&���c�r��&-o�f����������g6��G��P�Z��LJ��|�8n��g�/j��L��<��O^�H�V�Z3"v�����>�� ����wq!�B��$�,������|��O���h�������$��H���vD�qY�RNw�R�7��������Xq;�BG��{�
�FQ�LC�U������+/N5<��@����i-�O)Ts���<��'����p�Y�S�Q��: ������������rJ-�����lY����Fr���^(mamT@�&��l|��k�wB'2�t��	�I�������a�U'�	F��E��ED�)���
��:���?9k�#�����J���@e�!�����*Z����0��������5hm5��Z~�2kf�B~�#9(���d�a�Oi�
~�T�� ���xF�������@�u����c���i��ci�Q|��Q
�c�i�����/8WBn��C��g�v���0��}���7���y�&v��7�g?�T������L�2�h���*MnR�PF-�p'
�lh���Q;pz���l�N@����MPN���|xJ�������|�&���|����0J�m)bhP�Mf�|2O�/�Uj��q.�
C/�1��������N�mW��^�s�|��Eo�*�oi+���
J����9���e���dJ^Sy�.<p�N%�
`��a�h���mfA�n�������L�J���7�Z4�5.t�ncn���PWL-����B��D�����D�@�����C�AKnN2+��}�p��+�-�q�[����5�i�lm������
���\��r$J����?��$�~�,EPJ�xj�660@���H����{~�E�����rI������hL�{��%�_0/��d�`3�Pol�	���������i.L�f���UH������'�t�X�`��E�&�E&��&��4G 4��Ld+�+���Z/�<������r���~��Q��Ix�6�L��R>P��[Nh
l=�	nc�����4���1O��S��K�I�f����e0�R���R�g���Im�C(#����6Bb�~6P�=��~K�t8�|��7�L3�Y�
�����7��q��+QT���[�Q2Dgi�p�R�A���FE7��L��d�f.�����.��f�T�(�F'�/�3Y;���F���2�����95��yG��UN)=�(1d�#
2���qHq���?��N)�� B/�������Q���������-2�����=c��$�.�y������{fg
��bB/
�`w�"<��GL~��]������
��c
��i��P6�m��\�n�~??a��9]�"�`�+�n������:.0\{\x`�D��,KWD��#���bj<[74�-���_4��;�J�����b��4}0%���s�Zk�EI��G�C�i�I�c}�0�ZJ����}��:���88���������L����4��r�]�w,�����E�R��G"�RL�1)����"M��z�U���c:��6�(Z:�*���*yG�^�-��|�����7h�>�����;��<�����N�`����_xV��UV���E�.�<��0Rh�)1o�;,�,Yb�i:\�{:-�����������VQ�i��h?�.
��Y�8�eD�H.C���L	��f���H���X�����G��~�.������AI�NO^�O���F�E�����vM����q��,�H��O�^�<���������Cq@ts�%�f$������#�~�����ZD��$�T8�$���9b���W�{�����'?�F������b�VV���L�I�n�]��������M�iP�Z�����`K*@k�G�k#
^g�>|u�W���Q�����M��_x�_� �I�nxSrf�"\v@@����_u��2z���c���vJ5Z���\���N�<7��L'X�br�K����*YxI���O���r���SDd�~B	����������C�(T��yP3�l��g��Au\�LGs{��9�4���4��8��:�JB/~�B��Asd��-����P+T��E�">���At9����T�p��A��*�q-��ZBZ��@�S�>������t�����6�����t�`��b?Q:�'�YMm�U�������..w���������<���O3�Q���z3k5
������r��"�G���� 9����J���.Y���5U�=cM
�@�v�������s������Bx/0����&�e��Nl�[��z��\��xS�Y7�j��<y�xo���{3�����12��zB��zvj��EeV���h���
l���_���WVk��U>.-���C���������+F"���M����8���(E>�������5��?X��j@�c����,B��n��������Y�h�2�-���v�d�r[C+���A?�#��o���|A�9�I�s����� z�,�����;x'2p&�'|���;�+w_�2�{��e��r����������������
$�G�}�����rE��wx���~>
B����oq/i��������e�����y=�����hC�Q�a���SC�i��p�J}/��n��1o���D?�*/�t������ny�g���P�p�QD+P���c/�����Y��Rn�����JA��@�{)7(�,�X���K�+�G5p0���{J
���c�h�
Ve��R�A���
���%�1�A�i�;6�#}6bn���+�v�	�@���qE��A}���
@te�7C1x})���"��i#���_��w���[�nK@������>�����gtc���6��e�9�o�
��7j82���j��'q�.V5-�V9D���~�
��$��&�a�`g�M��4m�~�LG�MkNi���W�c=��U�r��,�'0q<.��+��][&�J���.��dI���k�����X.�}� �J5?=���L���X�GsNM�$Z�9��9��L�%�q��k�d�$�=r*���Dp8];�r��B�`�y=�c?���3� ��VF����ue�dV�:�~�D&n����`R
u��35������t����Q���,^2��P 1w�+����4�<�J��~��E�b�T�n��z"okJ/9�2  V�7����V��xN�������dv{����1������*w�6����0F-��1b�&W�<�����*j�Cs�|f�P�66i`]�l��^i��Zj���I�R:	�-�]�oATZ����C�h_�^����z���L��d�_�����b��*^�[����fT����=���Y�K���$��T��:�p���.2��>Z�<u	�^��q�����0���s�XQ���|��]����g!�J+i9G����
�W�i9�F[�%���o��<�:z���_����5#�
�*kl#V�v���TTn����;��d�yf.s���L�J���	�T(��.���3��s�)�Z�3:�#�i=?�����x��F��kl`m#Q"�C�a]��J��!b�*v4��pQ9����Z��UiV�7�VmU=Y���W��5�N��?7��@s�$�&��JR�!�Q$��gY��y�kB���_(�os��%,.�W�7*+��A&������`����CgG��X%��6����u��(��E����;h�����CI�<��?u"h
.���D(?���jk�a[-��N������;�oP�<�n(^u���
���FL�SPM��K�V��r����^+'5�s@�8�a>�.����j�PN(��`e!�B@������o��o�wV&>jw��X��M���aY?	�lJ�?	\��L�����*��2�����F��jN��^;��r����w���|�i�j���_	�(#��uG��$�>.��IOAL��`�������U0�L��@� ��c.��8�k+��x�"EP�HHE���������D3�����>���	|:���w[������_����|��&f'�?����=����Q2��i�;�:�Y����=����8���i�p��c$�O�R.G������?���k�E�8W����V��"�5�:���i����<!�Y�������w�p)��E�EV4��#����$cX����$�EK��0t�O�a:��
��Z��f���L���>����N�b�Y�.��2�����0J�i���������F���� �U�~@�b��Ery���J�E�^7�K���DE�|[�o�V�	|���N��GI�:��0S�������)q����d2��l3	��kt�{���_1�u(��F��E��)dfm5A��zzm����p�q������������P��>�����%o���|�e]m�d�8�	F�����Y)'��$~��
c
�E
�5��'��K<w�Y������~y�;_ :�~�P�fMF����.����Q������j�K����� S &Y�aly�hK0������K<��Xt�-d�Q@uA�����m���p�����r.T�G��3�����w��cf�5/�q'���M�^%��<-HZ���j�,io��D��7��Z���Y*�_s�,tWH}Y������Z�\�
�~�k�"�;�� �;�{-�hOL~,)�`�_2����W�" Y��p6��}�+����jI<`z�`Cb�|/��ub�$<�����?���-9&{�:�y:��dR�M��6!��q�T
/[���=��S�����f�d�����f��;s������%�@��H��Q�s����4z�k�>X�p	��OL�n<T��1.rkzv��q'��)�Q	����X{���#�]���Z�S��%(=F�^��"����~)�(����m��D=�xb�|@y��
��=��
����� �%��	;X��|P�2���.��xJ���S�Qb?Z����<x�t��>��Voy�>��{�r��5����2��D�����So����[K�:�v8ws���I�sjW��.\���w)a=�32N��1G���$w�0�R�����.k�����n1��rW�������"O(Y�(�������l���E6�T��s���
���wQ��0�0,�tKt�0$w��i��j�����k�S�%W�
��%����X��g���jy�8�l*�;�
�
ql{2u�EPG��<�����F|�t-	\�
��iC���.��>���������2���JF�S! �jxCd��n�����#�= N\n�2w�@�F��9R�-&R���������3���,j^W��L��<��U]X#��e2�VWC�����?w�$�
g�����������0��WwKh�������(�W�kYX}+!`�^�K
��qn�h�oX����/������?�>����b�?7���3F�J�r=[�'�~�o��H=j��su�A��E�g��EG9~d��;�J���4Y#_�K���>�Ed4 �S��^
�H�t�6���>&���_�����)�:�<�Y�����%J��z}�H�v���!����G��������[{GW~�|�{r���R����X��n���x1��a��6#��t�9Y�K��{�q,�W��&��V���\�����.������q����B������|��r�����qC���)u������Q@
S�aB�jy�5��s��"�0[�s'��i+�L4�����M�h�%��B���;^L{�~���7�����"���
�$�x�s�2V�1�>����Im�Wp�����z���\��}K|�{������
���0�2�3~� c�"Ef���dq�^Hd�-H�v
6i7yE�JFq<q�}o\���^��o�������&�)29��(I��ib�^��q:t9�F�l�L������4�G�*����'��G;��|��B����[I1�K��$*2d��)"�����A��+Y��A��)"$�=���o�o��)��q
#I=���N�nN���������Lv�>� ��'�G�n����j���Dm�S�<�<����<��v �f�<C��j����� ���)�l2<c�u1k_1T�#������7*��rO}'���n3�}@�|��~8J��1_j ��F��������sXT&��m>s�NB/n��.����kX�{���`X��
FN����/���pL�n�,���x��_^�N#�$>kT\	*�������������~;W����:_�������)��l���i�pzvp�w�u)"11�H�B�"��$h�XO-I�7���T��
�_������M���6z=L�;�%��e������[�����"��y��2�o-�H����}XD�{�d
�u�y T�$�~�x=+�T�	���r���<��|yV��k������^Z�9�Dg
O)>��i0�.��TL��WS��V�����U01d��?k�8���um�W*����S���Y��R-����-^S�B>�;��ew�G��:��t�W7�����
��?���=�q����F����t��=<�ye������_Kf���E!BvB�N����?����������h�,���$�;����������T��r %��A�Xt
Wi��b
������Y�{8���_����������qt��.7\M�O����@��"�~��(�<+(����O�	_\����z�x�zj��+���l�4�E1�i������to�|���p�R��"CuV���s��m�H�=P��_���s[�
���2]�I���T�%��:+"W�vb������p����v��B��t���L&E���������7�S���������v��o�O�������c� �K�9O��7����3;�.~��/���,�����w�����w-����Q���*W���_���r<o���B.�j���3��8a�R@mo�	)�3�1w�����
�zOF�^%<��w)�dY][���N�!��w�����eaD1e�G�'U��5����S������|p.��#=�[�G�{����{{�gD@���q|�������2�zj��k���n\Y��I��a�r����**�6�L����|h6�s��Mb��" ���Ak��dp�p�W\��O�uJ?Mu��;rk������0�} t������3���X���d�a"5��H?k�C���)�ZV��Ug���H��0���s�3@�����%c�
�
|�0�A�})D���V�N-� 5N4�/
�n�|��p�{��#�A�;���:|G��D7Q�����1����yC����Jv��|�Uz�c�e���S~E��/�]���������c?
�;L@�&�-)��!3�������p��6���������<��e�x�?8;�}��/~�;�%X*�(\�W�Q������Z��w�V��K�h
����j�6,�-������x}=r&|�L��P�K��H��F�e�E[�0b��%$O(�Z��������7��Q�46�D_!z�����V��/��T���~�1Qq����5W�Z����o�;,bQ��-�7H�~OKDEg�".�����!���PN��K�Eb��gm���Y���]>��J;��^�	6�w#L�������IB�tS�s��z>���N���������,����Nh�I��T�u���'J����	����P��;�y��N8����t`���T|���jTw��>��%�N������]�1���	����l����S`h��	�$�3�N�K���(�������Q�^��X����7:�W�qN)��bNE����)�3�t��xGU���[���F^����K�����Om���^����\����uT�I�p���,yc*��J�>��$���r�I���l�=LE��
��w�	��@�����*���@;*����\@�b�������dal�n��Q� ��H<����:����a�E`r�eI�����c$\J!9�A���D#��t�&#�cc�7�`0<~yI:3'c
���P>�U���m��z��(]~���g(�pWW)PCU��l���G�������1l�yq��c���6
j1"��v!��]1�>���s+�w�����Q���J�i:�6*h��C�ZU���e��Y�!�E�Yak�R�'W]����z�YUc��3(�L����
��)��I���J��um$p����X��!�VB:�q`�$�	W����gvn�B��&;�#��|"ay�O��fL�=�*N�^;�
'*�M�4��qp�2���)������J������q�[lZB����Z��������	�`|��-p���@n�M<�:��Wih�����r�����H��"r��������Q���7���UZ�^h+�#g��r�3F<��rW[��`����)y�H�wMi�I�L��K��G�{�(�z��
�0�(��\��}B����u�u/����A�o���P��$f����4�zwymf�
����tdA��#o
�KS���pt���$�.,E^HFf�0��p����A���e�O�k
�>��G�~���S��K������~��{r���+����AB�_����6]A�?�=��:b\% ��H�����W�(P"��0I�8W��w�[)���%y�����O���#{@q����(���n�0K�!� d "O2=7�Y>�EJ,%{j���T��� ��kj�?�s0,y
,sI���_~��.��
EUZq0a���_u����5s�z/��k�Y��r�m2�����	�%C��9<�gH�b)CV�dO�[g<'
��XZVK[�i��� -�.���bN�%,ag��M�#���gz*�t��s����*&F%@���%���}k�l8*�w�i����<��o��\�����'w��8.$�+oK���\D�6fl=�l	tK�H��^�m<����4@zPt��Y��"�b��7�8�3�S���C����F��|2T<�����qO�.���*�11#�����
�Q�N���<<1���M�����`��y�)�_^����
�����NR��i����M
��99��o������ZC�&��������^�����%>4N^Q-���2���ir��2�u�?�j���iS���qdp~3�j�%:�'������E53�|0��Yk�S�Q����f���9�������RZT����L����.��[4��DH�x��r�P����� e)P���F�������-Z�s����������h�H�8���/�g���Xxf��{�5�<��}��9�3���
_&�����E���E�	1���k���j��Q�?���~����c+c�w����&���O���{����~����>��#pr������g�����V�Z&3�2�[��� M�x���Q��_cf�,�����
�ea[��-��F���C���F�$e��o���Z�R4�
=1Ur�YJ��)��z%�
>��#�D���E��,$�a������H�t�c�P4F�UNh
sx����l*\���<�:iZ���������H�};��� �%L����O(�}{�����ys���h��"c]-�3p��F#1,����8O��q*�������7�3-r"LGm�-�$��,X�����d�/�;f��t��e@�Ch&p���GM�a���
�w��%�������d�e��Y�7����%����J��9����{��brm��Yq]�(>s;��*����{�r�F�m�|���39W��I��9'�C9��x�c%PZN�s���j�:xE�hx��Y��%�>e&;��Q
�2+���Fv����]O���,� Z�A���#Be��T��!;7����$�O�6���
-q_��0);]N�����,y ��-U�x.���z4�'K�4qL�4Y���d�5-����d��jSl���6����>�I����6��zoV�18�0t@5�:���PI�!N��E�J
���@�w������H�8�Iz�TY��
��<�z�z���}�������?Y��/9��o���X�}l�KH����^c�:
�A8ZE��r����+rTvwR���gs(\�eyL���EE>t��0����K�J����'���%*�����]f<>��,~�~PcT�����U��Ov3BR�ZL�J�m�L����r��L���GR	2Rl#�F�N�	t�6N]p����&7�^��M�Ea1/�1��gh$�L���X4����M����6��V��l|)���B�}�8(#f��*?���)Xz�=���aT�L�|6Fi�Ru������,�`h�X9�;73nK{�;��r33��0_�}+��Y����w���:��I�I+�����:�;ecL�;��Cz��J>��#��D]�i���/���l���[�_�,��&i6�p�����Ev��35������"�x���~:B�E�A�p	�!nG)��8���r�����������}x��1������������:�$��' ���[��h}�k�2p}���!xFt��s��8Y~&���z|��`o`��|m�?�.�jv���&����c	���#m}d"�(��
V�P*WC"�V
c��|1�����rw��xP�}D���u4`	�$���'�b���_��0��X{P�>_������.P��*)4a$[�����Z��;���>PO����5uR�P�����bF��Nw�.�����&�����$=�A���D�v�T���d$�)3�D<�ND
?����'N8;�>w����{[���_�0�r*
�?Gm2�.������=zZ�o���:^Hu�a�������g�|�%�y��+ov�K';5g�31!�%.=6�����������bq�q�&>��r0B�O�#t*Om��3�AU��`���B���px�~�}(��a�����0'KR{�%�|"+#��+��L�9�	^`a�(�I:���e����K��O��&&��M��)���Q�$���28G��[JH����E�/�%|��f���d��z>gy��p���G�#]�t����}���U�L8���t�|�N3�mW`�9�-�]2�����8�R�a��n�;�%����p�	{�J%��$�Q������=
���G�G�g{?����N���&�ahJ�$�e���0���fn����y��[���p�-�S2 `�@g7�����x1�t��5��$�����#�('f���J$/�����0��AG
Q�����0���%�����������
�(BP��������]FP� �4�v|qe�y�w�w7^�i�@���3���>��y�\��g
~V��I��������������^����w0��)��9�'-SA_D�OGm�B�f�?l"�3��� .�����~�0)2q���\��P�~c =�1w;���<�R�hY����#�Ec~�u�������]8A�rh+�Z��c�F	Y(rH���R9��'.����,d2k�����a�����@g�^�Es"x��W�����D��n��r�4
Up�	�������76]�H)U�F��=$�mE�(�3��2��

t��
�S�?���
���7���RoV����/N{g^7��?x�;4�1�U�Ob��T�v��j:>3q<~hnf��
�=E�xkq�+�n�_q�G�`�:Q���V/��{B�S��x�SQ
8���9@M?�T%1��f�v�������a?l����]k��.��'R�W�i���c�#O�@��\��5/�����`�t������6iT��,������h�(%�91`���*:��UX��5�{��~H=@cAY��iU)�au�H��"4O��u�������#'�����}������r�6�����O��{����g?��;m�-�G�BLJi E�7�# ��� 8�$`��j��Y��9y�bSUX���cQw���O�'D9�v���g��8�tg�/����mp/�����F�C&�����Z���Nn]�%]�
L	��|1Y�9��J9}���%��9A��H������c��
��^>��o5EP0F��,����i����\�8�G��r�X�����Rsv|�H��}h�����I�u��q�#0p��h4\c�z�q6��GX����������0��+&7)~nd�n��0�U���,����Ca�i`��iR(�u�D|2�Ze��R��9]����jgk*|���0�����&f~"��y7��`��U��Q�R@<�V�j���r������P�t�Um��UJ������T���-o��0��qi/;����b��]�I��W�9?���z$'�F�('TbH���>������G��YC0�Et�����	v��������������}�	�@}:����g�/?�+D��J#"=;�b�#��$t�=9� a�����9
hn���Gi\{��Z��6�ks�J�Z�[���D�/�dbq5�:�����=voa}o���M�4�*7!���c�T��=�0v	�+�����,��>w*}�k���$X���m�5)���
M
���=����B,c�U����g�O �����?���
df�km��������L���L�1J4���P�<��T����|�_Pt��ust�>����=�;M*�jMr8��$�[�����^����gOd�*f�����?�}���g+t�CC�W��a����%��R��hs1��d\N)J���O�OG�R���BK�����-If���$��/�D��N����T���h�P�,��|��i���A7��FW�������+�xfS�����d��*����a����&����{p��.�f?�\�$;�8/I��L5�@���<�%qa���c�Q:=��~x<�qZ����t:�K����Y�J�C�P:
�p��#;z�Y����_ss/1��?���(�(�L�Ky��9�����.x�|��/E��l�1
2�1a�T:��<������>���e|�S~����u�!��X�y�����b�UR$v�m��lJ\}���Q6�����W���C�VH9�a�3$���~C�������3�Ff�=��0;(��99sJ�34J���|3��C��#<���{z&���M>�+������{�k������.��9�0�NN�K��#������qK���o$���iDlT}��d2JUK�]����	��$haP�����x�����i�(��Y2�#����9���.b�/R�n����D��_���v��"����"��j�j2��X���^�_�^�����Pm_�������O_��a�������{�y78.�{�N����/�C�{^���l��,N�v�^6M���>����~B�`�Y��vC�1/�����a6��k������u�X
���a-��:�����gwQ��[�w>����+E��]7���N{����8��-6w3]������a��`4��c�����������;i!���������?�U����������R:�Pz�Hd�>��Z�
�jeA����A�}4}�K�V�q�r~������1&�If�mjc�;>8|j<�\q�,\e%���&����{[T�V{�X&b�G-5p)w/6��M!���Ks�N���HC�/1$�l���`�x�=:#ol8~��*K�N�ki#_�O�F�6�t���K�*�/.�1��}���t6j��7SQa��f��*���<U�������@AY�gX���4�a�w�-:�
��L:��w��?-�{�lzF�H��?Do���c����@���w�j?I��f�t;8�YC��`���3KeK.�no���F��"�v� ���t��T4
�����P��Q������Bsk>S��������?��O���e��/���EL��Y����i�;�����x�*U��C~�������3`Pyb*}����)�M4���o>w�+��W��V�����}(O�#Y����D�:{)$�i��<����*�?�hw�V�Oga�E����d����8���KN66�lC�����^���|7����n��_���J
������5�,�p����u���N�G�TNH�G`�m
�7�j�!���%(U��.���|u��{e�8������G�,%�g0FrG�)$�`���x�MW$�0FG(b:]�� ~�o@A�=������!�������|�&��j�w*J����y����X�^yng���:�����,E�DpLd���|�;	;��p�m�g�'5�a+p���cZ-�����+�f.������.�o�f���lA1*-�lH
mTI��wGS��:�����6������@8�0	��T�=�RJ{+_�7�V4JrJfm��%X��0e�;�fLemr ��+dX����<�\��w�|�hBSe��0y��M�����<A%f��1���Z�\YSzj�v�n�+�k�����������������S��s�Y�Y��$zHb��i������M�h�\9Z��v��V�@��x�4�V6CM ���d��8�t�28�����-�g���8m�h�h����
�������
t�l��Q����^�+! tR����pe[NQ/��]�K��T��s9dN��pY����_�6�^�,�CS��qyrB��w�`�Ii\��'f�Er�(J����;g���qc�7-��T@�PZ�b4��p���R5���KJ-������?��y��:9�U���
���H3�R����=']��hM�Z�����,-������������:����9�8aS:M
`��_��g��E_�lS�%�$HX�/.��#y�y��`�n�$,�q#>ia�_��A�@�^��j]P�"Z�����x���Tt9��&�n�Ul�?�<��"�����������H.��T�8����S����tC)e(�x�$Bdn�#�B�c�(JU�`�e%��eI��&(u.Mq��q���9���'2�����{!7����|�3����lQ�We����X��a#,|l&E$��4��P��3�H_<~4���r��%��J��O�n���F6��\��j
Y���>�p�%W���0���G��^.�dQ}�"���E�k4�@`����;�������C.�<
@kq�-�����Mu;8,�Q�:��5�w�"��l=/Gy6��c%��v��b�_�k`��i!��U��Af����v9��O�(-@ �b�j|��4+B���������L�����
�_l��������s1A���d�V��������57
J���]j�iL*g$�%�A>�]T��zbW���!A�Em4X�kAff%�~�3s�Yqj�b� ��7��H����S5��9zGx���~��ujX����a�![?`������t��.�N�]�������vt�����
����s���{~	���`��'�>{GDNH���NF���]in���l������U9sy�E��i^�RE5y�x��>K�}��e��F���:��q���]�����'��.-Y����.�r�@�Ct�F�x������%{��l?�f��&N�!�})�� �;��"S�fD_H��m.�Mo�9����$��6e�7��0���J�F�i��y�j ��V������R�-	[u�����~oW�����������oE�]N�P{G�X�+BE��x�9��u�Yw���#���>�y���'���~2��������v�o�W�DO)V'��O*�r���;N����
����g'��gH6����A*#>����{v�?�����ia52��(�
�����v�$��m�F���E�-�}�N�A�������_z$��8a~�*U6u�(m�r�������?�7l���&-f��6W�
R��;]?I%D�y�b�R�������)s8�=�:��f?.;���6EkH��S���6_�nv�E��R[�P�P8O)��U���y:���T��~K�� ���������0�H������

����F���	X���b)8��~<�F��
���Z�d������3���]�z���t��������e�j�V��m4����|[�:yu�nN9�jr]j�&��*O���
�'b�h6H<���C�}s<�].��/4����4�+YK��P-H-=��G�R��4��a�!�u���O�������H�5����K������Q%�����ds���p	2	�C��-]U����N��A+��gk�������(��������/�t�v�:���0��U��
�����i��pK�'A
J�\�vU���h���j��K���0��r�,��3d��[�����Z�r�+�8P�y;��-�5�����~����&u��� �/��������,d&Z�^`���%Z%��|1'[d�
����u���7����f��V�_f���m�|u�����������Xb�S������	+����8��z*y�����b��tT��KV-\h�}���_�G8d��(hjRkRJi,�(j���\)wS"��\�NF.r!g�;N�M	�%��H��&KI������\�n2����*[�CNxsy^�����=FR�&��*lU���g�t:��qz����m�f��A�Q�K4��Y1;$�p����sG����`�Us&	|�������\�TL��r��u >Xx�j����N�����������g�Io)���B���������+{�1o�	<S�k�6L�g�
��/���^i���
`����3��ov,�9�i��3���Hm��A�|(3�2
�bN��Mi]
-���r���a�:~!}���7�������1�����d��q�8I�>N8=�!�@�'�F|T�	Gl�?C�����:���2���>m`�j�q{Q���_-iwP�D������5~�����J�Q��^�[0�=Y5����_��7������
�^f��o�l��|�����U�$�|M&��<����}BJX�iOJJK%���Q�K�����x�
n�"������bM��9�`*b�Z�[�C�W�K���V�������7����/+{a]������y���5g}8���
���c] �
�L#��,�"�5T��g���qL5
�j��ZUe���='��N���ir
������!t���������������6Q���ogJc�2�Uf�;��
������H���'���'���B��]���E��n�#�<A�8�oc�Q��&(�?�"5=R�g�|,=9w�[*�r�\���x�,�����DnZ�����p}#����pp�t�����<��*O�Z�:j~�����L�^�_V�u�Lp"KeV?f�HG��r*.p.al4
�X`E���Lka�����p�86����<�fi���8ac	�oE�s�;��
�qXn��#$"�F����<����
k���Fr�F��|F�q=o]��2z�^"����������cq�������w]e�@����N���#U�?��A?�����3d��[��f��:�*108>2��8#�����L4d>��;�c
�g�W��#?cm�q�,�W���!���F��>}�F�����A���YWaN�������)}k �����#�9�2�
&^���'�HU��W�=V����������E�h���A?c nu�|L�R��uHJ��J�����9��8-��m�C�i�:�]�_���J���[S|{���!�\$��Q�I�L���,�����8���������W�P�e���������i�u��)%�E=}B�������A�Ig|���A�nH^������Q<X�=F�%Dc�/rrX�."���Ya��*��P��=,m�B�r���M�V�����I����1��$�[@������v��2���9`{w8fo#���hFy=K�I��:X������SX��r���xF����u��7>����9!D�{������Wg=��hE�{��w���?���4 �G��C����x�n��`T�L(`�XO@��z\�$.���Y\��&>Z�)�Nb���\�X��\x�Qm�oK>|;C�~ecmIUG|F��j� �����'C�	D����U�(j(+>��5�)�=�Qr���;y-�W:b?^4Z�IM�x�t���8�,P�#1
�������x�GF_��H2q\^�d�g�_L�h��b���`I�ju���}��
�q+�P�#�l1:NL]��1~�9e�/-���i���U�?.��2��BN��N
����	���NP��p��fc����;��������Q������tt�jLx`<�)�+�PRB^���7��L��Y����SNp<U�f�G��>�HF��BD%v$#���VJs!�?H������n|3 �zQ���F�6dI'��s<����`:`�	\�*�;�����_��v���nU��	�a0D�=t=��R���W�2�0�V��/z��
������/�'g�}��pQ�-�������f� m��daFm����|'��*y5F�i���4��\VoN�)���m�x�hC'	{�u��Bl�@1�#[3(mKe������s�73��)u���vD�����G�'/������z���1����X�$���
�
ig�s�������	�$��}9b���y��L���C,+�7��"�|�*�q����u�21��D.-.����\Dt��@�����FJ_M���a�i@/p��7[��{�\�a�3�x��O7��E���RS\�;��;A��NT %�9����7q��"x��%e��"���n��c%�������#vfO�-���E���%�ug���q���-�9G����!]
�D���|w�����e�m�)��"��
)�s4�wA������N�:�������m<]�����%�D�$���4"
s�����������b8uI�rmG}N;�!��o�����k"�1�"���x�}�i�P\T�RgSXy�^�U�Fd0tw�Q�����'		
&}�H�n�O��F�\��dFt���NJ�p&��#Z�V�(�
)Cpb�PJ��I�&	����D4�n{y���	��&>0��%O)t�xn������"�B�C�IV�s�%�.��%�0.t��y�~Q����fW���as�#	�T�.BF��S�x"�p�K�1�l�bNv9<,�1����!T�����f�����sH�D��m<I�	�Hk>5�+4g�(J}�Cf:�� v�U�L
�uys�����n�(�xu�>t4wi�i�.����<���2L�H�"���`�f�JQ�%�L�Z �3�Mb�!�5������-��!�:��"��b��H#?cU��M������a�y�4
zL���\`���������'o�(#�1Y�A�F�l��U���������U|�?�����w��(�3�W������"��k�K����"��t�_��cdf �/���W[�3���������dOwuwuuuuu-a�0��p��9�@��1��W`X��8>��h,�/�l�m6X�q!�� ��{ho-�=o���?E�s���3>8�r�v����C��}����� �v�_��O�a�G�[G����W�����E�TVq�6�d`������)`��~y��O�/��M��B��vV�A9,��>o����o�F�,��G�Op�;D]�;�:zK�@q�Y0P���/�	��vszu�2< �iD��h�G����K���������.��<��`V��m���n-`����*�����=��a2���6:�EBn)\������H�j�X�z��T9���Q�!]N������������L�������5����|�p���A�1Q���&p>����Qbg4��cAH��zq��9nhq�
*���\g�����t���:��_x�Y#���)����eX@�9[Y|#t�?�|SJ���0G��G��pB�������.�(5�]c����0M�@d<���Am:����n3�Q��T�}�x�FJU�������'��Fv�r���uV��������!>S����.t��|���QQ.*�������������mq�*��~��S�>��MWj���dO��� pti��d�p+w+h�6U��uS�����I��`<����N���,�p��F�w��s������B;��/,�q���#%�:p8���^m��|[)��NXt�r���uA�(����x]f+��������e����,�����D�9�Y4��X���G��>�p�9*��0�frb+�:�hM�}*����1����$�=������"-���������y��������{H!@fY�2���#�?�����s������+����)`W�Rf�d0��M���O/��J� �	�Cv>�E)�/�����o���Vx�����������/�C`;�4�%m�,��.�k����	�;L�d5Ie9�HVL2��H�LE��@�<�� ���1A�q�:��L���s8�o�����u���5��.�B�����K�c�[u�;�K��BRWT���-��E��B�x�8a	�B:W3R�\�R�%��FY��y4��e���Z�|ou��� �F���[�5^�
+�Et-Hfj����h��	����]�Lu���g�Nbt���E�?/���Z=��?t
�P�����~�
H�?�45������d�q�N<�FH�:/��4�����<a1����XJVp��V�[_m[��Y��wWVX���f�3��������a����E��#�r8%����P����9h����0��<YBN�rrBk��B<��H�.w�FK���8,*Q��V����`x�G'��������F1H��w���P��-�������2]�	�=K/I^����$�p2s�_ta]J;���r}�-i:(���4�_�E�Y���T������X�{��K�W��.�A+��~ 3�i��j��o}>i�/�qM��(��0��p8#d�\u��!S������6fX��4�eX#|,c9B��4��Ee�Q��:�,e_]�I������Z%=��paa���j<��V]�W�	�U�ex�t�W���:�p+6�"��f�Q����\j��$��-� [���.PS@m!DN���s!��W���7b�Z_���&~�e���u�}�_���w�������ma��:��#����d�d��U����������/��8s:Vz`�8�+�	p@�8mGVB��s�j��������?�@�`\���p����(�����|�eu��JxI��~QVv���
N��R�i����ZMZ�Ov��%.���*��^�4j�(����s��D��	��Wr���+�b���e��q�mfu�e�������Z��{��{�(��p)�;���}�,���;�v�����$|��������������^�����eYr�[���%+�8�Y��(8cQFv�0�j���p!z�y�t�"��#�^*J��i�e`p�������|�\�(<'?M�o��� '�]��U����{k�	�u��
����rr� �%�����W��
J���f���zd��]`%M���1=X����])��,�S�]�!S��QhWPCF��_h�9:�h�#��K1�L��*������V��^�i����!����xP?�h�o
�F�;�7�a�T��`���/�	�v�}>��
^�L�X�/���\�R�����2�����5TQ���&�AJb�VX��W
����0�������!g��ic�����zQ�q�<y�-�>xs(
���C���\�x�]�$������>����-8�	�N�����������|]�M��Fetk��d���B��
��
�M$x��r��e�K�3��2��uKiZ��^��*v)[J���@��w5�b.����1DO���.cMO�#���������c�b�	�5wFtv�I���	���&���(D_�_^�\��x;z�e����oPtJ��&��=\t���h\�p�
_�����2�����Vz�-1��y�2�����g��F��G�'V�K�E*b�!q���Qh �T)���a2���?d*�?��0�VAa�:�����<�y#lz'�YW�aM��P�i�����irO��xYu��R���{����9�}�d�����$yV!-������1���#�z����!�6Jj��/�
��I����g��9�A\�g]�f,�����L,$�0*�wO���Y����*%W�V|��Q��G�m�n�!|�$���z����;�����_������Q�2�]D�/J3p/��+&��We��F6���_]~�m��e�'�Q8����e3q�M���4��7O+��oJX0~�qv;����m<E?�/&��6��*�4��)@��K	����|a��T���e�]8�G��z���i������E������vvq�
��o�U/<����nSV����T.n�+�h�/����a}Qb`	�_��>.C�j���0Mf�F���5e2�&Y�L�@0q��e�[�/��<������Y����'�&X&v�����_�.�0:.��hJ����_M��hJf�p���W
�rg�����gt��)!d�@����DG�#H������sX�k�;��m��r��_��o�rz��q�Cm�Ea:�~�eU5��W��U�z�jn�M��-�����~�rK*���X�A-
��E7���@1�% 7d�pz�@}i�M��w4�SC*~Q�*~�C���Q�������w�T�����o@�?����f'.�RZ��,T��R|G�eQ���v)��;R��W�� ��(�Gw��)�����)F R���	��8���/L�5����i�Y,�(b^������ ������3��E��������$�KU�����&�/�Hw�z/���7	��?B�.c(Q�[��Z���u�,T$?ZI�6��
���/'�t~Qbq��|��'���<�4��F���t���~��	��0y$���tcP��F5�ET#y�T,�>��#8��`�=���t�S|Pa����BJ��k6����#���G�?������	�B/����dF������/NN_�]���^��8�Rk�k���X��z���a5ZJ�����&���91�p`>��dH�z��9%5��4~"��,.!���Fd��X/?Y����rNQi^6�z�xX���I��@��=@�PU��O)��~)�����}6���/�d���
7z�2'�wo]V����Hf��cF����P��:��i�o�/��#��0pS���<�a�t��1T�t:-������GR�w��)�����eX�V���.G%�E���M�<x�W�tJ�	,����JP��Dm9�V��U��JL�'��(�J���W�B��0����C�J|��L��b���i�����|�� ��- -�s�=Uu�Qy�i<�{���v��9���'Q:�����Z�9�|WS���m��|XN�����~���*��Q�lhr�
���U�!j��H�����N<A�x�{-`0�r��G���=B%a�	�s�gt���������D���*���T|�?��������]n3�I2��$���u�W�����#JR�Ce�7%GF���R�)������0�Q�x�^M���������M��&Kj����B�/t|E9�y������]�2G�0��iT�NG�epi���p���LHw?�9E����Y0J��Y>�(�yWX����P�t�cU������&�"M���!+F`���c��	��S!S��bZ����BU��'Q�j��})0O���)�3��u`�#�	u���a�Q�p�x/DK���q��$Jj�j�S�4
P�i��%������d�X���a8N8d�cDJ��D�����*zyY�h�0����RK��b�h����Ab�e�,� ��:�LN��\b\���>E�e��<�x���K���^�r�3� #���c��b{-:�������
��L����p��<�)����rc���l���	���MT�p{�ISv�A0�[�SI��6����w�����&��C���tp���&�����������(�4y����p��������������G�V�@j1Z�M?M��)�aCg���
�G1(n��18��0��������m)� �Tg���J�8����Q�NR�~���<JM��c����?��p��T��(o��{
dJ\��\�Ar�Y${_cZO��(Y�������=����Rr�R�!4�xp�jDa�r*�E���z����j��eUa��h����R���x<���7��#�^k\1�I�b\f6��<,p��SN}�Y�% G'H��HRa�:6�t6O1KUC�����J����
5f�d;���?�b
�T�P��Cw���.��ltP+����`���p8Yc.�M��7�dc<���c�)����d���<�L2��T�=
p?���dd:m�7���#�eSCiF�y&~�}���Rl�}U��8_�C�r��'����l*�U"�a����"*�|>�P)�N��2�G�o�p�H��p�8�&T���$�@��U�j��uT���u����9���(�qjs�i�.�;������MR���Be
M������Z{)��P�Qd���1�����U���b������P&C�4���p�����P--��v�D�����2�ub�M����
�+��q��h����th���9E$������$���*es.7�dx��)V��e^�����g��d��^Q�Oa�T��e(��y_�"q��v����M�w�7�����.��<M�Z>E��	��{�fz!9Nl	[N_;�>�JF7�6��:s��&�}S�R��:����}�$Y��K+�2+�������rR\���8j��������i �e�k:�F%,F=l�LmsY�p���o'���[|m8^zm�_�1���$�l:�d����*5�B��?4�$.\��Ml����W���l�6:{{A���=ht:��2�Z_ ����v�&Y�E�"�T��9�c�Bm0OoH��+����2'��zU[_GgZ�������s�F`5A���8&)b�VmS42
`��c��|��P��bP�,�����$��M$����C��7�NB���^Y�K�*�u�V�+��)�8�c����� *�$i0���qf������^Wt�;�S)�P��>�5�1�2��i4�d���OtN�����^��\����4f��S�I�.		^��������{�4y�k��.�����u(��J���J34���G
��cT����s�4q�|�ru�V)��

M
?�i�8	8WX������FMB.���> ���*�!����CC��m��G���?�"3��\�T_9��K^]�2�N��Y���gV�5hU�3�����&���P�9:��X�����i�������G��?z�*����Y�� �����@�I�:r�3m����������/$���p1DT
�fUo�O�s$�q���}��6O���dw��_�F�'"X86�:���s���#@��D��3��B�4iG�Al�U�0����9N�p#C24Ni��+����8i���j����`R�C�K�t	2��L����4���I�*8��Q�YR�0��+�+G�Y���[)qXG2��L�f���������N���T����D�\U�Q�`76�RO������N�r#XZ��@�)?i\�1��~����*l]b�7�l�������wz)�9��|�Y�������?�o�|r����e!H�b���HG�w�����-]���\���>�f������5��=5r5X����n�(v�$��E�h��������1�lG?�lp>V�c�"����#��mS�f�p��@	�)�J
�{zM�
T�X$����~�BgM45������i�'�,���aD�	kiJ5E�zQ�8����,���_}��^��]9�DOq/=)���X��ELh�C�����$��n:���1�����/�p����|	Z��[,�Y�����Z#�[��@�h	�d|B�c�I�%b�?H�V��D�7Q��v=4Z�&�F!z~�^4���n��E���<�m�s������(
PF������!�3�HgF���d�����wch)2���(��� ����,����/��v�,���m8�4p��]L����Q����s�P�Z��o����U��ncA�G��;��u7��]�����YNv�5,$�g���#��[�|�z�e���(��������[��~Z2#��&�_tK56��
����J����}����[-���&�C'����������%4c>���_��NM=g�l��|��:������H�7�������	E���0W4�;Yp�\��KvEu��-��\���i<Q�����f�}��$������'_rB�Gr���P�dG�������R����+��Mt�f��/��t�
������\��~���5��j�`����������p���l��;���V�i�w��_�����^����Cmh���@U(�E(~�|�x��?PK��^E$_��Q��~�|�jU���������n�\lT]I�
Lc���1���c��<�S�4�������0E���a0��@@��	"��B�T��?���Pj�.iX����Ul�6����E�����sz���<�F 
����\�V$��y<�S�TR�������`�j�t�] ������@-�EU
v��[�A�UK�&�Y�K�O������*���d8��Jmw�-��riP��9|Ux$:U������u�����(��EJ���)���V�.C�~5�4���6���gh�0��P3���pk��.�F��f�J��`���
H
��p�oE��&d�F
:]I�*&x����8��m��@(�d��d��"&�"�[)i$4v@�s$�8�g��%��	�!&��a��	
C��R6i7�ZzB� M�L
��KK�m n9��]8���`��0�8Hh����d})�
S(%��~*{���0�V�I��`|��:�jv�������w���~<��^�\Q�T�s�|���i�<��9�Q������J����K%�%��{�����2�.�E����?U)���Ch�i��%��I�1��)]	jcrQ���"B&���5�$C�8��C��������	�+#��Q#��gU*���Tn��{��#�V_L|)��&�-�p�<��ZSUA�L��%����+���#��3����\el����%�q�z	�';��Z���Q�o�EO�{BK7���q��F��>�@p�8�����}�������^!�?���^3��A������@���H��h���:Z5T��qD]b�	�!���iY�M����zh��4v$%�O�����k������/���;#8f'z�����k40Qf�}GM�������J�?|F����Q�>�"�f�|O�GA#��=�,��H�,����J��������|o�HU���wT�M��������Jz���s���n<DLg�4T��uU'H`	F�k�Z!����'=F���8�����7g�&K���T4p�CU�Rr����CM'�(����AGpeo6��u�;g�����j����R��p���0��>�ye�4��&�upU�&��������x��N��0�������77�(�xn��[>7�0\47
7{qr�������^��Xm��D'nz����~�X�u�/��9����8�{�H�r.�<
��l�7�b:��&���R<��z��P5����_�C|8A#r�z���l�"�S;p���~���9H�(�Hr%?��G4��$�#�K��*�hI5���T����Rt�B5)PJ��~�	<�V�m36[�7�C��rL1����������(��m�EtC�RK(����4����A��V*~[��~e�R�fK/q����a�Y����)�p�5�jc���+��_�v����X��_QGTG8�k������f��/Z�YC}���m�^MW�Z��;;���v��mm���n�W��@tj��Jw�HW�?�Hlz��m���f
�]@�V����D�!���OG?�]����C� v�&����;
�� ��� �0��������Y��e�j���6�7P*T6\�����k���R�i�h���!2�;��Fg+��/z�Q���[�c��g����hb���|�����M�|����}���\M7`����.O���������h��gH���
���Gp�2��4��(�dA=}`PpY����-��������oO��i�s���i(��0��+������l�i���Z%oK6;�4���Kt�f�DQK��W��vA�C�X[�����}~<T�U"���6�l��1� k����DO�{��������	<k���cW��/75�d�
\��S/o�4�?e�4�wQ�@'��[��
:��7�g�xR�E_�>`o k��by�
X�t�:��y���K�_���iGDW�,��[�����[���[��/��8�a���4���f��*��PE����8B9�b5�{E��@������
�/.��1���pc	T�;o����0��~��F��T+� (.)�{��.�,�@n�v~BQ��S��:��{T��i�����V_ k�8TU�6�(�4���?�aU���h�.t����c�i�������c�?�\���^���j�G�n�a�D���f��s�����2��u�t�����?��$����l�MAY]>G�����0j�[������vw�s���Y��D-���k��-���l����{�g?�*�C�*��@��r�r��i���S��|:��g<�1�E�#�Z-�5_���x��L��a������6@Dr��);��)����&W���!�aW"s`�z>��]�p0+���me/|���������
�^Ku%�#�y��7:;pw�������C��>�������-)#?]T�M����;$�]�~M��t�@l���]�5��ey����V����V��O��O������vn�����"�p�,==t%96�G�Q{����Z�~4�;�>��+����T��b���t���QH���������O��_oc</���<�,�\�#������
wv��V�n�louvw�����nU=:����?\��:L��W
?���Q�:����n�e�j���i��Z��i�k�7��wJ���$]��i�^V ���A�{����[�X_'i+���:��
������A�����u:{�U����v���3nwHe����P2��������hx/���?��T���T(���&���(�����24~v9���Cr&�t�L
���>��W�,,�=	��F�,�VE%��;�*;�
l��)|�`���v����p|����������������I���������2d�4�F�k�n�wQ�R���
��q�I�M�V�[^����Gh�.��|���������PwCbQ0R*��D��K^�@���dL��R`���<��J�+��?O!�?�]��e]g����A���$I_d/��-"3%�
m�HSk�+dg`^.l����kKU�/���+��n�C~X���QK-
�u����f��L*��u=�����I�8(|V����y�G �U�����X{
4b���6�����6?�OG��Q����g��3���f�X�l���wz�J�K��iRfV��MQ�n�v���5�O�I�����J%����n�_r4��vZ(_lX[�Q�q_�����G����[����_YV�qp8{Z�P������__(3�T�1�kE��MHqLJ+������pY���[�h��n������hXK$])o�C�1����S�_���`Y��k�auE������:�����k��B}V��
Z\-�R��<�w����`��j���~�}���V%R��F��(���;�<����__~xK3�$j#��J�Y��|^O-��
m�6U~��Z���lh$������&}����V�N�0���&�}T�����.�5��,6�=]�[��JW�_�~������}��\
�`��lnE��S��-f�\C������a�?h���;{����]UA!���T�Ey�f�<��X���|z������;"rt�}fb����v�.-�W FV���Y��#�0�^�����q����)����~�e�;OU��MVBi���_UB������-�nL�U�j`t7 $���hZ���ZT�iB\������x�a����w��A��o{u����Q{���=h�ABVD���x�a<�h28�i��������;�'�GFtC�&1�Y�Iq��L��v�����xS�fS�
+���,�W��r6����V���l�����hokk{�;�5X�@[�]JM���B{J�W�@-���"3!����4pQzA���v�������c�3��
��������[
*�T��C�q��h���P,�w������f��+�.K��F��Sr-������8�x: �k����Pv�NE�N�#�������T��f<;w��oU��}�C�����hac��iFCrh���\���?Q���(�����t����i����R���.U�%�,H�]be�/�,$��f-���f����E������i&b@}�������	���G��]1hM���FW�P��\�P�:���#��uU�Y=gV4)O�M�"�u)��[Snu�l��Y��`��U5���o��	������/b{�M���_�	�+�����8a`�J5������>�-4�`!��4:A}����l������H(�����k�d_�:��u�p�^�6a:]t��������Qx�+�T���1�R���[o��$\��a���Vk���m����Tg
P�W��j������<����H8�FZ(�SI�t��L����*D��7�HdY,hgA����0��1x9�o|g���t[�
lW���V)�"��:����b)��";�;���	|z;�6�H������3<��C�cl������$�d�/������e��5�(I�!�M����ffp.������� i�j�3�_b��VF^U��w�������i��0��=h���|��\r?����G4�����,2����]��y�����?�����Pv�������L�+;�s�3�joqv|p�7B���,q���f1�����&��g��4�a�D��$���O�+j�R%lZ�������(��|
���jR[�OC�+tW�����9���R��W�>�#3m����Rw���wv��.>��t�����`-��;�Yi����+5l��B�u+�h�;���6�8�(�<�=������R����v,�T�eT����K0X�'Q�A�#������~P����ikN��N�{yz��%�` g���G��	 hB�o0t��_���3��E�\��|�h��wD����}������Z<����u��q������������B�`o���A����zQ���M�x�.X�rhUS^����;D�~���� �h����0a�["L�U���mt���?��
��E��O��Y�\#0�/���yF�Wb$[���5Y��XZw�Y��T����V����1�~�p������-	��Q<`g�l>D����a83���2
`������������?�S���-�(}nY�:��)c����CD!Zv6��Of*����q_���@��J��V�������%l2������X�����"���Y45zVsb!�,{z��_[��P�(Z�8T�J����*�j��`7�s��?�NN|�����G�VE��r���U��z������[L\��������hZ����*p�������" �_����z�%����A�"t��|7��/��T�_�)�p���5��q���o�N���N�2��|���gK|�'��x�����?����q;
���`���G'�[�#�S2`���z�H����KnvE�^�uv����V�?��[���pU%�p����LL@��j_[����6����l��Y
�����_,6J-�h��F��8���Jf-���p��4��Z'�ih�z��Q������/V+���Lk�+�V,f��Zo�����h�_���'j���1�5!N���A6�|��,�<�/mX!
;R4OF�5��"*�%��ZYP�Y��|4��J�~��WTrvj�Y$f������8?w�����F�j���X���e`&d�=f_������~�{%�l}uE�� m1�GisU�`]��������N�������V�D���K�2��v}=��Gc���~�p�#����w�/v9�hGPQX(7A�%�������"Wd����>��f��$�Q��_���4��"s`���	�[��5j[A�"��)���e����L��m�B��K���G2��K�.��X�]�����s�J+-��+.���m������������NR/��vR.0�>�.S�p�$b�n�����Q��x�������������������V[U�p�'*?���|f$���R���/��d�4~����H�����,�zw/z�WQ�	�������%!��lIQ�^���S�b��q����|a�Bg�N���""1�M��0���8A����%;��\��NMo���|��B��kz�1VB]I ����S��K�PS�9���L��dOQ]0��Ej$���$z*�[Vs��,����"sn���W�m#;�N�O��@��!P{�@���v��f��3W�#��pD\^_^�>?;���wrts��NgM��W��^��iN$6
���4��
���a�j�D����3\H�<�L��uV�t��5cg���A�O��4�as�"��;Q� �pw��%��B��R��@���vA��!�%�OolK�@��T��,V�0=��_xa�q_6���2�w��#��h.�b���p]f�Y��sZ�)\:Y��v����jg\�\*t~����T�m��I{{)H{k�YI�6{��.����^��Xu@�,i���*�����;+(��L��zk���o�dS ���FSK4��p��;��~���u����[�5��%jF�&9?���@�
�P��~�^z
���v����X�g�yI����t(�z�N��]ss_������1��F�o��2Fyo�(�z�8rVL~�	���p���gp2TN�,����>��~�F1�Y���r X1u�:�M��cES^��Q�����/��^R��$������g�e7���3\��T���?���P�SF����@-�������I?���V����t���zJwt��������zyw�z�4P��7N��Wx������6�)/�0v�\Mpq�����]O��(����0�Z�����`��^Uxp�-��l�����-�_�Ke��V7T����-���ZuS�0��og�y��W�hB��Gu~+7�-sYV�\����,����J��-������vMwa����������������������u�������k� ����`��f���)<��Q�Q���\��%�-����'M��U������0��,����)n�H}f{�6����
�L��������$�R���{�Nfny�jMS��������p����<��/��_���$�F A���n�>��T���I��'��Sh��,S��<�z|������t>�5�/����������0�����0]��3B�V��;��kg�QMg{Mo����|�s��V�|�7��&�fe^�%i��Q����O%T}���,���?~�f�.��d�A)!��� f��yK�p� �~d�~�b�X����+�q#�o4[G��a2^'������S�B���B��N���������
�������\$�&��� �Q��=����	�x�����Gg�CV��E[��V�e!z���f7�,P�>������Tn�� �##T8�#~��`�)&�Fx��������-�8�Y��Z���Y
>����`�c]@0��H	)y���s>���YB{�`���(CEl��w|7%4�|}~��������������?8�:�����������?a�*@���y��E�8�K���
�_n����+��,X�[�6#RrJ�m8�A��kl�?F������f� "��E)m�8k���4��">���&�G����V�NF��T���/����H��l��D�D��������71.�]�*p\7/�a�3��kfJ��8D~`��B���T��yb�dl9�`������#�hh;�C�#	�GB~�9�=Q�����d��	�I��fc���M!5�\1�X~���o�B���Z�P��4O��,�9E�7��i�X��/|��z_��(TX�qx,f"���e�m��`-�����&3iI4G�q����_
in���R�J�U6��_�r�:�:"��6W��=� 5�6�c�J��PQ�z�z���N
nQ�(��������WVUG�����N���Dp]
�v������sLkXK������!��t���B���I>#��K��������wY����e����4/����[Pl�vU�������8������B�}@.l��u���}%�G����oN�k*����*:��m�+�"2Z�a�lf�F���W�@���P�������	*7��(����O[����h��n�� ��������o	�r���4b��N��J!���N�O�����G[i"9Z`�uG/=��
�E��!���	��0Pgj�4��~������V���R��v�v�Q;l�vv������pU%�����3�@5f���v��#������N��y�:]L���
�?�:�l�8�:[�[��`kkUZ������HQ�X�?v�-h�+M���s���S����R{�L8�i2_��k����+i
��#�M���cu����wP���,�����B���r���/��tJ5�(�"��Y�4P�!��
n���c#a8�xDt����,���b�~2
B�n��0U!�Z��	�)k��^/��c)M���`� �d2�^d,�X��/v�X��
E�����/�d��G�S�B�'��R�cn_���������d~��;�a��#Ov�s*�B�L������C���������xO?���Df��} ��*�o)���m2j�6
>�K���;�k�P� f�y�6�R�rG�4Y�T�����>$c_q��;�pr6du�3-2R,D�qJ�!s2���}������T#����Q�����9S��N��w-�JO�����_��D�1���Q����_�P��i����������^���kV���H�t�8��$/�4
�M#�Ucy�P�k�]A��w
��B
DYO@PA�	���d��{p������yj��{g�h����Z�����������?.���=
���)��ZV(d&�4f��n���f<��U���$������i�Qn�1�P#��+b�YiM. ���\��{�Q0%�N���wR�z�t�����Z�������F�p���?�����ER[����z$�~L^T&;�O�h�9�g.����`�g�@��8r���$�l��)�sy���O���l�-.�-���f#d��_2/et�t��<��Na �rRZ������c+=v�0�+���t�]Jztg<�1���9+LZg0������m��v^�*�AA���V��Q2���Q�K���F1~y8}���e����n��9���������Un�S<�o�t`h>�V�����j��@`��&���O��]\'�4���>E��k��4��<�D�g�mA�al�	�)��F35�6��(��863S5fN�RW��=Df�S��	�zr��{5���oOR�Io/5d�6��o�h|�=iL�D�����t~��j?���_p��g����0�"=1w��{62�+�.�iH V&�� ��Qh�������?�����%|�B6���VHY�]�tJ+=�:��bM9�S�=��"�I������x/;u�/��4�p�x0����'@��
B������(���P���������2
�%c$+A��?���� ����M�)P��M�X�?���U] �kB(�k.����1���6g��h������������X����#���7k������M�2��O�Q�4(��*�m�8O�O��+	+�7����#��N�4��&��J.������X-j2����w�y��_��t��N���F�
���v��}���v�I	B�B�o6[Ap32�n_T�c���#m�6D�+��(��������]]����z�&��sg�Z�,�{�M��
{+�`��=�h�|����^h����/����fk�f�{�J���^=+�J�u�6�������p{�~��^����;x�PR,T*"��6�)��8���(?�A��M+SK�P�X��WQ�>�����p���z�08�"����+C������G�*J��z��������v��u�aWz�^��r}�=�.�8�r�������h�=���cT��������%,���G[c��hi�1E�14������>| �l�A�c�l����0���)4I$�<���d��������kZ�1-��m��7������b��L����.=�"4�7�l�M59AI������,�u4�Dk�6��Xq��ezM
��)]�#r�������'�(�����=���3l��$��<�-��&t��mc<��`���yD�XS^���;��M?���z����#qir?���f�d��>m���d4j-{D_��!P���G�����&-�G�
�8��b���j����a��
����^���n�bC\n�b��b��n�3���l�e��5����d7�>�v��$U6��b��u]_���6�����&.�u]I�4������;{{�Vgow��F���[B��"�&!��wA�hB����-��3#�*$*���� ��	�^n����U��y���/\9z�P�{&�$.#��n������N�:��������?b��:��i� �gy4y�fUQJ��q����HW�N��)7��.����4�k�����J�����r"�:��R��_���p�`�f���,O�+��m�G�8�%3y����t��D
��*\{���n����A�#%n�d4[`��bQWi��3t�����dV3���+�^�~����������$t67�.U;���a�[�*m��*i����Q��������]:\�6����0�p+
�����������9m������AVcdq��{&]��E�2_�����Kk���5\"w:��Q8��m'�U��X��w"�Y���~��0e
vc=�����8bB�q��i����#�
��$�H������L)��w;����
M��;(�p��d@���cF-2�%���ou�D��TS�LQ�������#}y�v���[a�V�M�S�%�va}����>�$����!yu!cH�%�����\�]V��
�.������55�t�5�r�C�K[^[�S�dyw���=J���>���3�Z.���T�����p���j�ZQ���;������[rp����oIu�H��_�R�����Pt6�r��X>}2�A3��oe���$��@������5�d��+��"^���~x����QV������ 
���F��o��2F�XM���������8y�u�pl� O�%��p�z!�a�r?�_Y�?LC(V�W��ye��29�d��������]��@����� ��������X�}n����.f@V5�dA@���w��Qy�?8�����
n	�����#�o����y�uk���(�>V+�2}�T���gY��*-��L���@���s���A9���3�,�U|��5�C�|�^>��`�a.\��*�:��+������Ha��;�m;G3O������.�#�f�rZ���8��d�X��*���F
`*;���Lm����4f�b���C�L`������(������1�����BC-y�,`�����i���Z��TW�:G	��&����������/�����]���?�>��i��{���~���Y�ey ��-�:m�������X���x;��$�9�s`��p��&�`�������
����vg�����!��h���� i��G���h�!��r��^<����3�"�#U���|v����C�<Pt�g�O�5��/)T�{i4j�`�x�>v�Jag������������R��Y���,^��Z�Z�?���(��o��|:�z�U�&��#���&���hkg�`g����a*"�Cnu�n����]�<����B���#�J\ez���
=���F0����S�+/�3�n#�e�t��7_�)��Y����[�����Dt���=��d�Nf��t[���b~�I��{<ucp�H)�9�k@�#�����.R�B�U�i��h�$�Y���5e������3�$�Oo�����>�;t�)��-M&h��\���������lJ�c9U������L�v}U�`�>m�����V���a����������5�����{�B<�0Z�_Ma��������n{h��M&�p:�^
��W��|D��l{����+�0�<��f��Gzi���h�;{���'H�=�����	e'��������������R^W�a�i�+�/��+FV�}�,���D\�*�.������9���?�^�������K�������+
�Q����w~������U��"���V=��@�_�P����	T�l��\
��k�9_�rZ��r����kl�e���`�s�y���j7P�!�1�P�����k�[+1p7<*j���\c��)����>�>�@*�=�0$l�!Sx�W���@�P����L��4t`40pt�
��<�$��	�F%���{/j6�<�p���a���T��0��
�(�d�58��!�o�|�ol�W>������g8�����.�3���y��I@A�51����e��u�B�2C�*�t��3m?*PV	,R:F���
N��]���\eK#��Ys�R~�C�7�;��<������\�rkg��yS�c���ocN=�����GMxb��V\]/���\��+������2-|���b�Q�Kh������S��>�����|��
��E�1%o.
{h��Q��������o<���:4f[,�S������Mj�GG�	l��)��rZ�����`��Y8"K�t>Ul�D��+�+8���
���$����Awwk{�u�j��a����]�X�|Y�Hw�v���=���{����������K5[�����K>o#V>�s�l6~@!��!������]�wpV��.O#��3�#�������8#0���=g��IvXh{���&��>�=�#�C����Ie>��;D�cL��������p�c�
d��c��o��S�5��B���I�['�o{���7WGg7���N���k#�E�h2��j��u��?*�t����I�o��K?PfRv{�5�@��ED#|\�L���=�����S�'���j/����FR�p]�Xqs������������	�����_}V�����>��h�>����T5��s����*������x@��S�f�<����o�r<��O�?k ����I�`����m	=:K�YO)Mm��Z%��u���� �B(�F��9V-Ax���,��������x �f��" �:�Fy���"#�W�\�����J 8�V*��*���B���.�T��87�.���r���S�N��5^d|���	n~9��(k��Z�������~���W���+$V��Z�Rc�����p���zQ����'MJ������O�c�A`5��4�C�}RL��g�y�Q�3|��]�r���d�@6�Am�tv��vg�5��6���r�'�p�`�����I��l	��Fp���'g��i�9J3t]��c�����8��Z�t���h~���FS�vP�����z?A!��pwu
�Yqt��|Q�S�L4����ud�I4�����K@"����^�N./N������U�����O��ou����������b)�����mmP;������7��R^h/�`n����{=�8��������;�Y���^��un�v��U�~q�t����s���P
~�>zsZQ������y�_O����,��^^UU�/N�N!r��������
���P�ru���
q��;�:z��]���\hL]��>�tz���Jo�K�������
p|yt~z}�`[����z_Z����������Q������pQ��������S��������+3������Cv�������rNF�Hu7g�����N/n._��|tuu����;k$����[X�SgU���gfe��]�=�������������a��������z[��.��]���������7�����x}|u�������P��PLl��?�^����;���Mc�K��A����--P�|������T�������y�U���|����
qMQg'��"����#3�s��G7g?������o^��.:���&���~�Gr���f�Q��T��{|����dNw�����}�r��\�����bL
�/�CnnB��9���������O6�e���oQ�������a(I���cnhVC������`�5����o�f\��� pZ1��RE��yX��<%l{�Lgh��J���z]_�B0�:`?h3G�fr��6/A��W!*G 9F��l���:Xn�9�a���m�xl4kQ���m��?E��������z���R��^4�c��fz��p��m�� �r<M���Yw|���) �pO�x!�������<���&C�*���W�9L9�*W�P�T�3�&�~�x�&� ��5l(��E�ht������4-y�s�ci�����k2Q^��?��	u�p��LD���I��gy���4����e:cfX*��HQ�C�
*�����)��#R�c
�8����@z�G.p���a���1�L#kKll�{��o��=�+k�<��C�Pp��_��~i�[J���)�~�Of��Q:�ZrK.�H�?�w��s3���:{�+���-D<<tX�@p��'LR�H,x��,������BB��U`D�
c~�I�v�QE�Uq@5�d���%�tIrf��(��{�-'xS��?9	��miw�t��T%cI�8aB�� ��S�� J{�-��A���C����n�}	����i-�
t?fk����g4�����"v/�������i��@U%nW%�]|��&���Ap�����C��I*`��W��/�d��X��8�ga�5�M�����F��L�����/�{I��6**�vLk��=^'�<	aJ��=������ ��8��N��i)�*h�=R�O�4���L����M�	����F���3		�*��/��H��_���$d8��T�^y����oA5o�����C���S��	�
���;�F�1�"�t`6�F�f�K�_����Leo��������'���`O'��c�u�u�$ �����9���\g��Gy�r�
���P	��~�0J������5vp�2?E���
�U�$���h��S��)�O����:��'����_�8�����6q�4}�>)Y}���<�QdigHl���K�/*Fx�L?Gi��C�t*��
�zuM)H^1�D��[���X���&F���P�,=��!8��M�r(�4��c��]�B%|�Z��: ����8�o�C3��I�x ��~5�;KJ�5-��TjX0 #(lx~������N������c���)�m����&H��@��yh�P9}�n*m8>A�d)����������+[���=�{��H�]�"�$�t.U!�BHh��d5��>&
:
[�W��(�����*�����=�����;����V�]+q���s8&�����`"�.��|�y������=�xr����&�c���a�72+�4K��1�l�P��1��v��w�&W�O����O%�C%��8��!c�RX�T]Q�
K��v`�����/5<Ni-��5.5�����jV������A����
[2�gi����ER����U�;��Sy�q��s�]
�P�� �S������1�3��0����:�PK���/���!n�9��^�������p!�h������
=T�:fe������������{���X���|qy������o.'��$�|��
>�1�8F��Z	�O%���g�sD'�3��F����DI�D �U�+p2����q���,sVV�H�O�$��'@x#�6r�'�Q���}�J�A?{�M(#E>	�������x��U�@������=�r
1\��F�������j���{������~�%v�RK���
��C�<mS���Goz�n~�|��<;��hi��������4	��bh��5O�h��5��z�����#��O��hL��a?�|7�$�0f�����+�
���*����!���D��!�zZ�j�R<��*�M����p�����{%��iB�j�T�}
�M��3S���0j���=����<L���Xe*��P�R�}�^�hjt98dhk�p$�`��_47j������Jv�T��D�@2�)I>��q6���l����"%�
���k�]H]i��^:Uzw����j��<��z�7��S��E {���������.�n�
�%�*��^����[{�����y������<���b>���3R��/%�(��.^��i���z���'QU�f
X��W�j2�z}4�CZV��&t��t\@VJ���6�C �lq-H�Qns���p�p�\�|v
������:��
�8�w�FN����,�0����������#�*1xX�|����"���kv�LQY;���{���#XC2������+P���;Ge�_��'���wU��bY7U�����U~�&y�l"��&M0�S�=����j�N�\�i}��X�Y���d"^����wd��Fe��F_�(�w���*p���P74�(�l��'TM}�l�C[�=����.��-gI�b�dX��+�N�8�������+�c��$�����n;%��@,6�����sA�(7A�J���p�JC�,�2H0�Q�����
�����%�
�{6�
�X��r.�$���{YP��j?�MO9�W?���-eU���>��e�K4[���'�D���x+�\���@Z�ON)d���
��>|�Q|��C�Q�C���~� ;s�I�H� ��9�-�����(��=��D^��l4�%i�>�(2���SBz�Z��LLl�1&�����(���6���`�%�;E�@d+��PR�F�'��7����O����E�HO���=����'����P|�J�O[�~k�0�{��6�>V��:ut���r����&���.�o��4lq[��������|��k�$tR��7QlV������v*��RX�v������D����l�����Y��
v!���r��$��S����r}�w;���h��j��a���?��Ap�-P"��.�F��,�p�Q���N
Kr-h���
����ao����4Y�5�Q%}�kgD[��"�>:���{
��yD�{���#��wh��?w��Q�u0u�{��A5��	�X�h�����} :2��A���I^�g8:bnxD���m���� yZ�K�C>����f�R��]��*���q��Q%����q�r�V�������w\��#�E��a���c�P%�)�.��[f��	�
�=�����|2����c������U��>�|����PiN$obF��0>E���j�'J��@��3s�6���)����>O��_��4O��a�.��1b��j�@��7�
$���\[x���m��������!��w���� ]��f�.J��(0`� BI*��<� 0=y���h?I�������_��
���tZQ9��A/qy����o����q�[�a������~�^KaN���"��.��%HRH�����'W��HgpC>�b��L�e������TF'~]���!
�8�7�W�����i�)����=3�� xl�������F���
*���T�1������P�B$���W��r���`�7^��*d8ew�1D$3�����LC(�z�^4=�!�_41�J�7�-�Q\�����_,�Z��r�_�.NN?��}�������zh�����&Cx�9
l�u*��k�i���D�5������J� �!=4�I��#5�����H3L�����	�x% s6>���NZ�=��7ks��>b6;X}b����E�p�����[>�f��!�n�W@B�%U���B����x���X[*�.]���D�����y>L��-���lR���OJt�m�+�A��G9��-8����8e�FIJG�0�b��'�&�8��M����Z�Y�^�Hg���5`y��<�e�E�T
�!�*B���7[6��zq#om�6�aw��������f!~�/������r'���-�����i0uu	F�^DHa�rw�	��6,^tj��r�q�����J���-+?+�t����C�=1����W�����������
��$O��g��:�����O7��\b�������-u\��(�*�
�*(���x��6�,���`.(h��]<������;q����V����)����6���]�)�p�Z�������# �r��K���[e�����M����[�~�`�|:F��-��Y�T1�<��X4wB��\z�2N
��PHZVj���<@df�5�._!���P\@EIIh���{$�
�����k&�����B�[�B��J��;��1�,�����05*R`XdF���2Ng��~���_
z��H��m�|�0���������D0
�����`>D��#WO�����@x�M���u��=�v��p�k*��HcUAa�+AB-������;j���LH'Ds�_��$$0��>�A}S����D���)#�zIk|��t��7[�
����'��MFxJ�H"�y�p$�Pa��	�o�?r>�1���lHt�|aL
��f@�*r4��id�'����dS�I,�*�=�o[�1��~���k�~:�������:�\����2��:g�}�h��,�<�!����eV���z����?�����=n�F����o�L6�SVXg��+�L���z��p!�&�C���-��SF�:�y�%��E��i�'K3���`�����P��(�������
���a&]�",n)�g��02�]|,R9������D3���a����T�H��
 P_I�9�@���"��L��%��\����/��"���C��X[��J!o;|�6~�E>���P�i�\���!W�EF��b�W!��/�l��lJ(|P�[� �v���7V�n���)Y����^?�������Z��3ha��ia��zZ�����������-�dm-l��ZX���Z�����.����n�����g��Z0Xk��*�����������?Z�T�������]]�a��Z�ap�J-l}M-l}--le�%Z��Z���V���U��O��V@XO�Z&}k�i����k�[�y<F�Z/�[����r}k�Z�Zw���EA���W�� BhU��������h��(w9�h&S����y�F4[��n;�ou�g�����F#��|��=��J��x�qw��(e�{���P���	�#��nf��/�G�����tK���p�������d��������
����?);����\+�Zt�q�	����a<zp�GA�@_d��)��%�e.
�#W
5'����S}�#V�[-+��\*�%�~4�h����k�_�jv��V�q��%
��A+����������
E�v��/=��`�J������lo���vcg��t����f�BY�m�~��3)94NGx!����U�#��F�O��&��a�:��~������0��O�6���7(-�G�ST�b��5-	�t�0<�t��tl���U����S����N��*�	q�p���Q�\�� ^��`4h�����(�������� �>t��2���E��+fF8M�c*������M1U`��&��kbRM'&Q8&P����@{l9�-G��.wq�3��
����y�1����-O�X/cz���1u�$K�t�[kMH��6f��8 .�wE�|lB$V/�l���Q82$jlZY�q��uw)�ZF�q2�5b�t�!��M3��$�p�a
�8���=H����s��J1o�����$�7����� ����
��o��B�^��zQ�������W�zgw�r;%U���S�w{�,�D�S���,3��ne�)L!P��$�Y���S�5��*�����s��6W��E�dXJ�
����;&0��\CZ�R�-��(��H"�C��SU
�y_��c� 1�*�_�:�i���'Tc���F���$����Ow�4e"����{k�An:�xL d�u������;����W�����^sT#�c����m9��D�
�BO!����0����;T��/��PT�x*U��M� �{5�7k��)%=ef�'��_�1#��Oy��/v��-�#~b�.���~2%U,i:�vM�I��|��<:�*P2�����2�������X���y"+���
����is-^S�������?j]Z���p���K��	W*�B���lkn�km���Q87wpfsd~UW����N��
8bv���4�����,��j7�Hi?�3}Z��K0F�:+7�
#S�������T��Q�%69�XP�0c?�MV���x������E��,�8p�TZK��qo}1Z�lV��q������>hvZ��CjX�BaGJ��_�f�l��mEs#�F&r�[��(���2���s��(�����n�������O'�|Z8��c��5+�����7���qFW,���h��n���I���4L&�Hii�C�6�R�
rCS0��g#^�C��Dr�������b'M�>�z��w��"�������K���ic���e��
R�L�Q���jVN�b�7%J��0���)�oS�M`RW����W�ok�VkS�^�N(/�����5�<�8�&I��hK�&Up��:l�Y�[Q�O`2m�x�5=�6$M�7[ ��G��Zb��Z��g���3I��K�j�%s�@���A�m�-�L�"������4��H$&�����3�fX�p<�"+"�m���6��Y��M���z�[)\Z_Fc���#6V�������p�L�������$���F��_�����C3>���%:��R
��@�����jS����v�(���1,6����L��}�&7�d�������FV:`g���H`5apn�x#K1��C�(?����BqS<��
�8����'��lu5��Xj!��}���B�+�b�#�||N�����&DYt��&���3�[YD����Y�f�,�����w1�����;�W�_V�I�lS�a�zF�Ye��L��E�������6l���)���Jo6����rFd��A�D�r��7�s��1��V���[E���B{W��8���5L�d�/�k�r�f�GWB���w�1���w:Hf�;�Tl��6|�u�!���
������w��i���m ��=����F����%�R]N���z:�os��4��,;�Qm��)f�U�D�$+D|�%�S
�q�%��bQ
�Ol�=��G�(���__��*%�T�%)0VK����7��L�D�}��u��N����er�C�P�%R%���P:��<���0��g��B�����F��)�:�QuR0�fk��j>-O��������z�b��x��0��������xB�*�	Y�j�0����A�]{n�������Y?��Rkx�]����1��_�`��9>#	l6
|F6�d����7�fBN3Y�oyr��y\�p#���S�q+[��,:�k�N�C���K�/���hv�G,�d6�p��0�:�pz�8K����g�%���j�Q�w���Y�^a��b����������S���h)�7�s��I���J�� M��.�;�8Z�h������W]�Q(�o��#���
���#����e&���y��I��_������K����}��|���5.v��4��a���t���	�!xTn���2C��aEj����"f�����BV��c�(����,��$.|��}+M~��~���Y����`�<�x�RT�/hh�����2��5^�o-������$c6�tl��=X>���'�^1��N��q��t*��t�$8q����5�m�������G2uo0�'.I&5k��JS����uuf���V��,|y��'�@���_�n��q��s�TY�w�x$��Lm�$�s��������V���$'|�	w{�3"M���
J�R���5���WX�k�[ L�����!xF�'�
&m%���i���;�4z���������E��'dq#s���6�B;��nc/��u���Mhi?E��`��H�������j�L�1K?j�������5���)�iv[v��9��>�tz��FR��(��i�E�����3m�	~+G4=��:�l���v�������cAf�����S��"[�&�HM=Q�[���4���^��B������H+@����b��K<3+��l6`Y���P����B�P�Z��U����h����)+������hP����2������f$�Q;[����$�^�WPx�k�|�$�1�R�Dg��+�w�JIB�30Q�4���w�h�;���Jx�8�S�
��]<\d�3z)��Bqf�
O���\�]� \���R�d��,J�h�gM��p������h������*��&g����t��k��bD"�`*�^A�O�t����Fv�i�v��^@4��QD]�g��F�"��(������U���k���(�?��8�?G�/������c��|b>����`H�T�O���%�+_'��p����7�'���D���`cco���W����I8[��������>A���k�qsv�A^��P��o�����k�|��D��H#�`�s���V��W�&>'BrX�<���
�q�	R�����_j�%gZ�!fn!���S��n�CX�����I��C�T"�u�F���91������`�	 Het�����,k���[<� �?�c�����[�H�_�,�xV<
��B�[!+�0�p[�x��1�J��1/�.�:f�%�f�xZu~��/|F1x������[.�@�N+�g�0N�l�(��m�(�<
��I��bA����.]
:�������,�)�l�^:E�bV�-��?7~]����V�3,��.����1y�k�"�@��W������� =�-6�TA_��*ty� �$G�_����3��C��Vk�3��F����eQ��E����w��[ �����ME��ue[V�n���^_^�8������"��9�9
..ON������oR��>�lgd��TAme
�:Kc8���j5�\�?��l�p���Z�4�!Q����|���N��X�{`���^A���G�O�MJJ3���:Qv�l���`2�p���Cb���Jg��l����"���EC
8������E��pN�ZbG������b��+SwF2|j
�z�PPc�z�3mPY���!���o�A������y�?���pW..�Yn����DL+�-KH�}��Y�[`�<�$�T��p�obM�3'P�K����fj������f�����@����s����B[o�%���F��Dc�j[������p0�^}�����"�u0��[�h��
��������j�jC(2V�+%���F}�]��uz ���4���E4���x������Ns'��f8�����	H'�������~�hY�J���w+��]O�-k�+~���'j^
�"������#>7�
���{����R��4Nv�@�YT�1�Yz_8.�:�J2�>���s���Gd�S9�1�Y#\�:�?���������p�$yM�������pl�=���H6��g�@�g�`����e����{#��3l�v��N�D��cvBe�o�=�~$�
�+���{(�IR;+�gJ��Z.2�5$���()o�_�?�������b
2va���u�{���d�5�������T� �r ;�TI}���C�Z��;�xv���+/D��0W���e��fZ�H�����L��)�bb'>���1���X"I����?�@7i8�Bq44#�^t[�N���v[����p{g�5�������&���m
0005-WIP-Add-configure-infrastructure-to-enable-LLVM.patch.gzapplication/x-patch-gzipDownload
0006-WIP-Beginning-of-a-LLVM-JIT-infrastructure.patch.gzapplication/x-patch-gzipDownload
0007-WIP-JITed-expression-evaluation.patch.gzapplication/x-patch-gzipDownload
#27Markus Nullmeier
dq124@uni-heidelberg.de
In reply to: Andres Freund (#15)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

On 12/06/16 21:40, Andres Freund wrote:

On 2016-12-06 14:35:43 -0600, Nico Williams wrote:

On Tue, Dec 06, 2016 at 12:27:51PM -0800, Andres Freund wrote:

On 2016-12-06 14:19:21 -0600, Nico Williams wrote:

I concur with your feeling that hand-rolled JIT is right out. But

Yeah, that way lies maintenance madness.

I'm not quite that sure about that. I had a lot of fun doing some
hand-rolled x86 JITing. Not that is a ward against me being mad. But
more seriously: Manually doing a JIT gives you a lot faster compilation
times, which makes JIT applicable in a lot more situations.

What I meant is that each time there are new ISA extensions, or
differences in how relevant/significant different implementations of the
same ISA implement certain instructions, and/or every time you want to
add a new architecture... someone has to do a lot of very low-level
work.

Yea, that's why I didn't pursue this path further. I *personally* think
it'd be perfectly fine to only support JITing on linux x86_64 and
aarch64 for now. And those I'd be willing to work on. But since I know
that's not project policy...

Well, if this thread of thought about hand-crafted JIT should be taken
up again by someone at some point in time, I guess it could be possible
to reuse tools that are already out there, such as "DynASM"
( http://luajit.org/dynasm_features.html ) from the LuaJIT project
(currently offers x86-64, x86-32, ARM-32, PPC-32, and MIPS-32).
Maybe one could even recycle parts of the LuaJIT project itself
( http://luajit.org/luajit.html ,
http://article.gmane.org/gmane.comp.lang.lua.general/58908 ).

--
Markus Nullmeier http://www.g-vo.org
German Astrophysical Virtual Observatory (GAVO)

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

#28Andres Freund
andres@anarazel.de
In reply to: Markus Nullmeier (#27)
Re: WIP: Faster Expression Processing and Tuple Deforming (including JIT)

Hi,

On 2017-02-10 18:18:13 +0100, Markus Nullmeier wrote:

Well, if this thread of thought about hand-crafted JIT should be taken
up again by someone at some point in time, I guess it could be possible
to reuse tools that are already out there, such as "DynASM"
( http://luajit.org/dynasm_features.html ) from the LuaJIT project
(currently offers x86-64, x86-32, ARM-32, PPC-32, and MIPS-32).
Maybe one could even recycle parts of the LuaJIT project itself
( http://luajit.org/luajit.html ,
http://article.gmane.org/gmane.comp.lang.lua.general/58908 ).

FWIW, I'd looked at dynasm/luajit.

One big reason to go for LLVM is that it has nearly all the
infrastructure to make backend-functions/operators inlineable.
Especially for some of the arithmetic operations and such, that'd be
quite useful performance-wise. With LLVM you can just use clang on C to
generate the IR, do some work to boil down the IR modules to the
relevant functions (i.e. remove non sql-callable functions), for which
LLVM has infrastructure, and then inline the functions that way. That's
a lot harder to do with nearly everything else (save gcc's jit library,
but the licensing and stability situation makes that unattractive.

Greetings,

Andres Freund

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

#29Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#26)
4 attachment(s)
Re: WIP: Faster Expression Processing v4

Hi,

Attached is an updated version of the patchset, but more importantly
some benchmark numbers.

I ran tpch scale 5, on my laptop with
-c shared_buffers=20GB # all data fits into memory
-c max_parallel_workers_per_gather=0 # reduce variability
-c work_mem=4GB # other suggestions welcome
-c huge_pages=on #reduces variability

My benchmarking script first prewarms the whole database, then runs the
tpch queries in sequence, repeated three times, and compares the shortes
execution time:

master q01 min: 16176.843 dev-llvm-off min: 13153.179 [diff -22.99]
master q02 min: 1280.691 dev-llvm-off min: 1263.032 [diff -1.40]
master q03 min: 7330.737 dev-llvm-off min: 7144.982 [diff -2.60]
master q04 min: 1014.347 dev-llvm-off min: 991.008 [diff -2.36]
master q05 min: 5490.103 dev-llvm-off min: 5439.878 [diff -0.92]
master q06 min: 1916.45 dev-llvm-off min: 1818.839 [diff -5.37]
master q07 min: 5282.129 dev-llvm-off min: 5222.879 [diff -1.13]
master q08 min: 1655.824 dev-llvm-off min: 1532.1 [diff -8.08]
master q09 min: 7009.372 dev-llvm-off min: 6724.515 [diff -4.24]
master q10 min: 6017.01 dev-llvm-off min: 5848.337 [diff -2.88]
master q11 min: 316.724 dev-llvm-off min: 292.51 [diff -8.28]
master q12 min: 4829.502 dev-llvm-off min: 4698.14 [diff -2.80]
master q13 min: 8679.991 dev-llvm-off min: 8427.614 [diff -2.99]
master q14 min: 814.109 dev-llvm-off min: 774.805 [diff -5.07]
master q15 min: 1957.248 dev-llvm-off min: 1841.377 [diff -6.29]
master q16 min: 1976.544 dev-llvm-off min: 1936.932 [diff -2.05]
master q17 min: 559.664 dev-llvm-off min: 446.199 [diff -25.43]
master q18 min: 16565.722 dev-llvm-off min: 15475.372 [diff -7.05]
master q19 min: 385.222 dev-llvm-off min: 310.398 [diff -24.11]
master q20 min: 1717.015 dev-llvm-off min: 1389.064 [diff -23.61]
master q22 min: 753.017 dev-llvm-off min: 637.152 [diff -18.18]

(note that there's a fair amount of per-run variance, I've seen one or
two of the small differences go the other way in previous runs)

It's clearly visible that the performance gain heavily depends on the
type of query. Which makes sense - expression evaluation isn't a
bottleneck everywhere. The actual gains in expression evalution are
larger than the maximum here, but bottlenecks shift.

Besides cleanups the important changes in this version of the patches
are:
- I've added a fastpath for the interpretation of very simple
expressions (non-sysvar, single column Vars and Consts), before that
performance regressed slightly for cases that evaluated a *lot* of
Vars/Consts. The only case where I could actually reproduce that is in
large hash-joins where the to-be-hashed value is extracted on its own.[1;5A
- I moved the invocation of expression evaluation back to a
callback. That's better for predicting branches both with JITing and
when using fastpath functions.
- removed used EXEC_EVALDEBUG code

- Andres

Attachments:

0001-Add-expression-dependencies-on-composite-type-whole-.patchtext/x-patch; charset=us-asciiDownload
From e1cee7476b3ef0a767fea298c64122808d9c7a27 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Fri, 3 Feb 2017 11:33:16 -0800
Subject: [PATCH 1/4] Add expression dependencies on composite type / whole row
 components.

This allows, in a later commit, to have fewer checks during expression
evaluation.
---
 src/backend/catalog/dependency.c          | 145 ++++++++++++++++++++++--------
 src/test/regress/expected/create_view.out |  21 +----
 src/test/regress/expected/rangefuncs.out  |  76 +++++++++++++---
 src/test/regress/sql/create_view.sql      |   6 +-
 src/test/regress/sql/rangefuncs.sql       |  24 ++++-
 5 files changed, 198 insertions(+), 74 deletions(-)

diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fc088b2165..3d6e86bafa 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -206,6 +206,9 @@ static bool object_address_present_add_flags(const ObjectAddress *object,
 static bool stack_address_present_add_flags(const ObjectAddress *object,
 								int flags,
 								ObjectAddressStack *stack);
+static void add_type_addresses(Oid typeid, bool include_components, ObjectAddresses *addrs);
+static void add_type_component_addresses(Oid typeid, ObjectAddresses *addrs);
+static void add_class_component_addresses(Oid relid, ObjectAddresses *addrs);
 static void DeleteInitPrivs(const ObjectAddress *object);
 
 
@@ -1364,10 +1367,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,
  * whereas 'behavior' is used for everything else.
  *
  * NOTE: the caller should ensure that a whole-table dependency on the
- * specified relation is created separately, if one is needed.  In particular,
- * a whole-row Var "relation.*" will not cause this routine to emit any
- * dependency item.  This is appropriate behavior for subexpressions of an
- * ordinary query, so other cases need to cope as necessary.
+ * specified relation is created separately, if one is needed.  E.g. SELECT
+ * FROM tbl will not cause this routine to emit any dependency items.
  */
 void
 recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
@@ -1484,19 +1485,23 @@ find_expr_references_walker(Node *node,
 			elog(ERROR, "invalid varno %d", var->varno);
 		rte = rt_fetch(var->varno, rtable);
 
-		/*
-		 * A whole-row Var references no specific columns, so adds no new
-		 * dependency.  (We assume that there is a whole-table dependency
-		 * arising from each underlying rangetable entry.  While we could
-		 * record such a dependency when finding a whole-row Var that
-		 * references a relation directly, it's quite unclear how to extend
-		 * that to whole-row Vars for JOINs, so it seems better to leave the
-		 * responsibility with the range table.  Note that this poses some
-		 * risks for identifying dependencies of stand-alone expressions:
-		 * whole-table references may need to be created separately.)
-		 */
 		if (var->varattno == InvalidAttrNumber)
-			return false;
+		{
+			/*
+			 * A whole-row Var essentially references all current columns, so
+			 * add dependencies for them - that allow adding new columns to
+			 * the type, but not removing or altering the type of existing
+			 * columns.
+			 *
+			 * That does not obviate the need for a whole-table dependency
+			 * arising from each underlying rangetable entry - this would
+			 * e.g. not to the right thing for column-less tables.  Note that
+			 * this requires some care for identifying dependencies of
+			 * stand-alone expressions: whole-table references may need to be
+			 * created separately.
+			 */
+			add_class_component_addresses(rte->relid, context->addrs);
+		}
 		if (rte->rtekind == RTE_RELATION)
 		{
 			/* If it's a plain relation, reference this column */
@@ -1529,8 +1534,7 @@ find_expr_references_walker(Node *node,
 		Oid			objoid;
 
 		/* A constant must depend on the constant's datatype */
-		add_object_address(OCLASS_TYPE, con->consttype, 0,
-						   context->addrs);
+		add_type_addresses(con->consttype, true, context->addrs);
 
 		/*
 		 * We must also depend on the constant's collation: it could be
@@ -1580,8 +1584,7 @@ find_expr_references_walker(Node *node,
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TYPEOID,
 											  ObjectIdGetDatum(objoid)))
-						add_object_address(OCLASS_TYPE, objoid, 0,
-										   context->addrs);
+						add_type_addresses(objoid, false, context->addrs);
 					break;
 				case REGCONFIGOID:
 					objoid = DatumGetObjectId(con->constvalue);
@@ -1625,8 +1628,7 @@ find_expr_references_walker(Node *node,
 		Param	   *param = (Param *) node;
 
 		/* A parameter must depend on the parameter's datatype */
-		add_object_address(OCLASS_TYPE, param->paramtype, 0,
-						   context->addrs);
+		add_type_addresses(param->paramtype, true, context->addrs);
 		/* and its collation, just as for Consts */
 		if (OidIsValid(param->paramcollid) &&
 			param->paramcollid != DEFAULT_COLLATION_OID)
@@ -1639,6 +1641,9 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_PROC, funcexpr->funcid, 0,
 						   context->addrs);
+		/* dependency on type itself already exists via function */
+		add_type_component_addresses(funcexpr->funcresulttype, context->addrs);
+
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, OpExpr))
@@ -1647,6 +1652,9 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
 						   context->addrs);
+		/* dependency on type itself already exists via function */
+		add_type_component_addresses(opexpr->opresulttype, context->addrs);
+
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, DistinctExpr))
@@ -1655,6 +1663,13 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_OPERATOR, distinctexpr->opno, 0,
 						   context->addrs);
+		/*
+		 * Dependency on type itself already exists via function. Be paranoid
+		 * and add deps to return type components (unlikely to matter due to
+		 * return type, but ...)
+		 */
+		add_type_component_addresses(distinctexpr->opresulttype,
+									 context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, NullIfExpr))
@@ -1663,6 +1678,7 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
 						   context->addrs);
+		/* can't add new type dependencies */
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, ScalarArrayOpExpr))
@@ -1679,6 +1695,7 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_PROC, aggref->aggfnoid, 0,
 						   context->addrs);
+		add_type_component_addresses(aggref->aggtype, context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, WindowFunc))
@@ -1687,6 +1704,7 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_PROC, wfunc->winfnoid, 0,
 						   context->addrs);
+		add_type_component_addresses(wfunc->wintype, context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, SubPlan))
@@ -1699,8 +1717,8 @@ find_expr_references_walker(Node *node,
 		RelabelType *relab = (RelabelType *) node;
 
 		/* since there is no function dependency, need to depend on type */
-		add_object_address(OCLASS_TYPE, relab->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(relab->resulttype, false, context->addrs);
+
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(relab->resultcollid) &&
 			relab->resultcollid != DEFAULT_COLLATION_OID)
@@ -1712,8 +1730,7 @@ find_expr_references_walker(Node *node,
 		CoerceViaIO *iocoerce = (CoerceViaIO *) node;
 
 		/* since there is no exposed function, need to depend on type */
-		add_object_address(OCLASS_TYPE, iocoerce->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(iocoerce->resulttype, true, context->addrs);
 	}
 	else if (IsA(node, ArrayCoerceExpr))
 	{
@@ -1722,8 +1739,7 @@ find_expr_references_walker(Node *node,
 		if (OidIsValid(acoerce->elemfuncid))
 			add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0,
 							   context->addrs);
-		add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(acoerce->resulttype, true, context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, ConvertRowtypeExpr))
@@ -1731,8 +1747,7 @@ find_expr_references_walker(Node *node,
 		ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
 
 		/* since there is no function dependency, need to depend on type */
-		add_object_address(OCLASS_TYPE, cvt->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(cvt->resulttype, true, context->addrs);
 	}
 	else if (IsA(node, CollateExpr))
 	{
@@ -1745,8 +1760,7 @@ find_expr_references_walker(Node *node,
 	{
 		RowExpr    *rowexpr = (RowExpr *) node;
 
-		add_object_address(OCLASS_TYPE, rowexpr->row_typeid, 0,
-						   context->addrs);
+		add_type_addresses(rowexpr->row_typeid, true, context->addrs);
 	}
 	else if (IsA(node, RowCompareExpr))
 	{
@@ -1769,8 +1783,7 @@ find_expr_references_walker(Node *node,
 	{
 		CoerceToDomain *cd = (CoerceToDomain *) node;
 
-		add_object_address(OCLASS_TYPE, cd->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(cd->resulttype, true, context->addrs);
 	}
 	else if (IsA(node, OnConflictExpr))
 	{
@@ -1898,13 +1911,13 @@ find_expr_references_walker(Node *node,
 
 		/*
 		 * Add refs for any datatypes and collations used in a column
-		 * definition list for a RECORD function.  (For other cases, it should
-		 * be enough to depend on the function itself.)
+		 * definition list for a RECORD function.  Additional dependencies
+		 * will possibly be added when recursing to the contained function
+		 * expression.
 		 */
 		foreach(ct, rtfunc->funccoltypes)
 		{
-			add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0,
-							   context->addrs);
+			add_type_addresses(lfirst_oid(ct), true, context->addrs);
 		}
 		foreach(ct, rtfunc->funccolcollations)
 		{
@@ -2228,6 +2241,62 @@ object_address_present_add_flags(const ObjectAddress *object,
 }
 
 /*
+ * Add ObjectAddresses entry for typeid and, if include_components = true and
+ * and the type is a composite type, for it's columns.
+ */
+static void
+add_type_addresses(Oid typeid, bool include_components, ObjectAddresses *addrs)
+{
+	add_object_address(OCLASS_TYPE, typeid, 0, addrs);
+
+	if (include_components)
+	{
+		add_type_component_addresses(typeid, addrs);
+	}
+}
+
+/*
+ * Add ObjectAddresses entry for the type's columns if it's a composite type.
+ *
+ * Besides being a helper for add_type_addresses it can make sense to add
+ * dependencies on the components if, as e.g. the case for a function return
+ * type, there already exists a dependency on the type, but not the typ's
+ * components.
+ */
+static void
+add_type_component_addresses(Oid typeid, ObjectAddresses *addrs)
+{
+	Oid typerelid = get_typ_typrelid(typeid);
+
+	if (typerelid != InvalidOid)
+	{
+		add_class_component_addresses(typerelid, addrs);
+	}
+}
+
+/*
+ * Add ObjectAddresses entries for the relation's columns.
+ */
+static void
+add_class_component_addresses(Oid relid, ObjectAddresses *addrs)
+{
+	Relation rel = relation_open(relid, NoLock);
+	TupleDesc tupDesc = RelationGetDescr(rel);
+	int i;
+
+	for (i = 0; i < tupDesc->natts; i++)
+	{
+		Form_pg_attribute att = tupDesc->attrs[i];
+
+		if (att->attisdropped)
+			continue;
+
+		add_object_address(OCLASS_CLASS, relid, att->attnum, addrs);
+	}
+	relation_close(rel, NoLock);
+}
+
+/*
  * Similar to above, except we search an ObjectAddressStack.
  */
 static bool
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ce0c8cedf8..379c070ca6 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -1429,24 +1429,11 @@ select * from tt14v;
  foo | baz | quux
 (1 row)
 
--- this perhaps should be rejected, but it isn't:
+-- this will be rejected as the column is referenced in the view:
 alter table tt14t drop column f3;
--- f3 is still in the view but will read as nulls
-select pg_get_viewdef('tt14v', true);
-         pg_get_viewdef         
---------------------------------
-  SELECT t.f1,                 +
-     t.f3,                     +
-     t.f4                      +
-    FROM tt14f() t(f1, f3, f4);
-(1 row)
-
-select * from tt14v;
- f1  | f3 |  f4  
------+----+------
- foo |    | quux
-(1 row)
-
+ERROR:  cannot drop table tt14t column f3 because other objects depend on it
+DETAIL:  view tt14v depends on table tt14t column f3
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
 -- check display of whole-row variables in some corner cases
 create type nestedcomposite as (x int8_tbl);
 create view tt15v as select row(i)::nestedcomposite from int8_tbl i;
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 526a4aed0a..33b462126a 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1899,9 +1899,13 @@ SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
  id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
--- check that we can cope with post-parsing changes in rowtypes
 create temp view usersview as
 SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
+create temp view usersview2 as
+SELECT * FROM (SELECT get_users()) f;
+create temp view usersview3 as
+SELECT * FROM (SELECT users FROM users) f;
+-- check that rowtypes referenced in views can't be dropped / changed
 select * from usersview;
  userid | seq | email  | moredrop | enabled | generate_series | ordinality 
 --------+-----+--------+----------+---------+-----------------+------------
@@ -1909,27 +1913,79 @@ select * from usersview;
  id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
+select * from usersview2;
+      get_users      
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
 alter table users drop column moredrop;
+ERROR:  cannot drop table users column moredrop because other objects depend on it
+DETAIL:  view usersview depends on table users column moredrop
+view usersview2 depends on table users column moredrop
+view usersview3 depends on table users column moredrop
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+alter table users alter column seq type numeric;
+ERROR:  cannot alter type of a column used by a view or rule
+DETAIL:  rule _RETURN on view usersview3 depends on column "seq"
 select * from usersview;
  userid | seq | email  | moredrop | enabled | generate_series | ordinality 
 --------+-----+--------+----------+---------+-----------------+------------
- id     |   1 | email  |          | t       |              10 |          1
- id2    |   2 | email2 |          | t       |              11 |          2
+ id     |   1 | email  |       11 | t       |              10 |          1
+ id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
+select * from usersview2;
+      get_users      
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
+select * from usersview3;
+        users        
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
+-- check that column additions are handled properly
 alter table users add column junk text;
 select * from usersview;
  userid | seq | email  | moredrop | enabled | generate_series | ordinality 
 --------+-----+--------+----------+---------+-----------------+------------
- id     |   1 | email  |          | t       |              10 |          1
- id2    |   2 | email2 |          | t       |              11 |          2
+ id     |   1 | email  |       11 | t       |              10 |          1
+ id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
-alter table users alter column seq type numeric;
-select * from usersview;  -- expect clean failure
-ERROR:  attribute 2 has wrong type
-DETAIL:  Table has type numeric, but query expects integer.
-drop view usersview;
+select * from usersview2;
+      get_users       
+----------------------
+ (id,1,email,11,t,)
+ (id2,2,email2,12,t,)
+(2 rows)
+
+select * from usersview3;
+        users         
+----------------------
+ (id,1,email,11,t,)
+ (id2,2,email2,12,t,)
+(2 rows)
+
+-- check that cascade is handled properly
+alter table users drop column moredrop CASCADE;
+NOTICE:  drop cascades to 3 other objects
+DETAIL:  drop cascades to view usersview
+drop cascades to view usersview2
+drop cascades to view usersview3
+-- should all be gone now
+\dv usersview*
+      List of relations
+ Schema | Name | Type | Owner 
+--------+------+------+-------
+(0 rows)
+
 drop function get_first_user();
 drop function get_users();
 drop table users;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index c27f1034e1..da4236766c 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -483,13 +483,9 @@ create view tt14v as select t.* from tt14f() t;
 select pg_get_viewdef('tt14v', true);
 select * from tt14v;
 
--- this perhaps should be rejected, but it isn't:
+-- this will be rejected as the column is referenced in the view:
 alter table tt14t drop column f3;
 
--- f3 is still in the view but will read as nulls
-select pg_get_viewdef('tt14v', true);
-select * from tt14v;
-
 -- check display of whole-row variables in some corner cases
 
 create type nestedcomposite as (x int8_tbl);
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 09ac8fbdb4..902457f5f1 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -551,19 +551,35 @@ SELECT * FROM get_users() WITH ORDINALITY;   -- make sure ordinality copes
 SELECT * FROM ROWS FROM(generate_series(10,11), get_users()) WITH ORDINALITY;
 SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
 
--- check that we can cope with post-parsing changes in rowtypes
 create temp view usersview as
 SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
 
+create temp view usersview2 as
+SELECT * FROM (SELECT get_users()) f;
+
+create temp view usersview3 as
+SELECT * FROM (SELECT users FROM users) f;
+
+-- check that rowtypes referenced in views can't be dropped / changed
 select * from usersview;
+select * from usersview2;
 alter table users drop column moredrop;
+alter table users alter column seq type numeric;
 select * from usersview;
+select * from usersview2;
+select * from usersview3;
+
+-- check that column additions are handled properly
 alter table users add column junk text;
 select * from usersview;
-alter table users alter column seq type numeric;
-select * from usersview;  -- expect clean failure
+select * from usersview2;
+select * from usersview3;
+
+-- check that cascade is handled properly
+alter table users drop column moredrop CASCADE;
+-- should all be gone now
+\dv usersview*
 
-drop view usersview;
 drop function get_first_user();
 drop function get_users();
 drop table users;
-- 
2.11.0.22.g8d7a455.dirty

0002-Make-get_last_attnums-more-generic.patchtext/x-patch; charset=us-asciiDownload
From b924ca4d4a4cb22bccad057c008d5593c42a50a1 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Mon, 5 Dec 2016 18:10:04 -0800
Subject: [PATCH 2/4] Make get_last_attnums more generic.

---
 src/backend/executor/execUtils.c | 48 +++++++++++++++++++++++++++++-----------
 src/include/executor/executor.h  |  4 ++++
 2 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 3d6a3801c0..d205101b89 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -47,7 +47,14 @@
 #include "utils/rel.h"
 
 
-static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
+typedef struct LastLastAttnumInfo
+{
+	AttrNumber last_outer;
+	AttrNumber last_inner;
+	AttrNumber last_scan;
+} LastAttnumInfo;
+
+
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -580,7 +587,10 @@ ExecBuildProjectionInfo(List *targetList,
 			/* Not a simple variable, add it to generic targetlist */
 			exprlist = lappend(exprlist, gstate);
 			/* Examine expr to include contained Vars in lastXXXVar counts */
-			get_last_attnums((Node *) variable, projInfo);
+			ExecGetLastAttnums((Node *) variable,
+							   &projInfo->pi_lastOuterVar,
+							   &projInfo->pi_lastInnerVar,
+							   &projInfo->pi_lastScanVar);
 		}
 	}
 	projInfo->pi_targetlist = exprlist;
@@ -591,13 +601,13 @@ ExecBuildProjectionInfo(List *targetList,
 }
 
 /*
- * get_last_attnums: expression walker for ExecBuildProjectionInfo
+ * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
  *	Update the lastXXXVar counts to be at least as large as the largest
  *	attribute numbers found in the expression
  */
 static bool
-get_last_attnums(Node *node, ProjectionInfo *projInfo)
+get_last_attnums_walker(Node *node, LastAttnumInfo *info)
 {
 	if (node == NULL)
 		return false;
@@ -609,21 +619,17 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo)
 		switch (variable->varno)
 		{
 			case INNER_VAR:
-				if (projInfo->pi_lastInnerVar < attnum)
-					projInfo->pi_lastInnerVar = attnum;
+				info->last_inner = Max(info->last_inner, attnum);
 				break;
 
 			case OUTER_VAR:
-				if (projInfo->pi_lastOuterVar < attnum)
-					projInfo->pi_lastOuterVar = attnum;
+				info->last_outer = Max(info->last_outer, attnum);
 				break;
 
 				/* INDEX_VAR is handled by default case */
 
 			default:
-				if (projInfo->pi_lastScanVar < attnum)
-					projInfo->pi_lastScanVar = attnum;
-				break;
+				info->last_scan = Max(info->last_scan, attnum);
 		}
 		return false;
 	}
@@ -640,10 +646,26 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo)
 		return false;
 	if (IsA(node, GroupingFunc))
 		return false;
-	return expression_tree_walker(node, get_last_attnums,
-								  (void *) projInfo);
+	return expression_tree_walker(node, get_last_attnums_walker,
+								  (void *) info);
 }
 
+void
+ExecGetLastAttnums(Node *node, int *last_outer, int *last_inner,
+				   int *last_scan)
+{
+	LastAttnumInfo info = {0,0,0};
+
+	get_last_attnums_walker(node, &info);
+	if (last_outer && *last_outer < info.last_outer)
+		*last_outer = info.last_outer;
+	if (last_inner && *last_inner < info.last_inner)
+		*last_inner = info.last_inner;
+	if (last_scan && *last_scan < info.last_scan)
+		*last_scan = info.last_scan;
+}
+
+
 /* ----------------
  *		ExecAssignProjectionInfo
  *
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 02dbe7b228..32e1838e15 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -354,6 +354,10 @@ extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
 extern void ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc);
 extern void ExecAssignResultTypeFromTL(PlanState *planstate);
 extern TupleDesc ExecGetResultType(PlanState *planstate);
+extern void ExecGetLastAttnums(Node *node,
+							   int *last_outer,
+							   int *last_inner,
+							   int *last_scan);
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
-- 
2.11.0.22.g8d7a455.dirty

0003-Add-autoconf-test-for-computed-goto-support.patchtext/x-patch; charset=us-asciiDownload
From e724bfbb375fcb8cb176780948b744d82aa38361 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 7 Feb 2017 14:16:15 -0800
Subject: [PATCH 3/4] Add autoconf test for computed goto support.

Author: Andres Freund
---
 config/c-compiler.m4          | 24 ++++++++++++++++++++++++
 configure                     | 34 ++++++++++++++++++++++++++++++++++
 configure.in                  |  1 +
 src/include/pg_config.h.in    |  3 +++
 src/include/pg_config.h.win32 |  3 +++
 5 files changed, 65 insertions(+)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 7d901e1f1a..f56d8852f0 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -491,3 +491,27 @@ if test x"$Ac_cachevar" = x"yes"; then
 fi
 undefine([Ac_cachevar])dnl
 ])# PGAC_SSE42_CRC32_INTRINSICS
+
+
+
+# PGAC_C_COMPUTED_GOTO
+# -----------------------
+# Check if the C compiler knows computed gotos (gcc extension, also
+# available in at least clang).  If so define HAVE_COMPUTED_GOTO
+#
+# Checking whether computed gotos are supported syntax-wise ought to
+# be enough, as the syntax is otherwise illegal.
+AC_DEFUN([PGAC_C_COMPUTED_GOTO],
+[AC_CACHE_CHECK(for computed goto support, pgac_cv__computed_goto,
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[[void *labeladdrs[] = {&&my_label};
+  goto *labeladdrs[0];
+  my_label:
+  return 1;
+]])],
+[pgac_cv__computed_goto=yes],
+[pgac_cv__computed_goto=no])])
+if test x"$pgac_cv__computed_goto" = xyes ; then
+AC_DEFINE(HAVE__COMPUTED_GOTO, 1,
+  [Define to 1 if your compiler handles computed gotos.])
+fi])# PGAC_C_COMPUTED_GOTO
diff --git a/configure b/configure
index b5cdebb510..fafef4638e 100755
--- a/configure
+++ b/configure
@@ -11564,6 +11564,40 @@ if test x"$pgac_cv__va_args" = xyes ; then
 $as_echo "#define HAVE__VA_ARGS 1" >>confdefs.h
 
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for computed goto support" >&5
+$as_echo_n "checking for computed goto support... " >&6; }
+if ${pgac_cv__computed_goto+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+void *labeladdrs[] = {&&my_label};
+  goto *labeladdrs[0];
+  my_label:
+  return 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv__computed_goto=yes
+else
+  pgac_cv__computed_goto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__computed_goto" >&5
+$as_echo "$pgac_cv__computed_goto" >&6; }
+if test x"$pgac_cv__computed_goto" = xyes ; then
+
+$as_echo "#define HAVE__COMPUTED_GOTO 1" >>confdefs.h
+
+fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
 if ${ac_cv_struct_tm+:} false; then :
diff --git a/configure.in b/configure.in
index 1d99cda1d8..9aa9203c40 100644
--- a/configure.in
+++ b/configure.in
@@ -1323,6 +1323,7 @@ PGAC_C_BUILTIN_BSWAP64
 PGAC_C_BUILTIN_CONSTANT_P
 PGAC_C_BUILTIN_UNREACHABLE
 PGAC_C_VA_ARGS
+PGAC_C_COMPUTED_GOTO
 PGAC_STRUCT_TIMEZONE
 PGAC_UNION_SEMUN
 PGAC_STRUCT_SOCKADDR_UN
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5bcd8a1160..94de1a75a2 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -687,6 +687,9 @@
 /* Define to 1 if your compiler understands __builtin_unreachable. */
 #undef HAVE__BUILTIN_UNREACHABLE
 
+/* Define to 1 if your compiler handles computed gotos. */
+#undef HAVE__COMPUTED_GOTO
+
 /* Define to 1 if you have __cpuid. */
 #undef HAVE__CPUID
 
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 3e4132cd82..c5441855bd 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -514,6 +514,9 @@
 /* Define to 1 if your compiler understands __builtin_unreachable. */
 /* #undef HAVE__BUILTIN_UNREACHABLE */
 
+/* Define to 1 if your compiler handles computed gotos. */
+#undef HAVE__COMPUTED_GOTO
+
 /* Define to 1 if you have __cpuid. */
 #define HAVE__CPUID 1
 
-- 
2.11.0.22.g8d7a455.dirty

0004-WIP-Faster-expression-evaluation-and-targetlist-proj.patchtext/x-patch; charset=us-asciiDownload
From 2bbeb5fcaa04a57666830a125e8f08c884949b33 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Mon, 5 Dec 2016 18:10:04 -0800
Subject: [PATCH 4/4] WIP: Faster expression evaluation and targetlist
 projection.

This reimplements expression evaluation as a an opcode based
interpreter, instead of doing tree-traversal. This both saves on
once-per-execution overhead of expression initalization, but more
importantly makes execution faster.  It also has the distinct
advantage of making JITing of expression evaluation easier.
---
 contrib/postgres_fdw/postgres_fdw.c       |    2 +-
 src/backend/bootstrap/bootstrap.c         |    2 +-
 src/backend/catalog/index.c               |   43 +-
 src/backend/catalog/partition.c           |    4 +-
 src/backend/catalog/toasting.c            |    2 +-
 src/backend/commands/analyze.c            |   10 +-
 src/backend/commands/copy.c               |    2 +-
 src/backend/commands/explain.c            |    2 +-
 src/backend/commands/indexcmds.c          |    4 +-
 src/backend/commands/prepare.c            |    4 +-
 src/backend/commands/tablecmds.c          |   22 +-
 src/backend/commands/trigger.c            |    8 +-
 src/backend/executor/Makefile             |    6 +-
 src/backend/executor/execExpr.c           | 2231 ++++++++++++++
 src/backend/executor/execIndexing.c       |   20 +-
 src/backend/executor/execInterpExpr.c     | 2840 ++++++++++++++++++
 src/backend/executor/execMain.c           |   32 +-
 src/backend/executor/execQual.c           | 4539 +----------------------------
 src/backend/executor/execScan.c           |    4 +-
 src/backend/executor/execUtils.c          |  133 +-
 src/backend/executor/nodeAgg.c            |   20 +-
 src/backend/executor/nodeBitmapHeapscan.c |   17 +-
 src/backend/executor/nodeCtescan.c        |    8 +-
 src/backend/executor/nodeCustom.c         |    7 +-
 src/backend/executor/nodeForeignscan.c    |   15 +-
 src/backend/executor/nodeFunctionscan.c   |   18 +-
 src/backend/executor/nodeGather.c         |    8 +-
 src/backend/executor/nodeGroup.c          |   12 +-
 src/backend/executor/nodeHash.c           |   12 +-
 src/backend/executor/nodeHashjoin.c       |   43 +-
 src/backend/executor/nodeIndexonlyscan.c  |   15 +-
 src/backend/executor/nodeIndexscan.c      |   24 +-
 src/backend/executor/nodeMergejoin.c      |   33 +-
 src/backend/executor/nodeModifyTable.c    |   40 +-
 src/backend/executor/nodeNestloop.c       |   23 +-
 src/backend/executor/nodeProjectSet.c     |   52 +-
 src/backend/executor/nodeResult.c         |   16 +-
 src/backend/executor/nodeSamplescan.c     |   15 +-
 src/backend/executor/nodeSeqscan.c        |    8 +-
 src/backend/executor/nodeSubplan.c        |   91 +-
 src/backend/executor/nodeSubqueryscan.c   |    8 +-
 src/backend/executor/nodeTidscan.c        |   37 +-
 src/backend/executor/nodeValuesscan.c     |   10 +-
 src/backend/executor/nodeWindowAgg.c      |    8 +-
 src/backend/executor/nodeWorktablescan.c  |    8 +-
 src/backend/optimizer/util/clauses.c      |   12 +-
 src/backend/utils/adt/domains.c           |   18 +-
 src/backend/utils/adt/ruleutils.c         |    4 +-
 src/backend/utils/adt/xml.c               |   37 +-
 src/backend/utils/cache/typcache.c        |   10 +-
 src/include/executor/execExpr.h           |  454 +++
 src/include/executor/execdebug.h          |   21 -
 src/include/executor/executor.h           |   95 +-
 src/include/executor/nodeSubplan.h        |    3 +
 src/include/nodes/execnodes.h             |  562 +---
 src/include/nodes/nodes.h                 |   29 +-
 src/include/utils/xml.h                   |    2 +-
 src/pl/plpgsql/src/pl_exec.c              |    5 +-
 src/test/regress/expected/case.out        |    2 +-
 src/test/regress/sql/case.sql             |    2 +-
 60 files changed, 6254 insertions(+), 5460 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c
 create mode 100644 src/backend/executor/execInterpExpr.c
 create mode 100644 src/include/executor/execExpr.h

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 5d270b948a..0ba2d09e76 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3413,7 +3413,7 @@ prepare_query_params(PlanState *node,
 	 * benefit, and it'd require postgres_fdw to know more than is desirable
 	 * about Param evaluation.)
 	 */
-	*param_exprs = (List *) ExecInitExpr((Expr *) fdw_exprs, node);
+	*param_exprs = ExecInitExprList(fdw_exprs, node);
 
 	/* Allocate buffer for text form of query parameters. */
 	*param_values = (const char **) palloc0(numParams * sizeof(char *));
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 6511c6064b..6cfce4f8dd 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1084,7 +1084,7 @@ index_register(Oid heap,
 	/* predicate will likely be null, but may as well copy it */
 	newind->il_info->ii_Predicate = (List *)
 		copyObject(indexInfo->ii_Predicate);
-	newind->il_info->ii_PredicateState = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 8d42a347ea..d6be0915a7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1658,7 +1658,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -1774,9 +1774,8 @@ FormIndexDatum(IndexInfo *indexInfo,
 		indexInfo->ii_ExpressionsState == NIL)
 	{
 		/* First time through, set up expression evaluation state */
-		indexInfo->ii_ExpressionsState = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
-							estate);
+		indexInfo->ii_ExpressionsState =
+			ExecPrepareExprList(indexInfo->ii_Expressions, estate);
 		/* Check caller has set up context correctly */
 		Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
 	}
@@ -2208,7 +2207,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2247,9 +2246,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2552,9 +2549,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
@@ -2619,7 +2616,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2646,7 +2643,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2672,9 +2669,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2699,9 +2694,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
@@ -2732,7 +2727,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2962,7 +2957,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2992,9 +2987,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3121,9 +3114,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate, econtext))
 					continue;
 			}
 
@@ -3177,7 +3170,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index e01ef864f0..1e75777acb 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1618,8 +1618,8 @@ FormPartitionKeyDatum(PartitionDispatch pd,
 			   GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
 
 		/* First time through, set up expression evaluation state */
-		pd->keystate = (List *) ExecPrepareExpr((Expr *) pd->key->partexprs,
-												estate);
+		pd->keystate = ExecPrepareExprList(pd->key->partexprs,
+													estate);
 	}
 
 	partexpr_item = list_head(pd->keystate);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 0e4231668d..29756eb14e 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index a70c760341..0f15cd5369 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -703,7 +703,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState  *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -729,9 +729,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = castNode(List,
-							 ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-											 estate));
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -754,9 +752,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate, econtext))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3102ab18c5..7295853e15 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -3406,7 +3406,7 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
 		Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory);
 
 		values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext,
-										 &nulls[defmap[i]]);
+										  &nulls[defmap[i]]);
 	}
 
 	return true;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c9e0a3e42d..655be96eb2 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2831,7 +2831,7 @@ ExplainSubPlans(List *plans, List *ancestors,
 	foreach(lst, plans)
 	{
 		SubPlanState *sps = (SubPlanState *) lfirst(lst);
-		SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
+		SubPlan    *sp = sps->subplan;
 
 		/*
 		 * There can be multiple SubPlan nodes referencing the same physical
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 72bb06c760..fd0aeb06b9 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -179,7 +179,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -556,7 +556,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 116ed67547..fce3657298 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -391,7 +391,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 	}
 
 	/* Prepare the expressions for execution */
-	exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
+	exprstates = ExecPrepareExprList(params, estate);
 
 	paramLI = (ParamListInfo)
 		palloc(offsetof(ParamListInfoData, params) +
@@ -407,7 +407,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 	i = 0;
 	foreach(l, exprstates)
 	{
-		ExprState  *n = lfirst(l);
+		ExprState  *n = (ExprState *) lfirst(l);
 		ParamExternData *prm = &paramLI->params[i];
 
 		prm->ptype = param_types[i];
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1ddb72d164..07d0bb7af0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -185,7 +185,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState  *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4262,7 +4262,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 	CommandId	mycid;
 	BulkInsertState bistate;
 	int			hi_options;
-	List	   *partqualstate = NIL;
+	ExprState  *partqualstate = NULL;
 
 	/*
 	 * Open the relation(s).  We have surely already locked the existing
@@ -4315,8 +4315,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4331,9 +4330,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 	if (tab->partition_constraint)
 	{
 		needscan = true;
-		partqualstate = (List *)
-			ExecPrepareExpr((Expr *) tab->partition_constraint,
-							estate);
+		partqualstate = ExecPrepareCheck(tab->partition_constraint, estate);
 	}
 
 	foreach(l, tab->newvals)
@@ -4508,7 +4505,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate, econtext))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -4524,7 +4521,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				}
 			}
 
-			if (partqualstate && !ExecQual(partqualstate, econtext, true))
+			if (partqualstate && !ExecCheck(partqualstate, econtext))
 				ereport(ERROR,
 						(errcode(ERRCODE_CHECK_VIOLATION),
 					errmsg("partition constraint is violated by some row")));
@@ -7786,7 +7783,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState  *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7817,8 +7814,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7838,7 +7834,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate, econtext))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d80bff671c..a13cce05ca 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3057,7 +3057,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -3078,7 +3078,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -3089,7 +3089,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -3137,7 +3137,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate, econtext))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 2a2b7eb9bd..7b16c4218e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,9 +12,9 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
-       execReplication.o execScan.o execTuples.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execInterpExpr.o execGrouping.o \
+       execIndexing.o execJunk.o execMain.o execParallel.o execProcnode.o \
+       execQual.o execReplication.o execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
        nodeBitmapHeapscan.o nodeBitmapIndexscan.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000000..ae0ad0c006
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,2231 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.c
+ *	  Support for evaluating expressions.
+ *
+ *	Expression evaluation now works by first converting expression trees
+ *	(which went through planning first) into an ExprState using ExecInitExpr()
+ *	et al. This converts the tree into a opcode based program (ExprEvalStep
+ *	representing individual instructions); allocated as an array of stesps.
+ *
+ *	The ExprEvalStep representation is designed to be usable for interpreting
+ *	the expression, as well as compiling into native code. Thus, if possible,
+ *	as much complexity as possible should be handed by ExecInitExpr() (and
+ *	helpers), instead of handled at execution time where both interpreted and
+ *	compiled versions would need to deal with the complexity. Additionally
+ *	checks for initialization at run time have a small but noticeable cost at
+ *	every execution.
+ *
+ *	The next step is preparing the ExprState for execution, using
+ *	ExecInstantiateExpr(). This is internally done by ExecInitExpr() and other
+ *	functions that prepare for expression evaluation.  ExecInstantiateExpr()
+ *	initializes the expression tree for the relevant method chosen to evaluate
+ *	the expression.
+ *
+ *	Note that a lot of the more complex expression evaluation steps, which are
+ *	less performance critical than some of the simpler and more common ones,
+ *	are implemented as separate functions outside the fast-path of interpreted
+ *	expression, like e.g. ExecEvalRow(), so that the implementation can be
+ *	shared between interpreted and compiled expression evaluation.  That means
+ *	that ExecInstantiateExpr() always has to initialize the expression for
+ *	evaluation by execInterpExpr.c.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/execExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/execExpr.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+
+
+/*
+ * Support for building execution state.
+ */
+static void ExecInstantiateExpr(ExprState *state);
+static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state, Datum *resv, bool *resnull);
+static void ExprEvalPushStep(ExprState *es, ExprEvalStep *s);
+static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+						  PlanState *parent, ExprState *state,  Datum *resv, bool *resnull);
+static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent, ExprState *state, Datum *resv, bool *resnull);
+static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent, ExprState *state, Datum *resv, bool *resnull);
+static void ExecInitExprSlots(ExprState *state, Node *node);
+
+/* support functions */
+static bool isAssignmentIndirectionExpr(Expr *expr);
+
+
+/*
+ * ExecInitExpr: prepare an expression tree for execution
+ *
+ * This function builds and returns an ExprState implementing the given
+ * Expr node tree.  The return ExprState can then be handed to ExecEvalExpr
+ * for execution.  Because the Expr tree itself is read-only as far as
+ * ExecInitExpr and ExecEvalExpr are concerned, several different executions
+ * of the same plan tree can occur concurrently.
+ *
+ * This must be called in a memory context that will last as long as repeated
+ * executions of the expression are needed.  Typically the context will be
+ * the same as the per-query context of the associated ExprContext.
+ *
+ * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
+ * lists of such nodes held by the parent PlanState.
+ *
+ * Note: there is no ExecEndExpr function; we assume that any resource
+ * cleanup needed will be handled by just releasing the memory context
+ * in which the state tree is built.  Functions that require additional
+ * cleanup work can register a shutdown callback in the ExprContext.
+ *
+ *	'node' is the root of the expression tree to examine
+ *	'parent' is the PlanState node that owns the expression.
+ *
+ * 'parent' may be NULL if we are preparing an expression that is not
+ * associated with a plan tree.  (If so, it can't have aggs or subplans.)
+ * This case should usually come through ExecPrepareExpr, not directly here.
+ */
+ExprState *
+ExecInitExpr(Expr *node, PlanState *parent)
+{
+	ExprState *state = makeNode(ExprState);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->expr = node;
+	ExecInitExprSlots(state, (Node *) node);
+
+	ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecInstantiateExpr(state);
+
+	return state;
+}
+
+/*
+ * ExecInitQual: prepare a qual for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that returns true iff none of the subexpressions are false.  (We
+ * also return true if the list is empty.)
+ *
+ * If some of the subexpressions yield NULL, then the result of the
+ * conjunction is false.  This makes this routine primarily useful for
+ * evaluating WHERE clauses, since SQL specifies that tuples with null WHERE
+ * results do not get selected.
+ */
+ExprState *
+ExecInitQual(List *qual, PlanState *parent)
+{
+	ExprState *state = makeNode(ExprState);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	List *adjust_bailout = NIL;
+
+	/* short-circuit (here and in ExecQual) for empty restriction list */
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	/*
+	 * ExecQual() needs to return false for expression returning NULL. That
+	 * allows to short-circuit the evaluation the first time a NULL is
+	 * encountered.  As qual evaluation is a hot-path this warrants using a
+	 * special opcode for qual evaluation that's simpler than BOOL_AND (which
+	 * has more complex NULL handling).
+	 */
+	state->expr = (Expr *) qual;
+	ExecInitExprSlots(state, (Node *) qual);
+
+	scratch.opcode = EEO_QUAL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+
+	foreach (lc, qual)
+	{
+		Expr *node = (Expr *) lfirst(lc);
+
+		/* first evaluate expression */
+		ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+		/* then check whether it's false or NULL */
+		scratch.d.qualexpr.jumpdone = -1;
+		ExprEvalPushStep(state, &scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+	}
+
+	/* adjust early bail out jump target */
+	foreach (lc, adjust_bailout)
+	{
+		ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+		Assert(as->d.qualexpr.jumpdone == -1);
+		as->d.qualexpr.jumpdone = state->steps_len;
+	}
+
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecInstantiateExpr(state);
+
+	return state;
+}
+
+/*
+ * ExecInitCheck: prepare a check constraint for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that returns true iff none of the subexpressions are false.  (We
+ * also return true if the list is empty.)
+ *
+ * If some of the subexpressions yield NULL, then the result of the
+ * conjunction is true, since SQL specifies that NULL constraint conditions
+ * are not failures.
+ */
+ExprState *
+ExecInitCheck(List *qual, PlanState *parent)
+{
+	Expr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	/* just whip-up a boolean AND expression, that behaves just as needed */
+	andExpr = makeBoolExpr(AND_EXPR, qual, -1);
+
+	return ExecInitExpr(andExpr, parent);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ *
+ * This is implemented by internally building an ExprState that performs the
+ * projection. That way faster implementations of expression evaluation, e.g
+ * compiled to native code, can evaluate the whole projection in one go.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle->expr, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			switch (variable->varno)
+			{
+			case INNER_VAR: /* get the tuple from the inner node */
+				scratch.opcode = EEO_ASSIGN_INNER_VAR;
+			break;
+
+			case OUTER_VAR: /* get the tuple from the outer node */
+				scratch.opcode = EEO_ASSIGN_OUTER_VAR;
+				break;
+
+				/* INDEX_VAR is handled by default case */
+			default:				/* get the tuple from the relation being
+									 * scanned */
+				scratch.opcode = EEO_ASSIGN_SCAN_VAR;
+			break;
+			}
+
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			/*
+			 * We can't directly point into the result slot for the contained
+			 * expression, as the result slot (and the exprstate for that
+			 * matter) can change below us. So we instead evaluate into a
+			 * temporary value and then move.
+			 */
+			ExecInitExprRec(tle->expr, parent, state, &state->resvalue, &state->resnull);
+			if (get_typlen(exprType((Node *) tle->expr)) == -1)
+				scratch.opcode = EEO_ASSIGN_TMP_UNEXPAND;
+			else
+				scratch.opcode = EEO_ASSIGN_TMP;
+			scratch.d.assign_tmp.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+	}
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecInstantiateExpr(state);
+
+	return projInfo;
+}
+
+/*
+ * Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
+ */
+List *
+ExecInitExprList(List *nodes, PlanState *parent)
+{
+	List	   *result = NIL;
+	ListCell   *lc;
+
+	foreach (lc, nodes)
+	{
+		Expr   *e = lfirst(lc);
+
+		result = lappend(result, ExecInitExpr(e, parent));
+	}
+
+	return result;
+}
+
+/*
+ * ExecPrepareExpr --- initialize for expression execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState *
+ExecPrepareExpr(Expr *node, EState *estate)
+{
+	ExprState  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	node = expression_planner(node);
+
+	result = ExecInitExpr(node, NULL);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+/*
+ * ExecPrepareCheck -- initialize qual for execution outside a normal Plan
+ * tree context.
+ *
+ * See ExecPrepareExpr() and ExecInitQual() for details.
+ */
+ExprState *
+ExecPrepareCheck(List *qual, EState *estate)
+{
+	ExprState  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitCheck(qual, NULL);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+/*
+ * Call ExecPrepareExpr() on each member of nodes, and return list of
+ * ExprStates.
+ *
+ * See ExecPrepareExpr() for details.
+ */
+List *
+ExecPrepareExprList(List *nodes, EState *estate)
+{
+	List	   *result = NIL;
+	ListCell   *lc;
+
+	foreach (lc, nodes)
+	{
+		Expr   *e = lfirst(lc);
+
+		result = lappend(result, ExecPrepareExpr(e, estate));
+	}
+
+	return result;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ * Projects a tuple based on projection info and stores it in the specified
+ * tuple table slot.
+ *
+ * Note: the result is always a virtual tuple; therefore it
+ * may reference the contents of the exprContext's scan tuples
+ * and/or temporary results constructed in the exprContext.
+ * If the caller wishes the result to be valid longer than that
+ * data will be valid, he must call ExecMaterializeSlot on the
+ * result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext = projInfo->pi_exprContext;
+	ExprState *state;
+
+	state = &projInfo->pi_state;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr(state, econtext, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+/*
+ * ExecCheck - evaluate a check constraint prepared with ExecInitCheck
+ * (possibly via ExecPrepareCheck).
+ */
+bool
+ExecCheck(ExprState *state, ExprContext *econtext)
+{
+	bool isnull;
+	Datum ret;
+
+	ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/*
+ * Prepare an expression for execution.
+ */
+static void
+ExecInstantiateExpr(ExprState *state)
+{
+	ExecInstantiateInterpretedExpr(state);
+}
+
+/*
+ * Add evaluation of node to ExprState, possibly recursing into
+ * sub-expressions of node.
+ */
+static void
+ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	/*
+	 * Guard against stack overflow due to overly complex expressions. Because
+	 * expression evaluation is not recursive, but expression planning is,
+	 * we'll hit stack limits due to recursion either here, or when recursing
+	 * into a separate expression evaluation inside a function call.  This
+	 * lets us avoid repeatedly doing the quite expensive stack depth checks
+	 * during expression evaluation.
+	 */
+	check_stack_depth();
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+
+	/* cases should be ordered as they are in enum NodeTag */
+	switch (nodeTag(node))
+	{
+		case T_Var:
+			{
+				Var *variable = (Var *) node;
+
+				/* varattno == InvalidAttrNumber means it's a whole-row Var */
+				if (variable->varattno == InvalidAttrNumber)
+				{
+					ExecInitWholeRowVar(&scratch, variable, parent, state, resv, resnull);
+
+					scratch.opcode = EEO_WHOLEROW;
+				}
+				else if (variable->varattno <= 0)
+				{
+					scratch.d.var.attnum = variable->varattno;
+					switch (variable->varno)
+					{
+					case INNER_VAR:
+						scratch.opcode = EEO_INNER_SYSVAR;
+						break;
+					case OUTER_VAR:
+						scratch.opcode = EEO_OUTER_SYSVAR;
+						break;
+					default:
+						scratch.opcode = EEO_SCAN_SYSVAR;
+						break;
+					}
+				}
+				else
+				{
+					switch (variable->varno)
+					{
+						case INNER_VAR:
+							scratch.opcode = EEO_INNER_VAR;
+							break;
+						case OUTER_VAR:
+							scratch.opcode = EEO_OUTER_VAR;
+							break;
+						default:
+							scratch.opcode = EEO_SCAN_VAR;
+							break;
+					}
+					scratch.d.var.attnum = variable->varattno - 1;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_Param:
+			{
+				Param *param = (Param *) node;
+
+				switch (param->paramkind)
+				{
+					case PARAM_EXEC:
+						{
+							scratch.opcode = EEO_PARAM_EXEC;
+							scratch.d.param.paramid = param->paramid;
+							scratch.d.param.paramtype = InvalidOid;
+							break;
+						}
+					case PARAM_EXTERN:
+						{
+							scratch.opcode = EEO_PARAM_EXTERN;
+							scratch.d.param.paramid = param->paramid;
+							scratch.d.param.paramtype = param->paramtype;
+							break;
+						}
+					default:
+						elog(ERROR, "unrecognized paramkind: %d",
+							 (int) ((Param *) node)->paramkind);
+						break;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_Aggref:
+			{
+				Aggref *aggref = (Aggref *) node;
+				AggrefExprState *astate = makeNode(AggrefExprState);
+
+				scratch.opcode = EEO_AGGREF;
+				scratch.d.aggref.astate = astate;
+				astate->aggref = aggref;
+				if (parent && IsA(parent, AggState))
+				{
+					AggState   *aggstate = (AggState *) parent;
+
+					aggstate->aggs = lcons(astate, aggstate->aggs);
+					aggstate->numaggs++;
+				}
+				else
+				{
+					/* planner messed up */
+					elog(ERROR, "Aggref found in non-Agg plan node");
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_GroupingFunc:
+			{
+				GroupingFunc *grp_node = (GroupingFunc *) node;
+				Agg		   *agg = NULL;
+
+				if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
+					elog(ERROR, "parent of GROUPING is not Agg node");
+
+				scratch.opcode = EEO_GROUPING_FUNC;
+				scratch.d.grouping_func.parent = (AggState *) parent;
+
+				agg = (Agg *) (parent->plan);
+
+				if (agg->groupingSets)
+					scratch.d.grouping_func.clauses = grp_node->cols;
+				else
+					scratch.d.grouping_func.clauses = NIL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_WindowFunc:
+			{
+				WindowFunc *wfunc = (WindowFunc *) node;
+				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
+
+				wfstate->wfunc = wfunc;
+
+				if (parent && IsA(parent, WindowAggState))
+				{
+					WindowAggState *winstate = (WindowAggState *) parent;
+					int			nfuncs;
+
+					winstate->funcs = lcons(wfstate, winstate->funcs);
+					nfuncs = ++winstate->numfuncs;
+					if (wfunc->winagg)
+						winstate->numaggs++;
+
+					/* for now intialize agg using old style expressions */
+					wfstate->args = NIL;
+					wfstate->args = ExecInitExprList(wfunc->args, parent);
+					wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
+													  parent);
+
+					/*
+					 * Complain if the windowfunc's arguments contain any
+					 * windowfuncs; nested window functions are semantically
+					 * nonsensical.  (This should have been caught earlier,
+					 * but we defend against it here anyway.)
+					 */
+					if (nfuncs != winstate->numfuncs)
+						ereport(ERROR,
+								(errcode(ERRCODE_WINDOWING_ERROR),
+								 errmsg("window function calls cannot be nested")));
+				}
+				else
+				{
+					/* planner messed up */
+					elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
+				}
+
+				scratch.opcode = EEO_WINDOW_FUNC;
+				scratch.d.window_func.wfstate = wfstate;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ArrayRef:
+			{
+				ArrayRef   *aref = (ArrayRef *) node;
+
+				ExecInitArrayRef(&scratch, aref, parent, state, resv, resnull);
+				break;
+			}
+
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				ExecInitFunc(&scratch, node, func->args, func->funcid, func->inputcollid,
+							 parent, state, resv, resnull);
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+							 parent, state, resv, resnull);
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_DistinctExpr:
+			{
+				DistinctExpr   *op = (DistinctExpr *) node;
+
+				ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+							 parent, state, resv, resnull);
+
+				/* Can't use normal function call, override opcode for DISTINCT */
+				/*
+				 * XXX: historically we've not called the function usage
+				 * pgstat infrastructure - that seems inconsistent given that
+				 * we do so for normal function *and* operator evaluation
+				 */
+				scratch.opcode = EEO_DISTINCT;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_NullIfExpr:
+			{
+				NullIfExpr   *op = (NullIfExpr *) node;
+
+				ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+							 parent, state, resv, resnull);
+
+				/* Can't use normal function call, override opcode for NULL() */
+				/*
+				 * XXX: historically we've not called the function usage
+				 * pgstat infrastructure - that seems inconsistent given that
+				 * we do so for normal function *and* operator evaluation
+				 */
+				scratch.opcode = EEO_NULLIF;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ScalarArrayOpExpr:
+			{
+				ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+				FmgrInfo	*finfo;
+				FunctionCallInfo fcinfo;
+
+				finfo = palloc0(sizeof(FmgrInfo));
+				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fmgr_info(opexpr->opfuncid, finfo);
+				fmgr_info_set_expr((Node *) node, finfo);
+				InitFunctionCallInfoData(*fcinfo, finfo, 2,
+										 opexpr->inputcollid, NULL, NULL);
+
+				scratch.opcode = EEO_SCALARARRAYOP;
+				scratch.d.scalararrayop.opexpr = opexpr;
+				scratch.d.scalararrayop.finfo = finfo;
+				scratch.d.scalararrayop.fcinfo_data = fcinfo;
+				scratch.d.scalararrayop.fn_addr = fcinfo->flinfo->fn_addr;
+
+				Assert(fcinfo->nargs == 2);
+				Assert(list_length(opexpr->args) == 2);
+
+				/* evaluate scalar directly into function argument */
+				ExecInitExprRec((Expr *) linitial(opexpr->args), parent, state,
+								&fcinfo->arg[0], &fcinfo->argnull[0]);
+
+				/* evaluate array argument into our return value, overwrite later */
+				ExecInitExprRec((Expr *) lsecond(opexpr->args), parent, state,
+								resv, resnull);
+
+				scratch.d.scalararrayop.element_type = InvalidOid;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					ExecInitExprRec(arg, parent, state,
+									scratch.d.boolexpr.value,
+									scratch.d.boolexpr.isnull);
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_SubPlan:
+			{
+				SubPlan    *subplan = (SubPlan *) node;
+				SubPlanState *sstate;
+
+				if (!parent)
+					elog(ERROR, "SubPlan found with no parent plan");
+
+				sstate = ExecInitSubPlan(subplan, parent);
+
+				/* Add SubPlanState nodes to parent->subPlan */
+				parent->subPlan = lappend(parent->subPlan, sstate);
+
+				scratch.opcode = EEO_SUBPLAN;
+				scratch.d.subplan.sstate = sstate;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_AlternativeSubPlan:
+			{
+				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
+				AlternativeSubPlanState *asstate;
+
+				if (!parent)
+					elog(ERROR, "AlternativeSubPlan found with no parent plan");
+
+				asstate = ExecInitAlternativeSubPlan(asplan, parent);
+
+				scratch.opcode = EEO_ALTERNATIVE_SUBPLAN;
+				scratch.d.alternative_subplan.asstate = asstate;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_FieldSelect:
+			{
+				FieldSelect *fselect = (FieldSelect *) node;
+
+
+				/* evaluate argument */
+				ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
+
+				scratch.opcode = EEO_FIELDSELECT;
+				scratch.d.fieldselect.fieldnum = fselect->fieldnum;
+				scratch.d.fieldselect.resulttype = fselect->resulttype;
+				scratch.d.fieldselect.argdesc = NULL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_FieldStore:
+			{
+				FieldStore *fstore = (FieldStore *) node;
+				ListCell *l1, *l2;
+				Datum *values;
+				bool *nulls;
+				int biggest_fieldnum = 0;
+
+				foreach(l2, fstore->fieldnums)
+				{
+					AttrNumber	fieldnum = lfirst_int(l2);
+					if (fieldnum > biggest_fieldnum)
+						biggest_fieldnum = fieldnum;
+				}
+
+				/* FIXME: properly size workspace */
+				values = (Datum *) palloc(sizeof(Datum) * MaxTupleAttributeNumber);
+				nulls = (bool *) palloc(sizeof(bool) * MaxTupleAttributeNumber);
+
+				/* evaluate argument */
+				ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
+
+				/* first deform the input tuple */
+				scratch.opcode = EEO_FIELDSTORE_DEFORM;
+				scratch.d.fieldstore.argdesc = NULL;
+				scratch.d.fieldstore.fstore = fstore;
+				scratch.d.fieldstore.nvalues = biggest_fieldnum;
+				scratch.d.fieldstore.values = values;
+				scratch.d.fieldstore.nulls = nulls;
+				ExprEvalPushStep(state, &scratch);
+
+				/* evaluate new values, one step for each arg */
+				forboth(l1, fstore->newvals, l2, fstore->fieldnums)
+				{
+					Expr	   *e = (Expr *) lfirst(l1);
+					AttrNumber	fieldnum = lfirst_int(l2);
+					Datum *save_innermost_caseval = NULL;
+					bool *save_innermost_casenull = NULL;
+
+					/*
+					 * Use the CaseTestExpr mechanism to pass down the old value
+					 * of the field being replaced; this is needed in case the
+					 * newval is itself a FieldStore or ArrayRef that has to
+					 * obtain and modify the old value.  It's safe to reuse the
+					 * CASE mechanism because there cannot be a CASE between here
+					 * and where the value would be needed, and a field assignment
+					 * can't be within a CASE either.  (So saving and restoring
+					 * the caseValue is just paranoia, but let's do it anyway.)
+					 */
+					save_innermost_caseval = state->innermost_caseval;
+					save_innermost_casenull = state->innermost_casenull;
+					state->innermost_caseval = &values[fieldnum - 1];
+					state->innermost_casenull = &nulls[fieldnum - 1];
+
+					ExecInitExprRec(e, parent, state,
+									&values[fieldnum - 1],
+									&nulls[fieldnum - 1]);
+					state->innermost_caseval = save_innermost_caseval;
+					state->innermost_casenull = save_innermost_casenull;
+				}
+
+				/* then form result tuple */
+				scratch.opcode = EEO_FIELDSTORE_FORM;
+				scratch.d.fieldstore.fstore = fstore;
+				scratch.d.fieldstore.argdesc = NULL;
+				scratch.d.fieldstore.nvalues = biggest_fieldnum;
+				scratch.d.fieldstore.values = values;
+				scratch.d.fieldstore.nulls = nulls;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_RelabelType:
+			{
+				/* at runtime relabel doesn't need to do anything */
+				RelabelType *relabel = (RelabelType *) node;
+
+				ExecInitExprRec(relabel->arg, parent, state, resv, resnull);
+				break;
+			}
+
+		case T_CoerceViaIO:
+			{
+				CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+				Oid			iofunc;
+				bool		typisvarlena;
+
+				/* evaluate argument */
+				ExecInitExprRec(iocoerce->arg, parent, state, resv, resnull);
+
+				scratch.opcode = EEO_IOCOERCE;
+
+				/* lookup the input type's output function */
+				scratch.d.iocoerce.finfo_out = palloc0(sizeof(*scratch.d.iocoerce.finfo_out));
+				scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_out));
+
+				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+								  &iofunc, &typisvarlena);
+				fmgr_info(iofunc, scratch.d.iocoerce.finfo_out);
+				fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out);
+				InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out,
+										 scratch.d.iocoerce.finfo_out,
+										 1, InvalidOid, NULL, NULL);
+
+				/* lookup the result type's input function */
+				scratch.d.iocoerce.finfo_in = palloc0(sizeof(*scratch.d.iocoerce.finfo_in));
+				scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_in));
+
+				getTypeInputInfo(iocoerce->resulttype, &iofunc,
+								 &scratch.d.iocoerce.intypioparam);
+				fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
+				fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
+				InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
+										 scratch.d.iocoerce.finfo_in,
+										 3, InvalidOid, NULL, NULL);
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ArrayCoerceExpr:
+			{
+				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+
+				/* evaluate argument */
+				ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
+
+				scratch.opcode = EEO_ARRAYCOERCE;
+				scratch.d.arraycoerce.coerceexpr = acoerce;
+				scratch.d.arraycoerce.resultelemtype =
+					get_element_type(acoerce->resulttype);
+				if (scratch.d.arraycoerce.resultelemtype == InvalidOid)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("target type is not an array")));
+				/* Arrays over domains aren't supported yet */
+				Assert(getBaseType(scratch.d.arraycoerce.resultelemtype) ==
+					   scratch.d.arraycoerce.resultelemtype);
+				scratch.d.arraycoerce.elemfunc = (FmgrInfo *) palloc(sizeof(FmgrInfo));
+				scratch.d.arraycoerce.elemfunc->fn_oid = InvalidOid;	/* not initialized */
+				scratch.d.arraycoerce.amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ConvertRowtypeExpr:
+			{
+				ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+
+				/* evaluate argument */
+				ExecInitExprRec(convert->arg, parent, state, resv, resnull);
+
+				/* and push conversion step */
+				scratch.opcode = EEO_CONVERT_ROWTYPE;
+				scratch.d.convert_rowtype.convert = convert;
+				scratch.d.convert_rowtype.indesc = NULL;
+				scratch.d.convert_rowtype.outdesc = NULL;
+				scratch.d.convert_rowtype.map = NULL;
+				scratch.d.convert_rowtype.initialized = false;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		/* note that all CaseWhen expressions handled within this block */
+		case T_CaseExpr:
+			{
+				CaseExpr *caseExpr = (CaseExpr *) node;
+				ListCell   *clause;
+				List *adjust_bailout = NIL;
+				ListCell *lc;
+				Datum *casevalue = palloc0(sizeof(Datum));
+				bool *caseisnull = palloc0(sizeof(bool));
+				Datum *save_innermost_caseval = NULL;
+				bool *save_innermost_casenull = NULL;
+				Datum *caseval = NULL;
+				bool *casenull = NULL;
+
+				/* arg == NULL -> CASE WHEN foo */
+				/* arg != NULL -> CASE foo WHEN blarg */
+				if (caseExpr->arg != NULL)
+				{
+					caseval = palloc0(sizeof(Datum));
+					casenull = palloc0(sizeof(bool));
+
+					ExecInitExprRec(caseExpr->arg, parent, state,
+									caseval, casenull);
+				}
+
+				foreach(clause, caseExpr->args)
+				{
+					CaseWhen *when = (CaseWhen *) lfirst(clause);
+					int whenstep;
+
+					/* evaluate condition */
+					save_innermost_caseval = state->innermost_caseval;
+					save_innermost_casenull = state->innermost_casenull;
+					state->innermost_caseval = caseval;
+					state->innermost_casenull = casenull;
+
+					ExecInitExprRec(when->expr, parent, state,
+									casevalue, caseisnull);
+
+					state->innermost_caseval = save_innermost_caseval;
+					state->innermost_casenull = save_innermost_casenull;
+
+					scratch.opcode = EEO_CASE_WHEN_STEP;
+					scratch.d.casewhen.value = casevalue;
+					scratch.d.casewhen.isnull = caseisnull;
+					scratch.d.casewhen.jumpfalse = -1; /* computed later */
+					ExprEvalPushStep(state, &scratch);
+					whenstep = state->steps_len - 1;
+
+					/* evaluate result */
+					ExecInitExprRec(when->result, parent, state, resv, resnull);
+
+					scratch.opcode = EEO_CASE_THEN_STEP;
+					scratch.d.casewhen.value = casevalue;
+					scratch.d.casewhen.isnull = caseisnull;
+					scratch.d.casethen.jumpdone = -1; /* computed later */
+					ExprEvalPushStep(state, &scratch);
+					/*
+					 * Don't know "address" of jump target yet, compute once the
+					 * whole case expression is built.
+					 */
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+
+					/* adjust jump target for WHEN step, for the !match case */
+					state->steps[whenstep].d.casewhen.jumpfalse = state->steps_len;
+				}
+
+				if (caseExpr->defresult)
+				{
+					/* evaluate result, directly into result datum */
+					ExecInitExprRec(caseExpr->defresult, parent, state,
+									resv, resnull);
+				}
+				else
+				{
+					/* statically return NULL */
+					scratch.opcode = EEO_CONST;
+					scratch.d.constval.isnull = true;
+					scratch.d.constval.value = 0;
+					ExprEvalPushStep(state, &scratch);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.casethen.jumpdone == -1);
+					as->d.casethen.jumpdone = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_CaseTestExpr:
+			{
+				CaseTestExpr *casetestexpr = (CaseTestExpr *) node;
+
+				scratch.d.casetest.value = state->innermost_caseval;
+				scratch.d.casetest.isnull = state->innermost_casenull;
+
+				if (get_typlen(casetestexpr->typeId) == -1)
+					scratch.opcode = EEO_CASE_TESTVAL_UNEXPAND;
+				else
+					scratch.opcode = EEO_CASE_TESTVAL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+
+		case T_ArrayExpr:
+			{
+				ArrayExpr  *arrayexpr = (ArrayExpr *) node;
+				int nelems = list_length(arrayexpr->elements);
+				ListCell *lc;
+				int elemoff;
+
+				scratch.opcode = EEO_ARRAYEXPR;
+				scratch.d.arrayexpr.arrayexpr = arrayexpr;
+				scratch.d.arrayexpr.nelems = nelems;
+				scratch.d.arrayexpr.elemvalues =
+					(Datum *) palloc(sizeof(Datum) * nelems);
+				scratch.d.arrayexpr.elemnulls =
+					(bool *) palloc(sizeof(bool) * nelems);
+
+				/* do one-time catalog lookup for type info */
+				get_typlenbyvalalign(arrayexpr->element_typeid,
+									 &scratch.d.arrayexpr.elemlength,
+									 &scratch.d.arrayexpr.elembyval,
+									 &scratch.d.arrayexpr.elemalign);
+
+				/* evaluate all arguments */
+				elemoff = 0;
+				foreach(lc, arrayexpr->elements)
+				{
+					Expr	   *e = (Expr *) lfirst(lc);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.arrayexpr.elemvalues[elemoff],
+									&scratch.d.arrayexpr.elemnulls[elemoff]);
+					elemoff++;
+				}
+
+				/* and then collect into an array */
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_RowExpr:
+			{
+				RowExpr    *rowexpr = (RowExpr *) node;
+				int nelems = list_length(rowexpr->args);
+				TupleDesc tupdesc;
+				Form_pg_attribute *attrs;
+				int			i;
+				ListCell   *l;
+
+				scratch.opcode = EEO_ROW;
+				scratch.d.row.elemvalues =
+					(Datum *) palloc(sizeof(Datum) * nelems);
+				scratch.d.row.elemnulls =
+					(bool *) palloc(sizeof(bool) * nelems);
+
+				/* Build tupdesc to describe result tuples */
+				if (rowexpr->row_typeid == RECORDOID)
+				{
+					/* generic record, use types of given expressions */
+					tupdesc = ExecTypeFromExprList(rowexpr->args);
+				}
+				else
+				{
+					/* it's been cast to a named type, use that */
+					tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
+				}
+
+				scratch.d.row.tupdesc = tupdesc;
+
+				/* In either case, adopt RowExpr's column aliases */
+				ExecTypeSetColNames(tupdesc, rowexpr->colnames);
+				/* Bless the tupdesc in case it's now of type RECORD */
+				BlessTupleDesc(tupdesc);
+
+				/* Set up evaluation, skipping any deleted columns */
+				attrs = tupdesc->attrs;
+				i = 0;
+				foreach(l, rowexpr->args)
+				{
+					Expr	   *e = (Expr *) lfirst(l);
+
+					if (!attrs[i]->attisdropped)
+					{
+						/*
+						 * Guard against ALTER COLUMN TYPE on rowtype since
+						 * the RowExpr was created.  XXX should we check
+						 * typmod too?	Not sure we can be sure it'll be the
+						 * same.
+						 */
+						if (exprType((Node *) e) != attrs[i]->atttypid)
+							ereport(ERROR,
+									(errcode(ERRCODE_DATATYPE_MISMATCH),
+									 errmsg("ROW() column has type %s instead of type %s",
+											format_type_be(exprType((Node *) e)),
+											format_type_be(attrs[i]->atttypid))));
+					}
+					else
+					{
+						/*
+						 * Ignore original expression and insert a NULL. We
+						 * don't really care what type of NULL it is, so
+						 * always make an int4 NULL.
+						 */
+						e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
+					}
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.row.elemvalues[i],
+									&scratch.d.row.elemnulls[i]);
+					i++;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_RowCompareExpr:
+			{
+				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+				int			nopers = list_length(rcexpr->opnos);
+				List *adjust_bailout = NIL;
+				ListCell *l_left_expr, *l_right_expr, *l_opno, *l_opfamily, *l_inputcollid;
+				ListCell *lc;
+				int off;
+
+				/*
+				 * Iterate over each element, build comparisons. If result is
+				 * NULL, jump to after the expression. If expression yields a != 0
+				 * result jump to the final step.
+				 */
+				Assert(list_length(rcexpr->largs) == nopers);
+				Assert(list_length(rcexpr->rargs) == nopers);
+
+				off = 0;
+				for (off = 0,
+						 l_left_expr = list_head(rcexpr->largs),
+						 l_right_expr = list_head(rcexpr->rargs),
+						 l_opno = list_head(rcexpr->opnos),
+						 l_opfamily = list_head(rcexpr->opfamilies),
+						 l_inputcollid = list_head(rcexpr->inputcollids);
+					 off < nopers;
+					 off++,
+						 l_left_expr = lnext(l_left_expr),
+						 l_right_expr = lnext(l_right_expr),
+						 l_opno = lnext(l_opno),
+						 l_opfamily = lnext(l_opfamily),
+						 l_inputcollid = lnext(l_inputcollid))
+				{
+					Expr	   *left_expr = (Expr *) lfirst(l_left_expr);
+					Expr	   *right_expr = (Expr *) lfirst(l_right_expr);
+					Oid			opno = lfirst_oid(l_opno);
+					Oid			opfamily = lfirst_oid(l_opfamily);
+					Oid			inputcollid = lfirst_oid(l_inputcollid);
+					int			strategy;
+					Oid			lefttype;
+					Oid			righttype;
+					Oid			proc;
+					FmgrInfo	*finfo;
+					FunctionCallInfo fcinfo;
+
+					get_op_opfamily_properties(opno, opfamily, false,
+											   &strategy,
+											   &lefttype,
+											   &righttype);
+					proc = get_opfamily_proc(opfamily,
+											 lefttype,
+											 righttype,
+											 BTORDER_PROC);
+
+					/* Set up the primary fmgr lookup information */
+					finfo = palloc0(sizeof(FmgrInfo));
+					fcinfo = palloc0(sizeof(FunctionCallInfoData));
+					fmgr_info(proc, finfo);
+					fmgr_info_set_expr((Node *) node, finfo);
+					InitFunctionCallInfoData(*fcinfo, finfo, 2, inputcollid, NULL, NULL);
+
+					/*
+					 * If we enforced permissions checks on index support
+					 * functions, we'd need to make a check here.  But the index
+					 * support machinery doesn't do that, and neither does this
+					 * code.
+					 */
+
+					/* evaluate left and right expression directly into fcinfo */
+					ExecInitExprRec(left_expr, parent, state,
+									&fcinfo->arg[0], &fcinfo->argnull[0]);
+					ExecInitExprRec(right_expr, parent, state,
+									&fcinfo->arg[1], &fcinfo->argnull[1]);
+
+					scratch.opcode = EEO_ROWCOMPARE_STEP;
+
+					/* jump targets computed later */
+					scratch.d.rowcompare_step.jumpnull = -1;
+					scratch.d.rowcompare_step.jumpdone = -1;
+
+					scratch.d.rowcompare_step.finfo = finfo;
+					scratch.d.rowcompare_step.fcinfo_data = fcinfo;
+					scratch.d.rowcompare_step.fn_addr = fcinfo->flinfo->fn_addr;
+
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* and then compare the last result */
+				scratch.opcode = EEO_ROWCOMPARE_FINAL;
+				scratch.d.rowcompare_final.rctype = rcexpr->rctype;
+				ExprEvalPushStep(state, &scratch);
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.rowcompare_step.jumpdone == -1);
+					Assert(as->d.rowcompare_step.jumpnull == -1);
+
+					/* jump to comparison evaluation */
+					as->d.rowcompare_step.jumpdone = state->steps_len - 1;
+					/* jump to the following expression */
+					as->d.rowcompare_step.jumpnull = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_CoalesceExpr:
+			{
+				CoalesceExpr *coalesce = (CoalesceExpr *) node;
+				List *adjust_bailout = NIL;
+				ListCell   *lc;
+
+				Assert(list_length(coalesce->args) > 0);
+
+				foreach(lc, coalesce->args)
+				{
+					Expr	   *e = (Expr *) lfirst(lc);
+
+					/* evaluate result, directly into result datum */
+					ExecInitExprRec(e, parent, state, resv, resnull);
+
+					/* then push step checking for NULLs */
+					scratch.opcode = EEO_COALESCE;
+					scratch.d.coalesce.jumpdone = -1; /* adjust later */
+					ExprEvalPushStep(state, &scratch);
+
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/*
+				 * No need to add a constant NULL return - we only can get to the
+				 * end of the expression if a NULL already is being returned.
+				 */
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.coalesce.jumpdone == -1);
+					as->d.coalesce.jumpdone = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_MinMaxExpr:
+			{
+				MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+				int nelems = list_length(minmaxexpr->args);
+				TypeCacheEntry *typentry;
+				FmgrInfo	*finfo;
+				FunctionCallInfo fcinfo;
+				ListCell *lc;
+				int off;
+
+				/* Look up the btree comparison function for the datatype */
+				typentry = lookup_type_cache(minmaxexpr->minmaxtype,
+											 TYPECACHE_CMP_PROC);
+				if (!OidIsValid(typentry->cmp_proc))
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_FUNCTION),
+							 errmsg("could not identify a comparison function for type %s",
+									format_type_be(minmaxexpr->minmaxtype))));
+
+				/*
+				 * If we enforced permissions checks on index support functions,
+				 * we'd need to make a check here.  But the index support
+				 * machinery doesn't do that, and neither does this code.
+				 */
+				finfo = palloc0(sizeof(FmgrInfo));
+				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fmgr_info(typentry->cmp_proc, finfo);
+				fmgr_info_set_expr((Node *) node, finfo);
+				InitFunctionCallInfoData(*fcinfo, finfo, 2,
+										 minmaxexpr->inputcollid, NULL, NULL);
+
+				scratch.opcode = EEO_MINMAX;
+				scratch.d.minmax.values =
+					(Datum *) palloc(sizeof(Datum) * nelems);
+				scratch.d.minmax.nulls =
+					(bool *) palloc(sizeof(bool) * nelems);
+				scratch.d.minmax.nelems = nelems;
+				scratch.d.minmax.op = minmaxexpr->op;
+
+				scratch.d.minmax.finfo = finfo;
+				scratch.d.minmax.fcinfo_data = fcinfo;
+
+				/* evaluate expressions into minmax->values/nulls */
+				off = 0;
+				foreach(lc, minmaxexpr->args)
+				{
+					Expr	   *e = (Expr *) lfirst(lc);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.minmax.values[off],
+									&scratch.d.minmax.nulls[off]);
+					off++;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_SQLValueFunction:
+			{
+				SQLValueFunction *svf = (SQLValueFunction *) node;
+
+				scratch.opcode = EEO_SQLVALUEFUNCTION;
+				scratch.d.sqlvaluefunction.svf = svf;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_XmlExpr:
+			{
+				XmlExpr    *xexpr = (XmlExpr *) node;
+				ListCell   *arg;
+				int nnamed = list_length(xexpr->named_args);
+				int nargs = list_length(xexpr->args);
+				int off;
+
+				scratch.opcode = EEO_XMLEXPR;
+				scratch.d.xmlexpr.xexpr = xexpr;
+				if (nnamed)
+				{
+					scratch.d.xmlexpr.named_argvalue =
+						(Datum *) palloc(sizeof(Datum) * nnamed);
+					scratch.d.xmlexpr.named_argnull =
+						(bool *) palloc(sizeof(bool) * nnamed);
+				}
+				else
+				{
+					scratch.d.xmlexpr.named_argvalue = NULL;
+					scratch.d.xmlexpr.named_argnull = NULL;
+				}
+
+				if (nargs)
+				{
+					scratch.d.xmlexpr.argvalue =
+						(Datum *) palloc(sizeof(Datum) * nargs);
+					scratch.d.xmlexpr.argnull =
+						(bool *) palloc(sizeof(bool) * nargs);
+				}
+				else
+				{
+					scratch.d.xmlexpr.argvalue = NULL;
+					scratch.d.xmlexpr.argnull = NULL;
+				}
+
+				off = 0;
+				foreach(arg, xexpr->named_args)
+				{
+					Expr	   *e = (Expr *) lfirst(arg);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.xmlexpr.named_argvalue[off],
+									&scratch.d.xmlexpr.named_argnull[off]);
+					off++;
+				}
+
+				off = 0;
+				foreach(arg, xexpr->args)
+				{
+					Expr	   *e = (Expr *) lfirst(arg);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.xmlexpr.argvalue[off],
+									&scratch.d.xmlexpr.argnull[off]);
+					off++;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_NullTest:
+			{
+				NullTest   *ntest = (NullTest *) node;
+
+				if (ntest->nulltesttype == IS_NULL)
+				{
+					if (ntest->argisrow)
+						scratch.opcode = EEO_NULLTEST_ROWISNULL;
+					else
+						scratch.opcode = EEO_NULLTEST_ISNULL;
+				}
+				else if (ntest->nulltesttype == IS_NOT_NULL)
+				{
+					if (ntest->argisrow)
+						scratch.opcode = EEO_NULLTEST_ROWISNOTNULL;
+					else
+						scratch.opcode = EEO_NULLTEST_ISNOTNULL;
+				}
+				else
+				{
+					elog(ERROR, "unrecognized nulltesttype: %d",
+						 (int) ntest->nulltesttype);
+				}
+
+				/* first evaluate argument */
+				ExecInitExprRec(ntest->arg, parent, state,
+								resv, resnull);
+
+				/* then push check of argument */
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_BooleanTest:
+			{
+				BooleanTest *btest = (BooleanTest *) node;
+
+				/*
+				 * Evaluate argument, directly into result datum. That's a bit
+				 * debatable, because the types will be different, but it's
+				 * efficient...
+				 */
+				ExecInitExprRec(btest->arg, parent, state, resv, resnull);
+
+				switch (btest->booltesttype)
+				{
+					case IS_TRUE:
+						scratch.opcode = EEO_BOOLTEST_IS_TRUE;
+						break;
+					case IS_NOT_TRUE:
+						scratch.opcode = EEO_BOOLTEST_IS_NOT_TRUE;
+						break;
+					case IS_FALSE:
+						scratch.opcode = EEO_BOOLTEST_IS_FALSE;
+						break;
+					case IS_NOT_FALSE:
+						scratch.opcode = EEO_BOOLTEST_IS_NOT_FALSE;
+						break;
+					case IS_UNKNOWN:
+						scratch.opcode = EEO_BOOLTEST_IS_UNKNOWN;
+						break;
+					case IS_NOT_UNKNOWN:
+						scratch.opcode = EEO_BOOLTEST_IS_NOT_UNKNOWN;
+						break;
+					default:
+						elog(ERROR, "unrecognized booltesttype: %d",
+							 (int) btest->booltesttype);
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_CoerceToDomain:
+			{
+				CoerceToDomain *ctest = (CoerceToDomain *) node;
+				Datum *save_innermost_domainval = NULL;
+				bool *save_innermost_domainnull = NULL;
+				DomainConstraintRef *constraint_ref =
+					palloc(sizeof(DomainConstraintRef));
+				ListCell   *l;
+
+				scratch.d.domaincheck.resulttype = ctest->resulttype;
+				scratch.d.domaincheck.checkvalue = (Datum *) palloc(sizeof(Datum));
+				scratch.d.domaincheck.checknull = (bool *) palloc(sizeof(bool));
+
+				/* evaluate argument */
+				ExecInitExprRec(ctest->arg, parent, state, resv, resnull);
+
+				/*
+				 * XXX: In contrast to the old implementation we're evaluating the
+				 * set of to-be-checked constraints at query start - that seems
+				 * perfectly sensible to me. But perhaps there's a reason the
+				 * previous implementation did what it did? ISTM that was just a
+				 * side-effect of using the typecache (which is longer lived than
+				 * a single query).
+				 */
+
+				/* Make sure we have up-to-date constraints */
+				InitDomainConstraintRef(ctest->resulttype,
+										constraint_ref,
+										CurrentMemoryContext);
+				UpdateDomainConstraintRef(constraint_ref);
+
+				/*
+				 * Set up value to be returned by CoerceToDomainValue nodes. We
+				 * must save and restore innermost_domainval/null fields, in case
+				 * this node is itself within a check expression for another
+				 * domain.
+				 */
+				save_innermost_domainval = state->innermost_domainval;
+				save_innermost_domainnull = state->innermost_domainnull;
+				state->innermost_domainval = resv;
+				state->innermost_domainnull = resnull;
+
+				foreach(l, constraint_ref->constraints)
+				{
+					DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+					scratch.d.domaincheck.constraintname = con->name;
+
+					switch (con->constrainttype)
+					{
+						case DOM_CONSTRAINT_NOTNULL:
+							scratch.opcode = EEO_DOMAIN_NOTNULL;
+							ExprEvalPushStep(state, &scratch);
+							break;
+						case DOM_CONSTRAINT_CHECK:
+							/* evaluate check expression value */
+							ExecInitExprRec(con->check_expr, parent, state,
+											scratch.d.domaincheck.checkvalue,
+											scratch.d.domaincheck.checknull);
+
+							/* and then check result value */
+							scratch.opcode = EEO_DOMAIN_CHECK;
+							ExprEvalPushStep(state, &scratch);
+							break;
+						default:
+							elog(ERROR, "unrecognized constraint type: %d",
+								 (int) con->constrainttype);
+							break;
+					}
+				}
+
+				state->innermost_domainval = save_innermost_domainval;
+				state->innermost_domainnull = save_innermost_domainnull;
+
+				break;
+			}
+
+		case T_CoerceToDomainValue:
+			{
+				CoerceToDomainValue *domainval = (CoerceToDomainValue *) node;
+
+				/*
+				 * Share implementation with case testval, but different pointers.
+				 */
+				scratch.d.casetest.value = state->innermost_domainval;
+				scratch.d.casetest.isnull = state->innermost_domainnull;
+
+				if (get_typlen(domainval->typeId) == -1)
+					scratch.opcode = EEO_DOMAIN_TESTVAL_UNEXPAND;
+				else
+					scratch.opcode = EEO_DOMAIN_TESTVAL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_CurrentOfExpr:
+			{
+				scratch.opcode = EEO_CURRENTOFEXPR;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+}
+
+static void
+ExprEvalPushStep(ExprState *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static void
+ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprState *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info(funcid, scratch->d.func.finfo);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else
+		{
+			ExecInitExprRec(arg, parent, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]);
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict && nargs > 0)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict && nargs > 0)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+}
+
+static void
+ExecInitExprSlots(ExprState *state, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_INNER_FETCHSOME;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_OUTER_FETCHSOME;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_SCAN_FETCHSOME;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+static void
+ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+	scratch->d.wholerow.tupdesc = NULL;
+	scratch->d.wholerow.junkFilter = NULL;
+	scratch->d.wholerow.var = variable;
+	scratch->d.wholerow.first = true;
+
+	/*
+	 * If the input tuple came from a subquery, it might contain "resjunk"
+	 * columns (such as GROUP BY or ORDER BY columns), which we don't want to
+	 * keep in the whole-row result.  We can get rid of such columns by
+	 * passing the tuple through a JunkFilter --- but to make one, we have to
+	 * lay our hands on the subquery's targetlist.  Fortunately, there are not
+	 * very many cases where this can happen, and we can identify all of them
+	 * by examining our parent PlanState.  We assume this is not an issue in
+	 * standalone expressions that don't have parent plans.  (Whole-row Vars
+	 * can occur in such expressions, but they will always be referencing
+	 * table rows.)
+	 */
+
+	if (parent)
+	{
+		PlanState  *subplan = NULL;
+
+		switch (nodeTag(parent))
+		{
+			case T_SubqueryScanState:
+				subplan = ((SubqueryScanState *) parent)->subplan;
+				break;
+			case T_CteScanState:
+				subplan = ((CteScanState *) parent)->cteplanstate;
+				break;
+			default:
+				break;
+		}
+
+		if (subplan)
+		{
+			bool		junk_filter_needed = false;
+			ListCell   *tlist;
+
+			/* Detect whether subplan tlist actually has any junk columns */
+			foreach(tlist, subplan->plan->targetlist)
+			{
+				TargetEntry *tle = (TargetEntry *) lfirst(tlist);
+
+				if (tle->resjunk)
+				{
+					junk_filter_needed = true;
+					break;
+				}
+			}
+
+			if (junk_filter_needed)
+			{
+				/* If so, build the junkfilter in the query memory context */
+				scratch->d.wholerow.junkFilter =
+					ExecInitJunkFilter(subplan->plan->targetlist,
+									   ExecGetResultType(subplan)->tdhasoid,
+									   ExecInitExtraTupleSlot(parent->state));
+			}
+		}
+	}
+}
+
+static void
+ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
+				 ExprState *state, Datum *resv, bool *resnull)
+{
+	List *adjust_bailout = NIL;
+	ListCell *lc;
+	bool		isAssignment = (aref->refassgnexpr != NULL);
+	ArrayRefState *arefstate = palloc(sizeof(ArrayRefState));
+	int i;
+
+	if (list_length(aref->refupperindexpr) >= MAXDIM)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+						list_length(aref->refupperindexpr), MAXDIM)));
+
+	if (list_length(aref->reflowerindexpr) >= MAXDIM)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+						list_length(aref->refupperindexpr), MAXDIM)));
+
+	if (list_length(aref->reflowerindexpr) > 0 &&
+		list_length(aref->refupperindexpr) != list_length(aref->reflowerindexpr))
+		elog(ERROR, "upper and lower index lists are not same length");
+
+	arefstate->isassignment = isAssignment;
+	arefstate->refattrlength = get_typlen(aref->refarraytype);
+	arefstate->refelemtype = aref->refelemtype;
+	get_typlenbyvalalign(aref->refelemtype,
+						 &arefstate->refelemlength,
+						 &arefstate->refelembyval,
+						 &arefstate->refelemalign);
+
+	/* evaluate input */
+	ExecInitExprRec(aref->refexpr, parent, state,
+					resv, resnull);
+
+	/*
+	 * If refexpr yields NULL, and it's a fetch, then result is
+	 * NULL. In the assignment case, we'll create an empty input.
+	 */
+	if (!isAssignment)
+	{
+		scratch->opcode = EEO_ARRAYREF_CHECKINPUT;
+		scratch->d.arrayref.state = arefstate;
+		scratch->d.arrayref.jumpdone = -1; /* adjust later */
+		ExprEvalPushStep(state, scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+	}
+
+	/* evaluate upper subscripts */
+	i = 0;
+	foreach(lc, aref->refupperindexpr)
+	{
+		Expr  *e = (Expr *) lfirst(lc);
+
+		if (!e)
+		{
+			arefstate->upperprovided[i] = false;
+			i++;
+			continue;
+		}
+
+		arefstate->upperprovided[i] = true;
+
+		ExecInitExprRec(e, parent, state,
+						&arefstate->upper[i], &arefstate->uppernull[i]);
+
+		scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+		scratch->d.arrayref_checksubscript.state = arefstate;
+		scratch->d.arrayref_checksubscript.off = i;
+		scratch->d.arrayref_checksubscript.isupper = true;
+		scratch->d.arrayref_checksubscript.jumpdone = -1; /* adjust later */
+		ExprEvalPushStep(state, scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+		i++;
+	}
+	arefstate->numupper = i;
+
+	/* evaluate lower subscripts */
+	i = 0;
+	foreach(lc, aref->reflowerindexpr)
+	{
+		Expr  *e = (Expr *) lfirst(lc);
+
+		if (!e)
+		{
+			arefstate->lowerprovided[i] = false;
+			i++;
+			continue;
+		}
+
+		arefstate->lowerprovided[i] = true;
+
+		ExecInitExprRec(e, parent, state,
+						&arefstate->lower[i], &arefstate->lowernull[i]);
+
+		scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+		scratch->d.arrayref_checksubscript.state = arefstate;
+		scratch->d.arrayref_checksubscript.off = i;
+		scratch->d.arrayref_checksubscript.isupper = false;
+		scratch->d.arrayref_checksubscript.jumpdone = -1; /* adjust later */
+		ExprEvalPushStep(state, scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+		i++;
+	}
+	arefstate->numlower = i;
+
+	if (isAssignment)
+	{
+		Datum *save_innermost_caseval = NULL;
+		bool *save_innermost_casenull = NULL;
+
+		/*
+		 * We might have a nested-assignment situation, in which the
+		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
+		 * obtain and modify the previous value of the array element or
+		 * slice being replaced.  If so, we have to extract that value
+		 * from the array and pass it down via the econtext's caseValue.
+		 * It's safe to reuse the CASE mechanism because there cannot be a
+		 * CASE between here and where the value would be needed, and an
+		 * array assignment can't be within a CASE either.  (So saving and
+		 * restoring the caseValue is just paranoia, but let's do it
+		 * anyway.)
+		 *
+		 * Since fetching the old element might be a nontrivial expense, do it
+		 * only if the argument appears to actually need it.
+		 */
+		if (isAssignmentIndirectionExpr(aref->refassgnexpr))
+		{
+			scratch->opcode = EEO_ARRAYREF_OLD;
+			scratch->d.arrayref.state = arefstate;
+			ExprEvalPushStep(state, scratch);
+		}
+
+		save_innermost_caseval = state->innermost_caseval;
+		save_innermost_casenull = state->innermost_casenull;
+		state->innermost_caseval = &arefstate->prevvalue;
+		state->innermost_casenull = &arefstate->prevnull;
+
+		/* evaluate replacement value */
+		ExecInitExprRec(aref->refassgnexpr, parent, state,
+						&arefstate->replacevalue, &arefstate->replacenull);
+
+		state->innermost_caseval = save_innermost_caseval;
+		state->innermost_casenull = save_innermost_casenull;
+
+		scratch->opcode = EEO_ARRAYREF_ASSIGN;
+		scratch->d.arrayref.state = arefstate;
+		ExprEvalPushStep(state, scratch);
+	}
+	else
+	{
+		scratch->opcode = EEO_ARRAYREF_FETCH;
+		scratch->d.arrayref.state = arefstate;
+		ExprEvalPushStep(state, scratch);
+	}
+
+	/* adjust early bail out jump target */
+	foreach (lc, adjust_bailout)
+	{
+		ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+		if (as->opcode == EEO_ARRAYREF_CHECKSUBSCRIPT)
+		{
+			Assert(as->d.arrayref_checksubscript.jumpdone == -1);
+			as->d.arrayref_checksubscript.jumpdone = state->steps_len;
+		}
+		else
+		{
+			Assert(as->d.arrayref.jumpdone == -1);
+			as->d.arrayref.jumpdone = state->steps_len;
+		}
+	}
+}
+
+/*
+ * Helper for preparing ArrayRef expressions for evaluation: is expr a nested
+ * FieldStore or ArrayRef that might need the old element value passed down?
+ *
+ * (We could use this in FieldStore too, but in that case passing the old
+ * value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(Expr *expr)
+{
+	if (expr == NULL)
+		return false;			/* just paranoia */
+	if (IsA(expr, FieldStore))
+	{
+		FieldStore *fstore = (FieldStore *) expr;
+
+		if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+			return true;
+	}
+	else if (IsA(expr, ArrayRef))
+	{
+		ArrayRef   *arrayRef = (ArrayRef *) expr;
+
+		if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
+			return true;
+	}
+	return false;
+}
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 5242dee006..108060ac0f 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState  *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
@@ -551,23 +549,21 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState  *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
diff --git a/src/backend/executor/execInterpExpr.c b/src/backend/executor/execInterpExpr.c
new file mode 100644
index 0000000000..9387ef02a0
--- /dev/null
+++ b/src/backend/executor/execInterpExpr.c
@@ -0,0 +1,2840 @@
+/*-------------------------------------------------------------------------
+ *
+ * execInterpExpr.c
+ *	  Opcode based expression evaluation.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/execInterprExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "executor/execExpr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/lsyscache.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+
+/*
+ * Use computed goto based opcode dispatch when computed gotos are
+ * available. But allow to disable locally in this file for development.
+ */
+#ifdef HAVE__COMPUTED_GOTO
+#	define EEO_USE_COMPUTED_GOTO
+#endif /* HAVE__COMPUTED_GOTO */
+
+/*
+ * Macros for opcode dispatch.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+static void **dispatch_table = NULL;
+#	define EEO_OPCODE(opc) dispatch_table[opc]
+#	define EEO_SWITCH(d)
+#	define EEO_DISPATCH_DIRECT(op) goto *((void *) op->opcode)
+#	define EEO_CASE(name) CASE_##name
+#else
+#	define EEO_OPCODE(opc) opc
+#	define EEO_SWITCH(d) switch ((ExprEvalOp) d)
+#	define EEO_DISPATCH_DIRECT(op) goto starteval
+#	define EEO_CASE(name) case name
+#endif /* EEO_USE_COMPUTED_GOTO */
+
+#define EEO_DISPATCH(op) \
+	do \
+	{ \
+		op++; \
+		EEO_DISPATCH_DIRECT(op); \
+	} \
+	while (0)
+
+static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
+static void ExecPrepareInterp(void);
+
+/* support functions */
+static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull);
+static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
+									TupleDesc *cache_field, ExprContext *econtext);
+static void ShutdownTupleDescRef(Datum arg);
+
+/* fast-path evaluation functions */
+static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull);
+
+
+/*
+ * Prepare ExprState for interpreted execution.
+ */
+void
+ExecInstantiateInterpretedExpr(ExprState *state)
+{
+	ExecPrepareInterp();
+
+	Assert(state->steps_len >= 1);
+	Assert(ExecEvalStepOp(state, &state->steps[state->steps_len - 1]) == EEO_DONE);
+
+	/*
+	 * Fast-path for very simple expressions. "starting up" the full
+	 * interpreter is a measurable overhead for these.  Plain Vars and Const
+	 * seem to be the only ones where the intrinsic cost is small enough that
+	 * the overhead of ExecInterpExpr matters.  For more complex expressions
+	 * it's cheaper to use ExecInterpExpr anyways.
+	 */
+	if (state->steps_len == 3)
+	{
+		ExprEvalOp step0 = ExecEvalStepOp(state, &state->steps[0]);
+		ExprEvalOp step1 = ExecEvalStepOp(state, &state->steps[1]);
+
+		if (step0 == EEO_INNER_FETCHSOME && step1 == EEO_INNER_VAR)
+		{
+			state->cb = ExecJustInnerVar;
+			return;
+		}
+		else if (step0 == EEO_OUTER_FETCHSOME && step1 == EEO_OUTER_VAR)
+		{
+			state->cb = ExecJustOuterVar;
+			return;
+		}
+		else if (step0 == EEO_SCAN_FETCHSOME && step1 == EEO_SCAN_VAR)
+		{
+			state->cb = ExecJustScanVar;
+			return;
+		}
+	}
+	else if (state->steps_len == 2 &&
+			 ExecEvalStepOp(state, &state->steps[0]) == EEO_CONST)
+	{
+		state->cb = ExecJustConst;
+		return;
+	}
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+	{
+		int off;
+
+		for (off = 0; off < state->steps_len; off++)
+		{
+			ExprEvalStep *op = &state->steps[off];
+
+			op->opcode = (size_t) EEO_OPCODE(op->opcode);
+		}
+
+		state->flags |= EEO_FLAG_JUMP_THREADED;
+	}
+#endif /* defined(EEO_USE_COMPUTED_GOTO) */
+
+	state->cb = ExecInterpExpr;
+}
+
+
+static Datum
+ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep   *op;
+	const TupleTableSlot *resultslot;
+	TupleTableSlot *innerslot;
+	TupleTableSlot *outerslot;
+	TupleTableSlot *scanslot;
+
+	/*
+	 * This array has to be in the same order as ExprEvalOp.
+	 */
+#if defined(EEO_USE_COMPUTED_GOTO)
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_DONE,
+		&&CASE_EEO_INNER_FETCHSOME,
+		&&CASE_EEO_OUTER_FETCHSOME,
+		&&CASE_EEO_SCAN_FETCHSOME,
+		&&CASE_EEO_INNER_VAR,
+		&&CASE_EEO_OUTER_VAR,
+		&&CASE_EEO_SCAN_VAR,
+		&&CASE_EEO_ASSIGN_INNER_VAR,
+		&&CASE_EEO_ASSIGN_OUTER_VAR,
+		&&CASE_EEO_ASSIGN_SCAN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_ASSIGN_TMP_UNEXPAND,
+		&&CASE_EEO_INNER_SYSVAR,
+		&&CASE_EEO_OUTER_SYSVAR,
+		&&CASE_EEO_SCAN_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_QUAL,
+		&&CASE_EEO_NULLTEST_ISNULL,
+		&&CASE_EEO_NULLTEST_ISNOTNULL,
+		&&CASE_EEO_NULLTEST_ROWISNULL,
+		&&CASE_EEO_NULLTEST_ROWISNOTNULL,
+		&&CASE_EEO_PARAM_EXEC,
+		&&CASE_EEO_PARAM_EXTERN,
+		&&CASE_EEO_CASE_WHEN_STEP,
+		&&CASE_EEO_CASE_THEN_STEP,
+		&&CASE_EEO_CASE_TESTVAL,
+		&&CASE_EEO_CASE_TESTVAL_UNEXPAND,
+		&&CASE_EEO_COALESCE,
+		&&CASE_EEO_BOOLTEST_IS_TRUE,
+		&&CASE_EEO_BOOLTEST_IS_NOT_TRUE,
+		&&CASE_EEO_BOOLTEST_IS_FALSE,
+		&&CASE_EEO_BOOLTEST_IS_NOT_FALSE,
+		&&CASE_EEO_BOOLTEST_IS_UNKNOWN,
+		&&CASE_EEO_BOOLTEST_IS_NOT_UNKNOWN,
+		&&CASE_EEO_WHOLEROW,
+		&&CASE_EEO_IOCOERCE,
+		&&CASE_EEO_DISTINCT,
+		&&CASE_EEO_NULLIF,
+		&&CASE_EEO_SQLVALUEFUNCTION,
+		&&CASE_EEO_CURRENTOFEXPR,
+		&&CASE_EEO_ARRAYEXPR,
+		&&CASE_EEO_ARRAYCOERCE,
+		&&CASE_EEO_ROW,
+		&&CASE_EEO_ROWCOMPARE_STEP,
+		&&CASE_EEO_ROWCOMPARE_FINAL,
+		&&CASE_EEO_MINMAX,
+		&&CASE_EEO_FIELDSELECT,
+		&&CASE_EEO_FIELDSTORE_DEFORM,
+		&&CASE_EEO_FIELDSTORE_FORM,
+		&&CASE_EEO_ARRAYREF_CHECKINPUT,
+		&&CASE_EEO_ARRAYREF_CHECKSUBSCRIPT,
+		&&CASE_EEO_ARRAYREF_OLD,
+		&&CASE_EEO_ARRAYREF_ASSIGN,
+		&&CASE_EEO_ARRAYREF_FETCH,
+		&&CASE_EEO_CONVERT_ROWTYPE,
+		&&CASE_EEO_SCALARARRAYOP,
+		&&CASE_EEO_DOMAIN_TESTVAL,
+		&&CASE_EEO_DOMAIN_TESTVAL_UNEXPAND,
+		&&CASE_EEO_DOMAIN_NOTNULL,
+		&&CASE_EEO_DOMAIN_CHECK,
+		&&CASE_EEO_XMLEXPR,
+		&&CASE_EEO_AGGREF,
+		&&CASE_EEO_GROUPING_FUNC,
+		&&CASE_EEO_WINDOW_FUNC,
+		&&CASE_EEO_SUBPLAN,
+		&&CASE_EEO_ALTERNATIVE_SUBPLAN,
+		&&CASE_EEO_LAST
+	};
+
+	StaticAssertStmt(EEO_LAST + 1 == lengthof(dispatch_table),
+					 "dispatch_table out of whack with ExprEvalOp");
+#endif
+
+#ifdef EEO_USE_COMPUTED_GOTO
+	if (unlikely(state == NULL))
+	{
+		return PointerGetDatum(dispatch_table);
+	}
+#endif
+
+	/* setup state */
+	op = state->steps;
+	resultslot = state->resultslot;
+	innerslot = econtext->ecxt_innertuple;
+	outerslot = econtext->ecxt_outertuple;
+	scanslot = econtext->ecxt_scantuple;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+	EEO_DISPATCH_DIRECT(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_DONE):
+			goto out;
+
+			/* */
+		EEO_CASE(EEO_INNER_FETCHSOME):
+			/* XXX: worthwhile to check tts_nvalid inline first? */
+			slot_getsomeattrs(innerslot, op->d.fetch.last_var);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_OUTER_FETCHSOME):
+			slot_getsomeattrs(outerslot, op->d.fetch.last_var);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_SCAN_FETCHSOME):
+			slot_getsomeattrs(scanslot, op->d.fetch.last_var);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_INNER_VAR):
+			{
+				int attnum = op->d.var.attnum;
+
+				/*
+				 * Can't assert tts_nvalid, as wholerow var evaluation or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = innerslot->tts_isnull[attnum];
+				*op->resvalue = innerslot->tts_values[attnum];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_OUTER_VAR):
+			{
+				int attnum = op->d.var.attnum;
+
+				/* See EEO_INNER_VAR comments */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = outerslot->tts_isnull[attnum];
+				*op->resvalue = outerslot->tts_values[attnum];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SCAN_VAR):
+			{
+				int attnum = op->d.var.attnum;
+
+				/* See EEO_INNER_VAR comments */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = scanslot->tts_isnull[attnum];
+				*op->resvalue = scanslot->tts_values[attnum];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_ASSIGN_INNER_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_OUTER_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_SCAN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_TMP_UNEXPAND):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				if (!resultslot->tts_isnull[resultnum] &&
+					resultslot->tts_tupleDescriptor->attrs[resultnum]->attlen == -1)
+					resultslot->tts_values[resultnum] = MakeExpandedObjectReadOnlyInternal(resultslot->tts_values[resultnum]);
+
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_INNER_SYSVAR):
+			{
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
+												innerslot->tts_tupleDescriptor,
+												op->resnull);
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_OUTER_SYSVAR):
+			{
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+												scanslot->tts_tupleDescriptor,
+												op->resnull);
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SCAN_SYSVAR):
+			{
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+												scanslot->tts_tupleDescriptor,
+												op->resnull);
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+			}
+strictfail:
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+			/* fallthrough */
+
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			/* XXX: could skip this step till last AND branch */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+			/* fallthrough */
+
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			/* XXX: could skip this step till last OR branch */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_QUAL):
+			/* special case for ExecQual() */
+
+			if (*op->resnull ||
+				!DatumGetBool(*op->resvalue))
+			{
+				/* bail out early */
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				op = &state->steps[op->d.qualexpr.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ISNULL):
+			*op->resvalue = BoolGetDatum(*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ISNOTNULL):
+			*op->resvalue = BoolGetDatum(!*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ROWISNULL):
+			/* out of line implementation: too large */
+			ExecEvalRowNull(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ROWISNOTNULL):
+			/* out of line implementation: too large */
+			ExecEvalRowNotNull(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_PARAM_EXEC):
+			ExecEvalParamExec(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_PARAM_EXTERN):
+			/* out of line implementation: too large */
+			ExecEvalParamExtern(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CASE_WHEN_STEP):
+			if (!*op->d.casewhen.isnull
+				&& DatumGetBool(*op->d.casewhen.value))
+			{
+				EEO_DISPATCH(op);
+			}
+			else
+			{
+				op = &state->steps[op->d.casewhen.jumpfalse];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+		EEO_CASE(EEO_CASE_THEN_STEP):
+			/* results already placed in correct place during preceding steps */
+			op = &state->steps[op->d.casethen.jumpdone];
+			EEO_DISPATCH_DIRECT(op);
+
+		EEO_CASE(EEO_CASE_TESTVAL):
+			/*
+			 * Normally upper parts in the expression tree have setup the
+			 * values to be returned here, but some parts of the system
+			 * currently misuse {caseValue,domainValue}_{datum,isNull} to set
+			 * run-time data.  So if no values have been set-up, use
+			 * ExprContext's.  This isn't pretty, but also not *that* ugly,
+			 * and this is unlikely to be performance sensitive enoiugh to
+			 * worry about a branch.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->caseValue_datum;
+				*op->resnull = econtext->caseValue_isNull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CASE_TESTVAL_UNEXPAND):
+			/*
+			 * See EEO_CASE_TESTVAL comment.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->caseValue_datum;
+				*op->resnull = econtext->caseValue_isNull;
+			}
+
+			/* Since caseValue_datum may be read multiple times, force to R/O */
+			if (!*op->resnull)
+			{
+				*op->resvalue = MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_TESTVAL):
+			/*
+			 * See EEO_CASE_TESTVAL comment.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->domainValue_datum;
+				*op->resnull = econtext->domainValue_isNull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_TESTVAL_UNEXPAND):
+			/*
+			 * See EEO_CASE_TESTVAL comment.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->domainValue_datum;
+				*op->resnull = econtext->domainValue_isNull;
+			}
+
+			/* Since domainValue_datum may be read multiple times, force to R/O */
+			if (!*op->resnull)
+			{
+				*op->resvalue = MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_COALESCE):
+			if (!*op->resnull)
+			{
+				op = &state->steps[op->d.coalesce.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_TRUE):
+		EEO_CASE(EEO_BOOLTEST_IS_NOT_FALSE):
+			if (*op->resnull)
+				*op->resvalue = false;
+			else
+				*op->resvalue = *op->resvalue;
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_FALSE):
+		EEO_CASE(EEO_BOOLTEST_IS_NOT_TRUE):
+			if (*op->resnull)
+				*op->resvalue = false;
+			else
+				*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_UNKNOWN):
+			*op->resvalue = BoolGetDatum(*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_NOT_UNKNOWN):
+			*op->resvalue = BoolGetDatum(!*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_WHOLEROW):
+			/* too complex for an inline implementation */
+			ExecEvalWholeRowVar(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_IOCOERCE):
+			{
+				char *str;
+
+				/* call output function (similar to OutputFunctionCall) */
+				if (*op->resnull)
+				{
+					/* output functions are not called on nulls */
+					str = NULL;
+				}
+				else
+				{
+					FunctionCallInfo fcinfo_out;
+
+					fcinfo_out = op->d.iocoerce.fcinfo_data_out;
+					fcinfo_out->arg[0] = *op->resvalue;
+					fcinfo_out->argnull[0] = false;
+
+					str = DatumGetPointer(FunctionCallInvoke(fcinfo_out));
+					Assert(!fcinfo_out->isnull);
+				}
+
+				/* call input function (similar to InputFunctionCall) */
+				if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
+				{
+					FunctionCallInfo fcinfo_in;
+
+					fcinfo_in = op->d.iocoerce.fcinfo_data_in;
+					fcinfo_in->arg[0] = PointerGetDatum(str);
+					fcinfo_in->argnull[0] = BoolGetDatum(*op->resnull);
+					fcinfo_in->arg[1] = op->d.iocoerce.intypioparam;
+					fcinfo_in->argnull[1] = false;
+					fcinfo_in->arg[2] = -1;
+					fcinfo_in->argnull[2] = false;
+
+					fcinfo_in->isnull = false;
+					*op->resvalue = FunctionCallInvoke(fcinfo_in);
+
+					/* Should get null result if and only if str is NULL */
+					if (str == NULL)
+					{
+						Assert(*op->resnull);
+						Assert(fcinfo_in->isnull);
+					}
+					else
+					{
+						Assert(!*op->resnull);
+						Assert(!fcinfo_in->isnull);
+					}
+
+				}
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_DISTINCT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				/* check function arguments for NULLness */
+				if (fcinfo->argnull[0] && fcinfo->argnull[1])
+				{
+					/* Both NULL? Then is not distinct... */
+					*op->resnull = false;
+					*op->resvalue = BoolGetDatum(false);
+				}
+				else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+				{
+					/* Only one is NULL? Then is distinct... */
+					*op->resnull = false;
+					*op->resvalue = BoolGetDatum(true);
+				}
+				else
+				{
+					fcinfo->isnull = false;
+					*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+					/* Must invert result of "=" */
+					*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+					*op->resnull = fcinfo->isnull;
+				}
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_NULLIF):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				/* if either argument is NULL they can't be equal */
+				if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+				{
+					bool result;
+
+					fcinfo->isnull = false;
+					result = (op->d.func.fn_addr)(fcinfo);
+
+					/* if the arguments are equal return null */
+					if (!fcinfo->isnull && DatumGetBool(result))
+					{
+						*op->resnull = true;
+						*op->resvalue = true;
+						EEO_DISPATCH(op);
+					}
+				}
+				*op->resnull = fcinfo->argnull[0];
+				*op->resvalue = fcinfo->arg[0];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SQLVALUEFUNCTION):
+			/*
+			 * Doesn't seem worthwhile to have an inline implementation
+			 * efficency-wise.
+			 */
+			ExecEvalSQLValueFunction(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CURRENTOFEXPR):
+			/* error invocation uses space, and shouldn't ever occur */
+			ExecEvalCurrentOfExpr(state, op);
+
+		EEO_CASE(EEO_ARRAYEXPR):
+			/* too complex for an inline implementation */
+			ExecEvalArrayExpr(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ARRAYCOERCE):
+			/* too complex for an inline implementation */
+			ExecEvalArrayCoerce(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ROW):
+			/* too complex for an inline implementation */
+			ExecEvalRow(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ROWCOMPARE_STEP):
+			{
+				FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
+
+				/* force NULL result */
+				if (op->d.rowcompare_step.finfo->fn_strict &&
+					(fcinfo->argnull[0] || fcinfo->argnull[1]))
+				{
+					*op->resnull = true;
+					op = &state->steps[op->d.rowcompare_step.jumpnull];
+					EEO_DISPATCH_DIRECT(op);
+				}
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.rowcompare_step.fn_addr)(fcinfo);
+
+				/* force NULL result */
+				if (fcinfo->isnull)
+				{
+					*op->resnull = true;
+					op = &state->steps[op->d.rowcompare_step.jumpnull];
+					EEO_DISPATCH_DIRECT(op);
+				}
+
+				/* no need to compare remaining columns */
+				if (DatumGetInt32(*op->resvalue) != 0)
+				{
+					op = &state->steps[op->d.rowcompare_step.jumpdone];
+					EEO_DISPATCH_DIRECT(op);
+				}
+
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_ROWCOMPARE_FINAL):
+			{
+				int32		cmpresult = DatumGetInt32(*op->resvalue);
+				RowCompareType rctype = op->d.rowcompare_final.rctype;
+
+				*op->resnull = false;
+				switch (rctype)
+				{
+					/* EQ and NE cases aren't allowed here */
+				case ROWCOMPARE_LT:
+					*op->resvalue = BoolGetDatum(cmpresult < 0);
+					break;
+				case ROWCOMPARE_LE:
+					*op->resvalue = BoolGetDatum(cmpresult <= 0);
+					break;
+				case ROWCOMPARE_GE:
+					*op->resvalue = BoolGetDatum(cmpresult >= 0);
+					break;
+				case ROWCOMPARE_GT:
+					*op->resvalue = BoolGetDatum(cmpresult > 0);
+					break;
+				default:
+					Assert(false);
+					break;
+				}
+
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_MINMAX):
+			/* too complex for an inline implementation */
+			ExecEvalMinMax(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FIELDSELECT):
+			/* too complex for an inline implementation */
+			ExecEvalFieldSelect(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FIELDSTORE_DEFORM):
+			/* too complex for an inline implementation */
+			ExecEvalFieldStoreDeForm(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FIELDSTORE_FORM):
+			/* too complex for an inline implementation */
+			ExecEvalFieldStoreForm(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ARRAYREF_CHECKINPUT):
+			Assert(!op->d.arrayref.state->isassignment);
+			/*
+			 * If refexpr yields NULL, and it's a fetch, then result is NULL.
+			 */
+			if (*op->resnull &&
+				!op->d.arrayref.state->isassignment)
+			{
+				op = &state->steps[op->d.arrayref.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			EEO_DISPATCH(op);
+
+		/*
+		 * Check whether a subscript is NULL and handle that.
+		 */
+		EEO_CASE(EEO_ARRAYREF_CHECKSUBSCRIPT):
+			if (ExecEvalArrayRefCheckSubscript(state, op))
+			{
+				op++;
+			}
+			else
+			{
+				op = &state->steps[op->d.arrayref_checksubscript.jumpdone];
+			}
+			EEO_DISPATCH_DIRECT(op);
+
+		/*
+		 * Fetch the old value in an arrayref, if referenced (via a
+		 * CaseTestExpr) inside the assignment expression.
+		 */
+		EEO_CASE(EEO_ARRAYREF_OLD):
+			ExecEvalArrayRefOld(state, op);
+			EEO_DISPATCH(op);
+
+		/*
+		 * Perform ArrayRef assignment
+		 */
+		EEO_CASE(EEO_ARRAYREF_ASSIGN):
+			ExecEvalArrayRefAssign(state, op);
+			EEO_DISPATCH(op);
+
+		/*
+		 * Fetch subset of an array.
+		 */
+		EEO_CASE(EEO_ARRAYREF_FETCH):
+			ExecEvalArrayRefFetch(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CONVERT_ROWTYPE):
+			ExecEvalConvertRowtype(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_SCALARARRAYOP):
+			/* too complex for an inline implementation */
+			ExecEvalScalarArrayOp(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_NOTNULL):
+			/* too complex for an inline implementation */
+			ExecEvalConstraintNotNull(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_CHECK):
+			/* too complex for an inline implementation */
+			ExecEvalConstraintCheck(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_XMLEXPR):
+			/* too complex for an inline implementation */
+			ExecEvalXmlExpr(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_AGGREF):
+			{
+				AggrefExprState *aggref = op->d.aggref.astate;
+				Assert(econtext->ecxt_aggvalues != NULL);
+
+				*op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
+				*op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_GROUPING_FUNC):
+			/* too complex/uncommon for an inline implementation */
+			ExecEvalGroupingFunc(state, op);
+
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_WINDOW_FUNC):
+			{
+				WindowFuncExprState *wfunc = op->d.window_func.wfstate;
+				Assert(econtext->ecxt_aggvalues != NULL);
+
+				*op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
+				*op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SUBPLAN):
+			/* too complex for an inline implementation */
+			ExecEvalSubPlan(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ALTERNATIVE_SUBPLAN):
+			/* too complex for an inline implementation */
+			ExecEvalAlternativeSubPlan(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_LAST):
+		{
+			Assert(false);
+		}
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+static Datum
+ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[1];
+	int attnum = op->d.var.attnum + 1;
+	TupleTableSlot *slot = econtext->ecxt_outertuple;
+	return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[1];
+	int attnum = op->d.var.attnum + 1;
+	TupleTableSlot *slot = econtext->ecxt_innertuple;
+	return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[1];
+	int attnum = op->d.var.attnum + 1;
+	TupleTableSlot *slot = econtext->ecxt_scantuple;
+	return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[0];
+	*isnull = op->d.constval.isnull;
+	return op->d.constval.value;
+}
+
+static void
+ExecPrepareInterp(void)
+{
+	static bool prepared = false;
+	if (prepared)
+		return;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+	if (dispatch_table == NULL)
+		dispatch_table = (void **) DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL));
+#endif
+
+	prepared = true;
+}
+
+ExprEvalOp
+ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
+{
+#if defined(EEO_USE_COMPUTED_GOTO)
+	if (state->flags & EEO_FLAG_JUMP_THREADED)
+	{
+		int		i;
+		for (i = 0; i < EEO_LAST; i++)
+		{
+			if ((void *) op->opcode == dispatch_table[i])
+			{
+				return (ExprEvalOp) i;
+			}
+		}
+		elog(ERROR, "unknown opcode");
+	}
+#endif
+	return (ExprEvalOp) op->opcode;
+}
+
+/*
+ * Computes the value for a PARAM_EXEC parameter.
+ */
+void
+ExecEvalParamExec(ExprState *state, ExprEvalStep *op,  ExprContext *econtext)
+{
+	ParamExecData *prm;
+
+	prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]);
+	if (unlikely(prm->execPlan != NULL))
+	{
+		/*
+		 * XXX: Worthwhile to extract this into a separate opcode?
+		 */
+		ExecSetParamPlan(prm->execPlan, econtext);
+		/* ExecSetParamPlan should have processed this param... */
+		Assert(prm->execPlan == NULL);
+	}
+	*op->resvalue = prm->value;
+	*op->resnull = prm->isnull;
+}
+
+/*
+ * Computes the value for a PARAM_EXTERN parameter.
+ */
+void
+ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,  ExprContext *econtext)
+{
+
+	ParamListInfo paramInfo = econtext->ecxt_param_list_info;
+	int			paramId = op->d.param.paramid;
+
+	if (likely(paramInfo &&
+			   paramId > 0 && paramId <= paramInfo->numParams))
+	{
+		ParamExternData *prm = &paramInfo->params[paramId - 1];
+
+		/* give hook a chance in case parameter is dynamic */
+		/* XXX: separate opcode */
+		if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
+			(*paramInfo->paramFetch) (paramInfo, paramId);
+
+		if (likely(OidIsValid(prm->ptype)))
+		{
+			/* safety check in case hook did something unexpected */
+			/* XXX: assert instead? */
+			if (unlikely(prm->ptype != op->d.param.paramtype))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
+								paramId,
+								format_type_be(prm->ptype),
+								format_type_be(op->d.param.paramtype))));
+			*op->resvalue = prm->value;
+			*op->resnull = prm->isnull;
+			return;
+		}
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_UNDEFINED_OBJECT),
+			 errmsg("no value found for parameter %d", paramId)));
+}
+
+/*
+ * Evaluate SQLValueFunction expressions.
+ */
+void
+ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
+{
+	FunctionCallInfoData fcinfo;
+	SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
+
+	*op->resnull = false;
+
+	/*
+	 * Note: current_schema() can return NULL.  current_user() etc currently
+	 * cannot, but might as well code those cases the same way for safety.
+	 */
+	switch (svf->op)
+	{
+		case SVFOP_CURRENT_DATE:
+			*op->resvalue = DateADTGetDatum(GetSQLCurrentDate());
+			break;
+		case SVFOP_CURRENT_TIME:
+		case SVFOP_CURRENT_TIME_N:
+			*op->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
+			break;
+		case SVFOP_CURRENT_TIMESTAMP:
+		case SVFOP_CURRENT_TIMESTAMP_N:
+			*op->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
+			break;
+		case SVFOP_LOCALTIME:
+		case SVFOP_LOCALTIME_N:
+			*op->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
+			break;
+		case SVFOP_LOCALTIMESTAMP:
+		case SVFOP_LOCALTIMESTAMP_N:
+			*op->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
+			break;
+		case SVFOP_CURRENT_ROLE:
+		case SVFOP_CURRENT_USER:
+		case SVFOP_USER:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_user(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+		case SVFOP_SESSION_USER:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = session_user(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+		case SVFOP_CURRENT_CATALOG:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_database(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+		case SVFOP_CURRENT_SCHEMA:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_schema(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+	}
+}
+
+void
+ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
+{
+	/*
+	 * The planner should convert CURRENT OF into a TidScan qualification, or
+	 * some other special handling in a ForeignScan node.  So we have to be
+	 * able to do ExecInitExpr on a CurrentOfExpr, but we shouldn't ever
+	 * actually execute it.  If we get here, we suppose we must be dealing
+	 * with CURRENT OF on a foreign table whose FDW doesn't handle it, and
+	 * complain accordingly.
+	 */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("WHERE CURRENT OF is not supported for this table type")));
+}
+
+/*
+ * Evaluate NullTest / IS NULL for rows.
+ */
+void
+ExecEvalRowNull(ExprState *state, ExprEvalStep *op)
+{
+	ExecEvalRowNullInt(state, op, true);
+}
+
+/*
+ * Evaluate NullTest / IS NOT NULL for rows.
+ */
+void
+ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op)
+{
+	ExecEvalRowNullInt(state, op, false);
+}
+
+static void
+ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull)
+{
+	Datum		value = *op->resvalue;
+	bool		isnull = *op->resnull;
+	HeapTupleHeader tuple;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupDesc;
+	HeapTupleData tmptup;
+	int			att;
+
+	*op->resnull = false;
+
+	if (isnull && checkisnull)
+	{
+		*op->resvalue = BoolGetDatum(true);
+		return;
+	}
+	else if (isnull)
+	{
+		*op->resvalue = BoolGetDatum(false);
+		return;
+	}
+
+	/*
+	 * The SQL standard defines IS [NOT] NULL for a non-null rowtype
+	 * argument as:
+	 *
+	 * "R IS NULL" is true if every field is the null value.
+	 *
+	 * "R IS NOT NULL" is true if no field is the null value.
+	 *
+	 * This definition is (apparently intentionally) not recursive; so our
+	 * tests on the fields are primitive attisnull tests, not recursive
+	 * checks to see if they are all-nulls or no-nulls rowtypes.
+	 *
+	 * The standard does not consider the possibility of zero-field rows,
+	 * but here we consider them to vacuously satisfy both predicates.
+	 */
+
+	tuple = DatumGetHeapTupleHeader(value);
+
+	tupType = HeapTupleHeaderGetTypeId(tuple);
+	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+	/* Lookup tupdesc if first time through or if type changes */
+	tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+	/*
+	 * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
+	 */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	tmptup.t_data = tuple;
+
+	for (att = 1; att <= tupDesc->natts; att++)
+	{
+		/* ignore dropped columns */
+		if (tupDesc->attrs[att - 1]->attisdropped)
+			continue;
+		if (heap_attisnull(&tmptup, att))
+		{
+			/* null field disproves IS NOT NULL */
+			if (!checkisnull)
+			{
+				ReleaseTupleDesc(tupDesc);
+				*op->resvalue = BoolGetDatum(false);
+				return;
+			}
+		}
+		else
+		{
+			/* non-null field disproves IS NULL */
+			if (checkisnull)
+			{
+				ReleaseTupleDesc(tupDesc);
+				*op->resvalue = BoolGetDatum(false);
+				return;
+			}
+		}
+	}
+
+	ReleaseTupleDesc(tupDesc);
+	*op->resvalue = BoolGetDatum(true);
+}
+
+
+/*
+ * Evaluate ARRAY[] expressions.
+ */
+void
+ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
+{
+	ArrayType  *result;
+	ArrayExpr * arrayExpr = op->d.arrayexpr.arrayexpr;
+	Oid			element_type = arrayExpr->element_typeid;
+	int			ndims = 0;
+	int			dims[MAXDIM];
+	int			lbs[MAXDIM];
+	int			nelems = op->d.arrayexpr.nelems;
+
+	/* Set default values for result flags: non-null */
+	*op->resnull = false;
+
+	if (!arrayExpr->multidims)
+	{
+		/* Elements are presumably of scalar type */
+		Datum	   *dvalues = op->d.arrayexpr.elemvalues;
+		bool	   *dnulls = op->d.arrayexpr.elemnulls;
+
+		ndims = 1;
+
+		/* Shouldn't happen here, but if length is 0, return empty array */
+		if (nelems == 0)
+		{
+			*op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+			return;
+		}
+
+		/* setup for 1-D array of the given length */
+		dims[0] = nelems;
+		lbs[0] = 1;
+
+		result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
+									element_type,
+									op->d.arrayexpr.elemlength,
+									op->d.arrayexpr.elembyval,
+									op->d.arrayexpr.elemalign);
+	}
+	else
+	{
+		/* Must be nested array expressions */
+		int			nbytes = 0;
+		int			nitems = 0;
+		int			outer_nelems = 0;
+		int			elem_ndims = 0;
+		int		   *elem_dims = NULL;
+		int		   *elem_lbs = NULL;
+		bool		firstone = true;
+		bool		havenulls = false;
+		bool		haveempty = false;
+		char	  **subdata;
+		bits8	  **subbitmaps;
+		int		   *subbytes;
+		int		   *subnitems;
+		int32		dataoffset;
+		char	   *dat;
+		int			iitem;
+		int			elemoff;
+		int			i;
+
+		subdata = (char **) palloc(nelems * sizeof(char *));
+		subbitmaps = (bits8 **) palloc(nelems * sizeof(bits8 *));
+		subbytes = (int *) palloc(nelems * sizeof(int));
+		subnitems = (int *) palloc(nelems * sizeof(int));
+
+		/* loop through and get data area from each element */
+		for (elemoff = 0; elemoff < nelems; elemoff++)
+		{
+			bool		eisnull;
+			Datum		arraydatum;
+			ArrayType  *array;
+			int			this_ndims;
+
+			arraydatum = op->d.arrayexpr.elemvalues[elemoff];
+			eisnull = op->d.arrayexpr.elemnulls[elemoff];
+
+			/* temporarily ignore null subarrays */
+			if (eisnull)
+			{
+				haveempty = true;
+				continue;
+			}
+
+			array = DatumGetArrayTypeP(arraydatum);
+
+			/* run-time double-check on element type */
+			if (element_type != ARR_ELEMTYPE(array))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("cannot merge incompatible arrays"),
+						 errdetail("Array with element type %s cannot be "
+						 "included in ARRAY construct with element type %s.",
+								   format_type_be(ARR_ELEMTYPE(array)),
+								   format_type_be(element_type))));
+
+			this_ndims = ARR_NDIM(array);
+			/* temporarily ignore zero-dimensional subarrays */
+			if (this_ndims <= 0)
+			{
+				haveempty = true;
+				continue;
+			}
+
+			if (firstone)
+			{
+				/* Get sub-array details from first member */
+				elem_ndims = this_ndims;
+				ndims = elem_ndims + 1;
+				if (ndims <= 0 || ndims > MAXDIM)
+					ereport(ERROR,
+							(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+						  errmsg("number of array dimensions (%d) exceeds " \
+								 "the maximum allowed (%d)", ndims, MAXDIM)));
+
+				elem_dims = (int *) palloc(elem_ndims * sizeof(int));
+				memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
+				elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
+				memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
+
+				firstone = false;
+			}
+			else
+			{
+				/* Check other sub-arrays are compatible */
+				if (elem_ndims != this_ndims ||
+					memcmp(elem_dims, ARR_DIMS(array),
+						   elem_ndims * sizeof(int)) != 0 ||
+					memcmp(elem_lbs, ARR_LBOUND(array),
+						   elem_ndims * sizeof(int)) != 0)
+					ereport(ERROR,
+							(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+							 errmsg("multidimensional arrays must have array "
+									"expressions with matching dimensions")));
+			}
+
+			subdata[outer_nelems] = ARR_DATA_PTR(array);
+			subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
+			subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
+			nbytes += subbytes[outer_nelems];
+			subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
+													 ARR_DIMS(array));
+			nitems += subnitems[outer_nelems];
+			havenulls |= ARR_HASNULL(array);
+			outer_nelems++;
+		}
+
+		/*
+		 * If all items were null or empty arrays, return an empty array;
+		 * otherwise, if some were and some weren't, raise error.  (Note: we
+		 * must special-case this somehow to avoid trying to generate a 1-D
+		 * array formed from empty arrays.  It's not ideal...)
+		 */
+		if (haveempty)
+		{
+			if (ndims == 0)		/* didn't find any nonempty array */
+			{
+				*op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+				return;
+			}
+			ereport(ERROR,
+					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+					 errmsg("multidimensional arrays must have array "
+							"expressions with matching dimensions")));
+		}
+
+		/* setup for multi-D array */
+		dims[0] = outer_nelems;
+		lbs[0] = 1;
+		for (i = 1; i < ndims; i++)
+		{
+			dims[i] = elem_dims[i - 1];
+			lbs[i] = elem_lbs[i - 1];
+		}
+
+		if (havenulls)
+		{
+			dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+			nbytes += dataoffset;
+		}
+		else
+		{
+			dataoffset = 0;		/* marker for no null bitmap */
+			nbytes += ARR_OVERHEAD_NONULLS(ndims);
+		}
+
+		result = (ArrayType *) palloc(nbytes);
+		SET_VARSIZE(result, nbytes);
+		result->ndim = ndims;
+		result->dataoffset = dataoffset;
+		result->elemtype = element_type;
+		memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+		memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+
+		dat = ARR_DATA_PTR(result);
+		iitem = 0;
+		for (i = 0; i < outer_nelems; i++)
+		{
+			memcpy(dat, subdata[i], subbytes[i]);
+			dat += subbytes[i];
+			if (havenulls)
+				array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
+								  subbitmaps[i], 0,
+								  subnitems[i]);
+			iitem += subnitems[i];
+		}
+	}
+
+	*op->resvalue = PointerGetDatum(result);
+}
+
+
+
+/*
+ * Evaluate an ArrayCoerceExpr expression.
+ */
+void
+ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
+{
+	ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
+	Datum		result;
+	FunctionCallInfoData locfcinfo;
+
+	/* input is in *step->resvalue/resnull */
+	result = *op->resvalue;
+	if (*op->resnull)
+	{
+		return;			/* nothing to do */
+	}
+
+	/*
+	 * If it's binary-compatible, modify the element type in the array header,
+	 * but otherwise leave the array as we received it.
+	 */
+	if (!OidIsValid(acoerce->elemfuncid))
+	{
+		/* Detoast input array if necessary, and copy in any case */
+		ArrayType  *array = DatumGetArrayTypePCopy(result);
+
+		ARR_ELEMTYPE(array) = op->d.arraycoerce.resultelemtype;
+		*op->resvalue = PointerGetDatum(array);
+		return;
+	}
+
+	/* Initialize function cache if first time through */
+	if (op->d.arraycoerce.elemfunc->fn_oid == InvalidOid)
+	{
+		AclResult	aclresult;
+
+		/* Check permission to call function */
+		aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
+									 ACL_EXECUTE);
+		if (aclresult != ACLCHECK_OK)
+			aclcheck_error(aclresult, ACL_KIND_PROC,
+						   get_func_name(acoerce->elemfuncid));
+		InvokeFunctionExecuteHook(acoerce->elemfuncid);
+
+		/* Set up the primary fmgr lookup information */
+		fmgr_info(acoerce->elemfuncid, op->d.arraycoerce.elemfunc);
+		fmgr_info_set_expr((Node *) acoerce, op->d.arraycoerce.elemfunc);
+	}
+
+	/*
+	 * Use array_map to apply the function to each array element.
+	 *
+	 * We pass on the desttypmod and isExplicit flags whether or not the
+	 * function wants them.
+	 *
+	 * Note: coercion functions are assumed to not use collation.
+	 */
+	InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3,
+							 InvalidOid, NULL, NULL);
+	locfcinfo.arg[0] = result;
+	locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
+	locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
+	locfcinfo.argnull[0] = false;
+	locfcinfo.argnull[1] = false;
+	locfcinfo.argnull[2] = false;
+
+	*op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype,
+							  op->d.arraycoerce.amstate);
+}
+
+/*
+ * Evaluate ROW() expressions.
+ *
+ * The individual columns have already been evaluated into
+ * op->d.row.elemvalues/nulls.
+ */
+void
+ExecEvalRow(ExprState *state, ExprEvalStep *op)
+{
+	HeapTuple	tuple;
+
+	/* Set default values for result flag: non-null */
+	*op->resnull = false;
+
+	/* preset to nulls in case rowtype has some later-added columns */
+	/* FIXME: unclear what's that supposed to do */
+	/* memset(isnull, true, natts * sizeof(bool)); */
+
+	/* build tuple from evaluated field values */
+	tuple = heap_form_tuple(op->d.row.tupdesc,
+							op->d.row.elemvalues,
+							op->d.row.elemnulls);
+
+	*op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+/*
+ * Evaluate GREATEST()/LEAST() type expression (note this is not MIN(),
+ * MAX()).
+ *
+ * All of the to-be-compared expressions have already been evaluated into
+ * ->op->d.minmax.values/nulls.
+ */
+void
+ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
+{
+	int off;
+	Datum *values = op->d.minmax.values;
+	bool *nulls = op->d.minmax.nulls;
+	FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data;
+	MinMaxOp	operator = op->d.minmax.op;
+
+	/* set at initialization */
+	Assert(fcinfo->argnull[0] == false);
+	Assert(fcinfo->argnull[1] == false);
+
+	*op->resnull = true;
+
+	for (off = 0; off < op->d.minmax.nelems; off++)
+	{
+		/* ignore NULL inputs */
+		if (nulls[off])
+			continue;
+
+		if (*op->resnull)
+		{
+			/* first nonnull input, adopt value */
+			*op->resvalue = values[off];
+			*op->resnull = false;
+		}
+		else
+		{
+			int cmpresult;
+
+			/* apply comparison function */
+			fcinfo->isnull = false;
+			fcinfo->arg[0] = *op->resvalue;
+			fcinfo->arg[1] = values[off];
+			Assert(!fcinfo->isnull);
+
+			cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
+
+			if (cmpresult > 0 && operator == IS_LEAST)
+				*op->resvalue = values[off];
+			else if (cmpresult < 0 && operator == IS_GREATEST)
+				*op->resvalue = values[off];
+		}
+	}
+}
+
+/*
+ * Evaluate a FieldSelect node.
+ */
+void
+ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	AttrNumber	fieldnum = op->d.fieldselect.fieldnum;
+	Datum		tupDatum;
+	HeapTupleHeader tuple;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupDesc;
+	Form_pg_attribute attr;
+	HeapTupleData tmptup;
+
+	tupDatum = *op->resvalue;
+
+	/* this test covers the isDone exception too: */
+	if (*op->resnull)
+		return;
+
+	tuple = DatumGetHeapTupleHeader(tupDatum);
+
+	tupType = HeapTupleHeaderGetTypeId(tuple);
+	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+	/* Lookup tupdesc if first time through or if type changes */
+	tupDesc = get_cached_rowtype(tupType, tupTypmod,
+								 &op->d.fieldselect.argdesc,
+								 econtext);
+
+	/*
+	 * Find field's attr record.  Note we don't support system columns here: a
+	 * datum tuple doesn't have valid values for most of the interesting
+	 * system columns anyway.
+	 */
+	if (fieldnum <= 0)			/* should never happen */
+		elog(ERROR, "unsupported reference to system column %d in FieldSelect",
+			 fieldnum);
+	if (fieldnum > tupDesc->natts)		/* should never happen */
+		elog(ERROR, "attribute number %d exceeds number of columns %d",
+			 fieldnum, tupDesc->natts);
+	attr = tupDesc->attrs[fieldnum - 1];
+
+	/* Check for dropped column, and force a NULL result if so */
+	if (attr->attisdropped)
+	{
+		*op->resnull = true;
+		return;
+	}
+
+	/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
+	/* As in ExecEvalScalarVar, we should but can't check typmod */
+	if (op->d.fieldselect.resulttype != attr->atttypid)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("attribute %d has wrong type", fieldnum),
+				 errdetail("Table has type %s, but query expects %s.",
+						   format_type_be(attr->atttypid),
+						   format_type_be(op->d.fieldselect.resulttype))));
+
+	/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	tmptup.t_data = tuple;
+
+	*op->resvalue = heap_getattr(&tmptup,
+								 fieldnum,
+								 tupDesc,
+								 op->resnull);
+}
+
+/*
+ * Deform tuple before evaluating the individual new values as part of a
+ * FieldStore expression.
+ */
+void
+ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	/* Lookup tupdesc if first time through or after rescan */
+	get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
+					   &op->d.fieldstore.argdesc, econtext);
+
+	if (*op->resnull)
+	{
+		/* Convert null input tuple into an all-nulls row */
+		memset(op->d.fieldstore.nulls, true,
+			   MaxTupleAttributeNumber * sizeof(bool));
+	}
+	else
+	{
+		/*
+		 * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
+		 * set all the fields in the struct just in case.
+		 */
+		Datum		tupDatum = *op->resvalue;
+		HeapTupleHeader tuphdr;
+		HeapTupleData tmptup;
+
+		tuphdr = DatumGetHeapTupleHeader(tupDatum);
+		tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
+		ItemPointerSetInvalid(&(tmptup.t_self));
+		tmptup.t_tableOid = InvalidOid;
+		tmptup.t_data = tuphdr;
+
+		heap_deform_tuple(&tmptup, op->d.fieldstore.argdesc,
+						  op->d.fieldstore.values,
+						  op->d.fieldstore.nulls);
+	}
+}
+
+/*
+ * Compute the new wholerow datum after each individual row part of a
+ * FieldStore expression has been evaluated.
+ */
+void
+ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	HeapTuple	tuple;
+
+	/* Lookup tupdesc if first time through or after rescan */
+	/*
+	 * FIXME: this currently can be hit due to invalidations, but effectively
+	 * is broken.
+	 */
+	get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
+					   &op->d.fieldstore.argdesc, econtext);
+
+	/* Result is never null */
+	*op->resnull = false;
+
+	tuple = heap_form_tuple(op->d.fieldstore.argdesc,
+							op->d.fieldstore.values,
+							op->d.fieldstore.nulls);
+
+	*op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+void
+ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref.state;
+
+	if (*op->resnull)
+	{
+		arefstate->prevnull = true;
+		arefstate->prevvalue = (Datum) 0;
+	}
+	else if (arefstate->numlower == 0)
+	{
+		arefstate->prevvalue =
+			array_get_element(*op->resvalue,
+							  arefstate->numupper,
+							  arefstate->upperindex,
+							  arefstate->refattrlength,
+							  arefstate->refelemlength,
+							  arefstate->refelembyval,
+							  arefstate->refelemalign,
+							  &arefstate->prevnull);
+	}
+	else
+	{
+		arefstate->prevvalue =
+			array_get_slice(*op->resvalue,
+							arefstate->numupper,
+							arefstate->upperindex,
+							arefstate->lowerindex,
+							arefstate->upperprovided,
+							arefstate->lowerprovided,
+							arefstate->refattrlength,
+							arefstate->refelemlength,
+							arefstate->refelembyval,
+							arefstate->refelemalign);
+	}
+}
+
+void
+ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref.state;
+
+	/*
+	 * For an assignment to a fixed-length array type, both the
+	 * original array and the value to be assigned into it must be
+	 * non-NULL, else we punt and return the original array.
+	 */
+	if (arefstate->refattrlength > 0 &&
+		(*op->resnull || arefstate->replacenull))
+	{
+		return;
+	}
+
+	/*
+	 * For assignment to varlena arrays, we handle a NULL original array
+	 * by substituting an empty (zero-dimensional) array; insertion of the
+	 * new element will result in a singleton array value.  It does not
+	 * matter whether the new element is NULL.
+	 */
+	if (*op->resnull)
+	{
+		*op->resvalue = PointerGetDatum(construct_empty_array(arefstate->refelemtype));
+		*op->resnull = false;
+	}
+
+	if (arefstate->numlower == 0)
+	{
+		*op->resvalue =
+			array_set_element(*op->resvalue, arefstate->numupper,
+							  arefstate->upperindex,
+							  arefstate->replacevalue,
+							  arefstate->replacenull,
+							  arefstate->refattrlength,
+							  arefstate->refelemlength,
+							  arefstate->refelembyval,
+							  arefstate->refelemalign);
+	}
+	else
+	{
+		*op->resvalue =
+			array_set_slice(*op->resvalue, arefstate->numupper,
+							arefstate->upperindex,
+							arefstate->lowerindex,
+							arefstate->upperprovided,
+							arefstate->lowerprovided,
+							arefstate->replacevalue,
+							arefstate->replacenull,
+							arefstate->refattrlength,
+							arefstate->refelemlength,
+							arefstate->refelembyval,
+							arefstate->refelemalign);
+	}
+}
+
+void
+ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref.state;
+
+	if (arefstate->numlower == 0)
+	{
+
+		*op->resvalue =
+			array_get_element(*op->resvalue,
+							  arefstate->numupper,
+							  arefstate->upperindex,
+							  arefstate->refattrlength,
+							  arefstate->refelemlength,
+							  arefstate->refelembyval,
+							  arefstate->refelemalign,
+							  op->resnull);
+	}
+	else
+	{
+		*op->resvalue =
+			array_get_slice(*op->resvalue,
+							arefstate->numupper,
+							arefstate->upperindex,
+							arefstate->lowerindex,
+							arefstate->upperprovided,
+							arefstate->lowerprovided,
+							arefstate->refattrlength,
+							arefstate->refelemlength,
+							arefstate->refelembyval,
+							arefstate->refelemalign);
+	}
+}
+
+bool
+ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref_checksubscript.state;
+	int off = op->d.arrayref_checksubscript.off;
+	bool *nulls;
+	Datum *values;
+	int *indexes;
+
+	if (op->d.arrayref_checksubscript.isupper)
+	{
+		nulls = arefstate->uppernull;
+		values = arefstate->upper;
+		indexes = arefstate->upperindex;
+	}
+	else
+	{
+		nulls = arefstate->lowernull;
+		values = arefstate->lower;
+		indexes = arefstate->lowerindex;
+	}
+
+	/* If any index expr yields NULL, result is NULL or error */
+	if (nulls[off])
+	{
+		if (arefstate->isassignment)
+			ereport(ERROR,
+					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					 errmsg("array subscript in assignment must not be null")));
+		*op->resnull = true;
+		return false;
+	}
+
+	/* convert datum to int */
+	indexes[off] = values[off];
+
+	return true;
+}
+
+void
+ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert;
+	HeapTuple	result;
+	Datum		tupDatum;
+	HeapTupleHeader tuple;
+	HeapTupleData tmptup;
+	TupleDesc indesc, outdesc;
+
+	tupDatum = *op->resvalue;
+
+	/* this test covers the isDone exception too: */
+	if (*op->resnull)
+		return;
+
+	tuple = DatumGetHeapTupleHeader(tupDatum);
+
+	/* Lookup tupdescs if first time through or after rescan */
+	if (op->d.convert_rowtype.indesc == NULL)
+	{
+		get_cached_rowtype(exprType((Node *) convert->arg), -1,
+						   &op->d.convert_rowtype.indesc,
+						   econtext);
+		op->d.convert_rowtype.initialized = false;
+	}
+	if (op->d.convert_rowtype.outdesc == NULL)
+	{
+		get_cached_rowtype(convert->resulttype, -1,
+						   &op->d.convert_rowtype.outdesc,
+						   econtext);
+		op->d.convert_rowtype.initialized = false;
+	}
+
+	indesc = op->d.convert_rowtype.indesc;
+	outdesc = op->d.convert_rowtype.outdesc;
+
+	/*
+	 * We used to be able to assert that incoming tuples are marked with
+	 * exactly the rowtype of cstate->indesc.  However, now that
+	 * ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
+	 * due to inserting aliases, we can only make this weak test:
+	 */
+	Assert(HeapTupleHeaderGetTypeId(tuple) == indesc->tdtypeid ||
+		   HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
+
+	/* if first time through, initialize conversion map */
+	if (!op->d.convert_rowtype.initialized)
+	{
+		MemoryContext old_cxt;
+
+		/* allocate map in long-lived memory context */
+		old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+		/* prepare map from old to new attribute numbers */
+		op->d.convert_rowtype.map =
+			convert_tuples_by_name(indesc, outdesc,
+								   gettext_noop("could not convert row type"));
+		op->d.convert_rowtype.initialized = true;
+
+		MemoryContextSwitchTo(old_cxt);
+	}
+
+	/*
+	 * No-op if no conversion needed (not clear this can happen here).
+	 */
+	if (op->d.convert_rowtype.map == NULL)
+		return;
+
+	/*
+	 * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
+	 */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	tmptup.t_data = tuple;
+
+	result = do_convert_tuple(&tmptup, op->d.convert_rowtype.map);
+
+	*op->resvalue = HeapTupleGetDatum(result);
+
+}
+
+/*
+ * ExecEvalScalarArrayOp
+ *
+ * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
+ * and we combine the results across all array elements using OR and AND
+ * (for ANY and ALL respectively).  Of course we short-circuit as soon as
+ * the result is known.
+ */
+void
+ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
+{
+	ScalarArrayOpExpr *opexpr = op->d.scalararrayop.opexpr;
+	bool		useOr = opexpr->useOr;
+	FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
+	bool strictfunc = op->d.scalararrayop.finfo->fn_strict;
+	ArrayType  *arr;
+	int			nitems;
+	Datum		result;
+	bool		resultnull;
+	int			i;
+	int16		typlen;
+	bool		typbyval;
+	char		typalign;
+	char	   *s;
+	bits8	   *bitmap;
+	int			bitmask;
+
+	/*
+	 * If the array is NULL then we return NULL --- it's not very meaningful
+	 * to do anything else, even if the operator isn't strict.
+	 */
+	if (*op->resnull)
+	{
+		return;
+	}
+
+	/* no "bad" nulls, okay to fetch and detoast the array */
+	arr = DatumGetArrayTypeP(*op->resvalue);
+
+	/*
+	 * If the array is empty, we return either FALSE or TRUE per the useOr
+	 * flag.  This is correct even if the scalar is NULL; since we would
+	 * evaluate the operator zero times, it matters not whether it would want
+	 * to return NULL.
+	 */
+	nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+	if (nitems <= 0)
+	{
+		*op->resnull = false;
+		*op->resvalue = BoolGetDatum(!useOr);
+		return;
+	}
+
+	/*
+	 * If the scalar is NULL, and the function is strict, return NULL; no
+	 * point in iterating the loop.
+	 */
+	if (fcinfo->argnull[0] && strictfunc)
+	{
+		*op->resnull = true;
+		return;
+	}
+
+	/*
+	 * We arrange to look up info about the element type only once per series
+	 * of calls, assuming the element type doesn't change underneath us.
+	 */
+	if (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr))
+	{
+		get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+							 &op->d.scalararrayop.typlen,
+							 &op->d.scalararrayop.typbyval,
+							 &op->d.scalararrayop.typalign);
+		op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr);
+	}
+
+	typlen = op->d.scalararrayop.typlen;
+	typbyval = op->d.scalararrayop.typbyval;
+	typalign = op->d.scalararrayop.typalign;
+
+	result = BoolGetDatum(!useOr);
+	resultnull = false;
+
+	/* Loop over the array elements */
+	s = (char *) ARR_DATA_PTR(arr);
+	bitmap = ARR_NULLBITMAP(arr);
+	bitmask = 1;
+
+	for (i = 0; i < nitems; i++)
+	{
+		Datum		elt;
+		Datum		thisresult;
+
+		/* Get array element, checking for NULL */
+		if (bitmap && (*bitmap & bitmask) == 0)
+		{
+			fcinfo->arg[1] = (Datum) 0;
+			fcinfo->argnull[1] = true;
+		}
+		else
+		{
+			elt = fetch_att(s, typbyval, typlen);
+			s = att_addlength_pointer(s, typlen, s);
+			s = (char *) att_align_nominal(s, typalign);
+			fcinfo->arg[1] = elt;
+			fcinfo->argnull[1] = false;
+		}
+
+		/* Call comparison function */
+		if (fcinfo->argnull[1] && strictfunc)
+		{
+			fcinfo->isnull = true;
+			thisresult = (Datum) 0;
+		}
+		else
+		{
+			fcinfo->isnull = false;
+			thisresult = (op->d.scalararrayop.fn_addr)(fcinfo);
+		}
+
+		/* Combine results per OR or AND semantics */
+		if (fcinfo->isnull)
+			resultnull = true;
+		else if (useOr)
+		{
+			if (DatumGetBool(thisresult))
+			{
+				result = BoolGetDatum(true);
+				resultnull = false;
+				break;			/* needn't look at any more elements */
+			}
+		}
+		else
+		{
+			if (!DatumGetBool(thisresult))
+			{
+				result = BoolGetDatum(false);
+				resultnull = false;
+				break;			/* needn't look at any more elements */
+			}
+		}
+
+		/* advance bitmap pointer if any */
+		if (bitmap)
+		{
+			bitmask <<= 1;
+			if (bitmask == 0x100)
+			{
+				bitmap++;
+				bitmask = 1;
+			}
+		}
+	}
+
+	*op->resnull = resultnull;
+	*op->resvalue = result;
+}
+
+void
+ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
+{
+	if (*op->resnull)
+		ereport(ERROR,
+				(errcode(ERRCODE_NOT_NULL_VIOLATION),
+				 errmsg("domain %s does not allow null values",
+						format_type_be(op->d.domaincheck.resulttype)),
+				 errdatatype(op->d.domaincheck.resulttype)));
+}
+
+void
+ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
+{
+	if (!*op->d.domaincheck.checknull &&
+		!DatumGetBool(*op->d.domaincheck.checkvalue))
+		ereport(ERROR,
+				(errcode(ERRCODE_CHECK_VIOLATION),
+				 errmsg("value for domain %s violates check constraint \"%s\"",
+						format_type_be(op->d.domaincheck.resulttype),
+						op->d.domaincheck.constraintname),
+				 errdomainconstraint(op->d.domaincheck.resulttype,
+									 op->d.domaincheck.constraintname)));
+}
+
+/*
+ * Evaluate the various forms of XmlExpr.
+ */
+void
+ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
+{
+	XmlExpr    *xexpr = op->d.xmlexpr.xexpr;
+	Datum		value;
+	ListCell   *arg;
+	ListCell   *narg;
+	int i;
+
+	*op->resnull = true;		/* until we get a result */
+	*op->resvalue = (Datum) 0;
+
+	switch (xexpr->op)
+	{
+		case IS_XMLCONCAT:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+				List	   *values = NIL;
+
+				for (i = 0; i < list_length(xexpr->args); i++)
+				{
+					if (!argnull[i])
+						values = lappend(values, DatumGetPointer(argvalue[i]));
+				}
+
+				if (list_length(values) > 0)
+				{
+					*op->resnull = false;
+					*op->resvalue = PointerGetDatum(xmlconcat(values));
+				}
+			}
+			break;
+
+		case IS_XMLFOREST:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.named_argvalue;
+				bool	   *argnull = op->d.xmlexpr.named_argnull;
+				StringInfoData buf;
+
+				initStringInfo(&buf);
+
+				i = 0;
+				forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
+				{
+					char	   *argname = strVal(lfirst(narg));
+					Expr	   *e = (Expr *) lfirst(arg);
+
+					if (!argnull[i])
+					{
+						value = argvalue[i];
+						appendStringInfo(&buf, "<%s>%s</%s>",
+										 argname,
+										 map_sql_value_to_xml_value(value, exprType((Node *) e), true),
+										 argname);
+						*op->resnull = false;
+					}
+					i++;
+				}
+
+				if (*op->resnull)
+				{
+					pfree(buf.data);
+				}
+				else
+				{
+					text	   *result;
+
+					result = cstring_to_text_with_len(buf.data, buf.len);
+					pfree(buf.data);
+
+					*op->resvalue = PointerGetDatum(result);
+				}
+			}
+			break;
+
+		case IS_XMLELEMENT:
+			*op->resnull = false;
+			*op->resvalue = PointerGetDatum(
+				xmlelement(xexpr,
+						   op->d.xmlexpr.named_argnull,
+						   op->d.xmlexpr.named_argvalue,
+						   op->d.xmlexpr.argnull,
+						   op->d.xmlexpr.argvalue));
+			break;
+		case IS_XMLPARSE:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+				text	   *data;
+				bool		preserve_whitespace;
+
+				/* arguments are known to be text, bool */
+				Assert(list_length(xexpr->args) == 2);
+
+				if (argnull[0])
+					return;
+				value = argvalue[0];
+				data = DatumGetTextP(value);
+
+				if (argnull[1])		/* probably can't happen */
+					return;
+				value = argvalue[1];
+				preserve_whitespace = DatumGetBool(value);
+
+				*op->resnull = false;
+				*op->resvalue = PointerGetDatum(
+					xmlparse(data, xexpr->xmloption, preserve_whitespace));
+			}
+			break;
+
+		case IS_XMLPI:
+			{
+				text	   *arg;
+				bool		isnull;
+
+				/* optional argument is known to be text */
+				Assert(list_length(xexpr->args) <= 1);
+
+				if (xexpr->args)
+				{
+					isnull = op->d.xmlexpr.argnull[0];
+					if (isnull)
+						arg = NULL;
+					else
+						arg = DatumGetTextP(op->d.xmlexpr.argvalue[0]);
+
+				}
+				else
+				{
+					arg = NULL;
+					isnull = false;
+				}
+
+				*op->resvalue = PointerGetDatum(xmlpi(xexpr->name, arg,
+													  isnull, op->resnull));
+			}
+			break;
+
+		case IS_XMLROOT:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+				xmltype    *data;
+				text	   *version;
+				int			standalone;
+
+				/* arguments are known to be xml, text, int */
+				Assert(list_length(xexpr->args) == 3);
+
+				if (argnull[0])
+					return;
+				data = DatumGetXmlP(argvalue[0]);
+
+				if (argnull[1])
+					version = NULL;
+				else
+					version = DatumGetTextP(argvalue[1]);
+
+				/* FIXME: missing NULL check??? */
+				Assert(!argnull[2]);
+				standalone = DatumGetInt32(argvalue[2]);
+
+				*op->resnull = false;
+				*op->resvalue = PointerGetDatum(xmlroot(data,
+														version,
+														standalone));
+			}
+			break;
+
+		case IS_XMLSERIALIZE:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+
+				/* argument type is known to be xml */
+				Assert(list_length(xexpr->args) == 1);
+
+				if (argnull[0])
+					return;
+
+				value = argvalue[0];
+				*op->resnull = false;
+				*op->resvalue = PointerGetDatum(
+					xmltotext_with_xmloption(DatumGetXmlP(value),
+											 xexpr->xmloption));
+			}
+			break;
+
+		case IS_DOCUMENT:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+
+				/* optional argument is known to be xml */
+				Assert(list_length(xexpr->args) == 1);
+
+				if (argnull[0])
+					break;
+
+				value = argvalue[0];
+				*op->resnull = false;
+				*op->resvalue =
+					BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+			}
+			break;
+		default:
+			elog(ERROR, "unrecognized XML operation");
+	}
+}
+
+/*
+ * ExecEvalGroupingFunc
+ *
+ * Computes a bitmask with a bit for each (unevaluated) argument expression
+ * (rightmost arg is least significant bit).
+ *
+ * A bit is set if the corresponding expression is NOT part of the set of
+ * grouping expressions in the current grouping set.
+ */
+void
+ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op)
+{
+	int			result = 0;
+	int			attnum = 0;
+	Bitmapset  *grouped_cols = op->d.grouping_func.parent->grouped_cols;
+	ListCell   *lc;
+
+	*op->resnull = false;
+
+	foreach(lc, op->d.grouping_func.clauses)
+	{
+		attnum = lfirst_int(lc);
+
+		result = result << 1;
+
+		if (!bms_is_member(attnum, grouped_cols))
+			result = result | 1;
+	}
+
+	*op->resvalue = Int32GetDatum(result);
+}
+
+/*
+ * Hand off evaluation of a subplan to nodeSubplan.c
+ */
+void
+ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	SubPlanState *sstate = op->d.subplan.sstate;
+
+	*op->resvalue = ExecSubPlan(sstate, econtext, op->resnull);
+}
+
+/*
+ * Hand off evaluation of an alternative subplan to nodeSubplan.c
+ */
+void
+ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	AlternativeSubPlanState *asstate = op->d.alternative_subplan.asstate;
+
+	*op->resvalue = ExecAlternativeSubPlan(asstate, econtext, op->resnull);
+}
+
+/*
+ * Evaluate a wholerow Var expression.
+ */
+void
+ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	Var		   *variable = op->d.wholerow.var;
+	TupleTableSlot *slot;
+	TupleDesc	output_tupdesc;
+	MemoryContext oldcontext;
+	bool		needslow = false;
+	HeapTupleHeader dtuple;
+	HeapTuple	tuple;
+
+	/* This was checked by ExecInitExpr */
+	Assert(variable->varattno == InvalidAttrNumber);
+
+	/* Get the input slot we want */
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = econtext->ecxt_scantuple;
+			break;
+	}
+
+
+	/* Apply the junkfilter if any */
+	if (op->d.wholerow.junkFilter != NULL)
+		slot = ExecFilterJunk(op->d.wholerow.junkFilter, slot);
+
+	/*
+	 * First time through, perform initialization.
+	 *
+	 * XXX: It'd be great if this could be moved to the the expression
+	 * initialization phase, but due to using slots that's currently not
+	 * feasible.
+	 */
+	if (op->d.wholerow.first)
+	{
+		/*
+		 * If the Var identifies a named composite type, we must check that the
+		 * actual tuple type is compatible with it.
+		 */
+		if (variable->vartype != RECORDOID)
+		{
+			TupleDesc	var_tupdesc;
+			TupleDesc	slot_tupdesc;
+			int			i;
+
+			/*
+			 * We really only care about numbers of attributes and data types.
+			 * Also, we can ignore type mismatch on columns that are dropped in
+			 * the destination type, so long as (1) the physical storage matches
+			 * or (2) the actual column value is NULL.  Case (1) is helpful in
+			 * some cases involving out-of-date cached plans, while case (2) is
+			 * expected behavior in situations such as an INSERT into a table with
+			 * dropped columns (the planner typically generates an INT4 NULL
+			 * regardless of the dropped column type).  If we find a dropped
+			 * column and cannot verify that case (1) holds, we have to use
+			 * ExecEvalWholeRowSlow to check (2) for each row.
+			 */
+			var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+
+			slot_tupdesc = slot->tts_tupleDescriptor;
+
+			if (var_tupdesc->natts != slot_tupdesc->natts)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("table row type and query-specified row type do not match"),
+						 errdetail_plural("Table row contains %d attribute, but query expects %d.",
+										  "Table row contains %d attributes, but query expects %d.",
+										  slot_tupdesc->natts,
+										  slot_tupdesc->natts,
+										  var_tupdesc->natts)));
+
+			for (i = 0; i < var_tupdesc->natts; i++)
+			{
+				Form_pg_attribute vattr = var_tupdesc->attrs[i];
+				Form_pg_attribute sattr = slot_tupdesc->attrs[i];
+
+				if (vattr->atttypid == sattr->atttypid)
+					continue;		/* no worries */
+				if (!vattr->attisdropped)
+					ereport(ERROR,
+							(errcode(ERRCODE_DATATYPE_MISMATCH),
+							 errmsg("table row type and query-specified row type do not match"),
+							 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
+									   format_type_be(sattr->atttypid),
+									   i + 1,
+									   format_type_be(vattr->atttypid))));
+
+				if (vattr->attlen != sattr->attlen ||
+					vattr->attalign != sattr->attalign)
+					needslow = true;	/* need runtime check for null */
+			}
+
+			/*
+			 * Use the variable's declared rowtype as the descriptor for the
+			 * output values, modulo possibly assigning new column names below. In
+			 * particular, we *must* absorb any attisdropped markings.
+			 */
+			oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+			output_tupdesc = CreateTupleDescCopy(var_tupdesc);
+			MemoryContextSwitchTo(oldcontext);
+
+			ReleaseTupleDesc(var_tupdesc);
+		}
+		else
+		{
+			/*
+			 * In the RECORD case, we use the input slot's rowtype as the
+			 * descriptor for the output values, modulo possibly assigning new
+			 * column names below.
+			 */
+			oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+			output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+			MemoryContextSwitchTo(oldcontext);
+		}
+
+		/*
+		 * Construct a tuple descriptor for the composite values we'll produce,
+		 * and make sure its record type is "blessed".  The main reason to do this
+		 * is to be sure that operations such as row_to_json() will see the
+		 * desired column names when they look up the descriptor from the type
+		 * information embedded in the composite values.
+		 *
+		 * We already got the correct physical datatype info above, but now we
+		 * should try to find the source RTE and adopt its column aliases, in case
+		 * they are different from the original rowtype's names.  For example, in
+		 * "SELECT foo(t) FROM tab t(x,y)", the first two columns in the composite
+		 * output should be named "x" and "y" regardless of tab's column names.
+		 *
+		 * If we can't locate the RTE, assume the column names we've got are OK.
+		 * (As of this writing, the only cases where we can't locate the RTE are
+		 * in execution of trigger WHEN clauses, and then the Var will have the
+		 * trigger's relation's rowtype, so its names are fine.)  Also, if the
+		 * creator of the RTE didn't bother to fill in an eref field, assume our
+		 * column names are OK.  (This happens in COPY, and perhaps other places.)
+		 */
+		if (econtext->ecxt_estate &&
+			variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
+		{
+			RangeTblEntry *rte = rt_fetch(variable->varno,
+										  econtext->ecxt_estate->es_range_table);
+
+			if (rte->eref)
+				ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
+		}
+
+		/* Bless the tupdesc if needed, and save it in the execution state */
+		op->d.wholerow.tupdesc = BlessTupleDesc(output_tupdesc);
+
+		op->d.wholerow.first = false;
+	}
+
+	if (needslow)
+	{
+		TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+		TupleDesc var_tupdesc = op->d.wholerow.tupdesc;
+		int i;
+
+		tuple = ExecFetchSlotTuple(slot);
+
+
+		/* Check to see if any dropped attributes are non-null */
+		for (i = 0; i < var_tupdesc->natts; i++)
+		{
+			Form_pg_attribute vattr = var_tupdesc->attrs[i];
+			Form_pg_attribute sattr = tupleDesc->attrs[i];
+
+			if (!vattr->attisdropped)
+				continue;			/* already checked non-dropped cols */
+			if (heap_attisnull(tuple, i + 1))
+				continue;			/* null is always okay */
+			if (vattr->attlen != sattr->attlen ||
+				vattr->attalign != sattr->attalign)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("table row type and query-specified row type do not match"),
+						 errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
+								   i + 1)));
+		}
+	}
+
+	/*
+	 * Copy the slot tuple and make sure any toasted fields get detoasted.
+	 */
+	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
+
+	/*
+	 * Label the datum with the composite type info we identified before.
+	 */
+	HeapTupleHeaderSetTypeId(dtuple, op->d.wholerow.tupdesc->tdtypeid);
+	HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod);
+
+	*op->resnull = false;
+	*op->resvalue = PointerGetDatum(dtuple);
+}
+
+/*
+ * Callback function to release a tupdesc refcount at expression tree shutdown
+ */
+static void
+ShutdownTupleDescRef(Datum arg)
+{
+	TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
+
+	if (*cache_field)
+		ReleaseTupleDesc(*cache_field);
+	*cache_field = NULL;
+}
+
+/*
+ * get_cached_rowtype: utility function to lookup a rowtype tupdesc
+ *
+ * type_id, typmod: identity of the rowtype
+ * cache_field: where to cache the TupleDesc pointer in expression state node
+ *		(field must be initialized to NULL)
+ * econtext: expression context we are executing in
+ *
+ * NOTE: because the shutdown callback will be called during plan rescan,
+ * must be prepared to re-do this during any node execution; cannot call
+ * just once during expression initialization
+ */
+static TupleDesc
+get_cached_rowtype(Oid type_id, int32 typmod,
+				   TupleDesc *cache_field, ExprContext *econtext)
+{
+	TupleDesc	tupDesc = *cache_field;
+
+	/* Do lookup if no cached value or if requested type changed */
+	if (tupDesc == NULL ||
+		type_id != tupDesc->tdtypeid ||
+		typmod != tupDesc->tdtypmod)
+	{
+		tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
+
+		if (*cache_field)
+		{
+			/* Release old tupdesc; but callback is already registered */
+			ReleaseTupleDesc(*cache_field);
+		}
+		else
+		{
+			/* Need to register shutdown callback to release tupdesc */
+			RegisterExprContextCallback(econtext,
+										ShutdownTupleDescRef,
+										PointerGetDatum(cache_field));
+		}
+		*cache_field = tupDesc;
+	}
+	return tupDesc;
+}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index f5cd65d8a0..ede675fa61 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1246,7 +1246,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1689,7 +1689,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1701,13 +1700,15 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState **) palloc(ncheck * sizeof(ExprState *));
 		for (i = 0; i < ncheck; i++)
 		{
-			/* ExecQual wants implicit-AND form */
+			List	   *qual;
+
+			/* ExecPrepareQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] =
+				ExecPrepareCheck(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1724,14 +1725,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState  *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
-		 * is not to be treated as a failure.  Therefore, tell ExecQual to
-		 * return TRUE for NULL.
+		 * is not to be treated as a failure.  Therefore, use ExecCheck not
+		 * ExecQual.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual, econtext))
 			return check[i].ccname;
 	}
 
@@ -1759,8 +1760,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 	{
 		List	   *qual = resultRelInfo->ri_PartitionCheck;
 
-		resultRelInfo->ri_PartitionCheckExpr = (List *)
-			ExecPrepareExpr((Expr *) qual, estate);
+		resultRelInfo->ri_PartitionCheckExpr = ExecPrepareCheck(qual, estate);
 	}
 
 	/*
@@ -1776,7 +1776,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 	 * As in case of the catalogued constraints, we treat a NULL result as
 	 * success here, not a failure.
 	 */
-	return ExecQual(resultRelInfo->ri_PartitionCheckExpr, econtext, true);
+	return ExecCheck(resultRelInfo->ri_PartitionCheckExpr, econtext);
 }
 
 /*
@@ -1956,11 +1956,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * is visible (in the case of a view) or that it passes the
 		 * 'with-check' policy (in the case of row security). If the qual
 		 * evaluates to NULL or FALSE, then the new tuple won't be included in
-		 * the view or doesn't pass the 'with-check' policy for the table.  We
-		 * need ExecQual to return FALSE for NULL to handle the view case (the
-		 * opposite of what we do above for CHECK constraints).
+		 * the view or doesn't pass the 'with-check' policy for the table.
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr, econtext))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 4566219ca8..d250627bbe 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * execQual.c
- *	  Routines to evaluate qualification and targetlist expressions
+ *	  Former Routines to evaluate qualification and targetlist expressions
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -12,28 +12,6 @@
  *
  *-------------------------------------------------------------------------
  */
-/*
- *	 INTERFACE ROUTINES
- *		ExecEvalExpr	- (now a macro) evaluate an expression, return a datum
- *		ExecEvalExprSwitchContext - same, but switch into eval memory context
- *		ExecQual		- return true/false if qualification is satisfied
- *		ExecProject		- form a new tuple by projecting the given tuple
- *
- *	 NOTES
- *		The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar,
- *		are hotspots. Making these faster will speed up the entire system.
- *
- *		ExecProject() is used to make tuple projections.  Rather then
- *		trying to speed it up, the execution plan should be pre-processed
- *		to facilitate attribute sharing between nodes wherever possible,
- *		instead of doing needless copying.  -cim 5/31/91
- *
- *		During expression evaluation, we check_stack_depth only in
- *		ExecMakeFunctionResultSet/ExecMakeFunctionResultNoSets rather than at
- *		every single node.  This is a compromise that trades off precision of
- *		the stack limit setting to gain speed.
- */
-
 #include "postgres.h"
 
 #include "access/htup_details.h"
@@ -62,1090 +40,18 @@
 
 
 /* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
-				 ExprContext *econtext,
-				 bool *isNull);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
-static Datum ExecEvalAggref(AggrefExprState *aggref,
-			   ExprContext *econtext,
-			   bool *isNull);
-static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc,
-				   ExprContext *econtext,
-				   bool *isNull);
-static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull);
-static Datum ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull);
-static Datum ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate,
-					 ExprContext *econtext,
-					 bool *isNull);
-static Datum ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate,
-					 ExprContext *econtext,
-					 bool *isNull);
-static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-			  bool *isNull);
-static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull);
-static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-					bool *isNull);
-static void init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-			MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF);
-static void ShutdownFuncExpr(Datum arg);
-static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
-				   TupleDesc *cache_field, ExprContext *econtext);
-static void ShutdownTupleDescRef(Datum arg);
+static void init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+			MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF);
+static void ShutdownSetExpr(Datum arg);
 static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 				 List *argList, ExprContext *econtext);
-static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
+static void ExecPrepareTuplestoreResult(SetExprState *sexpr,
 							ExprContext *econtext,
 							Tuplestorestate *resultStore,
 							TupleDesc resultDesc);
 static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
-static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
-							 ExprContext *econtext,
-							 bool *isNull);
-static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
-			 bool *isNull);
-static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
-			 bool *isNull);
-static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
-				 bool *isNull);
-static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
-					  ExprContext *econtext,
-					  bool *isNull);
-static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-		   bool *isNull);
-static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-					   ExprContext *econtext,
-					   bool *isNull);
-static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-			 bool *isNull);
-static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
-					 ExprContext *econtext,
-					 bool *isNull);
-static Datum ExecEvalArray(ArrayExprState *astate,
-			  ExprContext *econtext,
-			  bool *isNull);
-static Datum ExecEvalRow(RowExprState *rstate,
-			ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
-				   ExprContext *econtext,
-				   bool *isNull);
-static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
-				 ExprContext *econtext,
-				 bool *isNull);
-static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
-			   ExprContext *econtext,
-			   bool *isNull);
-static Datum ExecEvalSQLValueFunction(ExprState *svfExpr,
-						 ExprContext *econtext,
-						 bool *isNull);
-static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
-			   ExprContext *econtext,
-			   bool *isNull);
-static Datum ExecEvalNullTest(NullTestState *nstate,
-				 ExprContext *econtext,
-				 bool *isNull);
-static Datum ExecEvalBooleanTest(GenericExprState *bstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
-					   ExprContext *econtext,
-					   bool *isNull);
-static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
-							ExprContext *econtext,
-							bool *isNull);
-static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalFieldStore(FieldStoreState *fstate,
-				   ExprContext *econtext,
-				   bool *isNull);
-static Datum ExecEvalRelabelType(GenericExprState *exprstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
-						ExprContext *econtext,
-						bool *isNull);
-static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull);
-static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
-						 ExprContext *econtext,
-						 bool *isNull);
 
 
-/* ----------------------------------------------------------------
- *		ExecEvalExpr routines
- *
- *		Recursively evaluate a targetlist or qualification expression.
- *
- * Each of the following routines having the signature
- *		Datum ExecEvalFoo(ExprState *expression,
- *						  ExprContext *econtext,
- *						  bool *isNull);
- * is responsible for evaluating one type or subtype of ExprState node.
- * They are normally called via the ExecEvalExpr macro, which makes use of
- * the function pointer set up when the ExprState node was built by
- * ExecInitExpr.  (In some cases, we change this pointer later to avoid
- * re-executing one-time overhead.)
- *
- * Note: for notational simplicity we declare these functions as taking the
- * specific type of ExprState that they work on.  This requires casting when
- * assigning the function pointer in ExecInitExpr.  Be careful that the
- * function signature is declared correctly, because the cast suppresses
- * automatic checking!
- *
- *
- * All these functions share this calling convention:
- *
- * Inputs:
- *		expression: the expression state tree to evaluate
- *		econtext: evaluation context information
- *
- * Outputs:
- *		return value: Datum value of result
- *		*isNull: set to TRUE if result is NULL (actual return value is
- *				 meaningless if so); set to FALSE if non-null result
- *
- * The caller should already have switched into the temporary memory
- * context econtext->ecxt_per_tuple_memory.  The convenience entry point
- * ExecEvalExprSwitchContext() is provided for callers who don't prefer to
- * do the switch in an outer loop.  We do not do the switch in these routines
- * because it'd be a waste of cycles during nested expression evaluation.
- * ----------------------------------------------------------------
- */
-
-
-/*----------
- *	  ExecEvalArrayRef
- *
- *	   This function takes an ArrayRef and returns the extracted Datum
- *	   if it's a simple reference, or the modified array value if it's
- *	   an array assignment (i.e., array element or slice insertion).
- *
- * NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
- *----------
- */
-static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
-				 ExprContext *econtext,
-				 bool *isNull)
-{
-	ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
-	Datum		array_source;
-	bool		isAssignment = (arrayRef->refassgnexpr != NULL);
-	bool		eisnull;
-	ListCell   *l;
-	int			i = 0,
-				j = 0;
-	IntArray	upper,
-				lower;
-	bool		upperProvided[MAXDIM],
-				lowerProvided[MAXDIM];
-	int		   *lIndex;
-
-	array_source = ExecEvalExpr(astate->refexpr,
-								econtext,
-								isNull);
-
-	/*
-	 * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
-	 * assignment case, we'll cons up something below.
-	 */
-	if (*isNull)
-	{
-		if (!isAssignment)
-			return (Datum) NULL;
-	}
-
-	foreach(l, astate->refupperindexpr)
-	{
-		ExprState  *eltstate = (ExprState *) lfirst(l);
-
-		if (i >= MAXDIM)
-			ereport(ERROR,
-					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-					 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
-							i + 1, MAXDIM)));
-
-		if (eltstate == NULL)
-		{
-			/* Slice bound is omitted, so use array's upper bound */
-			Assert(astate->reflowerindexpr != NIL);
-			upperProvided[i++] = false;
-			continue;
-		}
-		upperProvided[i] = true;
-
-		upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
-													 econtext,
-													 &eisnull));
-		/* If any index expr yields NULL, result is NULL or error */
-		if (eisnull)
-		{
-			if (isAssignment)
-				ereport(ERROR,
-						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-				  errmsg("array subscript in assignment must not be null")));
-			*isNull = true;
-			return (Datum) NULL;
-		}
-	}
-
-	if (astate->reflowerindexpr != NIL)
-	{
-		foreach(l, astate->reflowerindexpr)
-		{
-			ExprState  *eltstate = (ExprState *) lfirst(l);
-
-			if (j >= MAXDIM)
-				ereport(ERROR,
-						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-						 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
-								j + 1, MAXDIM)));
-
-			if (eltstate == NULL)
-			{
-				/* Slice bound is omitted, so use array's lower bound */
-				lowerProvided[j++] = false;
-				continue;
-			}
-			lowerProvided[j] = true;
-
-			lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
-														 econtext,
-														 &eisnull));
-			/* If any index expr yields NULL, result is NULL or error */
-			if (eisnull)
-			{
-				if (isAssignment)
-					ereport(ERROR,
-							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-							 errmsg("array subscript in assignment must not be null")));
-				*isNull = true;
-				return (Datum) NULL;
-			}
-		}
-		/* this can't happen unless parser messed up */
-		if (i != j)
-			elog(ERROR, "upper and lower index lists are not same length");
-		lIndex = lower.indx;
-	}
-	else
-		lIndex = NULL;
-
-	if (isAssignment)
-	{
-		Datum		sourceData;
-		Datum		save_datum;
-		bool		save_isNull;
-
-		/*
-		 * We might have a nested-assignment situation, in which the
-		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
-		 * obtain and modify the previous value of the array element or slice
-		 * being replaced.  If so, we have to extract that value from the
-		 * array and pass it down via the econtext's caseValue.  It's safe to
-		 * reuse the CASE mechanism because there cannot be a CASE between
-		 * here and where the value would be needed, and an array assignment
-		 * can't be within a CASE either.  (So saving and restoring the
-		 * caseValue is just paranoia, but let's do it anyway.)
-		 *
-		 * Since fetching the old element might be a nontrivial expense, do it
-		 * only if the argument appears to actually need it.
-		 */
-		save_datum = econtext->caseValue_datum;
-		save_isNull = econtext->caseValue_isNull;
-
-		if (isAssignmentIndirectionExpr(astate->refassgnexpr))
-		{
-			if (*isNull)
-			{
-				/* whole array is null, so any element or slice is too */
-				econtext->caseValue_datum = (Datum) 0;
-				econtext->caseValue_isNull = true;
-			}
-			else if (lIndex == NULL)
-			{
-				econtext->caseValue_datum =
-					array_get_element(array_source, i,
-									  upper.indx,
-									  astate->refattrlength,
-									  astate->refelemlength,
-									  astate->refelembyval,
-									  astate->refelemalign,
-									  &econtext->caseValue_isNull);
-			}
-			else
-			{
-				econtext->caseValue_datum =
-					array_get_slice(array_source, i,
-									upper.indx, lower.indx,
-									upperProvided, lowerProvided,
-									astate->refattrlength,
-									astate->refelemlength,
-									astate->refelembyval,
-									astate->refelemalign);
-				econtext->caseValue_isNull = false;
-			}
-		}
-		else
-		{
-			/* argument shouldn't need caseValue, but for safety set it null */
-			econtext->caseValue_datum = (Datum) 0;
-			econtext->caseValue_isNull = true;
-		}
-
-		/*
-		 * Evaluate the value to be assigned into the array.
-		 */
-		sourceData = ExecEvalExpr(astate->refassgnexpr,
-								  econtext,
-								  &eisnull);
-
-		econtext->caseValue_datum = save_datum;
-		econtext->caseValue_isNull = save_isNull;
-
-		/*
-		 * For an assignment to a fixed-length array type, both the original
-		 * array and the value to be assigned into it must be non-NULL, else
-		 * we punt and return the original array.
-		 */
-		if (astate->refattrlength > 0)	/* fixed-length array? */
-			if (eisnull || *isNull)
-				return array_source;
-
-		/*
-		 * For assignment to varlena arrays, we handle a NULL original array
-		 * by substituting an empty (zero-dimensional) array; insertion of the
-		 * new element will result in a singleton array value.  It does not
-		 * matter whether the new element is NULL.
-		 */
-		if (*isNull)
-		{
-			array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
-			*isNull = false;
-		}
-
-		if (lIndex == NULL)
-			return array_set_element(array_source, i,
-									 upper.indx,
-									 sourceData,
-									 eisnull,
-									 astate->refattrlength,
-									 astate->refelemlength,
-									 astate->refelembyval,
-									 astate->refelemalign);
-		else
-			return array_set_slice(array_source, i,
-								   upper.indx, lower.indx,
-								   upperProvided, lowerProvided,
-								   sourceData,
-								   eisnull,
-								   astate->refattrlength,
-								   astate->refelemlength,
-								   astate->refelembyval,
-								   astate->refelemalign);
-	}
-
-	if (lIndex == NULL)
-		return array_get_element(array_source, i,
-								 upper.indx,
-								 astate->refattrlength,
-								 astate->refelemlength,
-								 astate->refelembyval,
-								 astate->refelemalign,
-								 isNull);
-	else
-		return array_get_slice(array_source, i,
-							   upper.indx, lower.indx,
-							   upperProvided, lowerProvided,
-							   astate->refattrlength,
-							   astate->refelemlength,
-							   astate->refelembyval,
-							   astate->refelemalign);
-}
-
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
-	if (exprstate == NULL)
-		return false;			/* just paranoia */
-	if (IsA(exprstate, FieldStoreState))
-	{
-		FieldStore *fstore = (FieldStore *) exprstate->expr;
-
-		if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
-			return true;
-	}
-	else if (IsA(exprstate, ArrayRefExprState))
-	{
-		ArrayRef   *arrayRef = (ArrayRef *) exprstate->expr;
-
-		if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
-			return true;
-	}
-	return false;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalAggref
- *
- *		Returns a Datum whose value is the value of the precomputed
- *		aggregate found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
-			   bool *isNull)
-{
-	if (econtext->ecxt_aggvalues == NULL)		/* safety check */
-		elog(ERROR, "no aggregates in this expression context");
-
-	*isNull = econtext->ecxt_aggnulls[aggref->aggno];
-	return econtext->ecxt_aggvalues[aggref->aggno];
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWindowFunc
- *
- *		Returns a Datum whose value is the value of the precomputed
- *		window function found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
-				   bool *isNull)
-{
-	if (econtext->ecxt_aggvalues == NULL)		/* safety check */
-		elog(ERROR, "no window functions in this expression context");
-
-	*isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
-	return econtext->ecxt_aggvalues[wfunc->wfuncno];
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalScalarVar
- *
- *		Returns a Datum whose value is the value of a scalar (not whole-row)
- *		range variable with respect to given expression context.
- *
- * Note: ExecEvalScalarVar is executed only the first time through in a given
- * plan; it changes the ExprState's function pointer to pass control directly
- * to ExecEvalScalarVarFast after making one-time checks.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull)
-{
-	Var		   *variable = (Var *) exprstate->expr;
-	TupleTableSlot *slot;
-	AttrNumber	attnum;
-
-	/* Get the input slot and attribute number we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	attnum = variable->varattno;
-
-	/* This was checked by ExecInitExpr */
-	Assert(attnum != InvalidAttrNumber);
-
-	/*
-	 * If it's a user attribute, check validity (bogus system attnums will be
-	 * caught inside slot_getattr).  What we have to check for here is the
-	 * possibility of an attribute having been changed in type since the plan
-	 * tree was created.  Ideally the plan will get invalidated and not
-	 * re-used, but just in case, we keep these defenses.  Fortunately it's
-	 * sufficient to check once on the first time through.
-	 *
-	 * Note: we allow a reference to a dropped attribute.  slot_getattr will
-	 * force a NULL result in such cases.
-	 *
-	 * Note: ideally we'd check typmod as well as typid, but that seems
-	 * impractical at the moment: in many cases the tupdesc will have been
-	 * generated by ExecTypeFromTL(), and that can't guarantee to generate an
-	 * accurate typmod in all cases, because some expression node types don't
-	 * carry typmod.
-	 */
-	if (attnum > 0)
-	{
-		TupleDesc	slot_tupdesc = slot->tts_tupleDescriptor;
-		Form_pg_attribute attr;
-
-		if (attnum > slot_tupdesc->natts)		/* should never happen */
-			elog(ERROR, "attribute number %d exceeds number of columns %d",
-				 attnum, slot_tupdesc->natts);
-
-		attr = slot_tupdesc->attrs[attnum - 1];
-
-		/* can't check type if dropped, since atttypid is probably 0 */
-		if (!attr->attisdropped)
-		{
-			if (variable->vartype != attr->atttypid)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("attribute %d has wrong type", attnum),
-						 errdetail("Table has type %s, but query expects %s.",
-								   format_type_be(attr->atttypid),
-								   format_type_be(variable->vartype))));
-		}
-	}
-
-	/* Skip the checking on future executions of node */
-	exprstate->evalfunc = ExecEvalScalarVarFast;
-
-	/* Fetch the value from the slot */
-	return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalScalarVarFast
- *
- *		Returns a Datum for a scalar variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull)
-{
-	Var		   *variable = (Var *) exprstate->expr;
-	TupleTableSlot *slot;
-	AttrNumber	attnum;
-
-	/* Get the input slot and attribute number we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	attnum = variable->varattno;
-
-	/* Fetch the value from the slot */
-	return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWholeRowVar
- *
- *		Returns a Datum whose value is the value of a whole-row range
- *		variable with respect to given expression context.
- *
- * Note: ExecEvalWholeRowVar is executed only the first time through in a
- * given plan; it changes the ExprState's function pointer to pass control
- * directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making
- * one-time checks.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					bool *isNull)
-{
-	Var		   *variable = (Var *) wrvstate->xprstate.expr;
-	TupleTableSlot *slot;
-	TupleDesc	output_tupdesc;
-	MemoryContext oldcontext;
-	bool		needslow = false;
-
-	/* This was checked by ExecInitExpr */
-	Assert(variable->varattno == InvalidAttrNumber);
-
-	/* Get the input slot we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	/*
-	 * If the input tuple came from a subquery, it might contain "resjunk"
-	 * columns (such as GROUP BY or ORDER BY columns), which we don't want to
-	 * keep in the whole-row result.  We can get rid of such columns by
-	 * passing the tuple through a JunkFilter --- but to make one, we have to
-	 * lay our hands on the subquery's targetlist.  Fortunately, there are not
-	 * very many cases where this can happen, and we can identify all of them
-	 * by examining our parent PlanState.  We assume this is not an issue in
-	 * standalone expressions that don't have parent plans.  (Whole-row Vars
-	 * can occur in such expressions, but they will always be referencing
-	 * table rows.)
-	 */
-	if (wrvstate->parent)
-	{
-		PlanState  *subplan = NULL;
-
-		switch (nodeTag(wrvstate->parent))
-		{
-			case T_SubqueryScanState:
-				subplan = ((SubqueryScanState *) wrvstate->parent)->subplan;
-				break;
-			case T_CteScanState:
-				subplan = ((CteScanState *) wrvstate->parent)->cteplanstate;
-				break;
-			default:
-				break;
-		}
-
-		if (subplan)
-		{
-			bool		junk_filter_needed = false;
-			ListCell   *tlist;
-
-			/* Detect whether subplan tlist actually has any junk columns */
-			foreach(tlist, subplan->plan->targetlist)
-			{
-				TargetEntry *tle = (TargetEntry *) lfirst(tlist);
-
-				if (tle->resjunk)
-				{
-					junk_filter_needed = true;
-					break;
-				}
-			}
-
-			/* If so, build the junkfilter in the query memory context */
-			if (junk_filter_needed)
-			{
-				oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-				wrvstate->wrv_junkFilter =
-					ExecInitJunkFilter(subplan->plan->targetlist,
-									   ExecGetResultType(subplan)->tdhasoid,
-							ExecInitExtraTupleSlot(wrvstate->parent->state));
-				MemoryContextSwitchTo(oldcontext);
-			}
-		}
-	}
-
-	/* Apply the junkfilter if any */
-	if (wrvstate->wrv_junkFilter != NULL)
-		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-	/*
-	 * If the Var identifies a named composite type, we must check that the
-	 * actual tuple type is compatible with it.
-	 */
-	if (variable->vartype != RECORDOID)
-	{
-		TupleDesc	var_tupdesc;
-		TupleDesc	slot_tupdesc;
-		int			i;
-
-		/*
-		 * We really only care about numbers of attributes and data types.
-		 * Also, we can ignore type mismatch on columns that are dropped in
-		 * the destination type, so long as (1) the physical storage matches
-		 * or (2) the actual column value is NULL.  Case (1) is helpful in
-		 * some cases involving out-of-date cached plans, while case (2) is
-		 * expected behavior in situations such as an INSERT into a table with
-		 * dropped columns (the planner typically generates an INT4 NULL
-		 * regardless of the dropped column type).  If we find a dropped
-		 * column and cannot verify that case (1) holds, we have to use
-		 * ExecEvalWholeRowSlow to check (2) for each row.
-		 */
-		var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
-
-		slot_tupdesc = slot->tts_tupleDescriptor;
-
-		if (var_tupdesc->natts != slot_tupdesc->natts)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("table row type and query-specified row type do not match"),
-					 errdetail_plural("Table row contains %d attribute, but query expects %d.",
-				   "Table row contains %d attributes, but query expects %d.",
-									  slot_tupdesc->natts,
-									  slot_tupdesc->natts,
-									  var_tupdesc->natts)));
-
-		for (i = 0; i < var_tupdesc->natts; i++)
-		{
-			Form_pg_attribute vattr = var_tupdesc->attrs[i];
-			Form_pg_attribute sattr = slot_tupdesc->attrs[i];
-
-			if (vattr->atttypid == sattr->atttypid)
-				continue;		/* no worries */
-			if (!vattr->attisdropped)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("table row type and query-specified row type do not match"),
-						 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
-								   format_type_be(sattr->atttypid),
-								   i + 1,
-								   format_type_be(vattr->atttypid))));
-
-			if (vattr->attlen != sattr->attlen ||
-				vattr->attalign != sattr->attalign)
-				needslow = true;	/* need runtime check for null */
-		}
-
-		/*
-		 * Use the variable's declared rowtype as the descriptor for the
-		 * output values, modulo possibly assigning new column names below. In
-		 * particular, we *must* absorb any attisdropped markings.
-		 */
-		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-		output_tupdesc = CreateTupleDescCopy(var_tupdesc);
-		MemoryContextSwitchTo(oldcontext);
-
-		ReleaseTupleDesc(var_tupdesc);
-	}
-	else
-	{
-		/*
-		 * In the RECORD case, we use the input slot's rowtype as the
-		 * descriptor for the output values, modulo possibly assigning new
-		 * column names below.
-		 */
-		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-		output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
-		MemoryContextSwitchTo(oldcontext);
-	}
-
-	/*
-	 * Construct a tuple descriptor for the composite values we'll produce,
-	 * and make sure its record type is "blessed".  The main reason to do this
-	 * is to be sure that operations such as row_to_json() will see the
-	 * desired column names when they look up the descriptor from the type
-	 * information embedded in the composite values.
-	 *
-	 * We already got the correct physical datatype info above, but now we
-	 * should try to find the source RTE and adopt its column aliases, in case
-	 * they are different from the original rowtype's names.  For example, in
-	 * "SELECT foo(t) FROM tab t(x,y)", the first two columns in the composite
-	 * output should be named "x" and "y" regardless of tab's column names.
-	 *
-	 * If we can't locate the RTE, assume the column names we've got are OK.
-	 * (As of this writing, the only cases where we can't locate the RTE are
-	 * in execution of trigger WHEN clauses, and then the Var will have the
-	 * trigger's relation's rowtype, so its names are fine.)  Also, if the
-	 * creator of the RTE didn't bother to fill in an eref field, assume our
-	 * column names are OK.  (This happens in COPY, and perhaps other places.)
-	 */
-	if (econtext->ecxt_estate &&
-		variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
-	{
-		RangeTblEntry *rte = rt_fetch(variable->varno,
-									  econtext->ecxt_estate->es_range_table);
-
-		if (rte->eref)
-			ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
-	}
-
-	/* Bless the tupdesc if needed, and save it in the execution state */
-	wrvstate->wrv_tupdesc = BlessTupleDesc(output_tupdesc);
-
-	/* Skip all the above on future executions of node */
-	if (needslow)
-		wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowSlow;
-	else
-		wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowFast;
-
-	/* Fetch the value */
-	return (*wrvstate->xprstate.evalfunc) ((ExprState *) wrvstate, econtext,
-										   isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWholeRowFast
- *
- *		Returns a Datum for a whole-row variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					 bool *isNull)
-{
-	Var		   *variable = (Var *) wrvstate->xprstate.expr;
-	TupleTableSlot *slot;
-	HeapTupleHeader dtuple;
-
-	*isNull = false;
-
-	/* Get the input slot we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	/* Apply the junkfilter if any */
-	if (wrvstate->wrv_junkFilter != NULL)
-		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-	/*
-	 * Copy the slot tuple and make sure any toasted fields get detoasted.
-	 */
-	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
-	/*
-	 * Label the datum with the composite type info we identified before.
-	 */
-	HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
-	HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
-	return PointerGetDatum(dtuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWholeRowSlow
- *
- *		Returns a Datum for a whole-row variable, in the "slow" case where
- *		we can't just copy the subplan's output.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					 bool *isNull)
-{
-	Var		   *variable = (Var *) wrvstate->xprstate.expr;
-	TupleTableSlot *slot;
-	HeapTuple	tuple;
-	TupleDesc	tupleDesc;
-	TupleDesc	var_tupdesc;
-	HeapTupleHeader dtuple;
-	int			i;
-
-	*isNull = false;
-
-	/* Get the input slot we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	/* Apply the junkfilter if any */
-	if (wrvstate->wrv_junkFilter != NULL)
-		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-	tuple = ExecFetchSlotTuple(slot);
-	tupleDesc = slot->tts_tupleDescriptor;
-
-	/* wrv_tupdesc is a good enough representation of the Var's rowtype */
-	Assert(variable->vartype != RECORDOID);
-	var_tupdesc = wrvstate->wrv_tupdesc;
-
-	/* Check to see if any dropped attributes are non-null */
-	for (i = 0; i < var_tupdesc->natts; i++)
-	{
-		Form_pg_attribute vattr = var_tupdesc->attrs[i];
-		Form_pg_attribute sattr = tupleDesc->attrs[i];
-
-		if (!vattr->attisdropped)
-			continue;			/* already checked non-dropped cols */
-		if (heap_attisnull(tuple, i + 1))
-			continue;			/* null is always okay */
-		if (vattr->attlen != sattr->attlen ||
-			vattr->attalign != sattr->attalign)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("table row type and query-specified row type do not match"),
-					 errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
-							   i + 1)));
-	}
-
-	/*
-	 * Copy the slot tuple and make sure any toasted fields get detoasted.
-	 */
-	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
-	/*
-	 * Label the datum with the composite type info we identified before.
-	 */
-	HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
-	HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
-	return PointerGetDatum(dtuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalConst
- *
- *		Returns the value of a constant.
- *
- *		Note that for pass-by-ref datatypes, we return a pointer to the
- *		actual constant node.  This is one of the reasons why functions
- *		must treat their input arguments as read-only.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-			  bool *isNull)
-{
-	Const	   *con = (Const *) exprstate->expr;
-
-	*isNull = con->constisnull;
-	return con->constvalue;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalParamExec
- *
- *		Returns the value of a PARAM_EXEC parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull)
-{
-	Param	   *expression = (Param *) exprstate->expr;
-	int			thisParamId = expression->paramid;
-	ParamExecData *prm;
-
-	/*
-	 * PARAM_EXEC params (internal executor parameters) are stored in the
-	 * ecxt_param_exec_vals array, and can be accessed by array index.
-	 */
-	prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
-	if (prm->execPlan != NULL)
-	{
-		/* Parameter not evaluated yet, so go do it */
-		ExecSetParamPlan(prm->execPlan, econtext);
-		/* ExecSetParamPlan should have processed this param... */
-		Assert(prm->execPlan == NULL);
-	}
-	*isNull = prm->isnull;
-	return prm->value;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalParamExtern
- *
- *		Returns the value of a PARAM_EXTERN parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-					bool *isNull)
-{
-	Param	   *expression = (Param *) exprstate->expr;
-	int			thisParamId = expression->paramid;
-	ParamListInfo paramInfo = econtext->ecxt_param_list_info;
-
-	/*
-	 * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
-	 */
-	if (paramInfo &&
-		thisParamId > 0 && thisParamId <= paramInfo->numParams)
-	{
-		ParamExternData *prm = &paramInfo->params[thisParamId - 1];
-
-		/* give hook a chance in case parameter is dynamic */
-		if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
-			(*paramInfo->paramFetch) (paramInfo, thisParamId);
-
-		if (OidIsValid(prm->ptype))
-		{
-			/* safety check in case hook did something unexpected */
-			if (prm->ptype != expression->paramtype)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
-								thisParamId,
-								format_type_be(prm->ptype),
-								format_type_be(expression->paramtype))));
-
-			*isNull = prm->isnull;
-			return prm->value;
-		}
-	}
-
-	ereport(ERROR,
-			(errcode(ERRCODE_UNDEFINED_OBJECT),
-			 errmsg("no value found for parameter %d", thisParamId)));
-	return (Datum) 0;			/* keep compiler quiet */
-}
-
-
-/* ----------------------------------------------------------------
- *		ExecEvalOper / ExecEvalFunc support routines
- * ----------------------------------------------------------------
- */
-
 /*
  *		GetAttributeByName
  *		GetAttributeByNum
@@ -1267,11 +173,11 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
 }
 
 /*
- * init_fcache - initialize a FuncExprState node during first use
+ * init_sexpr - initialize a SetExprState node during first use
  */
 static void
-init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-			MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
+init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+		   MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
 {
 	AclResult	aclresult;
 
@@ -1287,7 +193,7 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
 	 * declared in pg_proc?
 	 */
-	if (list_length(fcache->args) > FUNC_MAX_ARGS)
+	if (list_length(sexpr->args) > FUNC_MAX_ARGS)
 		ereport(ERROR,
 				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
 			 errmsg_plural("cannot pass more than %d argument to a function",
@@ -1296,45 +202,45 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 						   FUNC_MAX_ARGS)));
 
 	/* Set up the primary fmgr lookup information */
-	fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
-	fmgr_info_set_expr((Node *) fcache->xprstate.expr, &(fcache->func));
+	fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
+	fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
 
 	/* Initialize the function call parameter struct as well */
-	InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func),
-							 list_length(fcache->args),
+	InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+							 list_length(sexpr->args),
 							 input_collation, NULL, NULL);
 
 	/* If function returns set, check if that's allowed by caller */
-	if (fcache->func.fn_retset && !allowSRF)
+	if (sexpr->func.fn_retset && !allowSRF)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("set-valued function called in context that cannot accept a set")));
 
-	/* Otherwise, ExecInitExpr should have marked the fcache correctly */
-	Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
+	/* Otherwise, ExecInitExpr should have marked the sexpr correctly */
+	Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
 
 	/* If function returns set, prepare expected tuple descriptor */
-	if (fcache->func.fn_retset && needDescForSRF)
+	if (sexpr->func.fn_retset && needDescForSRF)
 	{
 		TypeFuncClass functypclass;
 		Oid			funcrettype;
 		TupleDesc	tupdesc;
 		MemoryContext oldcontext;
 
-		functypclass = get_expr_result_type(fcache->func.fn_expr,
+		functypclass = get_expr_result_type(sexpr->func.fn_expr,
 											&funcrettype,
 											&tupdesc);
 
-		/* Must save tupdesc in fcache's context */
-		oldcontext = MemoryContextSwitchTo(fcacheCxt);
+		/* Must save tupdesc in sexpr's context */
+		oldcontext = MemoryContextSwitchTo(sexprCxt);
 
 		if (functypclass == TYPEFUNC_COMPOSITE)
 		{
 			/* Composite data type, e.g. a table's row type */
 			Assert(tupdesc);
 			/* Must copy it out of typcache for safety */
-			fcache->funcResultDesc = CreateTupleDescCopy(tupdesc);
-			fcache->funcReturnsTuple = true;
+			sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
+			sexpr->funcReturnsTuple = true;
 		}
 		else if (functypclass == TYPEFUNC_SCALAR)
 		{
@@ -1346,110 +252,55 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 							   funcrettype,
 							   -1,
 							   0);
-			fcache->funcResultDesc = tupdesc;
-			fcache->funcReturnsTuple = false;
+			sexpr->funcResultDesc = tupdesc;
+			sexpr->funcReturnsTuple = false;
 		}
 		else if (functypclass == TYPEFUNC_RECORD)
 		{
 			/* This will work if function doesn't need an expectedDesc */
-			fcache->funcResultDesc = NULL;
-			fcache->funcReturnsTuple = true;
+			sexpr->funcResultDesc = NULL;
+			sexpr->funcReturnsTuple = true;
 		}
 		else
 		{
 			/* Else, we will fail if function needs an expectedDesc */
-			fcache->funcResultDesc = NULL;
+			sexpr->funcResultDesc = NULL;
 		}
 
 		MemoryContextSwitchTo(oldcontext);
 	}
 	else
-		fcache->funcResultDesc = NULL;
+		sexpr->funcResultDesc = NULL;
 
 	/* Initialize additional state */
-	fcache->funcResultStore = NULL;
-	fcache->funcResultSlot = NULL;
-	fcache->shutdown_reg = false;
+	sexpr->funcResultStore = NULL;
+	sexpr->funcResultSlot = NULL;
+	sexpr->shutdown_reg = false;
 }
 
 /*
- * callback function in case a FuncExpr returning a set needs to be shut down
- * before it has been run to completion
+ * callback function in case a SetExpr needs to be shut down before it has
+ * been run to completion
  */
 static void
-ShutdownFuncExpr(Datum arg)
+ShutdownSetExpr(Datum arg)
 {
-	FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg);
+	SetExprState *sexpr = (SetExprState *) DatumGetPointer(arg);
 
 	/* If we have a slot, make sure it's let go of any tuplestore pointer */
-	if (fcache->funcResultSlot)
-		ExecClearTuple(fcache->funcResultSlot);
+	if (sexpr->funcResultSlot)
+		ExecClearTuple(sexpr->funcResultSlot);
 
 	/* Release any open tuplestore */
-	if (fcache->funcResultStore)
-		tuplestore_end(fcache->funcResultStore);
-	fcache->funcResultStore = NULL;
+	if (sexpr->funcResultStore)
+		tuplestore_end(sexpr->funcResultStore);
+	sexpr->funcResultStore = NULL;
 
 	/* Clear any active set-argument state */
-	fcache->setArgsValid = false;
+	sexpr->setArgsValid = false;
 
 	/* execUtils will deregister the callback... */
-	fcache->shutdown_reg = false;
-}
-
-/*
- * get_cached_rowtype: utility function to lookup a rowtype tupdesc
- *
- * type_id, typmod: identity of the rowtype
- * cache_field: where to cache the TupleDesc pointer in expression state node
- *		(field must be initialized to NULL)
- * econtext: expression context we are executing in
- *
- * NOTE: because the shutdown callback will be called during plan rescan,
- * must be prepared to re-do this during any node execution; cannot call
- * just once during expression initialization
- */
-static TupleDesc
-get_cached_rowtype(Oid type_id, int32 typmod,
-				   TupleDesc *cache_field, ExprContext *econtext)
-{
-	TupleDesc	tupDesc = *cache_field;
-
-	/* Do lookup if no cached value or if requested type changed */
-	if (tupDesc == NULL ||
-		type_id != tupDesc->tdtypeid ||
-		typmod != tupDesc->tdtypmod)
-	{
-		tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
-
-		if (*cache_field)
-		{
-			/* Release old tupdesc; but callback is already registered */
-			ReleaseTupleDesc(*cache_field);
-		}
-		else
-		{
-			/* Need to register shutdown callback to release tupdesc */
-			RegisterExprContextCallback(econtext,
-										ShutdownTupleDescRef,
-										PointerGetDatum(cache_field));
-		}
-		*cache_field = tupDesc;
-	}
-	return tupDesc;
-}
-
-/*
- * Callback function to release a tupdesc refcount at expression tree shutdown
- */
-static void
-ShutdownTupleDescRef(Datum arg)
-{
-	TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
-
-	if (*cache_field)
-		ReleaseTupleDesc(*cache_field);
-	*cache_field = NULL;
+	sexpr->shutdown_reg = false;
 }
 
 /*
@@ -1486,27 +337,27 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
  * returned the expected tuple descriptor.
  */
 static void
-ExecPrepareTuplestoreResult(FuncExprState *fcache,
+ExecPrepareTuplestoreResult(SetExprState *sexpr,
 							ExprContext *econtext,
 							Tuplestorestate *resultStore,
 							TupleDesc resultDesc)
 {
-	fcache->funcResultStore = resultStore;
+	sexpr->funcResultStore = resultStore;
 
-	if (fcache->funcResultSlot == NULL)
+	if (sexpr->funcResultSlot == NULL)
 	{
 		/* Create a slot so we can read data out of the tuplestore */
 		TupleDesc	slotDesc;
 		MemoryContext oldcontext;
 
-		oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
+		oldcontext = MemoryContextSwitchTo(sexpr->func.fn_mcxt);
 
 		/*
 		 * If we were not able to determine the result rowtype from context,
 		 * and the function didn't return a tupdesc, we have to fail.
 		 */
-		if (fcache->funcResultDesc)
-			slotDesc = fcache->funcResultDesc;
+		if (sexpr->funcResultDesc)
+			slotDesc = sexpr->funcResultDesc;
 		else if (resultDesc)
 		{
 			/* don't assume resultDesc is long-lived */
@@ -1521,7 +372,7 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
 			slotDesc = NULL;	/* keep compiler quiet */
 		}
 
-		fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
+		sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
 		MemoryContextSwitchTo(oldcontext);
 	}
 
@@ -1531,8 +382,8 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
 	 */
 	if (resultDesc)
 	{
-		if (fcache->funcResultDesc)
-			tupledesc_match(fcache->funcResultDesc, resultDesc);
+		if (sexpr->funcResultDesc)
+			tupledesc_match(sexpr->funcResultDesc, resultDesc);
 
 		/*
 		 * If it is a dynamically-allocated TupleDesc, free it: it is
@@ -1544,12 +395,12 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
 	}
 
 	/* Register cleanup callback if we didn't already */
-	if (!fcache->shutdown_reg)
+	if (!sexpr->shutdown_reg)
 	{
 		RegisterExprContextCallback(econtext,
-									ShutdownFuncExpr,
-									PointerGetDatum(fcache));
-		fcache->shutdown_reg = true;
+									ShutdownSetExpr,
+									PointerGetDatum(sexpr));
+		sexpr->shutdown_reg = true;
 	}
 }
 
@@ -1611,7 +462,7 @@ tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
  * functions (the planner is supposed to have separated evaluation for those).
  */
 Datum
-ExecMakeFunctionResultSet(FuncExprState *fcache,
+ExecMakeFunctionResultSet(SetExprState *fcache,
 						  ExprContext *econtext,
 						  bool *isNull,
 						  ExprDoneCond *isDone)
@@ -1630,34 +481,6 @@ restart:
 	check_stack_depth();
 
 	/*
-	 * Initialize function cache if first time through.  The expression node
-	 * could be either a FuncExpr or an OpExpr.
-	 */
-	if (fcache->func.fn_oid == InvalidOid)
-	{
-		if (IsA(fcache->xprstate.expr, FuncExpr))
-		{
-			FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-			init_fcache(func->funcid, func->inputcollid, fcache,
-						econtext->ecxt_per_query_memory, true, true);
-		}
-		else if (IsA(fcache->xprstate.expr, OpExpr))
-		{
-			OpExpr	   *op = (OpExpr *) fcache->xprstate.expr;
-
-			init_fcache(op->opfuncid, op->inputcollid, fcache,
-						econtext->ecxt_per_query_memory, true, true);
-		}
-		else
-			elog(ERROR, "unrecognized node type: %d",
-				 (int) nodeTag(fcache->xprstate.expr));
-
-		/* shouldn't get here otherwise */
-		Assert(fcache->func.fn_retset);
-	}
-
-	/*
 	 * If a previous call of the function returned a set result in the form of
 	 * a tuplestore, continue reading rows from the tuplestore until it's
 	 * empty.
@@ -1773,7 +596,7 @@ restart:
 				if (!fcache->shutdown_reg)
 				{
 					RegisterExprContextCallback(econtext,
-												ShutdownFuncExpr,
+												ShutdownSetExpr,
 												PointerGetDatum(fcache));
 					fcache->shutdown_reg = true;
 				}
@@ -1811,63 +634,100 @@ restart:
 }
 
 /*
- *		ExecMakeFunctionResultNoSets
- *
- * Evaluate a function or operator node with a non-set-returning function.
- * Assumes init_fcache() already done.  Hand-tuned for speed.
+ * Prepare function call in nodeProjectSet.c (taretlist SRF) for execution.
  */
-static Datum
-ExecMakeFunctionResultNoSets(FuncExprState *fcache,
-							 ExprContext *econtext,
-							 bool *isNull)
+SetExprState *
+ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent)
 {
-	ListCell   *arg;
-	Datum		result;
-	FunctionCallInfo fcinfo;
-	PgStat_FunctionCallUsage fcusage;
-	int			i;
+	SetExprState *state = makeNode(SetExprState);
+	ListCell *lc;
 
-	/* Guard against stack overflow due to overly complex expressions */
-	check_stack_depth();
-
-	/* inlined, simplified version of ExecEvalFuncArgs */
-	fcinfo = &fcache->fcinfo_data;
-	i = 0;
-	foreach(arg, fcache->args)
-	{
-		ExprState  *argstate = (ExprState *) lfirst(arg);
-
-		fcinfo->arg[i] = ExecEvalExpr(argstate,
-									  econtext,
-									  &fcinfo->argnull[i]);
-		i++;
-	}
+	state->funcReturnsSet = true;
+	state->expr = expr;
+	state->func.fn_oid = InvalidOid;
 
 	/*
-	 * If function is strict, and there are any NULL arguments, skip calling
-	 * the function and return NULL.
+	 * Initialize metadata.  The expression node could be either a FuncExpr or
+	 * an OpExpr.
 	 */
-	if (fcache->func.fn_strict)
+	if (IsA(expr, FuncExpr))
 	{
-		while (--i >= 0)
+		FuncExpr   *func = (FuncExpr *) expr;
+
+		foreach(lc, func->args)
 		{
-			if (fcinfo->argnull[i])
-			{
-				*isNull = true;
-				return (Datum) 0;
-			}
+			state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
 		}
+
+		init_sexpr(func->funcid, func->inputcollid, state,
+				   econtext->ecxt_per_query_memory, true, true);
+	}
+	else if (IsA(expr, OpExpr))
+	{
+		OpExpr	   *op = (OpExpr *) expr;
+
+		foreach(lc, op->args)
+		{
+			state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+		}
+
+		init_sexpr(op->opfuncid, op->inputcollid, state,
+				   econtext->ecxt_per_query_memory, true, true);
+	}
+	else
+		elog(ERROR, "unrecognized node type: %d",
+			 (int) nodeTag(expr));
+
+	/* shouldn't get here otherwise */
+	Assert(state->func.fn_retset);
+
+	return state;
+}
+
+/*
+ * Prepare function call in nodeFunctionscan.c (FROM function/ROWS FROM) for
+ * execution.
+ */
+SetExprState *
+ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent)
+{
+	SetExprState *state = makeNode(SetExprState);
+	ListCell *lc;
+
+	state->funcReturnsSet = false;
+	state->expr = expr;
+	state->func.fn_oid = InvalidOid;
+
+	/*
+	 * Normally the passed expression tree will be a FuncExpr, since the
+	 * grammar only allows a function call at the top level of a table
+	 * function reference.  However, if the function doesn't return set then
+	 * the planner might have replaced the function call via constant-folding
+	 * or inlining.  So if we see any other kind of expression node, execute
+	 * it via the general ExecEvalExpr() code; the only difference is that we
+	 * don't get a chance to pass a special ReturnSetInfo to any functions
+	 * buried in the expression.
+	 */
+	if (IsA(expr, FuncExpr))
+	{
+		FuncExpr   *func = (FuncExpr *) expr;
+
+		state->funcReturnsSet = func->funcretset;
+
+		foreach(lc, func->args)
+		{
+			state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+		}
+
+		init_sexpr(func->funcid, func->inputcollid, state,
+				   econtext->ecxt_per_query_memory, func->funcretset, false);
+	}
+	else
+	{
+		state->elidedFuncState = ExecInitExpr(expr, parent);
 	}
 
-	pgstat_init_function_usage(fcinfo, &fcusage);
-
-	fcinfo->isnull = false;
-	result = FunctionCallInvoke(fcinfo);
-	*isNull = fcinfo->isnull;
-
-	pgstat_end_function_usage(&fcusage, true);
-
-	return result;
+	return state;
 }
 
 
@@ -1878,7 +738,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
  * object.
  */
 Tuplestorestate *
-ExecMakeTableFunctionResult(ExprState *funcexpr,
+ExecMakeTableFunctionResult(SetExprState *setexpr,
 							ExprContext *econtext,
 							MemoryContext argContext,
 							TupleDesc expectedDesc,
@@ -1895,12 +755,11 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	HeapTupleData tmptup;
 	MemoryContext callerContext;
 	MemoryContext oldcontext;
-	bool		direct_function_call;
 	bool		first_time = true;
 
 	callerContext = CurrentMemoryContext;
 
-	funcrettype = exprType((Node *) funcexpr->expr);
+	funcrettype = exprType((Node *) setexpr->expr);
 
 	returnsTuple = type_is_rowtype(funcrettype);
 
@@ -1923,7 +782,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	rsinfo.setDesc = NULL;
 
 	/*
-	 * Normally the passed expression tree will be a FuncExprState, since the
+	 * Normally the passed expression tree will be a SetExprState, since the
 	 * grammar only allows a function call at the top level of a table
 	 * function reference.  However, if the function doesn't return set then
 	 * the planner might have replaced the function call via constant-folding
@@ -1932,30 +791,15 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	 * don't get a chance to pass a special ReturnSetInfo to any functions
 	 * buried in the expression.
 	 */
-	if (funcexpr && IsA(funcexpr, FuncExprState) &&
-		IsA(funcexpr->expr, FuncExpr))
+	if (!setexpr->elidedFuncState)
 	{
-		FuncExprState *fcache = (FuncExprState *) funcexpr;
-
 		/*
 		 * This path is similar to ExecMakeFunctionResultSet.
 		 */
-		direct_function_call = true;
-
-		/*
-		 * Initialize function cache if first time through
-		 */
-		if (fcache->func.fn_oid == InvalidOid)
-		{
-			FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-			init_fcache(func->funcid, func->inputcollid, fcache,
-						econtext->ecxt_per_query_memory, true, false);
-		}
-		returnsSet = fcache->func.fn_retset;
-		InitFunctionCallInfoData(fcinfo, &(fcache->func),
-								 list_length(fcache->args),
-								 fcache->fcinfo_data.fncollation,
+		returnsSet = setexpr->funcReturnsSet;
+		InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+								 list_length(setexpr->args),
+								 setexpr->fcinfo_data.fncollation,
 								 NULL, (Node *) &rsinfo);
 
 		/*
@@ -1970,7 +814,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		 */
 		MemoryContextReset(argContext);
 		oldcontext = MemoryContextSwitchTo(argContext);
-		ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
+		ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
 		MemoryContextSwitchTo(oldcontext);
 
 		/*
@@ -1978,7 +822,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		 * calling the function and act like it returned NULL (or an empty
 		 * set, in the returns-set case).
 		 */
-		if (fcache->func.fn_strict)
+		if (setexpr->func.fn_strict)
 		{
 			int			i;
 
@@ -1991,8 +835,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	}
 	else
 	{
-		/* Treat funcexpr as a generic expression */
-		direct_function_call = false;
+		/* Treat setexpr as a generic expression */
 		InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
 	}
 
@@ -2019,7 +862,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		ResetExprContext(econtext);
 
 		/* Call the function or expression one time */
-		if (direct_function_call)
+		if (!setexpr->elidedFuncState)
 		{
 			pgstat_init_function_usage(&fcinfo, &fcusage);
 
@@ -2032,7 +875,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		}
 		else
 		{
-			result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull);
+			result =
+				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
 			rsinfo.isDone = ExprSingleResult;
 		}
 
@@ -2210,2911 +1054,6 @@ no_function_result:
 	return rsinfo.setResult;
 }
 
-
-/* ----------------------------------------------------------------
- *		ExecEvalFunc
- *		ExecEvalOper
- *
- *		Evaluate the functional result of a list of arguments by calling the
- *		function manager.
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- *		ExecEvalFunc
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFunc(FuncExprState *fcache,
-			 ExprContext *econtext,
-			 bool *isNull)
-{
-	/* This is called only the first time through */
-	FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-	/* Initialize function lookup info */
-	init_fcache(func->funcid, func->inputcollid, fcache,
-				econtext->ecxt_per_query_memory, false, false);
-
-	/* Change the evalfunc pointer to save a few cycles in additional calls */
-	fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-	return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalOper
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOper(FuncExprState *fcache,
-			 ExprContext *econtext,
-			 bool *isNull)
-{
-	/* This is called only the first time through */
-	OpExpr	   *op = (OpExpr *) fcache->xprstate.expr;
-
-	/* Initialize function lookup info */
-	init_fcache(op->opfuncid, op->inputcollid, fcache,
-				econtext->ecxt_per_query_memory, false, false);
-
-	/* Change the evalfunc pointer to save a few cycles in additional calls */
-	fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-	return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalDistinct
- *
- * IS DISTINCT FROM must evaluate arguments to determine whether
- * they are NULL; if either is NULL then the result is already
- * known. If neither is NULL, then proceed to evaluate the
- * function. Note that this is *always* derived from the equals
- * operator, but since we need special processing of the arguments
- * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalDistinct(FuncExprState *fcache,
-				 ExprContext *econtext,
-				 bool *isNull)
-{
-	Datum		result;
-	FunctionCallInfo fcinfo;
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	/*
-	 * Initialize function cache if first time through
-	 */
-	if (fcache->func.fn_oid == InvalidOid)
-	{
-		DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
-
-		init_fcache(op->opfuncid, op->inputcollid, fcache,
-					econtext->ecxt_per_query_memory, false, false);
-	}
-
-	/*
-	 * Evaluate arguments
-	 */
-	fcinfo = &fcache->fcinfo_data;
-	ExecEvalFuncArgs(fcinfo, fcache->args, econtext);
-	Assert(fcinfo->nargs == 2);
-
-	if (fcinfo->argnull[0] && fcinfo->argnull[1])
-	{
-		/* Both NULL? Then is not distinct... */
-		result = BoolGetDatum(FALSE);
-	}
-	else if (fcinfo->argnull[0] || fcinfo->argnull[1])
-	{
-		/* Only one is NULL? Then is distinct... */
-		result = BoolGetDatum(TRUE);
-	}
-	else
-	{
-		fcinfo->isnull = false;
-		result = FunctionCallInvoke(fcinfo);
-		*isNull = fcinfo->isnull;
-		/* Must invert result of "=" */
-		result = BoolGetDatum(!DatumGetBool(result));
-	}
-
-	return result;
-}
-
-/*
- * ExecEvalScalarArrayOp
- *
- * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
- * and we combine the results across all array elements using OR and AND
- * (for ANY and ALL respectively).  Of course we short-circuit as soon as
- * the result is known.
- */
-static Datum
-ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
-					  ExprContext *econtext,
-					  bool *isNull)
-{
-	ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
-	bool		useOr = opexpr->useOr;
-	ArrayType  *arr;
-	int			nitems;
-	Datum		result;
-	bool		resultnull;
-	FunctionCallInfo fcinfo;
-	int			i;
-	int16		typlen;
-	bool		typbyval;
-	char		typalign;
-	char	   *s;
-	bits8	   *bitmap;
-	int			bitmask;
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	/*
-	 * Initialize function cache if first time through
-	 */
-	if (sstate->fxprstate.func.fn_oid == InvalidOid)
-	{
-		init_fcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
-					econtext->ecxt_per_query_memory, false, false);
-	}
-
-	/*
-	 * Evaluate arguments
-	 */
-	fcinfo = &sstate->fxprstate.fcinfo_data;
-	ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext);
-	Assert(fcinfo->nargs == 2);
-
-	/*
-	 * If the array is NULL then we return NULL --- it's not very meaningful
-	 * to do anything else, even if the operator isn't strict.
-	 */
-	if (fcinfo->argnull[1])
-	{
-		*isNull = true;
-		return (Datum) 0;
-	}
-	/* Else okay to fetch and detoast the array */
-	arr = DatumGetArrayTypeP(fcinfo->arg[1]);
-
-	/*
-	 * If the array is empty, we return either FALSE or TRUE per the useOr
-	 * flag.  This is correct even if the scalar is NULL; since we would
-	 * evaluate the operator zero times, it matters not whether it would want
-	 * to return NULL.
-	 */
-	nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
-	if (nitems <= 0)
-		return BoolGetDatum(!useOr);
-
-	/*
-	 * If the scalar is NULL, and the function is strict, return NULL; no
-	 * point in iterating the loop.
-	 */
-	if (fcinfo->argnull[0] && sstate->fxprstate.func.fn_strict)
-	{
-		*isNull = true;
-		return (Datum) 0;
-	}
-
-	/*
-	 * We arrange to look up info about the element type only once per series
-	 * of calls, assuming the element type doesn't change underneath us.
-	 */
-	if (sstate->element_type != ARR_ELEMTYPE(arr))
-	{
-		get_typlenbyvalalign(ARR_ELEMTYPE(arr),
-							 &sstate->typlen,
-							 &sstate->typbyval,
-							 &sstate->typalign);
-		sstate->element_type = ARR_ELEMTYPE(arr);
-	}
-	typlen = sstate->typlen;
-	typbyval = sstate->typbyval;
-	typalign = sstate->typalign;
-
-	result = BoolGetDatum(!useOr);
-	resultnull = false;
-
-	/* Loop over the array elements */
-	s = (char *) ARR_DATA_PTR(arr);
-	bitmap = ARR_NULLBITMAP(arr);
-	bitmask = 1;
-
-	for (i = 0; i < nitems; i++)
-	{
-		Datum		elt;
-		Datum		thisresult;
-
-		/* Get array element, checking for NULL */
-		if (bitmap && (*bitmap & bitmask) == 0)
-		{
-			fcinfo->arg[1] = (Datum) 0;
-			fcinfo->argnull[1] = true;
-		}
-		else
-		{
-			elt = fetch_att(s, typbyval, typlen);
-			s = att_addlength_pointer(s, typlen, s);
-			s = (char *) att_align_nominal(s, typalign);
-			fcinfo->arg[1] = elt;
-			fcinfo->argnull[1] = false;
-		}
-
-		/* Call comparison function */
-		if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict)
-		{
-			fcinfo->isnull = true;
-			thisresult = (Datum) 0;
-		}
-		else
-		{
-			fcinfo->isnull = false;
-			thisresult = FunctionCallInvoke(fcinfo);
-		}
-
-		/* Combine results per OR or AND semantics */
-		if (fcinfo->isnull)
-			resultnull = true;
-		else if (useOr)
-		{
-			if (DatumGetBool(thisresult))
-			{
-				result = BoolGetDatum(true);
-				resultnull = false;
-				break;			/* needn't look at any more elements */
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(thisresult))
-			{
-				result = BoolGetDatum(false);
-				resultnull = false;
-				break;			/* needn't look at any more elements */
-			}
-		}
-
-		/* advance bitmap pointer if any */
-		if (bitmap)
-		{
-			bitmask <<= 1;
-			if (bitmask == 0x100)
-			{
-				bitmap++;
-				bitmask = 1;
-			}
-		}
-	}
-
-	*isNull = resultnull;
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalNot
- *		ExecEvalOr
- *		ExecEvalAnd
- *
- *		Evaluate boolean expressions, with appropriate short-circuiting.
- *
- *		The query planner reformulates clause expressions in the
- *		qualification to conjunctive normal form.  If we ever get
- *		an AND to evaluate, we can be sure that it's not a top-level
- *		clause in the qualification, but appears lower (as a function
- *		argument, for example), or in the target list.  Not that you
- *		need to know this, mind you...
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-			bool *isNull)
-{
-	ExprState  *clause = linitial(notclause->args);
-	Datum		expr_value;
-
-	expr_value = ExecEvalExpr(clause, econtext, isNull);
-
-	/*
-	 * if the expression evaluates to null, then we just cascade the null back
-	 * to whoever called us.
-	 */
-	if (*isNull)
-		return expr_value;
-
-	/*
-	 * evaluation of 'not' is simple.. expr is false, then return 'true' and
-	 * vice versa.
-	 */
-	return BoolGetDatum(!DatumGetBool(expr_value));
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalOr
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-		   bool *isNull)
-{
-	List	   *clauses = orExpr->args;
-	ListCell   *clause;
-	bool		AnyNull;
-
-	AnyNull = false;
-
-	/*
-	 * If any of the clauses is TRUE, the OR result is TRUE regardless of the
-	 * states of the rest of the clauses, so we can stop evaluating and return
-	 * TRUE immediately.  If none are TRUE and one or more is NULL, we return
-	 * NULL; otherwise we return FALSE.  This makes sense when you interpret
-	 * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we
-	 * aren't sure about some of the other inputs. If all the known inputs are
-	 * FALSE, but we have one or more "don't knows", then we have to report
-	 * that we "don't know" what the OR's result should be --- perhaps one of
-	 * the "don't knows" would have been TRUE if we'd known its value.  Only
-	 * when all the inputs are known to be FALSE can we state confidently that
-	 * the OR's result is FALSE.
-	 */
-	foreach(clause, clauses)
-	{
-		ExprState  *clausestate = (ExprState *) lfirst(clause);
-		Datum		clause_value;
-
-		clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
-		/*
-		 * if we have a non-null true result, then return it.
-		 */
-		if (*isNull)
-			AnyNull = true;		/* remember we got a null */
-		else if (DatumGetBool(clause_value))
-			return clause_value;
-	}
-
-	/* AnyNull is true if at least one clause evaluated to NULL */
-	*isNull = AnyNull;
-	return BoolGetDatum(false);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalAnd
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-			bool *isNull)
-{
-	List	   *clauses = andExpr->args;
-	ListCell   *clause;
-	bool		AnyNull;
-
-	AnyNull = false;
-
-	/*
-	 * If any of the clauses is FALSE, the AND result is FALSE regardless of
-	 * the states of the rest of the clauses, so we can stop evaluating and
-	 * return FALSE immediately.  If none are FALSE and one or more is NULL,
-	 * we return NULL; otherwise we return TRUE.  This makes sense when you
-	 * interpret NULL as "don't know", using the same sort of reasoning as for
-	 * OR, above.
-	 */
-
-	foreach(clause, clauses)
-	{
-		ExprState  *clausestate = (ExprState *) lfirst(clause);
-		Datum		clause_value;
-
-		clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
-		/*
-		 * if we have a non-null false result, then return it.
-		 */
-		if (*isNull)
-			AnyNull = true;		/* remember we got a null */
-		else if (!DatumGetBool(clause_value))
-			return clause_value;
-	}
-
-	/* AnyNull is true if at least one clause evaluated to NULL */
-	*isNull = AnyNull;
-	return BoolGetDatum(!AnyNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalConvertRowtype
- *
- *		Evaluate a rowtype coercion operation.  This may require
- *		rearranging field positions.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-					   ExprContext *econtext,
-					   bool *isNull)
-{
-	ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
-	HeapTuple	result;
-	Datum		tupDatum;
-	HeapTupleHeader tuple;
-	HeapTupleData tmptup;
-
-	tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull);
-
-	/* this test covers the isDone exception too: */
-	if (*isNull)
-		return tupDatum;
-
-	tuple = DatumGetHeapTupleHeader(tupDatum);
-
-	/* Lookup tupdescs if first time through or after rescan */
-	if (cstate->indesc == NULL)
-	{
-		get_cached_rowtype(exprType((Node *) convert->arg), -1,
-						   &cstate->indesc, econtext);
-		cstate->initialized = false;
-	}
-	if (cstate->outdesc == NULL)
-	{
-		get_cached_rowtype(convert->resulttype, -1,
-						   &cstate->outdesc, econtext);
-		cstate->initialized = false;
-	}
-
-	/*
-	 * We used to be able to assert that incoming tuples are marked with
-	 * exactly the rowtype of cstate->indesc.  However, now that
-	 * ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
-	 * due to inserting aliases, we can only make this weak test:
-	 */
-	Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid ||
-		   HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
-
-	/* if first time through, initialize conversion map */
-	if (!cstate->initialized)
-	{
-		MemoryContext old_cxt;
-
-		/* allocate map in long-lived memory context */
-		old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-
-		/* prepare map from old to new attribute numbers */
-		cstate->map = convert_tuples_by_name(cstate->indesc,
-											 cstate->outdesc,
-								 gettext_noop("could not convert row type"));
-		cstate->initialized = true;
-
-		MemoryContextSwitchTo(old_cxt);
-	}
-
-	/*
-	 * No-op if no conversion needed (not clear this can happen here).
-	 */
-	if (cstate->map == NULL)
-		return tupDatum;
-
-	/*
-	 * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
-	 */
-	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-	tmptup.t_data = tuple;
-
-	result = do_convert_tuple(&tmptup, cstate->map);
-
-	return HeapTupleGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCase
- *
- *		Evaluate a CASE clause. Will have boolean expressions
- *		inside the WHEN clauses, and will have expressions
- *		for results.
- *		- thomas 1998-11-09
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-			 bool *isNull)
-{
-	List	   *clauses = caseExpr->args;
-	ListCell   *clause;
-	Datum		save_datum;
-	bool		save_isNull;
-
-	/*
-	 * If there's a test expression, we have to evaluate it and save the value
-	 * where the CaseTestExpr placeholders can find it.  We must save and
-	 * restore prior setting of econtext's caseValue fields, in case this node
-	 * is itself within a larger CASE.  Furthermore, don't assign to the
-	 * econtext fields until after returning from evaluation of the test
-	 * expression.  We used to pass &econtext->caseValue_isNull to the
-	 * recursive call, but that leads to aliasing that variable within said
-	 * call, which can (and did) produce bugs when the test expression itself
-	 * contains a CASE.
-	 *
-	 * If there's no test expression, we don't actually need to save and
-	 * restore these fields; but it's less code to just do so unconditionally.
-	 */
-	save_datum = econtext->caseValue_datum;
-	save_isNull = econtext->caseValue_isNull;
-
-	if (caseExpr->arg)
-	{
-		Datum		arg_value;
-		bool		arg_isNull;
-
-		arg_value = ExecEvalExpr(caseExpr->arg,
-								 econtext,
-								 &arg_isNull);
-		/* Since caseValue_datum may be read multiple times, force to R/O */
-		econtext->caseValue_datum =
-			MakeExpandedObjectReadOnly(arg_value,
-									   arg_isNull,
-									   caseExpr->argtyplen);
-		econtext->caseValue_isNull = arg_isNull;
-	}
-
-	/*
-	 * we evaluate each of the WHEN clauses in turn, as soon as one is true we
-	 * return the corresponding result. If none are true then we return the
-	 * value of the default clause, or NULL if there is none.
-	 */
-	foreach(clause, clauses)
-	{
-		CaseWhenState *wclause = lfirst(clause);
-		Datum		clause_value;
-		bool		clause_isNull;
-
-		clause_value = ExecEvalExpr(wclause->expr,
-									econtext,
-									&clause_isNull);
-
-		/*
-		 * if we have a true test, then we return the result, since the case
-		 * statement is satisfied.  A NULL result from the test is not
-		 * considered true.
-		 */
-		if (DatumGetBool(clause_value) && !clause_isNull)
-		{
-			econtext->caseValue_datum = save_datum;
-			econtext->caseValue_isNull = save_isNull;
-			return ExecEvalExpr(wclause->result,
-								econtext,
-								isNull);
-		}
-	}
-
-	econtext->caseValue_datum = save_datum;
-	econtext->caseValue_isNull = save_isNull;
-
-	if (caseExpr->defresult)
-	{
-		return ExecEvalExpr(caseExpr->defresult,
-							econtext,
-							isNull);
-	}
-
-	*isNull = true;
-	return (Datum) 0;
-}
-
-/*
- * ExecEvalCaseTestExpr
- *
- * Return the value stored by CASE.
- */
-static Datum
-ExecEvalCaseTestExpr(ExprState *exprstate,
-					 ExprContext *econtext,
-					 bool *isNull)
-{
-	*isNull = econtext->caseValue_isNull;
-	return econtext->caseValue_datum;
-}
-
-/*
- * ExecEvalGroupingFuncExpr
- *
- * Return a bitmask with a bit for each (unevaluated) argument expression
- * (rightmost arg is least significant bit).
- *
- * A bit is set if the corresponding expression is NOT part of the set of
- * grouping expressions in the current grouping set.
- */
-static Datum
-ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
-						 ExprContext *econtext,
-						 bool *isNull)
-{
-	int			result = 0;
-	int			attnum = 0;
-	Bitmapset  *grouped_cols = gstate->aggstate->grouped_cols;
-	ListCell   *lc;
-
-	*isNull = false;
-
-	foreach(lc, (gstate->clauses))
-	{
-		attnum = lfirst_int(lc);
-
-		result = result << 1;
-
-		if (!bms_is_member(attnum, grouped_cols))
-			result = result | 1;
-	}
-
-	return (Datum) result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalArray - ARRAY[] expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
-			  bool *isNull)
-{
-	ArrayExpr  *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
-	ArrayType  *result;
-	ListCell   *element;
-	Oid			element_type = arrayExpr->element_typeid;
-	int			ndims = 0;
-	int			dims[MAXDIM];
-	int			lbs[MAXDIM];
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	if (!arrayExpr->multidims)
-	{
-		/* Elements are presumably of scalar type */
-		int			nelems;
-		Datum	   *dvalues;
-		bool	   *dnulls;
-		int			i = 0;
-
-		ndims = 1;
-		nelems = list_length(astate->elements);
-
-		/* Shouldn't happen here, but if length is 0, return empty array */
-		if (nelems == 0)
-			return PointerGetDatum(construct_empty_array(element_type));
-
-		dvalues = (Datum *) palloc(nelems * sizeof(Datum));
-		dnulls = (bool *) palloc(nelems * sizeof(bool));
-
-		/* loop through and build array of datums */
-		foreach(element, astate->elements)
-		{
-			ExprState  *e = (ExprState *) lfirst(element);
-
-			dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i]);
-			i++;
-		}
-
-		/* setup for 1-D array of the given length */
-		dims[0] = nelems;
-		lbs[0] = 1;
-
-		result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
-									element_type,
-									astate->elemlength,
-									astate->elembyval,
-									astate->elemalign);
-	}
-	else
-	{
-		/* Must be nested array expressions */
-		int			nbytes = 0;
-		int			nitems = 0;
-		int			outer_nelems = 0;
-		int			elem_ndims = 0;
-		int		   *elem_dims = NULL;
-		int		   *elem_lbs = NULL;
-		bool		firstone = true;
-		bool		havenulls = false;
-		bool		haveempty = false;
-		char	  **subdata;
-		bits8	  **subbitmaps;
-		int		   *subbytes;
-		int		   *subnitems;
-		int			i;
-		int32		dataoffset;
-		char	   *dat;
-		int			iitem;
-
-		i = list_length(astate->elements);
-		subdata = (char **) palloc(i * sizeof(char *));
-		subbitmaps = (bits8 **) palloc(i * sizeof(bits8 *));
-		subbytes = (int *) palloc(i * sizeof(int));
-		subnitems = (int *) palloc(i * sizeof(int));
-
-		/* loop through and get data area from each element */
-		foreach(element, astate->elements)
-		{
-			ExprState  *e = (ExprState *) lfirst(element);
-			bool		eisnull;
-			Datum		arraydatum;
-			ArrayType  *array;
-			int			this_ndims;
-
-			arraydatum = ExecEvalExpr(e, econtext, &eisnull);
-			/* temporarily ignore null subarrays */
-			if (eisnull)
-			{
-				haveempty = true;
-				continue;
-			}
-
-			array = DatumGetArrayTypeP(arraydatum);
-
-			/* run-time double-check on element type */
-			if (element_type != ARR_ELEMTYPE(array))
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("cannot merge incompatible arrays"),
-						 errdetail("Array with element type %s cannot be "
-						 "included in ARRAY construct with element type %s.",
-								   format_type_be(ARR_ELEMTYPE(array)),
-								   format_type_be(element_type))));
-
-			this_ndims = ARR_NDIM(array);
-			/* temporarily ignore zero-dimensional subarrays */
-			if (this_ndims <= 0)
-			{
-				haveempty = true;
-				continue;
-			}
-
-			if (firstone)
-			{
-				/* Get sub-array details from first member */
-				elem_ndims = this_ndims;
-				ndims = elem_ndims + 1;
-				if (ndims <= 0 || ndims > MAXDIM)
-					ereport(ERROR,
-							(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-						  errmsg("number of array dimensions (%d) exceeds " \
-								 "the maximum allowed (%d)", ndims, MAXDIM)));
-
-				elem_dims = (int *) palloc(elem_ndims * sizeof(int));
-				memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
-				elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
-				memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
-
-				firstone = false;
-			}
-			else
-			{
-				/* Check other sub-arrays are compatible */
-				if (elem_ndims != this_ndims ||
-					memcmp(elem_dims, ARR_DIMS(array),
-						   elem_ndims * sizeof(int)) != 0 ||
-					memcmp(elem_lbs, ARR_LBOUND(array),
-						   elem_ndims * sizeof(int)) != 0)
-					ereport(ERROR,
-							(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-							 errmsg("multidimensional arrays must have array "
-									"expressions with matching dimensions")));
-			}
-
-			subdata[outer_nelems] = ARR_DATA_PTR(array);
-			subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
-			subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
-			nbytes += subbytes[outer_nelems];
-			subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
-													 ARR_DIMS(array));
-			nitems += subnitems[outer_nelems];
-			havenulls |= ARR_HASNULL(array);
-			outer_nelems++;
-		}
-
-		/*
-		 * If all items were null or empty arrays, return an empty array;
-		 * otherwise, if some were and some weren't, raise error.  (Note: we
-		 * must special-case this somehow to avoid trying to generate a 1-D
-		 * array formed from empty arrays.  It's not ideal...)
-		 */
-		if (haveempty)
-		{
-			if (ndims == 0)		/* didn't find any nonempty array */
-				return PointerGetDatum(construct_empty_array(element_type));
-			ereport(ERROR,
-					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-					 errmsg("multidimensional arrays must have array "
-							"expressions with matching dimensions")));
-		}
-
-		/* setup for multi-D array */
-		dims[0] = outer_nelems;
-		lbs[0] = 1;
-		for (i = 1; i < ndims; i++)
-		{
-			dims[i] = elem_dims[i - 1];
-			lbs[i] = elem_lbs[i - 1];
-		}
-
-		if (havenulls)
-		{
-			dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
-			nbytes += dataoffset;
-		}
-		else
-		{
-			dataoffset = 0;		/* marker for no null bitmap */
-			nbytes += ARR_OVERHEAD_NONULLS(ndims);
-		}
-
-		result = (ArrayType *) palloc(nbytes);
-		SET_VARSIZE(result, nbytes);
-		result->ndim = ndims;
-		result->dataoffset = dataoffset;
-		result->elemtype = element_type;
-		memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
-		memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
-
-		dat = ARR_DATA_PTR(result);
-		iitem = 0;
-		for (i = 0; i < outer_nelems; i++)
-		{
-			memcpy(dat, subdata[i], subbytes[i]);
-			dat += subbytes[i];
-			if (havenulls)
-				array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
-								  subbitmaps[i], 0,
-								  subnitems[i]);
-			iitem += subnitems[i];
-		}
-	}
-
-	return PointerGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalRow - ROW() expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRow(RowExprState *rstate,
-			ExprContext *econtext,
-			bool *isNull)
-{
-	HeapTuple	tuple;
-	Datum	   *values;
-	bool	   *isnull;
-	int			natts;
-	ListCell   *arg;
-	int			i;
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	/* Allocate workspace */
-	natts = rstate->tupdesc->natts;
-	values = (Datum *) palloc0(natts * sizeof(Datum));
-	isnull = (bool *) palloc(natts * sizeof(bool));
-
-	/* preset to nulls in case rowtype has some later-added columns */
-	memset(isnull, true, natts * sizeof(bool));
-
-	/* Evaluate field values */
-	i = 0;
-	foreach(arg, rstate->args)
-	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-
-		values[i] = ExecEvalExpr(e, econtext, &isnull[i]);
-		i++;
-	}
-
-	tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
-
-	pfree(values);
-	pfree(isnull);
-
-	return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalRowCompare - ROW() comparison-op ROW()
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRowCompare(RowCompareExprState *rstate,
-				   ExprContext *econtext,
-				   bool *isNull)
-{
-	bool		result;
-	RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype;
-	int32		cmpresult = 0;
-	ListCell   *l;
-	ListCell   *r;
-	int			i;
-
-	*isNull = true;				/* until we get a result */
-
-	i = 0;
-	forboth(l, rstate->largs, r, rstate->rargs)
-	{
-		ExprState  *le = (ExprState *) lfirst(l);
-		ExprState  *re = (ExprState *) lfirst(r);
-		FunctionCallInfoData locfcinfo;
-
-		InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2,
-								 rstate->collations[i],
-								 NULL, NULL);
-		locfcinfo.arg[0] = ExecEvalExpr(le, econtext,
-										&locfcinfo.argnull[0]);
-		locfcinfo.arg[1] = ExecEvalExpr(re, econtext,
-										&locfcinfo.argnull[1]);
-		if (rstate->funcs[i].fn_strict &&
-			(locfcinfo.argnull[0] || locfcinfo.argnull[1]))
-			return (Datum) 0;	/* force NULL result */
-		locfcinfo.isnull = false;
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
-		if (locfcinfo.isnull)
-			return (Datum) 0;	/* force NULL result */
-		if (cmpresult != 0)
-			break;				/* no need to compare remaining columns */
-		i++;
-	}
-
-	switch (rctype)
-	{
-			/* EQ and NE cases aren't allowed here */
-		case ROWCOMPARE_LT:
-			result = (cmpresult < 0);
-			break;
-		case ROWCOMPARE_LE:
-			result = (cmpresult <= 0);
-			break;
-		case ROWCOMPARE_GE:
-			result = (cmpresult >= 0);
-			break;
-		case ROWCOMPARE_GT:
-			result = (cmpresult > 0);
-			break;
-		default:
-			elog(ERROR, "unrecognized RowCompareType: %d", (int) rctype);
-			result = 0;			/* keep compiler quiet */
-			break;
-	}
-
-	*isNull = false;
-	return BoolGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCoalesce
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
-				 bool *isNull)
-{
-	ListCell   *arg;
-
-	/* Simply loop through until something NOT NULL is found */
-	foreach(arg, coalesceExpr->args)
-	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-
-		value = ExecEvalExpr(e, econtext, isNull);
-		if (!*isNull)
-			return value;
-	}
-
-	/* Else return NULL */
-	*isNull = true;
-	return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalMinMax
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
-			   bool *isNull)
-{
-	Datum		result = (Datum) 0;
-	MinMaxExpr *minmax = (MinMaxExpr *) minmaxExpr->xprstate.expr;
-	Oid			collation = minmax->inputcollid;
-	MinMaxOp	op = minmax->op;
-	FunctionCallInfoData locfcinfo;
-	ListCell   *arg;
-
-	*isNull = true;				/* until we get a result */
-
-	InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2,
-							 collation, NULL, NULL);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-
-	foreach(arg, minmaxExpr->args)
-	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-		bool		valueIsNull;
-		int32		cmpresult;
-
-		value = ExecEvalExpr(e, econtext, &valueIsNull);
-		if (valueIsNull)
-			continue;			/* ignore NULL inputs */
-
-		if (*isNull)
-		{
-			/* first nonnull input, adopt value */
-			result = value;
-			*isNull = false;
-		}
-		else
-		{
-			/* apply comparison function */
-			locfcinfo.arg[0] = result;
-			locfcinfo.arg[1] = value;
-			locfcinfo.isnull = false;
-			cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
-			if (locfcinfo.isnull)		/* probably should not happen */
-				continue;
-			if (cmpresult > 0 && op == IS_LEAST)
-				result = value;
-			else if (cmpresult < 0 && op == IS_GREATEST)
-				result = value;
-		}
-	}
-
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalSQLValueFunction
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalSQLValueFunction(ExprState *svfExpr,
-						 ExprContext *econtext,
-						 bool *isNull)
-{
-	Datum		result = (Datum) 0;
-	SQLValueFunction *svf = (SQLValueFunction *) svfExpr->expr;
-	FunctionCallInfoData fcinfo;
-
-	*isNull = false;
-
-	/*
-	 * Note: current_schema() can return NULL.  current_user() etc currently
-	 * cannot, but might as well code those cases the same way for safety.
-	 */
-	switch (svf->op)
-	{
-		case SVFOP_CURRENT_DATE:
-			result = DateADTGetDatum(GetSQLCurrentDate());
-			break;
-		case SVFOP_CURRENT_TIME:
-		case SVFOP_CURRENT_TIME_N:
-			result = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
-			break;
-		case SVFOP_CURRENT_TIMESTAMP:
-		case SVFOP_CURRENT_TIMESTAMP_N:
-			result = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
-			break;
-		case SVFOP_LOCALTIME:
-		case SVFOP_LOCALTIME_N:
-			result = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
-			break;
-		case SVFOP_LOCALTIMESTAMP:
-		case SVFOP_LOCALTIMESTAMP_N:
-			result = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
-			break;
-		case SVFOP_CURRENT_ROLE:
-		case SVFOP_CURRENT_USER:
-		case SVFOP_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = current_user(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-		case SVFOP_SESSION_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = session_user(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-		case SVFOP_CURRENT_CATALOG:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = current_database(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-		case SVFOP_CURRENT_SCHEMA:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = current_schema(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-	}
-
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalXml
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-			bool *isNull)
-{
-	XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
-	Datum		value;
-	bool		isnull;
-	ListCell   *arg;
-	ListCell   *narg;
-
-	*isNull = true;				/* until we get a result */
-
-	switch (xexpr->op)
-	{
-		case IS_XMLCONCAT:
-			{
-				List	   *values = NIL;
-
-				foreach(arg, xmlExpr->args)
-				{
-					ExprState  *e = (ExprState *) lfirst(arg);
-
-					value = ExecEvalExpr(e, econtext, &isnull);
-					if (!isnull)
-						values = lappend(values, DatumGetPointer(value));
-				}
-
-				if (list_length(values) > 0)
-				{
-					*isNull = false;
-					return PointerGetDatum(xmlconcat(values));
-				}
-				else
-					return (Datum) 0;
-			}
-			break;
-
-		case IS_XMLFOREST:
-			{
-				StringInfoData buf;
-
-				initStringInfo(&buf);
-				forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
-				{
-					ExprState  *e = (ExprState *) lfirst(arg);
-					char	   *argname = strVal(lfirst(narg));
-
-					value = ExecEvalExpr(e, econtext, &isnull);
-					if (!isnull)
-					{
-						appendStringInfo(&buf, "<%s>%s</%s>",
-										 argname,
-										 map_sql_value_to_xml_value(value, exprType((Node *) e->expr), true),
-										 argname);
-						*isNull = false;
-					}
-				}
-
-				if (*isNull)
-				{
-					pfree(buf.data);
-					return (Datum) 0;
-				}
-				else
-				{
-					text	   *result;
-
-					result = cstring_to_text_with_len(buf.data, buf.len);
-					pfree(buf.data);
-
-					return PointerGetDatum(result);
-				}
-			}
-			break;
-
-		case IS_XMLELEMENT:
-			*isNull = false;
-			return PointerGetDatum(xmlelement(xmlExpr, econtext));
-			break;
-
-		case IS_XMLPARSE:
-			{
-				ExprState  *e;
-				text	   *data;
-				bool		preserve_whitespace;
-
-				/* arguments are known to be text, bool */
-				Assert(list_length(xmlExpr->args) == 2);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-				data = DatumGetTextP(value);
-
-				e = (ExprState *) lsecond(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)		/* probably can't happen */
-					return (Datum) 0;
-				preserve_whitespace = DatumGetBool(value);
-
-				*isNull = false;
-
-				return PointerGetDatum(xmlparse(data,
-												xexpr->xmloption,
-												preserve_whitespace));
-			}
-			break;
-
-		case IS_XMLPI:
-			{
-				ExprState  *e;
-				text	   *arg;
-
-				/* optional argument is known to be text */
-				Assert(list_length(xmlExpr->args) <= 1);
-
-				if (xmlExpr->args)
-				{
-					e = (ExprState *) linitial(xmlExpr->args);
-					value = ExecEvalExpr(e, econtext, &isnull);
-					if (isnull)
-						arg = NULL;
-					else
-						arg = DatumGetTextP(value);
-				}
-				else
-				{
-					arg = NULL;
-					isnull = false;
-				}
-
-				return PointerGetDatum(xmlpi(xexpr->name, arg, isnull, isNull));
-			}
-			break;
-
-		case IS_XMLROOT:
-			{
-				ExprState  *e;
-				xmltype    *data;
-				text	   *version;
-				int			standalone;
-
-				/* arguments are known to be xml, text, int */
-				Assert(list_length(xmlExpr->args) == 3);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-				data = DatumGetXmlP(value);
-
-				e = (ExprState *) lsecond(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					version = NULL;
-				else
-					version = DatumGetTextP(value);
-
-				e = (ExprState *) lthird(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				standalone = DatumGetInt32(value);
-
-				*isNull = false;
-
-				return PointerGetDatum(xmlroot(data,
-											   version,
-											   standalone));
-			}
-			break;
-
-		case IS_XMLSERIALIZE:
-			{
-				ExprState  *e;
-
-				/* argument type is known to be xml */
-				Assert(list_length(xmlExpr->args) == 1);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-
-				*isNull = false;
-
-				return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption));
-			}
-			break;
-
-		case IS_DOCUMENT:
-			{
-				ExprState  *e;
-
-				/* optional argument is known to be xml */
-				Assert(list_length(xmlExpr->args) == 1);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-				else
-				{
-					*isNull = false;
-					return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
-				}
-			}
-			break;
-	}
-
-	elog(ERROR, "unrecognized XML operation");
-	return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalNullIf
- *
- * Note that this is *always* derived from the equals operator,
- * but since we need special processing of the arguments
- * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNullIf(FuncExprState *nullIfExpr,
-			   ExprContext *econtext,
-			   bool *isNull)
-{
-	Datum		result;
-	FunctionCallInfo fcinfo;
-
-	/*
-	 * Initialize function cache if first time through
-	 */
-	if (nullIfExpr->func.fn_oid == InvalidOid)
-	{
-		NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
-
-		init_fcache(op->opfuncid, op->inputcollid, nullIfExpr,
-					econtext->ecxt_per_query_memory, false, false);
-	}
-
-	/*
-	 * Evaluate arguments
-	 */
-	fcinfo = &nullIfExpr->fcinfo_data;
-	ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext);
-	Assert(fcinfo->nargs == 2);
-
-	/* if either argument is NULL they can't be equal */
-	if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
-	{
-		fcinfo->isnull = false;
-		result = FunctionCallInvoke(fcinfo);
-		/* if the arguments are equal return null */
-		if (!fcinfo->isnull && DatumGetBool(result))
-		{
-			*isNull = true;
-			return (Datum) 0;
-		}
-	}
-
-	/* else return first argument */
-	*isNull = fcinfo->argnull[0];
-	return fcinfo->arg[0];
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalNullTest
- *
- *		Evaluate a NullTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNullTest(NullTestState *nstate,
-				 ExprContext *econtext,
-				 bool *isNull)
-{
-	NullTest   *ntest = (NullTest *) nstate->xprstate.expr;
-	Datum		result;
-
-	result = ExecEvalExpr(nstate->arg, econtext, isNull);
-
-	if (ntest->argisrow && !(*isNull))
-	{
-		/*
-		 * The SQL standard defines IS [NOT] NULL for a non-null rowtype
-		 * argument as:
-		 *
-		 * "R IS NULL" is true if every field is the null value.
-		 *
-		 * "R IS NOT NULL" is true if no field is the null value.
-		 *
-		 * This definition is (apparently intentionally) not recursive; so our
-		 * tests on the fields are primitive attisnull tests, not recursive
-		 * checks to see if they are all-nulls or no-nulls rowtypes.
-		 *
-		 * The standard does not consider the possibility of zero-field rows,
-		 * but here we consider them to vacuously satisfy both predicates.
-		 */
-		HeapTupleHeader tuple;
-		Oid			tupType;
-		int32		tupTypmod;
-		TupleDesc	tupDesc;
-		HeapTupleData tmptup;
-		int			att;
-
-		tuple = DatumGetHeapTupleHeader(result);
-
-		tupType = HeapTupleHeaderGetTypeId(tuple);
-		tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
-		/* Lookup tupdesc if first time through or if type changes */
-		tupDesc = get_cached_rowtype(tupType, tupTypmod,
-									 &nstate->argdesc, econtext);
-
-		/*
-		 * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
-		 */
-		tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-		tmptup.t_data = tuple;
-
-		for (att = 1; att <= tupDesc->natts; att++)
-		{
-			/* ignore dropped columns */
-			if (tupDesc->attrs[att - 1]->attisdropped)
-				continue;
-			if (heap_attisnull(&tmptup, att))
-			{
-				/* null field disproves IS NOT NULL */
-				if (ntest->nulltesttype == IS_NOT_NULL)
-					return BoolGetDatum(false);
-			}
-			else
-			{
-				/* non-null field disproves IS NULL */
-				if (ntest->nulltesttype == IS_NULL)
-					return BoolGetDatum(false);
-			}
-		}
-
-		return BoolGetDatum(true);
-	}
-	else
-	{
-		/* Simple scalar-argument case, or a null rowtype datum */
-		switch (ntest->nulltesttype)
-		{
-			case IS_NULL:
-				if (*isNull)
-				{
-					*isNull = false;
-					return BoolGetDatum(true);
-				}
-				else
-					return BoolGetDatum(false);
-			case IS_NOT_NULL:
-				if (*isNull)
-				{
-					*isNull = false;
-					return BoolGetDatum(false);
-				}
-				else
-					return BoolGetDatum(true);
-			default:
-				elog(ERROR, "unrecognized nulltesttype: %d",
-					 (int) ntest->nulltesttype);
-				return (Datum) 0;		/* keep compiler quiet */
-		}
-	}
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalBooleanTest
- *
- *		Evaluate a BooleanTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalBooleanTest(GenericExprState *bstate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
-	Datum		result;
-
-	result = ExecEvalExpr(bstate->arg, econtext, isNull);
-
-	switch (btest->booltesttype)
-	{
-		case IS_TRUE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(false);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(true);
-			else
-				return BoolGetDatum(false);
-		case IS_NOT_TRUE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(true);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(false);
-			else
-				return BoolGetDatum(true);
-		case IS_FALSE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(false);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(false);
-			else
-				return BoolGetDatum(true);
-		case IS_NOT_FALSE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(true);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(true);
-			else
-				return BoolGetDatum(false);
-		case IS_UNKNOWN:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(true);
-			}
-			else
-				return BoolGetDatum(false);
-		case IS_NOT_UNKNOWN:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(false);
-			}
-			else
-				return BoolGetDatum(true);
-		default:
-			elog(ERROR, "unrecognized booltesttype: %d",
-				 (int) btest->booltesttype);
-			return (Datum) 0;	/* keep compiler quiet */
-	}
-}
-
-/*
- * ExecEvalCoerceToDomain
- *
- * Test the provided data against the domain constraint(s).  If the data
- * passes the constraint specifications, pass it through (return the
- * datum) otherwise throw an error.
- */
-static Datum
-ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
-					   bool *isNull)
-{
-	CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
-	Datum		result;
-	ListCell   *l;
-
-	result = ExecEvalExpr(cstate->arg, econtext, isNull);
-
-	/* Make sure we have up-to-date constraints */
-	UpdateDomainConstraintRef(cstate->constraint_ref);
-
-	foreach(l, cstate->constraint_ref->constraints)
-	{
-		DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
-
-		switch (con->constrainttype)
-		{
-			case DOM_CONSTRAINT_NOTNULL:
-				if (*isNull)
-					ereport(ERROR,
-							(errcode(ERRCODE_NOT_NULL_VIOLATION),
-							 errmsg("domain %s does not allow null values",
-									format_type_be(ctest->resulttype)),
-							 errdatatype(ctest->resulttype)));
-				break;
-			case DOM_CONSTRAINT_CHECK:
-				{
-					Datum		conResult;
-					bool		conIsNull;
-					Datum		save_datum;
-					bool		save_isNull;
-
-					/*
-					 * Set up value to be returned by CoerceToDomainValue
-					 * nodes. We must save and restore prior setting of
-					 * econtext's domainValue fields, in case this node is
-					 * itself within a check expression for another domain.
-					 *
-					 * Also, if we are working with a read-write expanded
-					 * datum, be sure that what we pass to CHECK expressions
-					 * is a read-only pointer; else called functions might
-					 * modify or even delete the expanded object.
-					 */
-					save_datum = econtext->domainValue_datum;
-					save_isNull = econtext->domainValue_isNull;
-
-					econtext->domainValue_datum =
-						MakeExpandedObjectReadOnly(result, *isNull,
-									 cstate->constraint_ref->tcache->typlen);
-					econtext->domainValue_isNull = *isNull;
-
-					conResult = ExecEvalExpr(con->check_expr, econtext,
-											 &conIsNull);
-
-					if (!conIsNull &&
-						!DatumGetBool(conResult))
-						ereport(ERROR,
-								(errcode(ERRCODE_CHECK_VIOLATION),
-								 errmsg("value for domain %s violates check constraint \"%s\"",
-										format_type_be(ctest->resulttype),
-										con->name),
-								 errdomainconstraint(ctest->resulttype,
-													 con->name)));
-					econtext->domainValue_datum = save_datum;
-					econtext->domainValue_isNull = save_isNull;
-
-					break;
-				}
-			default:
-				elog(ERROR, "unrecognized constraint type: %d",
-					 (int) con->constrainttype);
-				break;
-		}
-	}
-
-	/* If all has gone well (constraints did not fail) return the datum */
-	return result;
-}
-
-/*
- * ExecEvalCoerceToDomainValue
- *
- * Return the value stored by CoerceToDomain.
- */
-static Datum
-ExecEvalCoerceToDomainValue(ExprState *exprstate,
-							ExprContext *econtext,
-							bool *isNull)
-{
-	*isNull = econtext->domainValue_isNull;
-	return econtext->domainValue_datum;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalFieldSelect
- *
- *		Evaluate a FieldSelect node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldSelect(FieldSelectState *fstate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
-	AttrNumber	fieldnum = fselect->fieldnum;
-	Datum		result;
-	Datum		tupDatum;
-	HeapTupleHeader tuple;
-	Oid			tupType;
-	int32		tupTypmod;
-	TupleDesc	tupDesc;
-	Form_pg_attribute attr;
-	HeapTupleData tmptup;
-
-	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
-	if (*isNull)
-		return tupDatum;
-
-	tuple = DatumGetHeapTupleHeader(tupDatum);
-
-	tupType = HeapTupleHeaderGetTypeId(tuple);
-	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
-	/* Lookup tupdesc if first time through or if type changes */
-	tupDesc = get_cached_rowtype(tupType, tupTypmod,
-								 &fstate->argdesc, econtext);
-
-	/*
-	 * Find field's attr record.  Note we don't support system columns here: a
-	 * datum tuple doesn't have valid values for most of the interesting
-	 * system columns anyway.
-	 */
-	if (fieldnum <= 0)			/* should never happen */
-		elog(ERROR, "unsupported reference to system column %d in FieldSelect",
-			 fieldnum);
-	if (fieldnum > tupDesc->natts)		/* should never happen */
-		elog(ERROR, "attribute number %d exceeds number of columns %d",
-			 fieldnum, tupDesc->natts);
-	attr = tupDesc->attrs[fieldnum - 1];
-
-	/* Check for dropped column, and force a NULL result if so */
-	if (attr->attisdropped)
-	{
-		*isNull = true;
-		return (Datum) 0;
-	}
-
-	/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
-	/* As in ExecEvalScalarVar, we should but can't check typmod */
-	if (fselect->resulttype != attr->atttypid)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("attribute %d has wrong type", fieldnum),
-				 errdetail("Table has type %s, but query expects %s.",
-						   format_type_be(attr->atttypid),
-						   format_type_be(fselect->resulttype))));
-
-	/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
-	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-	tmptup.t_data = tuple;
-
-	result = heap_getattr(&tmptup,
-						  fieldnum,
-						  tupDesc,
-						  isNull);
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalFieldStore
- *
- *		Evaluate a FieldStore node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldStore(FieldStoreState *fstate,
-				   ExprContext *econtext,
-				   bool *isNull)
-{
-	FieldStore *fstore = (FieldStore *) fstate->xprstate.expr;
-	HeapTuple	tuple;
-	Datum		tupDatum;
-	TupleDesc	tupDesc;
-	Datum	   *values;
-	bool	   *isnull;
-	Datum		save_datum;
-	bool		save_isNull;
-	ListCell   *l1,
-			   *l2;
-
-	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
-	/* Lookup tupdesc if first time through or after rescan */
-	tupDesc = get_cached_rowtype(fstore->resulttype, -1,
-								 &fstate->argdesc, econtext);
-
-	/* Allocate workspace */
-	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
-	isnull = (bool *) palloc(tupDesc->natts * sizeof(bool));
-
-	if (!*isNull)
-	{
-		/*
-		 * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
-		 * set all the fields in the struct just in case.
-		 */
-		HeapTupleHeader tuphdr;
-		HeapTupleData tmptup;
-
-		tuphdr = DatumGetHeapTupleHeader(tupDatum);
-		tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
-		ItemPointerSetInvalid(&(tmptup.t_self));
-		tmptup.t_tableOid = InvalidOid;
-		tmptup.t_data = tuphdr;
-
-		heap_deform_tuple(&tmptup, tupDesc, values, isnull);
-	}
-	else
-	{
-		/* Convert null input tuple into an all-nulls row */
-		memset(isnull, true, tupDesc->natts * sizeof(bool));
-	}
-
-	/* Result is never null */
-	*isNull = false;
-
-	save_datum = econtext->caseValue_datum;
-	save_isNull = econtext->caseValue_isNull;
-
-	forboth(l1, fstate->newvals, l2, fstore->fieldnums)
-	{
-		ExprState  *newval = (ExprState *) lfirst(l1);
-		AttrNumber	fieldnum = lfirst_int(l2);
-
-		Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
-
-		/*
-		 * Use the CaseTestExpr mechanism to pass down the old value of the
-		 * field being replaced; this is needed in case the newval is itself a
-		 * FieldStore or ArrayRef that has to obtain and modify the old value.
-		 * It's safe to reuse the CASE mechanism because there cannot be a
-		 * CASE between here and where the value would be needed, and a field
-		 * assignment can't be within a CASE either.  (So saving and restoring
-		 * the caseValue is just paranoia, but let's do it anyway.)
-		 */
-		econtext->caseValue_datum = values[fieldnum - 1];
-		econtext->caseValue_isNull = isnull[fieldnum - 1];
-
-		values[fieldnum - 1] = ExecEvalExpr(newval,
-											econtext,
-											&isnull[fieldnum - 1]);
-	}
-
-	econtext->caseValue_datum = save_datum;
-	econtext->caseValue_isNull = save_isNull;
-
-	tuple = heap_form_tuple(tupDesc, values, isnull);
-
-	pfree(values);
-	pfree(isnull);
-
-	return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalRelabelType
- *
- *		Evaluate a RelabelType node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRelabelType(GenericExprState *exprstate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	return ExecEvalExpr(exprstate->arg, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCoerceViaIO
- *
- *		Evaluate a CoerceViaIO node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	Datum		result;
-	Datum		inputval;
-	char	   *string;
-
-	inputval = ExecEvalExpr(iostate->arg, econtext, isNull);
-
-	if (*isNull)
-		string = NULL;			/* output functions are not called on nulls */
-	else
-		string = OutputFunctionCall(&iostate->outfunc, inputval);
-
-	result = InputFunctionCall(&iostate->infunc,
-							   string,
-							   iostate->intypioparam,
-							   -1);
-
-	/* The input function cannot change the null/not-null status */
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalArrayCoerceExpr
- *
- *		Evaluate an ArrayCoerceExpr node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
-						ExprContext *econtext,
-						bool *isNull)
-{
-	ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
-	Datum		result;
-	FunctionCallInfoData locfcinfo;
-
-	result = ExecEvalExpr(astate->arg, econtext, isNull);
-
-	if (*isNull)
-		return result;			/* nothing to do */
-
-	/*
-	 * If it's binary-compatible, modify the element type in the array header,
-	 * but otherwise leave the array as we received it.
-	 */
-	if (!OidIsValid(acoerce->elemfuncid))
-	{
-		/* Detoast input array if necessary, and copy in any case */
-		ArrayType  *array = DatumGetArrayTypePCopy(result);
-
-		ARR_ELEMTYPE(array) = astate->resultelemtype;
-		PG_RETURN_ARRAYTYPE_P(array);
-	}
-
-	/* Initialize function cache if first time through */
-	if (astate->elemfunc.fn_oid == InvalidOid)
-	{
-		AclResult	aclresult;
-
-		/* Check permission to call function */
-		aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
-									 ACL_EXECUTE);
-		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult, ACL_KIND_PROC,
-						   get_func_name(acoerce->elemfuncid));
-		InvokeFunctionExecuteHook(acoerce->elemfuncid);
-
-		/* Set up the primary fmgr lookup information */
-		fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
-					  econtext->ecxt_per_query_memory);
-		fmgr_info_set_expr((Node *) acoerce, &(astate->elemfunc));
-	}
-
-	/*
-	 * Use array_map to apply the function to each array element.
-	 *
-	 * We pass on the desttypmod and isExplicit flags whether or not the
-	 * function wants them.
-	 *
-	 * Note: coercion functions are assumed to not use collation.
-	 */
-	InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
-							 InvalidOid, NULL, NULL);
-	locfcinfo.arg[0] = result;
-	locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
-	locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
-
-	return array_map(&locfcinfo, astate->resultelemtype, astate->amstate);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCurrentOfExpr
- *
- * The planner should convert CURRENT OF into a TidScan qualification, or some
- * other special handling in a ForeignScan node.  So we have to be able to do
- * ExecInitExpr on a CurrentOfExpr, but we shouldn't ever actually execute it.
- * If we get here, we suppose we must be dealing with CURRENT OF on a foreign
- * table whose FDW doesn't handle it, and complain accordingly.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull)
-{
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-		   errmsg("WHERE CURRENT OF is not supported for this table type")));
-	return 0;					/* keep compiler quiet */
-}
-
-
-/*
- * ExecEvalExprSwitchContext
- *
- * Same as ExecEvalExpr, but get into the right allocation context explicitly.
- */
-Datum
-ExecEvalExprSwitchContext(ExprState *expression,
-						  ExprContext *econtext,
-						  bool *isNull)
-{
-	Datum		retDatum;
-	MemoryContext oldContext;
-
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-	retDatum = ExecEvalExpr(expression, econtext, isNull);
-	MemoryContextSwitchTo(oldContext);
-	return retDatum;
-}
-
-
-/*
- * ExecInitExpr: prepare an expression tree for execution
- *
- * This function builds and returns an ExprState tree paralleling the given
- * Expr node tree.  The ExprState tree can then be handed to ExecEvalExpr
- * for execution.  Because the Expr tree itself is read-only as far as
- * ExecInitExpr and ExecEvalExpr are concerned, several different executions
- * of the same plan tree can occur concurrently.
- *
- * This must be called in a memory context that will last as long as repeated
- * executions of the expression are needed.  Typically the context will be
- * the same as the per-query context of the associated ExprContext.
- *
- * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
- * lists of such nodes held by the parent PlanState. Otherwise, we do very
- * little initialization here other than building the state-node tree.  Any
- * nontrivial work associated with initializing runtime info for a node should
- * happen during the first actual evaluation of that node.  (This policy lets
- * us avoid work if the node is never actually evaluated.)
- *
- * Note: there is no ExecEndExpr function; we assume that any resource
- * cleanup needed will be handled by just releasing the memory context
- * in which the state tree is built.  Functions that require additional
- * cleanup work can register a shutdown callback in the ExprContext.
- *
- *	'node' is the root of the expression tree to examine
- *	'parent' is the PlanState node that owns the expression.
- *
- * 'parent' may be NULL if we are preparing an expression that is not
- * associated with a plan tree.  (If so, it can't have aggs or subplans.)
- * This case should usually come through ExecPrepareExpr, not directly here.
- */
-ExprState *
-ExecInitExpr(Expr *node, PlanState *parent)
-{
-	ExprState  *state;
-
-	if (node == NULL)
-		return NULL;
-
-	/* Guard against stack overflow due to overly complex expressions */
-	check_stack_depth();
-
-	switch (nodeTag(node))
-	{
-		case T_Var:
-			/* varattno == InvalidAttrNumber means it's a whole-row Var */
-			if (((Var *) node)->varattno == InvalidAttrNumber)
-			{
-				WholeRowVarExprState *wstate = makeNode(WholeRowVarExprState);
-
-				wstate->parent = parent;
-				wstate->wrv_tupdesc = NULL;
-				wstate->wrv_junkFilter = NULL;
-				state = (ExprState *) wstate;
-				state->evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowVar;
-			}
-			else
-			{
-				state = makeNode(ExprState);
-				state->evalfunc = ExecEvalScalarVar;
-			}
-			break;
-		case T_Const:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalConst;
-			break;
-		case T_Param:
-			state = makeNode(ExprState);
-			switch (((Param *) node)->paramkind)
-			{
-				case PARAM_EXEC:
-					state->evalfunc = ExecEvalParamExec;
-					break;
-				case PARAM_EXTERN:
-					state->evalfunc = ExecEvalParamExtern;
-					break;
-				default:
-					elog(ERROR, "unrecognized paramkind: %d",
-						 (int) ((Param *) node)->paramkind);
-					break;
-			}
-			break;
-		case T_CoerceToDomainValue:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalCoerceToDomainValue;
-			break;
-		case T_CaseTestExpr:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalCaseTestExpr;
-			break;
-		case T_Aggref:
-			{
-				AggrefExprState *astate = makeNode(AggrefExprState);
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
-				if (parent && IsA(parent, AggState))
-				{
-					AggState   *aggstate = (AggState *) parent;
-
-					aggstate->aggs = lcons(astate, aggstate->aggs);
-					aggstate->numaggs++;
-				}
-				else
-				{
-					/* planner messed up */
-					elog(ERROR, "Aggref found in non-Agg plan node");
-				}
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_GroupingFunc:
-			{
-				GroupingFunc *grp_node = (GroupingFunc *) node;
-				GroupingFuncExprState *grp_state = makeNode(GroupingFuncExprState);
-				Agg		   *agg = NULL;
-
-				if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
-					elog(ERROR, "parent of GROUPING is not Agg node");
-
-				grp_state->aggstate = (AggState *) parent;
-
-				agg = (Agg *) (parent->plan);
-
-				if (agg->groupingSets)
-					grp_state->clauses = grp_node->cols;
-				else
-					grp_state->clauses = NIL;
-
-				state = (ExprState *) grp_state;
-				state->evalfunc = (ExprStateEvalFunc) ExecEvalGroupingFuncExpr;
-			}
-			break;
-		case T_WindowFunc:
-			{
-				WindowFunc *wfunc = (WindowFunc *) node;
-				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
-
-				wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
-				if (parent && IsA(parent, WindowAggState))
-				{
-					WindowAggState *winstate = (WindowAggState *) parent;
-					int			nfuncs;
-
-					winstate->funcs = lcons(wfstate, winstate->funcs);
-					nfuncs = ++winstate->numfuncs;
-					if (wfunc->winagg)
-						winstate->numaggs++;
-
-					wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
-														  parent);
-					wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
-													  parent);
-
-					/*
-					 * Complain if the windowfunc's arguments contain any
-					 * windowfuncs; nested window functions are semantically
-					 * nonsensical.  (This should have been caught earlier,
-					 * but we defend against it here anyway.)
-					 */
-					if (nfuncs != winstate->numfuncs)
-						ereport(ERROR,
-								(errcode(ERRCODE_WINDOWING_ERROR),
-						  errmsg("window function calls cannot be nested")));
-				}
-				else
-				{
-					/* planner messed up */
-					elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
-				}
-				state = (ExprState *) wfstate;
-			}
-			break;
-		case T_ArrayRef:
-			{
-				ArrayRef   *aref = (ArrayRef *) node;
-				ArrayRefExprState *astate = makeNode(ArrayRefExprState);
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
-				astate->refupperindexpr = (List *)
-					ExecInitExpr((Expr *) aref->refupperindexpr, parent);
-				astate->reflowerindexpr = (List *)
-					ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
-				astate->refexpr = ExecInitExpr(aref->refexpr, parent);
-				astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
-													parent);
-				/* do one-time catalog lookups for type info */
-				astate->refattrlength = get_typlen(aref->refarraytype);
-				get_typlenbyvalalign(aref->refelemtype,
-									 &astate->refelemlength,
-									 &astate->refelembyval,
-									 &astate->refelemalign);
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_FuncExpr:
-			{
-				FuncExpr   *funcexpr = (FuncExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) funcexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = funcexpr->funcretset;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_OpExpr:
-			{
-				OpExpr	   *opexpr = (OpExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) opexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = opexpr->opretset;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_DistinctExpr:
-			{
-				DistinctExpr *distinctexpr = (DistinctExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) distinctexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = false; /* not supported */
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_NullIfExpr:
-			{
-				NullIfExpr *nullifexpr = (NullIfExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) nullifexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = false; /* not supported */
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_ScalarArrayOpExpr:
-			{
-				ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
-				ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
-
-				sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
-				sstate->fxprstate.args = (List *)
-					ExecInitExpr((Expr *) opexpr->args, parent);
-				sstate->fxprstate.func.fn_oid = InvalidOid;		/* not initialized */
-				sstate->fxprstate.funcReturnsSet = false;		/* not supported */
-				sstate->element_type = InvalidOid;		/* ditto */
-				state = (ExprState *) sstate;
-			}
-			break;
-		case T_BoolExpr:
-			{
-				BoolExpr   *boolexpr = (BoolExpr *) node;
-				BoolExprState *bstate = makeNode(BoolExprState);
-
-				switch (boolexpr->boolop)
-				{
-					case AND_EXPR:
-						bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAnd;
-						break;
-					case OR_EXPR:
-						bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOr;
-						break;
-					case NOT_EXPR:
-						bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNot;
-						break;
-					default:
-						elog(ERROR, "unrecognized boolop: %d",
-							 (int) boolexpr->boolop);
-						break;
-				}
-				bstate->args = (List *)
-					ExecInitExpr((Expr *) boolexpr->args, parent);
-				state = (ExprState *) bstate;
-			}
-			break;
-		case T_SubPlan:
-			{
-				SubPlan    *subplan = (SubPlan *) node;
-				SubPlanState *sstate;
-
-				if (!parent)
-					elog(ERROR, "SubPlan found with no parent plan");
-
-				sstate = ExecInitSubPlan(subplan, parent);
-
-				/* Add SubPlanState nodes to parent->subPlan */
-				parent->subPlan = lappend(parent->subPlan, sstate);
-
-				state = (ExprState *) sstate;
-			}
-			break;
-		case T_AlternativeSubPlan:
-			{
-				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
-				AlternativeSubPlanState *asstate;
-
-				if (!parent)
-					elog(ERROR, "AlternativeSubPlan found with no parent plan");
-
-				asstate = ExecInitAlternativeSubPlan(asplan, parent);
-
-				state = (ExprState *) asstate;
-			}
-			break;
-		case T_FieldSelect:
-			{
-				FieldSelect *fselect = (FieldSelect *) node;
-				FieldSelectState *fstate = makeNode(FieldSelectState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
-				fstate->arg = ExecInitExpr(fselect->arg, parent);
-				fstate->argdesc = NULL;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_FieldStore:
-			{
-				FieldStore *fstore = (FieldStore *) node;
-				FieldStoreState *fstate = makeNode(FieldStoreState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
-				fstate->arg = ExecInitExpr(fstore->arg, parent);
-				fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
-				fstate->argdesc = NULL;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_RelabelType:
-			{
-				RelabelType *relabel = (RelabelType *) node;
-				GenericExprState *gstate = makeNode(GenericExprState);
-
-				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
-				gstate->arg = ExecInitExpr(relabel->arg, parent);
-				state = (ExprState *) gstate;
-			}
-			break;
-		case T_CoerceViaIO:
-			{
-				CoerceViaIO *iocoerce = (CoerceViaIO *) node;
-				CoerceViaIOState *iostate = makeNode(CoerceViaIOState);
-				Oid			iofunc;
-				bool		typisvarlena;
-
-				iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
-				iostate->arg = ExecInitExpr(iocoerce->arg, parent);
-				/* lookup the result type's input function */
-				getTypeInputInfo(iocoerce->resulttype, &iofunc,
-								 &iostate->intypioparam);
-				fmgr_info(iofunc, &iostate->infunc);
-				/* lookup the input type's output function */
-				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
-								  &iofunc, &typisvarlena);
-				fmgr_info(iofunc, &iostate->outfunc);
-				state = (ExprState *) iostate;
-			}
-			break;
-		case T_ArrayCoerceExpr:
-			{
-				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
-				ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
-				astate->arg = ExecInitExpr(acoerce->arg, parent);
-				astate->resultelemtype = get_element_type(acoerce->resulttype);
-				if (astate->resultelemtype == InvalidOid)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("target type is not an array")));
-				/* Arrays over domains aren't supported yet */
-				Assert(getBaseType(astate->resultelemtype) ==
-					   astate->resultelemtype);
-				astate->elemfunc.fn_oid = InvalidOid;	/* not initialized */
-				astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_ConvertRowtypeExpr:
-			{
-				ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
-				ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
-				cstate->arg = ExecInitExpr(convert->arg, parent);
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_CaseExpr:
-			{
-				CaseExpr   *caseexpr = (CaseExpr *) node;
-				CaseExprState *cstate = makeNode(CaseExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
-				cstate->arg = ExecInitExpr(caseexpr->arg, parent);
-				foreach(l, caseexpr->args)
-				{
-					CaseWhen   *when = castNode(CaseWhen, lfirst(l));
-					CaseWhenState *wstate = makeNode(CaseWhenState);
-
-					wstate->xprstate.evalfunc = NULL;	/* not used */
-					wstate->xprstate.expr = (Expr *) when;
-					wstate->expr = ExecInitExpr(when->expr, parent);
-					wstate->result = ExecInitExpr(when->result, parent);
-					outlist = lappend(outlist, wstate);
-				}
-				cstate->args = outlist;
-				cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
-				if (caseexpr->arg)
-					cstate->argtyplen = get_typlen(exprType((Node *) caseexpr->arg));
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_ArrayExpr:
-			{
-				ArrayExpr  *arrayexpr = (ArrayExpr *) node;
-				ArrayExprState *astate = makeNode(ArrayExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArray;
-				foreach(l, arrayexpr->elements)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				astate->elements = outlist;
-				/* do one-time catalog lookup for type info */
-				get_typlenbyvalalign(arrayexpr->element_typeid,
-									 &astate->elemlength,
-									 &astate->elembyval,
-									 &astate->elemalign);
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_RowExpr:
-			{
-				RowExpr    *rowexpr = (RowExpr *) node;
-				RowExprState *rstate = makeNode(RowExprState);
-				Form_pg_attribute *attrs;
-				List	   *outlist = NIL;
-				ListCell   *l;
-				int			i;
-
-				rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow;
-				/* Build tupdesc to describe result tuples */
-				if (rowexpr->row_typeid == RECORDOID)
-				{
-					/* generic record, use types of given expressions */
-					rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
-				}
-				else
-				{
-					/* it's been cast to a named type, use that */
-					rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
-				}
-				/* In either case, adopt RowExpr's column aliases */
-				ExecTypeSetColNames(rstate->tupdesc, rowexpr->colnames);
-				/* Bless the tupdesc in case it's now of type RECORD */
-				BlessTupleDesc(rstate->tupdesc);
-				/* Set up evaluation, skipping any deleted columns */
-				Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
-				attrs = rstate->tupdesc->attrs;
-				i = 0;
-				foreach(l, rowexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					if (!attrs[i]->attisdropped)
-					{
-						/*
-						 * Guard against ALTER COLUMN TYPE on rowtype since
-						 * the RowExpr was created.  XXX should we check
-						 * typmod too?	Not sure we can be sure it'll be the
-						 * same.
-						 */
-						if (exprType((Node *) e) != attrs[i]->atttypid)
-							ereport(ERROR,
-									(errcode(ERRCODE_DATATYPE_MISMATCH),
-									 errmsg("ROW() column has type %s instead of type %s",
-										format_type_be(exprType((Node *) e)),
-									   format_type_be(attrs[i]->atttypid))));
-					}
-					else
-					{
-						/*
-						 * Ignore original expression and insert a NULL. We
-						 * don't really care what type of NULL it is, so
-						 * always make an int4 NULL.
-						 */
-						e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
-					}
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-					i++;
-				}
-				rstate->args = outlist;
-				state = (ExprState *) rstate;
-			}
-			break;
-		case T_RowCompareExpr:
-			{
-				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
-				RowCompareExprState *rstate = makeNode(RowCompareExprState);
-				int			nopers = list_length(rcexpr->opnos);
-				List	   *outlist;
-				ListCell   *l;
-				ListCell   *l2;
-				ListCell   *l3;
-				int			i;
-
-				rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
-				Assert(list_length(rcexpr->largs) == nopers);
-				outlist = NIL;
-				foreach(l, rcexpr->largs)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				rstate->largs = outlist;
-				Assert(list_length(rcexpr->rargs) == nopers);
-				outlist = NIL;
-				foreach(l, rcexpr->rargs)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				rstate->rargs = outlist;
-				Assert(list_length(rcexpr->opfamilies) == nopers);
-				rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
-				rstate->collations = (Oid *) palloc(nopers * sizeof(Oid));
-				i = 0;
-				forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids)
-				{
-					Oid			opno = lfirst_oid(l);
-					Oid			opfamily = lfirst_oid(l2);
-					Oid			inputcollid = lfirst_oid(l3);
-					int			strategy;
-					Oid			lefttype;
-					Oid			righttype;
-					Oid			proc;
-
-					get_op_opfamily_properties(opno, opfamily, false,
-											   &strategy,
-											   &lefttype,
-											   &righttype);
-					proc = get_opfamily_proc(opfamily,
-											 lefttype,
-											 righttype,
-											 BTORDER_PROC);
-
-					/*
-					 * If we enforced permissions checks on index support
-					 * functions, we'd need to make a check here.  But the
-					 * index support machinery doesn't do that, and neither
-					 * does this code.
-					 */
-					fmgr_info(proc, &(rstate->funcs[i]));
-					rstate->collations[i] = inputcollid;
-					i++;
-				}
-				state = (ExprState *) rstate;
-			}
-			break;
-		case T_CoalesceExpr:
-			{
-				CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
-				CoalesceExprState *cstate = makeNode(CoalesceExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoalesce;
-				foreach(l, coalesceexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				cstate->args = outlist;
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_MinMaxExpr:
-			{
-				MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
-				MinMaxExprState *mstate = makeNode(MinMaxExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-				TypeCacheEntry *typentry;
-
-				mstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalMinMax;
-				foreach(l, minmaxexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				mstate->args = outlist;
-				/* Look up the btree comparison function for the datatype */
-				typentry = lookup_type_cache(minmaxexpr->minmaxtype,
-											 TYPECACHE_CMP_PROC);
-				if (!OidIsValid(typentry->cmp_proc))
-					ereport(ERROR,
-							(errcode(ERRCODE_UNDEFINED_FUNCTION),
-							 errmsg("could not identify a comparison function for type %s",
-									format_type_be(minmaxexpr->minmaxtype))));
-
-				/*
-				 * If we enforced permissions checks on index support
-				 * functions, we'd need to make a check here.  But the index
-				 * support machinery doesn't do that, and neither does this
-				 * code.
-				 */
-				fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
-				state = (ExprState *) mstate;
-			}
-			break;
-		case T_SQLValueFunction:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalSQLValueFunction;
-			break;
-		case T_XmlExpr:
-			{
-				XmlExpr    *xexpr = (XmlExpr *) node;
-				XmlExprState *xstate = makeNode(XmlExprState);
-				List	   *outlist;
-				ListCell   *arg;
-
-				xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml;
-				outlist = NIL;
-				foreach(arg, xexpr->named_args)
-				{
-					Expr	   *e = (Expr *) lfirst(arg);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				xstate->named_args = outlist;
-
-				outlist = NIL;
-				foreach(arg, xexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(arg);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				xstate->args = outlist;
-
-				state = (ExprState *) xstate;
-			}
-			break;
-		case T_NullTest:
-			{
-				NullTest   *ntest = (NullTest *) node;
-				NullTestState *nstate = makeNode(NullTestState);
-
-				nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
-				nstate->arg = ExecInitExpr(ntest->arg, parent);
-				nstate->argdesc = NULL;
-				state = (ExprState *) nstate;
-			}
-			break;
-		case T_BooleanTest:
-			{
-				BooleanTest *btest = (BooleanTest *) node;
-				GenericExprState *gstate = makeNode(GenericExprState);
-
-				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
-				gstate->arg = ExecInitExpr(btest->arg, parent);
-				state = (ExprState *) gstate;
-			}
-			break;
-		case T_CoerceToDomain:
-			{
-				CoerceToDomain *ctest = (CoerceToDomain *) node;
-				CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
-				cstate->arg = ExecInitExpr(ctest->arg, parent);
-				/* We spend an extra palloc to reduce header inclusions */
-				cstate->constraint_ref = (DomainConstraintRef *)
-					palloc(sizeof(DomainConstraintRef));
-				InitDomainConstraintRef(ctest->resulttype,
-										cstate->constraint_ref,
-										CurrentMemoryContext);
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_CurrentOfExpr:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalCurrentOfExpr;
-			break;
-		case T_TargetEntry:
-			{
-				TargetEntry *tle = (TargetEntry *) node;
-				GenericExprState *gstate = makeNode(GenericExprState);
-
-				gstate->xprstate.evalfunc = NULL;		/* not used */
-				gstate->arg = ExecInitExpr(tle->expr, parent);
-				state = (ExprState *) gstate;
-			}
-			break;
-		case T_List:
-			{
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				foreach(l, (List *) node)
-				{
-					outlist = lappend(outlist,
-									  ExecInitExpr((Expr *) lfirst(l),
-												   parent));
-				}
-				/* Don't fall through to the "common" code below */
-				return (ExprState *) outlist;
-			}
-		default:
-			elog(ERROR, "unrecognized node type: %d",
-				 (int) nodeTag(node));
-			state = NULL;		/* keep compiler quiet */
-			break;
-	}
-
-	/* Common code for all state-node types */
-	state->expr = node;
-
-	return state;
-}
-
-/*
- * ExecPrepareExpr --- initialize for expression execution outside a normal
- * Plan tree context.
- *
- * This differs from ExecInitExpr in that we don't assume the caller is
- * already running in the EState's per-query context.  Also, we run the
- * passed expression tree through expression_planner() to prepare it for
- * execution.  (In ordinary Plan trees the regular planning process will have
- * made the appropriate transformations on expressions, but for standalone
- * expressions this won't have happened.)
- */
-ExprState *
-ExecPrepareExpr(Expr *node, EState *estate)
-{
-	ExprState  *result;
-	MemoryContext oldcontext;
-
-	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
-
-	node = expression_planner(node);
-
-	result = ExecInitExpr(node, NULL);
-
-	MemoryContextSwitchTo(oldcontext);
-
-	return result;
-}
-
-
-/* ----------------------------------------------------------------
- *					 ExecQual / ExecTargetList / ExecProject
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5136,177 +1075,11 @@ ExecCleanTargetListLength(List *targetlist)
 
 	foreach(tl, targetlist)
 	{
-		TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
+		TargetEntry *curTle = (TargetEntry *) lfirst(tl);
 
+		Assert(IsA(curTle, TargetEntry));
 		if (!curTle->resjunk)
 			len++;
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.
- *
- * tupdesc must describe the rowtype of the expected result.
- *
- * Results are stored into the passed values and isnull arrays.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static void
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-	}
-
-	MemoryContextSwitchTo(oldContext);
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace.
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		ExecTargetList(projInfo->pi_targetlist,
-					   slot->tts_tupleDescriptor,
-					   econtext,
-					   slot->tts_values,
-					   slot->tts_isnull);
-	}
-
-	/*
-	 * Mark the result slot as containing a valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 65196795d7..138a596743 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState *qual;
 	ProjectionInfo *projInfo;
 
 	/*
@@ -187,7 +187,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual, econtext))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index d205101b89..5bc89f8554 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -470,136 +470,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -680,9 +550,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index aa08152350..2f3355636d 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1639,7 +1639,7 @@ project_aggregates(AggState *aggstate)
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual, econtext))
 	{
 		/*
 		 * Form and return projection tuple using the aggregate results and
@@ -2506,13 +2506,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	 * under SQL semantics anyway (and it's forbidden by the spec). Because
 	 * that is true, we don't need to worry about evaluating the aggs in any
 	 * particular order.
+	 * FIXME: adjust comment (now added in projection)
 	 */
-	aggstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) aggstate);
 
 	/*
 	 * Initialize child nodes.
@@ -2724,7 +2721,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	foreach(l, aggstate->aggs)
 	{
 		AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
-		Aggref	   *aggref = (Aggref *) aggrefstate->xprstate.expr;
+		Aggref	   *aggref = aggrefstate->aggref;
 		AggStatePerAgg peragg;
 		AggStatePerTrans pertrans;
 		int			existing_aggno;
@@ -3024,11 +3021,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	/* and then create a projection for that targetlist */
 	aggstate->evaldesc = ExecTypeFromTL(combined_inputeval, false);
 	aggstate->evalslot = ExecInitExtraTupleSlot(estate);
-	combined_inputeval = (List *) ExecInitExpr((Expr *) combined_inputeval,
-											   (PlanState *) aggstate);
 	aggstate->evalproj = ExecBuildProjectionInfo(combined_inputeval,
 												 aggstate->tmpcontext,
 												 aggstate->evalslot,
+												 &aggstate->ss.ps,
 												 NULL);
 	ExecSetSlotDescriptor(aggstate->evalslot, aggstate->evaldesc);
 
@@ -3206,8 +3202,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 	naggs = aggstate->numaggs;
 	pertrans->aggfilter = ExecInitExpr(aggref->aggfilter,
 									   (PlanState *) aggstate);
-	pertrans->aggdirectargs = (List *) ExecInitExpr((Expr *) aggref->aggdirectargs,
-													(PlanState *) aggstate);
+	pertrans->aggdirectargs = ExecInitExprList(aggref->aggdirectargs,
+											   (PlanState *) aggstate);
 
 	/*
 	 * Complain if the aggregate's arguments contain any aggregates; nested
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index c1aa9f13bd..0d90172731 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -238,7 +238,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -457,7 +457,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig, econtext);
 }
 
 /* ----------------------------------------------------------------
@@ -608,15 +608,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 8f4e0f527e..bed7949c5a 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,12 +242,8 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index d464748290..57a4ce3218 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -49,11 +49,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	ExecAssignExprContext(estate, &css->ss.ps);
 
 	/* initialize child expressions */
-	css->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
-					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
 					 (PlanState *) css);
 
 	/* tuple table initialization */
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 3b6d1390eb..9ae1561404 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals, econtext);
 }
 
 /* ----------------------------------------------------------------
@@ -155,15 +155,10 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 972022784d..250efcb449 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -35,7 +35,7 @@
  */
 typedef struct FunctionScanPerFuncState
 {
-	ExprState  *funcexpr;		/* state of the expression being evaluated */
+	SetExprState *setexpr;		/* state of the expression being evaluated */
 	TupleDesc	tupdesc;		/* desc of the function result type */
 	int			colcount;		/* expected number of result columns */
 	Tuplestorestate *tstore;	/* holds the function result set */
@@ -92,7 +92,7 @@ FunctionNext(FunctionScanState *node)
 		if (tstore == NULL)
 		{
 			node->funcstates[0].tstore = tstore =
-				ExecMakeTableFunctionResult(node->funcstates[0].funcexpr,
+				ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
 											node->ss.ps.ps_ExprContext,
 											node->argcontext,
 											node->funcstates[0].tupdesc,
@@ -151,7 +151,7 @@ FunctionNext(FunctionScanState *node)
 		if (fs->tstore == NULL)
 		{
 			fs->tstore =
-				ExecMakeTableFunctionResult(fs->funcexpr,
+				ExecMakeTableFunctionResult(fs->setexpr,
 											node->ss.ps.ps_ExprContext,
 											node->argcontext,
 											fs->tupdesc,
@@ -340,11 +340,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
 					 (PlanState *) scanstate);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
@@ -361,7 +358,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 		Oid			funcrettype;
 		TupleDesc	tupdesc;
 
-		fs->funcexpr = ExecInitExpr((Expr *) funcexpr, (PlanState *) scanstate);
+		fs->setexpr =
+			ExecInitTableFunctionResult((Expr *) funcexpr,
+										scanstate->ss.ps.ps_ExprContext,
+										&scanstate->ss.ps);
 
 		/*
 		 * Don't allocate the tuplestores; the actual calls to the functions
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 32c97d390e..1e5b1b7675 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -81,12 +81,8 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	gatherstate->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) gatherstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index 66c095bc72..af9ba4905e 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -85,7 +85,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual, econtext))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -139,7 +139,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual, econtext))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -188,12 +188,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	grpstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) grpstate);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index e695d8834b..cfc6b96093 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -190,12 +190,8 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	hashstate->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) hashstate);
 
 	/*
 	 * initialize child nodes
@@ -1063,7 +1059,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState  *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1097,7 +1093,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses, econtext))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index c50d93f43d..1aa133925f 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState  *joinqual;
+	ExprState  *otherqual;
 	ExprContext *econtext;
 	HashJoinTable hashtable;
 	TupleTableSlot *outerTupleSlot;
@@ -275,7 +275,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual, econtext))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -294,8 +294,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual, econtext))
 						return ExecProject(node->js.ps.ps_ProjInfo);
 					else
 						InstrCountFiltered2(node, 1);
@@ -322,8 +321,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual, econtext))
 						return ExecProject(node->js.ps.ps_ProjInfo);
 					else
 						InstrCountFiltered2(node, 1);
@@ -350,8 +348,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual, econtext))
 					return ExecProject(node->js.ps.ps_ProjInfo);
 				else
 					InstrCountFiltered2(node, 1);
@@ -411,19 +408,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	hjstate->js.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->join.plan.targetlist,
-					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses, (PlanState *) hjstate);
 
 	/*
 	 * initialize child nodes
@@ -517,13 +508,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
-		OpExpr	   *hclause = castNode(OpExpr, fstate->xprstate.expr);
+		OpExpr	   *hclause =  castNode(OpExpr, lfirst(l));
+
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4a7f39a7c7..54f6a873b3 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -179,7 +179,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -456,15 +456,10 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	 * Note: we don't initialize all of the indexorderby expression, only the
 	 * sub-parts corresponding to runtime keys (see below).
 	 */
-	indexstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual, (PlanState *) indexstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 0a9dfdbaf3..ab1add9757 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -125,7 +125,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -244,7 +244,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -364,7 +364,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig, econtext);
 }
 
 
@@ -870,18 +870,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	 * would be nice to improve that.  (Problem is that any SubPlans present
 	 * in the expression must be found now...)
 	 */
-	indexstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
-	indexstate->indexorderbyorig = (List *)
-		ExecInitExpr((Expr *) node->indexorderbyorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
+	indexstate->indexorderbyorig =
+		ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 105e2dcedb..62784af304 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState  *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual, econtext))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -483,14 +483,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState  *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual, econtext))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -598,8 +598,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState  *joinqual;
+	ExprState  *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -785,8 +785,8 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL ||
+							  ExecQual(joinqual, econtext));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -808,8 +808,8 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL ||
+								  ExecQual(otherqual, econtext));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1455,16 +1455,11 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	mergestate->js.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->join.plan.targetlist,
-					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 95e158970c..127a64eb31 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1151,7 +1151,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState  *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1270,7 +1270,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere, econtext))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1645,7 +1645,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	mtstate = makeNode(ModifyTableState);
 	mtstate->ps.plan = (Plan *) node;
 	mtstate->ps.state = estate;
-	mtstate->ps.targetlist = NIL;		/* not actually used */
 
 	mtstate->operation = operation;
 	mtstate->canSetTag = node->canSetTag;
@@ -1765,8 +1764,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i]);
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1805,7 +1804,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			foreach(ll, mapped_wcoList)
 			{
 				WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-				ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
+				ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
 											   mtstate->mt_plans[i]);
 
 				wcoExprs = lappend(wcoExprs, wcoExpr);
@@ -1839,8 +1838,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1849,11 +1849,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
 									 resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
@@ -1870,17 +1868,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		for (i = 0; i < mtstate->mt_num_partitions; i++)
 		{
 			Relation	partrel = resultRelInfo->ri_RelationDesc;
-			List	   *rlist,
-					   *rliststate;
+			List	   *rlist;
 
 			/* varno = node->nominalRelation */
 			rlist = map_partition_varattnos(returningList,
 											node->nominalRelation,
 											partrel, rel);
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1905,7 +1901,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1931,11 +1926,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
@@ -1943,10 +1937,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		{
 			ExprState  *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
 									&mtstate->ps);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index cac7ba1b9b..53977e0b32 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState  *joinqual;
+	ExprState  *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -176,7 +176,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual, econtext))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -207,7 +207,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual, econtext))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -225,7 +225,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual, econtext))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -282,16 +282,11 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	nlstate->js.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->join.plan.targetlist,
-					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
index eae0f1dad9..60862cf142 100644
--- a/src/backend/executor/nodeProjectSet.c
+++ b/src/backend/executor/nodeProjectSet.c
@@ -24,6 +24,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeProjectSet.h"
+#include "nodes/nodeFuncs.h"
 #include "utils/memutils.h"
 
 
@@ -122,7 +123,6 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 	bool		hassrf PG_USED_FOR_ASSERTS_ONLY = false;
 	bool		hasresult;
 	int			argno;
-	ListCell   *lc;
 
 	ExecClearTuple(resultSlot);
 
@@ -133,10 +133,9 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 	node->pending_srf_tuples = false;
 
 	hasresult = false;
-	argno = 0;
-	foreach(lc, node->ps.targetlist)
+	for (argno = 0; argno < node->nelems; argno++)
 	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(lc);
+		Node *elem = node->elems[argno];
 		ExprDoneCond *isdone = &node->elemdone[argno];
 		Datum	   *result = &resultSlot->tts_values[argno];
 		bool	   *isnull = &resultSlot->tts_isnull[argno];
@@ -151,13 +150,12 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 			*isnull = true;
 			hassrf = true;
 		}
-		else if (IsA(gstate->arg, FuncExprState) &&
-				 ((FuncExprState *) gstate->arg)->funcReturnsSet)
+		else if (IsA(elem, SetExprState))
 		{
 			/*
 			 * Evaluate SRF - possibly continuing previously started output.
 			 */
-			*result = ExecMakeFunctionResultSet((FuncExprState *) gstate->arg,
+			*result = ExecMakeFunctionResultSet((SetExprState *) elem,
 												econtext, isnull, isdone);
 
 			if (*isdone != ExprEndResult)
@@ -169,11 +167,10 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 		else
 		{
 			/* Non-SRF tlist expression, just evaluate normally. */
-			*result = ExecEvalExpr(gstate->arg, econtext, isnull);
+			*result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
 			*isdone = ExprSingleResult;
 		}
 
-		argno++;
 	}
 
 	/* ProjectSet should not be used if there's no SRFs */
@@ -204,6 +201,8 @@ ProjectSetState *
 ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 {
 	ProjectSetState *state;
+	ListCell *lc;
+	int off;
 
 	/* check for unsupported flags */
 	Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
@@ -229,12 +228,6 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 	 */
 	ExecInitResultTupleSlot(estate, &state->ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	state->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) state);
 	Assert(node->plan.qual == NIL);
 
 	/*
@@ -254,9 +247,38 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 
 	/* Create workspace for per-SRF is-done state */
 	state->nelems = list_length(node->plan.targetlist);
+	state->elems = (Node **)
+		palloc(sizeof(Node *) * state->nelems);
 	state->elemdone = (ExprDoneCond *)
 		palloc(sizeof(ExprDoneCond) * state->nelems);
 
+	/*
+	 * Build expressions to evaluate targetlist. Can't use
+	 * ExecBuildProjectionInfo here, since that doesn't deal with
+	 * SRFs. Instead evaluate all expressions individually, using
+	 * ExecInitFunctionResultSet where applicable.
+	 */
+	off = 0;
+	foreach(lc, node->plan.targetlist)
+	{
+		TargetEntry *te = (TargetEntry *) lfirst(lc);
+		Expr *expr = te->expr;
+
+		if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
+			(IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
+		{
+			state->elems[off] = (Node *)
+				ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
+										  &state->ps);
+		}
+		else
+		{
+			state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
+			Assert(!expression_returns_set((Node *) expr));
+		}
+
+		off++;
+	}
 	return state;
 }
 
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index b5b50b21e9..a753a53419 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -77,9 +77,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual, econtext);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -209,14 +207,10 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	resstate->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) resstate);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index d38265e810..0247bd2347 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -164,19 +164,12 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
-	scanstate->args = (List *)
-		ExecInitExpr((Expr *) tsc->args,
-					 (PlanState *) scanstate);
+	scanstate->args = ExecInitExprList(tsc->args, (PlanState *) scanstate);
 	scanstate->repeatable =
-		ExecInitExpr(tsc->repeatable,
-					 (PlanState *) scanstate);
+		ExecInitExpr(tsc->repeatable, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index e61895de0a..5680464fa2 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -188,12 +188,8 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 8f419a13ac..01383fdc33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -39,12 +39,6 @@
 #include "utils/memutils.h"
 
 
-static Datum ExecSubPlan(SubPlanState *node,
-			ExprContext *econtext,
-			bool *isNull);
-static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node,
-					   ExprContext *econtext,
-					   bool *isNull);
 static Datum ExecHashSubPlan(SubPlanState *node,
 				ExprContext *econtext,
 				bool *isNull);
@@ -64,12 +58,12 @@ static bool slotNoNulls(TupleTableSlot *slot);
  * This is the main entry point for execution of a regular SubPlan.
  * ----------------------------------------------------------------
  */
-static Datum
+Datum
 ExecSubPlan(SubPlanState *node,
 			ExprContext *econtext,
 			bool *isNull)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 
 	/* Set non-null as default */
 	*isNull = false;
@@ -95,7 +89,7 @@ ExecHashSubPlan(SubPlanState *node,
 				ExprContext *econtext,
 				bool *isNull)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	TupleTableSlot *slot;
 
@@ -217,7 +211,7 @@ ExecScanSubPlan(SubPlanState *node,
 				ExprContext *econtext,
 				bool *isNull)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	SubLinkType subLinkType = subplan->subLinkType;
 	MemoryContext oldcontext;
@@ -462,7 +456,7 @@ ExecScanSubPlan(SubPlanState *node,
 static void
 buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	int			ncols = list_length(subplan->paramIds);
 	ExprContext *innerecontext = node->innerecontext;
@@ -694,8 +688,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 	SubPlanState *sstate = makeNode(SubPlanState);
 	EState	   *estate = parent->state;
 
-	sstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecSubPlan;
-	sstate->xprstate.expr = (Expr *) subplan;
+	sstate->subplan = subplan;
 
 	/* Link the SubPlanState to already-initialized subplan */
 	sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates,
@@ -706,7 +699,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 
 	/* Initialize subexpressions */
 	sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
-	sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent);
+	sstate->args = ExecInitExprList(subplan->args, parent);
 
 	/*
 	 * initialize my state
@@ -763,9 +756,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		TupleTableSlot *slot;
 		List	   *oplist,
 				   *lefttlist,
-				   *righttlist,
-				   *leftptlist,
-				   *rightptlist;
+				   *righttlist;
 		ListCell   *l;
 
 		/* We need a memory context to hold the hash table(s) */
@@ -800,27 +791,27 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * We also extract the combining operators themselves to initialize
 		 * the equality and hashing functions for the hash tables.
 		 */
-		if (IsA(sstate->testexpr->expr, OpExpr))
+		if (IsA(subplan->testexpr, OpExpr))
 		{
 			/* single combining operator */
-			oplist = list_make1(sstate->testexpr);
+			oplist = list_make1(subplan->testexpr);
 		}
-		else if (and_clause((Node *) sstate->testexpr->expr))
+		else if (and_clause((Node *) subplan->testexpr))
 		{
 			/* multiple combining operators */
-			oplist = castNode(BoolExprState, sstate->testexpr)->args;
+			Assert(IsA(subplan->testexpr, BoolExpr));
+			oplist = castNode(BoolExpr, subplan->testexpr)->args;
 		}
 		else
 		{
 			/* shouldn't see anything else in a hashable subplan */
 			elog(ERROR, "unrecognized testexpr type: %d",
-				 (int) nodeTag(sstate->testexpr->expr));
+				 (int) nodeTag(subplan->testexpr));
 			oplist = NIL;		/* keep compiler quiet */
 		}
 		Assert(list_length(oplist) == ncols);
 
 		lefttlist = righttlist = NIL;
-		leftptlist = rightptlist = NIL;
 		sstate->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
 		sstate->tab_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
 		sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
@@ -828,45 +819,30 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		i = 1;
 		foreach(l, oplist)
 		{
-			FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
-			OpExpr	   *opexpr = castNode(OpExpr, fstate->xprstate.expr);
-			ExprState  *exstate;
+			OpExpr	   *opexpr = castNode(OpExpr, lfirst(l));
 			Expr	   *expr;
 			TargetEntry *tle;
-			GenericExprState *tlestate;
 			Oid			rhs_eq_oper;
 			Oid			left_hashfn;
 			Oid			right_hashfn;
 
-			Assert(list_length(fstate->args) == 2);
+			Assert(list_length(opexpr->args) == 2);
 
 			/* Process lefthand argument */
-			exstate = (ExprState *) linitial(fstate->args);
-			expr = exstate->expr;
+			expr = (Expr *) linitial(opexpr->args);
 			tle = makeTargetEntry(expr,
 								  i,
 								  NULL,
 								  false);
-			tlestate = makeNode(GenericExprState);
-			tlestate->xprstate.expr = (Expr *) tle;
-			tlestate->xprstate.evalfunc = NULL;
-			tlestate->arg = exstate;
-			lefttlist = lappend(lefttlist, tlestate);
-			leftptlist = lappend(leftptlist, tle);
+			lefttlist = lappend(lefttlist, tle);
 
 			/* Process righthand argument */
-			exstate = (ExprState *) lsecond(fstate->args);
-			expr = exstate->expr;
+			expr = (Expr *) lsecond(opexpr->args);
 			tle = makeTargetEntry(expr,
 								  i,
 								  NULL,
 								  false);
-			tlestate = makeNode(GenericExprState);
-			tlestate->xprstate.expr = (Expr *) tle;
-			tlestate->xprstate.evalfunc = NULL;
-			tlestate->arg = exstate;
-			righttlist = lappend(righttlist, tlestate);
-			rightptlist = lappend(rightptlist, tle);
+			righttlist = lappend(righttlist, tle);
 
 			/* Lookup the equality function (potentially cross-type) */
 			fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
@@ -898,20 +874,22 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
 		 */
-		tupDesc = ExecTypeFromTL(leftptlist, false);
+		tupDesc = ExecTypeFromTL(lefttlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
 		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+												   parent->ps_ExprContext,
 												   slot,
+												   parent,
 												   NULL);
 
-		tupDesc = ExecTypeFromTL(rightptlist, false);
+		tupDesc = ExecTypeFromTL(righttlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
 		sstate->projRight = ExecBuildProjectionInfo(righttlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
@@ -934,7 +912,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 void
 ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	SubLinkType subLinkType = subplan->subLinkType;
 	MemoryContext oldcontext;
@@ -1111,7 +1089,7 @@ void
 ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
 {
 	PlanState  *planstate = node->planstate;
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	EState	   *estate = parent->state;
 	ListCell   *l;
 
@@ -1162,16 +1140,21 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
 	SubPlan    *subplan2;
 	Cost		cost1;
 	Cost		cost2;
+	ListCell   *lc;
 
-	asstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecAlternativeSubPlan;
-	asstate->xprstate.expr = (Expr *) asplan;
+	asstate->subplan = asplan;
 
 	/*
 	 * Initialize subplans.  (Can we get away with only initializing the one
 	 * we're going to use?)
 	 */
-	asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
-											  parent);
+	foreach(lc, asplan->subplans)
+	{
+		SubPlanState *sp = ExecInitSubPlan(lfirst(lc), parent);
+		asstate->subplans =
+			lappend(asstate->subplans, sp);
+		parent->subPlan = lappend(parent->subPlan, sp);
+	}
 
 	/*
 	 * Select the one to be used.  For this, we need an estimate of the number
@@ -1209,7 +1192,7 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
  * Note: in future we might consider changing to different subplans on the
  * fly, in case the original rowcount estimate turns out to be way off.
  */
-static Datum
+Datum
 ExecAlternativeSubPlan(AlternativeSubPlanState *node,
 					   ExprContext *econtext,
 					   bool *isNull)
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 230a96f9d2..ae184700a6 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -120,12 +120,8 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	subquerystate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 13ed886577..1763be5cf0 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -52,7 +52,8 @@ static TupleTableSlot *TidNext(TidScanState *node);
 static void
 TidListCreate(TidScanState *tidstate)
 {
-	List	   *evalList = tidstate->tss_tidquals;
+	TidScan	   *node = (TidScan *) tidstate->ss.ps.plan;
+	List	   *evalList = node->tidquals;
 	ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
 	BlockNumber nblocks;
 	ItemPointerData *tidList;
@@ -81,28 +82,29 @@ TidListCreate(TidScanState *tidstate)
 
 	foreach(l, evalList)
 	{
-		ExprState  *exstate = (ExprState *) lfirst(l);
-		Expr	   *expr = exstate->expr;
+		Expr	   *expr = (Expr*) lfirst(l);
 		ItemPointer itemptr;
 		bool		isNull;
 
 		if (is_opclause(expr))
 		{
-			FuncExprState *fexstate = (FuncExprState *) exstate;
+			FuncExpr   *fex = (FuncExpr *) expr;
+			ExprState  *exprstate;
 			Node	   *arg1;
 			Node	   *arg2;
 
 			arg1 = get_leftop(expr);
 			arg2 = get_rightop(expr);
 			if (IsCTIDVar(arg1))
-				exstate = (ExprState *) lsecond(fexstate->args);
+				exprstate = ExecInitExpr((Expr *) lsecond(fex->args),
+										  &tidstate->ss.ps);
 			else if (IsCTIDVar(arg2))
-				exstate = (ExprState *) linitial(fexstate->args);
-			else
+				exprstate = ExecInitExpr((Expr *) linitial(fex->args),
+										  &tidstate->ss.ps);
 				elog(ERROR, "could not identify CTID variable");
 
 			itemptr = (ItemPointer)
-				DatumGetPointer(ExecEvalExprSwitchContext(exstate,
+				DatumGetPointer(ExecEvalExprSwitchContext(exprstate,
 														  econtext,
 														  &isNull));
 			if (!isNull &&
@@ -121,7 +123,8 @@ TidListCreate(TidScanState *tidstate)
 		}
 		else if (expr && IsA(expr, ScalarArrayOpExpr))
 		{
-			ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
+			ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
+			ExprState  *exprstate;
 			Datum		arraydatum;
 			ArrayType  *itemarray;
 			Datum	   *ipdatums;
@@ -129,8 +132,8 @@ TidListCreate(TidScanState *tidstate)
 			int			ndatums;
 			int			i;
 
-			exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
-			arraydatum = ExecEvalExprSwitchContext(exstate,
+			exprstate = ExecInitExpr(lsecond(saex->args), &tidstate->ss.ps);
+			arraydatum = ExecEvalExprSwitchContext(exprstate,
 												   econtext,
 												   &isNull);
 			if (isNull)
@@ -470,16 +473,8 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	tidstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
-
-	tidstate->tss_tidquals = (List *)
-		ExecInitExpr((Expr *) node->tidquals,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9883a8b130..9ee776c4c3 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -120,7 +120,7 @@ ValuesNext(ValuesScanState *node)
 		 * is a SubPlan, and there shouldn't be any (any subselects in the
 		 * VALUES list should be InitPlans).
 		 */
-		exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
+		exprstatelist = ExecInitExprList(exprlist, NULL);
 
 		/* parser should have checked all sublists are the same length */
 		Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
@@ -242,12 +242,8 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 2a123e8452..628bc9f00b 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1826,16 +1826,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate);
 	winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate);
 
-	winstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) winstate);
-
 	/*
 	 * WindowAgg nodes never have quals, since they can only occur at the
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
@@ -1894,7 +1890,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	foreach(l, winstate->funcs)
 	{
 		WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
-		WindowFunc *wfunc = (WindowFunc *) wfuncstate->xprstate.expr;
+		WindowFunc *wfunc = wfuncstate->wfunc;
 		WindowStatePerFunc perfuncstate;
 		AclResult	aclresult;
 		int			i;
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index 23b5b94985..d7616be065 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -156,12 +156,8 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index b19380e1b1..42bba543e9 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -3395,7 +3395,7 @@ eval_const_expressions_mutator(Node *node,
 						 * Else, make a scalar (argisrow == false) NullTest
 						 * for this field.  Scalar semantics are required
 						 * because IS [NOT] NULL doesn't recurse; see comments
-						 * in ExecEvalNullTest().
+						 * in ExecEvalRowNullInt().
 						 */
 						newntest = makeNode(NullTest);
 						newntest->arg = (Expr *) relem;
@@ -3539,8 +3539,8 @@ eval_const_expressions_mutator(Node *node,
  *		FALSE: drop (does not affect result)
  *		TRUE: force result to TRUE
  *		NULL: keep only one
- * We must keep one NULL input because ExecEvalOr returns NULL when no input
- * is TRUE and at least one is NULL.  We don't actually include the NULL
+ * We must keep one NULL input because OR expressions evaluate to NULL when no
+ * input is TRUE and at least one is NULL.  We don't actually include the NULL
  * here, that's supposed to be done by the caller.
  *
  * The output arguments *haveNull and *forceTrue must be initialized FALSE
@@ -3651,9 +3651,9 @@ simplify_or_arguments(List *args,
  *		TRUE: drop (does not affect result)
  *		FALSE: force result to FALSE
  *		NULL: keep only one
- * We must keep one NULL input because ExecEvalAnd returns NULL when no input
- * is FALSE and at least one is NULL.  We don't actually include the NULL
- * here, that's supposed to be done by the caller.
+ * We must keep one NULL input because AND expressions evaluate to NULL when
+ * no input is FALSE and at least one is NULL.  We don't actually include the
+ * NULL here, that's supposed to be done by the caller.
  *
  * The output arguments *haveNull and *forceFalse must be initialized FALSE
  * by the caller.  They will be set TRUE if a null constant or false constant,
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index c2ad440013..f85d3c8111 100644
--- a/src/backend/utils/adt/domains.c
+++ b/src/backend/utils/adt/domains.c
@@ -122,7 +122,8 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
 /*
  * domain_check_input - apply the cached checks.
  *
- * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
+ * This is similar to the handling of CoerceToDomain nodes in
+ * execExpr.c/execInterpExpr.c.
  */
 static void
 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
@@ -165,19 +166,20 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
 
 					/*
 					 * Set up value to be returned by CoerceToDomainValue
-					 * nodes.  Unlike ExecEvalCoerceToDomain, this econtext
-					 * couldn't be shared with anything else, so no need to
-					 * save and restore fields.  But we do need to protect the
-					 * passed-in value against being changed by called
-					 * functions.  (It couldn't be a R/W expanded object for
-					 * most uses, but that seems possible for domain_check().)
+					 * nodes.  Unlike in the generic expression case, this
+					 * econtext couldn't be shared with anything else, so no
+					 * need to save and restore fields.  But we do need to
+					 * protect the passed-in value against being changed by
+					 * called functions.  (It couldn't be a R/W expanded
+					 * object for most uses, but that seems possible for
+					 * domain_check().)
 					 */
 					econtext->domainValue_datum =
 						MakeExpandedObjectReadOnly(value, isnull,
 									my_extra->constraint_ref.tcache->typlen);
 					econtext->domainValue_isNull = isnull;
 
-					conResult = ExecEvalExprSwitchContext(con->check_expr,
+					conResult = ExecEvalExprSwitchContext(con->check_exprstate,
 														  econtext,
 														  &conIsNull);
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index cf79f4126e..bef409694d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6913,7 +6913,7 @@ find_param_referent(Param *param, deparse_context *context,
 			foreach(lc2, ps->subPlan)
 			{
 				SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
-				SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+				SubPlan    *subplan = sstate->subplan;
 				ListCell   *lc3;
 				ListCell   *lc4;
 
@@ -6954,7 +6954,7 @@ find_param_referent(Param *param, deparse_context *context,
 					continue;
 
 				/* No parameters to be had here. */
-				Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL);
+				Assert(sstate->subplan->parParam == NIL);
 
 				/* Keep looking, but we are emerging from an initplan. */
 				in_same_plan_level = false;
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index e8bce3b806..e5a5aa0101 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -71,7 +71,6 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
-#include "executor/executor.h"
 #include "executor/spi.h"
 #include "fmgr.h"
 #include "lib/stringinfo.h"
@@ -574,10 +573,9 @@ xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
 
 
 xmltype *
-xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
+xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue)
 {
 #ifdef USE_LIBXML
-	XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
 	xmltype    *result;
 	List	   *named_arg_strings;
 	List	   *arg_strings;
@@ -589,45 +587,40 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
 	volatile xmlTextWriterPtr writer = NULL;
 
 	/*
-	 * We first evaluate all the arguments, then start up libxml and create
-	 * the result.  This avoids issues if one of the arguments involves a call
-	 * to some other function or subsystem that wants to use libxml on its own
-	 * terms.
+	 * All arguments are already evaluated.  This avoids issues if one of the
+	 * arguments involves a call to some other function or subsystem that
+	 * wants to use libxml on its own terms.
 	 */
 	named_arg_strings = NIL;
 	i = 0;
-	foreach(arg, xmlExpr->named_args)
+	foreach(arg, xexpr->named_args)
 	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-		bool		isnull;
+		Expr	   *e = (Expr *) lfirst(arg);
 		char	   *str;
 
-		value = ExecEvalExpr(e, econtext, &isnull);
-		if (isnull)
+		if (named_argnull[i])
 			str = NULL;
 		else
-			str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr), false);
+			str = map_sql_value_to_xml_value(named_argvalue[i], exprType((Node *) e), false);
 		named_arg_strings = lappend(named_arg_strings, str);
 		i++;
 	}
 
 	arg_strings = NIL;
-	foreach(arg, xmlExpr->args)
+	i = 0;
+	foreach(arg, xexpr->args)
 	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-		bool		isnull;
+		Expr	   *e = (Expr *) lfirst(arg);
 		char	   *str;
 
-		value = ExecEvalExpr(e, econtext, &isnull);
 		/* here we can just forget NULL elements immediately */
-		if (!isnull)
+		if (!argnull[i])
 		{
-			str = map_sql_value_to_xml_value(value,
-										   exprType((Node *) e->expr), true);
+			str = map_sql_value_to_xml_value(argvalue[i],
+											 exprType((Node *) e), true);
 			arg_strings = lappend(arg_strings, str);
 		}
+		i++;
 	}
 
 	/* now safe to run libxml */
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 6992634c39..07cae44069 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -102,6 +102,7 @@ static TypeCacheEntry *firstDomainTypeEntry = NULL;
  * the DomainConstraintState nodes and applying ExecInitExpr to check_expr.
  * Such a state tree is not part of the DomainConstraintCache, but is
  * considered to belong to a DomainConstraintRef.
+ * FIXME: update.
  */
 struct DomainConstraintCache
 {
@@ -779,8 +780,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
 			r = makeNode(DomainConstraintState);
 			r->constrainttype = DOM_CONSTRAINT_CHECK;
 			r->name = pstrdup(NameStr(c->conname));
-			/* Must cast here because we're not storing an expr state node */
-			r->check_expr = (ExprState *) check_expr;
+			r->check_expr = check_expr;
+			r->check_exprstate = NULL;
 
 			MemoryContextSwitchTo(oldcxt);
 
@@ -859,6 +860,7 @@ load_domaintype_info(TypeCacheEntry *typentry)
 		r->constrainttype = DOM_CONSTRAINT_NOTNULL;
 		r->name = pstrdup("NOT NULL");
 		r->check_expr = NULL;
+		r->check_exprstate = NULL;
 
 		/* lcons to apply the nullness check FIRST */
 		dcc->constraints = lcons(r, dcc->constraints);
@@ -946,8 +948,8 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
 		newr = makeNode(DomainConstraintState);
 		newr->constrainttype = r->constrainttype;
 		newr->name = r->name;
-		/* Must cast here because cache items contain expr plan trees */
-		newr->check_expr = ExecInitExpr((Expr *) r->check_expr, NULL);
+		newr->check_expr = r->check_expr;
+		newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
 
 		result = lappend(result, newr);
 	}
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
new file mode 100644
index 0000000000..44d09ba09e
--- /dev/null
+++ b/src/include/executor/execExpr.h
@@ -0,0 +1,454 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.h
+ *	  Low level infrastructure related to expression evaluation
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/execExpr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXEC_EXPR_H
+#define EXEC_EXPR_H
+
+#include "nodes/execnodes.h"
+
+struct ArrayRefState;
+
+#define EEO_FLAG_JUMP_THREADED (1 << 0)
+
+typedef enum ExprEvalOp
+{
+	EEO_DONE,
+	EEO_INNER_FETCHSOME,
+	EEO_OUTER_FETCHSOME,
+	EEO_SCAN_FETCHSOME,
+	EEO_INNER_VAR,
+	EEO_OUTER_VAR,
+	EEO_SCAN_VAR,
+	EEO_ASSIGN_INNER_VAR,
+	EEO_ASSIGN_OUTER_VAR,
+	EEO_ASSIGN_SCAN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_ASSIGN_TMP_UNEXPAND,
+	EEO_INNER_SYSVAR,
+	EEO_OUTER_SYSVAR,
+	EEO_SCAN_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_QUAL,
+	EEO_NULLTEST_ISNULL,
+	EEO_NULLTEST_ISNOTNULL,
+	EEO_NULLTEST_ROWISNULL,
+	EEO_NULLTEST_ROWISNOTNULL,
+	EEO_PARAM_EXEC,
+	EEO_PARAM_EXTERN,
+	EEO_CASE_WHEN_STEP,
+	EEO_CASE_THEN_STEP,
+	EEO_CASE_TESTVAL,
+	EEO_CASE_TESTVAL_UNEXPAND,
+	EEO_COALESCE,
+	EEO_BOOLTEST_IS_TRUE,
+	EEO_BOOLTEST_IS_NOT_TRUE,
+	EEO_BOOLTEST_IS_FALSE,
+	EEO_BOOLTEST_IS_NOT_FALSE,
+	EEO_BOOLTEST_IS_UNKNOWN,
+	EEO_BOOLTEST_IS_NOT_UNKNOWN,
+	EEO_WHOLEROW,
+	EEO_IOCOERCE,
+	EEO_DISTINCT,
+	EEO_NULLIF,
+	EEO_SQLVALUEFUNCTION,
+	EEO_CURRENTOFEXPR,
+	EEO_ARRAYEXPR,
+	EEO_ARRAYCOERCE,
+	EEO_ROW,
+	EEO_ROWCOMPARE_STEP,
+	EEO_ROWCOMPARE_FINAL,
+	EEO_MINMAX,
+	EEO_FIELDSELECT,
+	EEO_FIELDSTORE_DEFORM,
+	EEO_FIELDSTORE_FORM,
+	EEO_ARRAYREF_CHECKINPUT,
+	EEO_ARRAYREF_CHECKSUBSCRIPT,
+	EEO_ARRAYREF_OLD,
+	EEO_ARRAYREF_ASSIGN,
+	EEO_ARRAYREF_FETCH,
+	EEO_CONVERT_ROWTYPE,
+	EEO_SCALARARRAYOP,
+	EEO_DOMAIN_TESTVAL,
+	EEO_DOMAIN_TESTVAL_UNEXPAND,
+	EEO_DOMAIN_NOTNULL,
+	EEO_DOMAIN_CHECK,
+	EEO_XMLEXPR,
+	EEO_AGGREF,
+	EEO_GROUPING_FUNC,
+	EEO_WINDOW_FUNC,
+	EEO_SUBPLAN,
+	EEO_ALTERNATIVE_SUBPLAN,
+	EEO_LAST
+} ExprEvalOp;
+
+
+typedef struct ExprEvalStep
+{
+	/*
+	 * Instruction to be executed. During instruction preparation this is an
+	 * ExprEvalOp, but during execution it can be swapped out to some other
+	 * type, e.g. a pointer for computed goto (that's why it's a size_t).
+	 */
+	size_t		opcode;
+
+	/* target for the result of the current instruction */
+	bool	   *resnull;
+	Datum	   *resvalue;
+
+	/*
+	 * Data for an operation. Inline stored data is faster to access, but also
+	 * bloats the size of all instructions. The union should be kept below 48
+	 * bytes (so the entire struct is below 64bytes, a single cacheline on
+	 * common systems).
+	 */
+	union
+	{
+		struct
+		{
+			int attnum;
+		} var;
+
+		struct
+		{
+			Var		   *var;
+			bool		first;			/* first time through, initialize */
+			TupleDesc	tupdesc;	/* descriptor for resulting tuples */
+			JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */
+		} wholerow;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			/* faster to access without additional indirection */
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			int jumpdone;
+		} qualexpr;
+
+		struct
+		{
+			TupleDesc argdesc;
+		} nulltest_row;
+
+		struct
+		{
+			int paramid;
+			int paramtype;
+		} param;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			int jumpfalse;
+		} casewhen;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			int jumpdone;
+		} casethen;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+		} casetest;
+
+		struct
+		{
+			int jumpdone;
+		} coalesce;
+
+		struct
+		{
+			/* lookup info for source output function */
+			FmgrInfo	*finfo_out;
+			FunctionCallInfo fcinfo_data_out;
+			/* lookup info for result input function */
+			FmgrInfo	*finfo_in;
+			FunctionCallInfo fcinfo_data_in;
+			Oid			intypioparam;	/* argument needed for input function */
+		} iocoerce;
+
+		struct
+		{
+			SQLValueFunction *svf;
+		} sqlvaluefunction;
+
+		struct
+		{
+			ArrayExpr  *arrayexpr;
+			Datum	   *elemvalues;
+			bool	   *elemnulls;
+			int			nelems;
+			int16		elemlength;		/* typlen of the array element type */
+			bool		elembyval;		/* is the element type pass-by-value? */
+			char		elemalign;		/* typalign of the element type */
+		} arrayexpr;
+
+		struct
+		{
+			ArrayCoerceExpr *coerceexpr;
+			Oid			resultelemtype; /* element type of result array */
+			FmgrInfo   *elemfunc;		/* lookup info for element coercion function */
+			ArrayMapState *amstate;		/* workspace for array_map */
+		} arraycoerce;
+
+		struct
+		{
+			RowExpr	   *rowexpr;
+			/* the arguments */
+			Datum	   *elemvalues;
+			bool	   *elemnulls;
+			TupleDesc	tupdesc;		/* descriptor for result tuples */
+		} row;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int jumpnull;
+			int jumpdone;
+		} rowcompare_step;
+
+		struct
+		{
+			RowCompareType rctype;
+		} rowcompare_final;
+
+		struct
+		{
+			/* the arguments */
+			Datum	   *values;
+			bool	   *nulls;
+			int			nelems;
+
+			MinMaxOp	op;
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+		} minmax;
+
+		struct
+		{
+			/* tupdesc for most recent input */
+			TupleDesc	argdesc;
+			AttrNumber	fieldnum;
+			Oid			resulttype;
+		} fieldselect;
+
+		struct
+		{
+			/* tupdesc for most recent input */
+			TupleDesc	argdesc;
+			FieldStore *fstore;
+
+			/* the arguments */
+			int			nvalues;
+			Datum	   *values;
+			bool	   *nulls;
+		} fieldstore;
+
+		struct
+		{
+			/* too big to have inline */
+			struct ArrayRefState *state;
+			int jumpdone;
+		} arrayref;
+
+		struct
+		{
+			/* too big to have inline */
+			struct ArrayRefState *state;
+			int off;
+			int jumpdone;
+			bool isupper;
+		} arrayref_checksubscript;
+
+		struct
+		{
+			ConvertRowtypeExpr *convert;
+			TupleDesc indesc;
+			TupleDesc outdesc;
+			TupleConversionMap *map;
+			bool initialized;
+		} convert_rowtype;
+
+		struct
+		{
+			ScalarArrayOpExpr *opexpr;
+			Oid			element_type;
+			int16		typlen;
+			bool		typbyval;
+			char		typalign;
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+		} scalararrayop;
+
+		struct
+		{
+			char *constraintname;
+			Datum *checkvalue;
+			bool *checknull;
+			Oid resulttype;
+		} domaincheck;
+
+		struct
+		{
+			XmlExpr *xexpr;
+			Datum *named_argvalue;
+			bool *named_argnull;
+			Datum *argvalue;
+			bool *argnull;
+		} xmlexpr;
+
+		struct
+		{
+			AggrefExprState *astate;
+		} aggref;
+
+		struct
+		{
+			AggState *parent;
+			List *clauses;
+		} grouping_func;
+
+		struct
+		{
+			WindowFuncExprState *wfstate;
+		} window_func;
+
+		struct
+		{
+			SubPlanState *sstate;
+		} subplan;
+
+		struct
+		{
+			AlternativeSubPlanState *asstate;
+		} alternative_subplan;
+
+		struct
+		{
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			int last_var;
+		} fetch;
+	} d;
+} ExprEvalStep;
+
+
+typedef struct ArrayRefState
+{
+	bool		isassignment;
+	int			numupper;
+	Datum		upper[MAXDIM];
+	int			upperindex[MAXDIM];
+	bool		uppernull[MAXDIM];
+	bool		upperprovided[MAXDIM];
+	int			numlower;
+	Datum		lower[MAXDIM];
+	int			lowerindex[MAXDIM];
+	bool		lowernull[MAXDIM];
+	bool		lowerprovided[MAXDIM];
+
+	Oid			refelemtype;
+	int16		refattrlength;	/* typlen of array type */
+	int16		refelemlength;	/* typlen of the array element type */
+	bool		refelembyval;	/* is the element type pass-by-value? */
+	char		refelemalign;	/* typalign of the element type */
+
+	Datum		replacevalue;
+	bool		replacenull;
+
+	Datum		prevvalue;
+	bool		prevnull;
+} ArrayRefState;
+
+void ExecInstantiateInterpretedExpr(ExprState *state);
+
+ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
+
+/*
+ * Non fast-path execution functions. These are externs instead of static in
+ * execInterpExpr.c, because that allows them to be used by other methods of
+ * expression evaluation.
+ */
+extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern bool ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+
+#endif /* EXEC_EXPR_H */
diff --git a/src/include/executor/execdebug.h b/src/include/executor/execdebug.h
index cf44c3edbb..d828a3f907 100644
--- a/src/include/executor/execdebug.h
+++ b/src/include/executor/execdebug.h
@@ -38,13 +38,6 @@
  */
 
 /* ----------------
- *		EXEC_EVALDEBUG is a flag which turns on debugging of
- *		ExecEval and ExecTargetList() stuff by EV_printf() in execQual.c
- * ----------------
-#undef EXEC_EVALDEBUG
- */
-
-/* ----------------
  *		EXEC_SORTDEBUG is a flag which turns on debugging of
  *		the ExecSort() stuff by SO_printf() in nodeSort.c
  * ----------------
@@ -86,20 +79,6 @@
 #endif   /* EXEC_NESTLOOPDEBUG */
 
 /* ----------------
- *		exec eval / target list debugging defines
- * ----------------
- */
-#ifdef EXEC_EVALDEBUG
-#define EV_nodeDisplay(l)				nodeDisplay(l)
-#define EV_printf(s)					printf(s)
-#define EV1_printf(s, a)				printf(s, a)
-#else
-#define EV_nodeDisplay(l)
-#define EV_printf(s)
-#define EV1_printf(s, a)
-#endif   /* EXEC_EVALDEBUG */
-
-/* ----------------
  *		sort node debugging defines
  * ----------------
  */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 32e1838e15..0c3212153c 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -70,8 +70,8 @@
  * now it's just a macro invoking the function pointed to by an ExprState
  * node.  Beware of double evaluation of the ExprState argument!
  */
-#define ExecEvalExpr(expr, econtext, isNull) \
-	((*(expr)->evalfunc) (expr, econtext, isNull))
+#define ExecEvalExpr(state, econtext, isNull) \
+	(state)->cb((state), econtext, isNull)
 
 
 /* Hook for plugins to get control in ExecutorStart() */
@@ -247,23 +247,101 @@ extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
 				  bool *isNull);
 extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
 				   bool *isNull);
-extern Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr,
+extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
 							ExprContext *econtext,
 							MemoryContext argContext,
 							TupleDesc expectedDesc,
 							bool randomAccess);
-extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache,
+extern Datum ExecMakeFunctionResultSet(SetExprState *setexpr,
 						  ExprContext *econtext,
 						  bool *isNull,
 						  ExprDoneCond *isDone);
-extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
-						  bool *isNull);
+extern SetExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent);
+extern SetExprState *ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent);
+
+/*
+ * prototypes from functions in execExpr.c
+ */
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern List *ExecInitExprList(List *nodes, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern List *ExecPrepareExprList(List *nodes, EState *estate);
+extern ExprState *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState *ExecInitQual(List *qual, PlanState *parent);
+extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
+
+extern bool ExecCheck(ExprState *state, ExprContext *context);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo);
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+#ifndef FRONTEND
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo)
+{
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
+#endif
+
+/*
+ * ExecEvalExprSwitchContext
+ *
+ * Same as ExecEvalExpr, but get into the right allocation context explicitly.
+ */
+#ifndef FRONTEND
+static inline Datum
+ExecEvalExprSwitchContext(ExprState *state,
+						   ExprContext *econtext,
+						   bool *isNull)
+{
+	Datum		retDatum;
+	MemoryContext oldContext;
+
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+	retDatum = ExecEvalExpr(state, econtext, isNull);
+	MemoryContextSwitchTo(oldContext);
+	return retDatum;
+}
+#endif
+
+/*
+ * ExecQual - evaluate a qual prepared with ExecInitQual (possibly via
+ * ExecPrepareQual).
+ */
+#ifndef FRONTEND
+static inline bool
+ExecQual(ExprState *state, ExprContext *econtext)
+{
+	bool isnull;
+	Datum ret;
+
+	/* short-circuit (here and in ExecInitQual) for empty restriction list */
+	if (state == NULL)
+		return true;
+
+	ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+	/* EEO_QUAL should never return NULL */
+	Assert(!isnull);
+
+	return DatumGetBool(ret);
+}
+#endif
 
 /*
  * prototypes from functions in execScan.c
@@ -361,6 +439,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h
index 0f821dc8f6..0bdaa548df 100644
--- a/src/include/executor/nodeSubplan.h
+++ b/src/include/executor/nodeSubplan.h
@@ -20,6 +20,9 @@ extern SubPlanState *ExecInitSubPlan(SubPlan *subplan, PlanState *parent);
 
 extern AlternativeSubPlanState *ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent);
 
+extern Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);
+extern Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, bool *isNull);
+
 extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
 
 extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 6332ea0620..bcf8060f7d 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -29,60 +29,6 @@
 
 
 /* ----------------
- *	  IndexInfo information
- *
- *		this struct holds the information needed to construct new index
- *		entries for a particular index.  Used for both index_build and
- *		retail creation of index entries.
- *
- *		NumIndexAttrs		number of columns in this index
- *		KeyAttrNumbers		underlying-rel attribute numbers used as keys
- *							(zeroes indicate expressions)
- *		Expressions			expr trees for expression entries, or NIL if none
- *		ExpressionsState	exec state for expressions, or NIL if none
- *		Predicate			partial-index predicate, or NIL if none
- *		PredicateState		exec state for predicate, or NIL if none
- *		ExclusionOps		Per-column exclusion operators, or NULL if none
- *		ExclusionProcs		Underlying function OIDs for ExclusionOps
- *		ExclusionStrats		Opclass strategy numbers for ExclusionOps
- *		UniqueOps			Theses are like Exclusion*, but for unique indexes
- *		UniqueProcs
- *		UniqueStrats
- *		Unique				is it a unique index?
- *		ReadyForInserts		is it valid for inserts?
- *		Concurrent			are we doing a concurrent index build?
- *		BrokenHotChain		did we detect any broken HOT chains?
- *		AmCache				private cache area for index AM
- *		Context				memory context holding this IndexInfo
- *
- * ii_Concurrent and ii_BrokenHotChain are used only during index build;
- * they're conventionally set to false otherwise.
- * ----------------
- */
-typedef struct IndexInfo
-{
-	NodeTag		type;
-	int			ii_NumIndexAttrs;
-	AttrNumber	ii_KeyAttrNumbers[INDEX_MAX_KEYS];
-	List	   *ii_Expressions; /* list of Expr */
-	List	   *ii_ExpressionsState;	/* list of ExprState */
-	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
-	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
-	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
-	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
-	Oid		   *ii_UniqueOps;	/* array with one entry per column */
-	Oid		   *ii_UniqueProcs; /* array with one entry per column */
-	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
-	bool		ii_Unique;
-	bool		ii_ReadyForInserts;
-	bool		ii_Concurrent;
-	bool		ii_BrokenHotChain;
-	void	   *ii_AmCache;
-	MemoryContext ii_Context;
-} IndexInfo;
-
-/* ----------------
  *	  ExprContext_CB
  *
  *		List of callbacks to be called at ExprContext shutdown.
@@ -206,57 +152,60 @@ typedef struct ReturnSetInfo
 } ReturnSetInfo;
 
 /* ----------------
+ *		ExprState node
+ *
+ * ExprState is the top-level node for expression evaluation. It contains
+ * instructions (in ->steps) to evaluate the expression.
+ * ----------------
+ */
+struct ExprState;
+struct ExprContext;
+typedef Datum (*ExecEvalExprCB)(struct ExprState *, struct ExprContext *, bool *);
+
+typedef struct ExprState
+{
+	Node		tag;
+	bool		resnull;
+	int8		flags;
+	Datum		resvalue;
+	struct ExprEvalStep *steps;
+	TupleTableSlot *resultslot;
+
+	/* evaluate expression */
+	ExecEvalExprCB cb;
+
+	/* original expression */
+	Expr	   *expr;
+
+	/* FIXME: only needed during "compilation", should be thrown away */
+	size_t		steps_alloc;
+	size_t		steps_len;
+
+	Datum	   *innermost_caseval;
+	bool	   *innermost_casenull;
+
+	Datum	   *innermost_domainval;
+	bool	   *innermost_domainnull;
+} ExprState;
+
+/* ----------------
  *		ProjectionInfo node information
  *
  *		This is all the information needed to perform projections ---
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
+ *		pi_state		instructions to evaluate projection
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState	pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -298,6 +247,60 @@ typedef struct JunkFilter
 } JunkFilter;
 
 /* ----------------
+ *	  IndexInfo information
+ *
+ *		this struct holds the information needed to construct new index
+ *		entries for a particular index.  Used for both index_build and
+ *		retail creation of index entries.
+ *
+ *		NumIndexAttrs		number of columns in this index
+ *		KeyAttrNumbers		underlying-rel attribute numbers used as keys
+ *							(zeroes indicate expressions)
+ *		Expressions			expr trees for expression entries, or NIL if none
+ *		ExpressionsState	exec state for expressions, or NIL if none
+ *		Predicate			partial-index predicate, or NIL if none
+ *		PredicateState		exec state for predicate, or NIL if none
+ *		ExclusionOps		Per-column exclusion operators, or NULL if none
+ *		ExclusionProcs		Underlying function OIDs for ExclusionOps
+ *		ExclusionStrats		Opclass strategy numbers for ExclusionOps
+ *		UniqueOps			Theses are like Exclusion*, but for unique indexes
+ *		UniqueProcs
+ *		UniqueStrats
+ *		Unique				is it a unique index?
+ *		ReadyForInserts		is it valid for inserts?
+ *		Concurrent			are we doing a concurrent index build?
+ *		BrokenHotChain		did we detect any broken HOT chains?
+ *		AmCache				private cache area for index AM
+ *		Context				memory context holding this IndexInfo
+ *
+ * ii_Concurrent and ii_BrokenHotChain are used only during index build;
+ * they're conventionally set to false otherwise.
+ * ----------------
+ */
+typedef struct IndexInfo
+{
+	NodeTag		type;
+	int			ii_NumIndexAttrs;
+	AttrNumber	ii_KeyAttrNumbers[INDEX_MAX_KEYS];
+	List	   *ii_Expressions; /* list of Expr */
+	List	   *ii_ExpressionsState;	/* list of ExprState */
+	List	   *ii_Predicate;	/* list of Expr */
+	ExprState  *ii_PredicateState;
+	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
+	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
+	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
+	Oid		   *ii_UniqueOps;	/* array with one entry per column */
+	Oid		   *ii_UniqueProcs; /* array with one entry per column */
+	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	bool		ii_Unique;
+	bool		ii_ReadyForInserts;
+	bool		ii_Concurrent;
+	bool		ii_BrokenHotChain;
+	void	   *ii_AmCache;
+	MemoryContext ii_Context;
+} IndexInfo;
+
+/* ----------------
  *	  ResultRelInfo information
  *
  *		Whenever we update an existing relation, we have to
@@ -338,20 +341,20 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState *ri_onConflictSetWhere;
 	List	   *ri_PartitionCheck;
-	List	   *ri_PartitionCheckExpr;
+	ExprState  *ri_PartitionCheckExpr;
 	Relation	ri_PartitionRoot;
 } ResultRelInfo;
 
@@ -560,143 +563,54 @@ typedef tuplehash_iterator TupleHashIterator;
 #define ScanTupleHashTable(htable, iter) \
 	tuplehash_iterate(htable->hashtab, iter)
 
-
-/* ----------------------------------------------------------------
- *				 Expression State Trees
- *
- * Each executable expression tree has a parallel ExprState tree.
- *
- * Unlike PlanState, there is not an exact one-for-one correspondence between
- * ExprState node types and Expr node types.  Many Expr node types have no
- * need for node-type-specific run-time state, and so they can use plain
- * ExprState or GenericExprState as their associated ExprState node type.
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- *		ExprState node
- *
- * ExprState is the common superclass for all ExprState-type nodes.
- *
- * It can also be instantiated directly for leaf Expr nodes that need no
- * local run-time state (such as Var, Const, or Param).
- *
- * To save on dispatch overhead, each ExprState node contains a function
- * pointer to the routine to execute to evaluate the node.
- * ----------------
- */
-
-typedef struct ExprState ExprState;
-
-typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
-												ExprContext *econtext,
-												bool *isNull);
-
-struct ExprState
-{
-	NodeTag		type;
-	Expr	   *expr;			/* associated Expr node */
-	ExprStateEvalFunc evalfunc; /* routine to run to execute node */
-};
-
-/* ----------------
- *		GenericExprState node
- *
- * This is used for Expr node types that need no local run-time state,
- * but have one child Expr node.
- * ----------------
- */
-typedef struct GenericExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* state of my child node */
-} GenericExprState;
-
-/* ----------------
- *		WholeRowVarExprState node
- * ----------------
- */
-typedef struct WholeRowVarExprState
-{
-	ExprState	xprstate;
-	struct PlanState *parent;	/* parent PlanState, or NULL if none */
-	TupleDesc	wrv_tupdesc;	/* descriptor for resulting tuples */
-	JunkFilter *wrv_junkFilter; /* JunkFilter to remove resjunk cols */
-} WholeRowVarExprState;
-
 /* ----------------
  *		AggrefExprState node
  * ----------------
  */
 typedef struct AggrefExprState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	Aggref	   *aggref;
 	int			aggno;			/* ID number for agg within its plan node */
 } AggrefExprState;
 
 /* ----------------
- *		GroupingFuncExprState node
- *
- * The list of column numbers refers to the input tuples of the Agg node to
- * which the GroupingFunc belongs, and may contain 0 for references to columns
- * that are only present in grouping sets processed by different Agg nodes (and
- * which are therefore always considered "grouping" here).
- * ----------------
- */
-typedef struct GroupingFuncExprState
-{
-	ExprState	xprstate;
-	struct AggState *aggstate;
-	List	   *clauses;		/* integer list of column numbers */
-} GroupingFuncExprState;
-
-/* ----------------
  *		WindowFuncExprState node
  * ----------------
  */
 typedef struct WindowFuncExprState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	WindowFunc *wfunc;
 	List	   *args;			/* states of argument expressions */
-	ExprState  *aggfilter;		/* FILTER expression */
+	ExprState *aggfilter;		/* FILTER expression */
 	int			wfuncno;		/* ID number for wfunc within its plan node */
 } WindowFuncExprState;
 
-/* ----------------
- *		ArrayRefExprState node
- *
- * Note: array types can be fixed-length (typlen > 0), but only when the
- * element type is itself fixed-length.  Otherwise they are varlena structures
- * and have typlen = -1.  In any case, an array type is never pass-by-value.
- * ----------------
- */
-typedef struct ArrayRefExprState
-{
-	ExprState	xprstate;
-	List	   *refupperindexpr;	/* states for child nodes */
-	List	   *reflowerindexpr;
-	ExprState  *refexpr;
-	ExprState  *refassgnexpr;
-	int16		refattrlength;	/* typlen of array type */
-	int16		refelemlength;	/* typlen of the array element type */
-	bool		refelembyval;	/* is the element type pass-by-value? */
-	char		refelemalign;	/* typalign of the element type */
-} ArrayRefExprState;
 
 /* ----------------
- *		FuncExprState node
+ *		SetExprState node
  *
- * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
- * and NullIf nodes; be careful to check what xprstate.expr is actually
- * pointing at!
+ * State for evaluating a potentially set returning expression (like FuncExpr
+ * or OpExpr).  In some cases, like some of the expressions in ROWS FROM(...)
+ * the expression might not be a SRF, but uses the same machinery as SRFs
+ * (i.e. are treated like a SRF returning a single row).
  * ----------------
  */
-typedef struct FuncExprState
+typedef struct SetExprState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	Expr	   *expr;
 	List	   *args;			/* states of argument expressions */
 
 	/*
+	 * In ROWS FROM functions possibly can be inlined removing the FuncExpr
+	 * normally inside, this is the containing expression (which cannot return
+	 * a set) which'll be evaluated using the normal ExecEvalExpr().
+	 */
+	ExprState	*elidedFuncState;
+
+	/*
 	 * Function manager's lookup info for the target function.  If func.fn_oid
 	 * is InvalidOid, we haven't initialized it yet (nor any of the following
 	 * fields, except funcReturnsSet).
@@ -748,33 +662,7 @@ typedef struct FuncExprState
 	 * argument values between calls, when setArgsValid is true.
 	 */
 	FunctionCallInfoData fcinfo_data;
-} FuncExprState;
-
-/* ----------------
- *		ScalarArrayOpExprState node
- *
- * This is a FuncExprState plus some additional data.
- * ----------------
- */
-typedef struct ScalarArrayOpExprState
-{
-	FuncExprState fxprstate;
-	/* Cached info about array element type */
-	Oid			element_type;
-	int16		typlen;
-	bool		typbyval;
-	char		typalign;
-} ScalarArrayOpExprState;
-
-/* ----------------
- *		BoolExprState node
- * ----------------
- */
-typedef struct BoolExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* states of argument expression(s) */
-} BoolExprState;
+} SetExprState;
 
 /* ----------------
  *		SubPlanState node
@@ -782,10 +670,11 @@ typedef struct BoolExprState
  */
 typedef struct SubPlanState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	SubPlan    *subplan;
 	struct PlanState *planstate;	/* subselect plan's state tree */
 	struct PlanState *parent;	/* parent plan node's state tree */
-	ExprState  *testexpr;		/* state of combining expression */
+	ExprState *testexpr;		/* state of combining expression */
 	List	   *args;			/* states of argument expression(s) */
 	HeapTuple	curTuple;		/* copy of most recent tuple from subplan */
 	Datum		curArray;		/* most recent array from ARRAY() subplan */
@@ -812,197 +701,12 @@ typedef struct SubPlanState
  */
 typedef struct AlternativeSubPlanState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	AlternativeSubPlan *subplan;
 	List	   *subplans;		/* states of alternative subplans */
 	int			active;			/* list index of the one we're using */
 } AlternativeSubPlanState;
 
-/* ----------------
- *		FieldSelectState node
- * ----------------
- */
-typedef struct FieldSelectState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	TupleDesc	argdesc;		/* tupdesc for most recent input */
-} FieldSelectState;
-
-/* ----------------
- *		FieldStoreState node
- * ----------------
- */
-typedef struct FieldStoreState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input tuple value */
-	List	   *newvals;		/* new value(s) for field(s) */
-	TupleDesc	argdesc;		/* tupdesc for most recent input */
-} FieldStoreState;
-
-/* ----------------
- *		CoerceViaIOState node
- * ----------------
- */
-typedef struct CoerceViaIOState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	FmgrInfo	outfunc;		/* lookup info for source output function */
-	FmgrInfo	infunc;			/* lookup info for result input function */
-	Oid			intypioparam;	/* argument needed for input function */
-} CoerceViaIOState;
-
-/* ----------------
- *		ArrayCoerceExprState node
- * ----------------
- */
-typedef struct ArrayCoerceExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input array value */
-	Oid			resultelemtype; /* element type of result array */
-	FmgrInfo	elemfunc;		/* lookup info for element coercion function */
-	/* use struct pointer to avoid including array.h here */
-	struct ArrayMapState *amstate;		/* workspace for array_map */
-} ArrayCoerceExprState;
-
-/* ----------------
- *		ConvertRowtypeExprState node
- * ----------------
- */
-typedef struct ConvertRowtypeExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input tuple value */
-	TupleDesc	indesc;			/* tupdesc for source rowtype */
-	TupleDesc	outdesc;		/* tupdesc for result rowtype */
-	/* use "struct" so we needn't include tupconvert.h here */
-	struct TupleConversionMap *map;
-	bool		initialized;
-} ConvertRowtypeExprState;
-
-/* ----------------
- *		CaseExprState node
- * ----------------
- */
-typedef struct CaseExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* implicit equality comparison argument */
-	List	   *args;			/* the arguments (list of WHEN clauses) */
-	ExprState  *defresult;		/* the default result (ELSE clause) */
-	int16		argtyplen;		/* if arg is provided, its typlen */
-} CaseExprState;
-
-/* ----------------
- *		CaseWhenState node
- * ----------------
- */
-typedef struct CaseWhenState
-{
-	ExprState	xprstate;
-	ExprState  *expr;			/* condition expression */
-	ExprState  *result;			/* substitution result */
-} CaseWhenState;
-
-/* ----------------
- *		ArrayExprState node
- *
- * Note: ARRAY[] expressions always produce varlena arrays, never fixed-length
- * arrays.
- * ----------------
- */
-typedef struct ArrayExprState
-{
-	ExprState	xprstate;
-	List	   *elements;		/* states for child nodes */
-	int16		elemlength;		/* typlen of the array element type */
-	bool		elembyval;		/* is the element type pass-by-value? */
-	char		elemalign;		/* typalign of the element type */
-} ArrayExprState;
-
-/* ----------------
- *		RowExprState node
- * ----------------
- */
-typedef struct RowExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* the arguments */
-	TupleDesc	tupdesc;		/* descriptor for result tuples */
-} RowExprState;
-
-/* ----------------
- *		RowCompareExprState node
- * ----------------
- */
-typedef struct RowCompareExprState
-{
-	ExprState	xprstate;
-	List	   *largs;			/* the left-hand input arguments */
-	List	   *rargs;			/* the right-hand input arguments */
-	FmgrInfo   *funcs;			/* array of comparison function info */
-	Oid		   *collations;		/* array of collations to use */
-} RowCompareExprState;
-
-/* ----------------
- *		CoalesceExprState node
- * ----------------
- */
-typedef struct CoalesceExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* the arguments */
-} CoalesceExprState;
-
-/* ----------------
- *		MinMaxExprState node
- * ----------------
- */
-typedef struct MinMaxExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* the arguments */
-	FmgrInfo	cfunc;			/* lookup info for comparison func */
-} MinMaxExprState;
-
-/* ----------------
- *		XmlExprState node
- * ----------------
- */
-typedef struct XmlExprState
-{
-	ExprState	xprstate;
-	List	   *named_args;		/* ExprStates for named arguments */
-	List	   *args;			/* ExprStates for other arguments */
-} XmlExprState;
-
-/* ----------------
- *		NullTestState node
- * ----------------
- */
-typedef struct NullTestState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	/* used only if input is of composite type: */
-	TupleDesc	argdesc;		/* tupdesc for most recent input */
-} NullTestState;
-
-/* ----------------
- *		CoerceToDomainState node
- * ----------------
- */
-typedef struct CoerceToDomainState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	/* Cached set of constraints that need to be checked */
-	/* use struct pointer to avoid including typcache.h here */
-	struct DomainConstraintRef *constraint_ref;
-} CoerceToDomainState;
-
 /*
  * DomainConstraintState - one item to check during CoerceToDomain
  *
@@ -1021,7 +725,8 @@ typedef struct DomainConstraintState
 	NodeTag		type;
 	DomainConstraintType constrainttype;		/* constraint type */
 	char	   *name;			/* name of constraint (for error msgs) */
-	ExprState  *check_expr;		/* for CHECK, a boolean expression */
+	Expr	   *check_expr;		/* for CHECK, a boolean expression */
+	ExprState  *check_exprstate; /* for CHECK, a boolean expression */
 } DomainConstraintState;
 
 
@@ -1058,8 +763,7 @@ typedef struct PlanState
 	 * state trees parallel links in the associated plan tree (except for the
 	 * subPlan list, which does not exist in the plan tree).
 	 */
-	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState  *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1136,6 +840,7 @@ typedef struct ResultState
 typedef struct ProjectSetState
 {
 	PlanState	ps;				/* its first field is NodeTag */
+	Node	  **elems;				/* array of expression states */
 	ExprDoneCond *elemdone;		/* array of per-SRF is-done states */
 	int			nelems;			/* length of elemdone[] array */
 	bool		pending_srf_tuples;		/* still evaluating srfs in tlist? */
@@ -1370,7 +1075,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState  *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1416,7 +1121,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState  *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1482,7 +1187,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState  *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1506,7 +1211,6 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1630,7 +1334,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1677,7 +1381,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState  *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1775,7 +1479,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState  *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ede7ace76b..b61b05683c 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -187,36 +187,19 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
 	 *
-	 * These correspond (not always one-for-one) to primitive nodes derived
-	 * from Expr.
+	 * ExprState represents the evaluation state for a whole expression tree.
+	 * Most Expr based nodes do not have a corresponding expression state
+	 * node, they're solely handled within execExpr* - but sometimes the state
+	 * needs to be shared with other parts of the executor, as e.g. the case
+	 * for AggrefExprState, which nodeAgg.c has to modify.
 	 */
 	T_ExprState,
-	T_GenericExprState,
-	T_WholeRowVarExprState,
 	T_AggrefExprState,
 	T_GroupingFuncExprState,
 	T_WindowFuncExprState,
-	T_ArrayRefExprState,
-	T_FuncExprState,
-	T_ScalarArrayOpExprState,
-	T_BoolExprState,
+	T_SetExprState,
 	T_SubPlanState,
 	T_AlternativeSubPlanState,
-	T_FieldSelectState,
-	T_FieldStoreState,
-	T_CoerceViaIOState,
-	T_ArrayCoerceExprState,
-	T_ConvertRowtypeExprState,
-	T_CaseExprState,
-	T_CaseWhenState,
-	T_ArrayExprState,
-	T_RowExprState,
-	T_RowCompareExprState,
-	T_CoalesceExprState,
-	T_MinMaxExprState,
-	T_XmlExprState,
-	T_NullTestState,
-	T_CoerceToDomainState,
 	T_DomainConstraintState,
 
 	/*
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index cc1fc390e8..fc7773e779 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -60,7 +60,7 @@ extern void xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode,
 			const char *msg);
 
 extern xmltype *xmlconcat(List *args);
-extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
+extern xmltype *xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue);
 extern xmltype *xmlparse(text *data, XmlOptionType xmloption, bool preserve_whitespace);
 extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null);
 extern xmltype *xmlroot(xmltype *data, text *version, int standalone);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 6fc3db07fe..2044d0427a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -143,7 +143,7 @@ typedef struct					/* cast_hash table entry */
 {
 	plpgsql_CastHashKey key;	/* hash key --- MUST BE FIRST */
 	Expr	   *cast_expr;		/* cast expression, or NULL if no-op cast */
-	/* The ExprState tree is valid only when cast_lxid matches current LXID */
+	/* ExprState is valid only when cast_lxid matches current LXID */
 	ExprState  *cast_exprstate; /* expression's eval tree */
 	bool		cast_in_use;	/* true while we're executing eval tree */
 	LocalTransactionId cast_lxid;
@@ -4709,7 +4709,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
 
 				/*
 				 * Evaluate the subscripts, switch into left-to-right order.
-				 * Like ExecEvalArrayRef(), complain if any subscript is null.
+				 * Like the expression built by ExecInitArrayRef(), complain
+				 * if any subscript is null.
 				 */
 				for (i = 0; i < nsubscripts; i++)
 				{
diff --git a/src/test/regress/expected/case.out b/src/test/regress/expected/case.out
index 09d5516fb5..ca0877931b 100644
--- a/src/test/regress/expected/case.out
+++ b/src/test/regress/expected/case.out
@@ -300,7 +300,7 @@ SELECT * FROM CASE_TBL;
 -- Nested CASE expressions
 --
 -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation.  After
 -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
 -- the isNull flag for the case test value incorrectly became true, causing
 -- the third WHEN-clause not to match.  The volatile function calls are needed
diff --git a/src/test/regress/sql/case.sql b/src/test/regress/sql/case.sql
index a7ae7b4a9e..a948bb9d8f 100644
--- a/src/test/regress/sql/case.sql
+++ b/src/test/regress/sql/case.sql
@@ -161,7 +161,7 @@ SELECT * FROM CASE_TBL;
 --
 
 -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation.  After
 -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
 -- the isNull flag for the case test value incorrectly became true, causing
 -- the third WHEN-clause not to match.  The volatile function calls are needed
-- 
2.11.0.22.g8d7a455.dirty

#30Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#29)
Re: WIP: Faster Expression Processing v4

On Mon, Mar 6, 2017 at 10:01 PM, Andres Freund <andres@anarazel.de> wrote:

My benchmarking script first prewarms the whole database, then runs the
tpch queries in sequence, repeated three times, and compares the shortes
execution time:

Those numbers are stupendous.

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

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

#31Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Andres Freund (#29)
Re: WIP: Faster Expression Processing v4

I looked over
0001-Add-expression-dependencies-on-composite-type-whole-.patch. That
seems useful independent of the things you have following.

Even though it is presented more as a preparatory change, it appears to
have noticeable user-facing impact, which we should analyze. For
example, in the regression test, the command

alter table tt14t drop column f3;

is now rejected, rightly, but the command

alter table tt14t drop column f3 cascade;

ends up dropping the whole view, which might be a bit surprising. We
don't support dropping columns from views, so there might be no
alternative, but I wonder what else is changing because of this. I
think that might deserve a bit more analysis and perhaps some test cases.

--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -206,6 +206,9 @@ static bool object_address_present_add_flags(const
ObjectAddress *object,
 static bool stack_address_present_add_flags(const ObjectAddress *object,
                                                                int flags,
ObjectAddressStack *stack);
+static void add_type_addresses(Oid typeid, bool include_components,
ObjectAddresses *addrs);
+static void add_type_component_addresses(Oid typeid, ObjectAddresses
*addrs);
+static void add_class_component_addresses(Oid relid, ObjectAddresses
*addrs);
 static void DeleteInitPrivs(const ObjectAddress *object);

For some reason, the function add_object_address() is singular, and
these new functions are plural Clearly, they take a plural argument, so
your version seems more correct, but I wonder if we should keep that
consistent.

+ * e.g. not to the right thing for column-less
tables. Note that

Small typo there. (to -> do)

@@ -1639,6 +1641,9 @@ find_expr_references_walker(Node *node,

                add_object_address(OCLASS_PROC, funcexpr->funcid, 0,
                                                   context->addrs);
+               /* dependency on type itself already exists via function */
+               add_type_component_addresses(funcexpr->funcresulttype,
context->addrs);
+
                /* fall through to examine arguments */
        }
        else if (IsA(node, OpExpr))

Why shouldn't the function itself also depend on the components of its
return type?

How are argument types handled?

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#32Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#31)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-07 18:46:31 -0500, Peter Eisentraut wrote:

I looked over
0001-Add-expression-dependencies-on-composite-type-whole-.patch. That
seems useful independent of the things you have following.

Even though it is presented more as a preparatory change, it appears to
have noticeable user-facing impact, which we should analyze.

Indeed.

For
example, in the regression test, the command

alter table tt14t drop column f3;

is now rejected, rightly, but the command

alter table tt14t drop column f3 cascade;

ends up dropping the whole view, which might be a bit surprising. We
don't support dropping columns from views, so there might be no
alternative

It seems strictly better than silently breaking the view.

, but I wonder what else is changing because of this. I
think that might deserve a bit more analysis and perhaps some test cases.

There are already some tests. More is probably good - if you have
specific cases in mind...

--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -206,6 +206,9 @@ static bool object_address_present_add_flags(const
ObjectAddress *object,
static bool stack_address_present_add_flags(const ObjectAddress *object,
int flags,
ObjectAddressStack *stack);
+static void add_type_addresses(Oid typeid, bool include_components,
ObjectAddresses *addrs);
+static void add_type_component_addresses(Oid typeid, ObjectAddresses
*addrs);
+static void add_class_component_addresses(Oid relid, ObjectAddresses
*addrs);
static void DeleteInitPrivs(const ObjectAddress *object);

For some reason, the function add_object_address() is singular, and
these new functions are plural Clearly, they take a plural argument, so
your version seems more correct, but I wonder if we should keep that
consistent.

I named them plural, because add_object_address only adds a single new
entry, but add_type_addresses etc potentially add multiple ones. So I
think plural/singular as used here is actually consistent?

+ * e.g. not to the right thing for column-less
tables. Note that

Small typo there. (to -> do)

Oops.

@@ -1639,6 +1641,9 @@ find_expr_references_walker(Node *node,

add_object_address(OCLASS_PROC, funcexpr->funcid, 0,
context->addrs);
+               /* dependency on type itself already exists via function */
+               add_type_component_addresses(funcexpr->funcresulttype,
context->addrs);
+
/* fall through to examine arguments */
}
else if (IsA(node, OpExpr))

Why shouldn't the function itself also depend on the components of its
return type?

Because that'd make it impossible to change the return type components -
if the return type is baked into the view that's necessary, but for a
"freestanding function" it's not. If you e.g. have a function that just
returns a table's rows, it'd certainly be annoying if that'd prevent you
from dropping columns.

How are argument types handled?

We fall through to

return expression_tree_walker(node, find_expr_references_walker,
(void *) context);

which'll add a dependency if necessary. If it's e.g. a table column,
function return type or such we'll add a dependency there.

Greetings,

Andres Freund

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

#33Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Andres Freund (#32)
Re: WIP: Faster Expression Processing v4

On 3/7/17 19:14, Andres Freund wrote:

Why shouldn't the function itself also depend on the components of its
return type?

Because that'd make it impossible to change the return type components -
if the return type is baked into the view that's necessary, but for a
"freestanding function" it's not. If you e.g. have a function that just
returns a table's rows, it'd certainly be annoying if that'd prevent you
from dropping columns.

I think functions breaking when the return type is fiddled with is
actually a not-uncommon problem in practice. It would be nice if that
could be addressed. Not necessarily in this patch, but I would like to
keep the options open. Comments from others would be welcome on this.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#34Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Andres Freund (#29)
Re: WIP: Faster Expression Processing v4

I have also looked at the 0002 and 0003 patches, and they seem OK, but
they are obviously not of much use without 0004. What is your ambition
for getting 0004 reviewed and committed for PG10?

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#35Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#33)
Re: WIP: Faster Expression Processing v4

On 2017-03-10 08:51:25 -0500, Peter Eisentraut wrote:

On 3/7/17 19:14, Andres Freund wrote:

Why shouldn't the function itself also depend on the components of its
return type?

Because that'd make it impossible to change the return type components -
if the return type is baked into the view that's necessary, but for a
"freestanding function" it's not. If you e.g. have a function that just
returns a table's rows, it'd certainly be annoying if that'd prevent you
from dropping columns.

I think functions breaking when the return type is fiddled with is
actually a not-uncommon problem in practice. It would be nice if that
could be addressed.

What problem are you thinking of exactly, and what'd be the solution?
I'd guess that people wouldn't like being unable to change columns in a
table if some function returned the type.

One rather extreme way would be to have functions return a "different"
type, that's initially identical to the table/composite type. If the
ocmposite type then changes, we'd transparently map between the actual
return type, and the one callers expect. But that'd a fair bit of
effort, and presumably also quite confusing for users that might
e.g. expect a new column to show up.

Not necessarily in this patch, but I would like to keep the options
open.

Yea, seems worth thinking about. I don't think the patch closes down
options?

Greetings,

Andres Freund

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

#36Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#34)
Re: WIP: Faster Expression Processing v4

On 2017-03-10 09:00:14 -0500, Peter Eisentraut wrote:

I have also looked at the 0002 and 0003 patches, and they seem OK, but
they are obviously not of much use without 0004. What is your ambition
for getting 0004 reviewed and committed for PG10?

I'd really like to get it in. The performance improvements on its own
are significant, and it provides the basis for significantly larger
improvements again (JIT) - those followup improvements are large patches
again though, so I'd rather not do all of that next cycle.

My next step (over the weekend) is to add tests to execQual.c to get it
a good chunk closer to 100% test coverage, and then do the same for the
new implementation (there's probably very little additional tests needed
after the conversion). Given all tests pass before/after, and there's a
lot of them, I think we can have a reasonable confidence of a low bug
density.

Large parts of the changes are fairly mechanical, the interesting bits
probably are:

- there's a lot fewer "full blown" node types for expression evaluation
anymore. In most cases there's now only a top-level ExprState node
that's nodeTag() able. The actual information to execute an
instruction now is in ExprState->steps[i]
- because of the previous point, it now isn't legal anymore to call
ExecInitExpr() on a list of expressions - ExecInitExprList() is a
convenience wrapper around manually iterating over the list (gets rid
of ugly casts, too)
- Because they behave differently on an actual expression evaluation
basis, quals / check constraint, now have to be initialized with
ExecInitQual/ExecInitCheck(). That way the shortcut behaviour and such
can be retained, while also being faster.
- PlanState->targetlist is gone. Because expressions aren't initialized
on their own anymore, but as part of ExecProject, there's no need for it.
- The seperation between ExecInitExprRec() and
ExecEvalExpr/ExecInterpExpr() is different - we try to do as much as
possible during initialization.
- I retained some ugly hackering around
innermost_caseval|null/domainval|null - I had started down a path of
removing the use of those in random parts of the system, but I ended
up not going for it.

Greetings,

Andres Freund

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

#37Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Andres Freund (#35)
Re: WIP: Faster Expression Processing v4

On 3/10/17 14:24, Andres Freund wrote:

What problem are you thinking of exactly, and what'd be the solution?
I'd guess that people wouldn't like being unable to change columns in a
table if some function returned the type.

Well, that's pretty much the problem.

For example:

create type t1 as (a int, b int);
create function f1() returns setof t1 language plpgsql
as $$ begin return query values (1, 2); end $$;
alter type t1 drop attribute b;
select * from f1(); -- fail

The dependency system should arguably guard against that. Yes, it would
make some things more cumbersome, but, well, it already does that.

Not necessarily in this patch, but I would like to keep the options
open.

Yea, seems worth thinking about. I don't think the patch closes down
options?

nope

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#38Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Andres Freund (#36)
Re: WIP: Faster Expression Processing v4

On 3/10/17 14:40, Andres Freund wrote:

I'd really like to get it in. The performance improvements on its own
are significant, and it provides the basis for significantly larger
improvements again (JIT) - those followup improvements are large patches
again though, so I'd rather not do all of that next cycle.

My next step (over the weekend) is to add tests to execQual.c to get it
a good chunk closer to 100% test coverage, and then do the same for the
new implementation (there's probably very little additional tests needed
after the conversion). Given all tests pass before/after, and there's a
lot of them, I think we can have a reasonable confidence of a low bug
density.

That seems like a plan, but now would probably be a good time for some
other hackers to take note of this proposal.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#39Bruce Momjian
bruce@momjian.us
In reply to: Peter Eisentraut (#38)
Re: WIP: Faster Expression Processing v4

On Fri, Mar 10, 2017 at 07:15:59PM -0500, Peter Eisentraut wrote:

On 3/10/17 14:40, Andres Freund wrote:

I'd really like to get it in. The performance improvements on its own
are significant, and it provides the basis for significantly larger
improvements again (JIT) - those followup improvements are large patches
again though, so I'd rather not do all of that next cycle.

My next step (over the weekend) is to add tests to execQual.c to get it
a good chunk closer to 100% test coverage, and then do the same for the
new implementation (there's probably very little additional tests needed
after the conversion). Given all tests pass before/after, and there's a
lot of them, I think we can have a reasonable confidence of a low bug
density.

That seems like a plan, but now would probably be a good time for some
other hackers to take note of this proposal.

Well, the executor is long overdue for improvement, so I would love to
have this in 10.0. I am not sure what additional polishing will happen
if we punt it for 11.0. I think the only downside of having it in 10.0
is that it will not have lived in the source tree for as long a time
between commit and PG release, but if it is tested, that seems fine.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

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

#40Robert Haas
robertmhaas@gmail.com
In reply to: Bruce Momjian (#39)
Re: WIP: Faster Expression Processing v4

On Fri, Mar 10, 2017 at 7:42 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Fri, Mar 10, 2017 at 07:15:59PM -0500, Peter Eisentraut wrote:

On 3/10/17 14:40, Andres Freund wrote:

I'd really like to get it in. The performance improvements on its own
are significant, and it provides the basis for significantly larger
improvements again (JIT) - those followup improvements are large patches
again though, so I'd rather not do all of that next cycle.

My next step (over the weekend) is to add tests to execQual.c to get it
a good chunk closer to 100% test coverage, and then do the same for the
new implementation (there's probably very little additional tests needed
after the conversion). Given all tests pass before/after, and there's a
lot of them, I think we can have a reasonable confidence of a low bug
density.

That seems like a plan, but now would probably be a good time for some
other hackers to take note of this proposal.

Well, the executor is long overdue for improvement, so I would love to
have this in 10.0. I am not sure what additional polishing will happen
if we punt it for 11.0. I think the only downside of having it in 10.0
is that it will not have lived in the source tree for as long a time
between commit and PG release, but if it is tested, that seems fine.

Yeah. I think we can't rule out the possibility that this patch is
full of bugs, but (1) a lot of the change is pretty mechanical and (2)
none of this touches the on-disk format or the catalog contents, so it
doesn't break anybody's existing install if we have to patch it. On
the other hand, (3) it's a big patch and (4) if expression evaluation
doesn't work, PostgreSQL is pretty much unusable.

So my feeling is:

1. If Andres commits this and it turns out to be seriously buggy, he
should be prepared to revert it without a big fight. We can try again
for v11.

2. If Andres commits this and it turns out to be mildly buggy, he
should be prepared to address bugs very proactively and very promptly.

3. If Andres wants to commit this for v10, it should be done RSN.
Feature freeze isn't technically until the end of March, but giant
potentialy-destabilizing patches that land on March 31st are likely to
lead to outcomes that nobody wants.

Like, I think, everyone else, I'd really like to have these speedups
and I think they will benefit everyone who uses PostgreSQL. However,
I think that we cannot afford to have a repeat of the simplehash thing
where serious bugs sat around for months without really getting
properly addressed. If that happens with this, I will be extremely
unhappy, and I'm fairly sure I won't be alone.

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

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

#41Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Andres Freund (#29)
Re: WIP: Faster Expression Processing v4

Hi,

On 03/07/2017 04:01 AM, Andres Freund wrote:

Hi,

Attached is an updated version of the patchset, but more importantly
some benchmark numbers.

I wanted to do a bit of testing and benchmarking on this, but 0004 seems
to be a bit broken. The patch does not apply anymore - there are some
conflicts in execQual.c, but I think I fixed those. But then I ran into
a bunch of compile-time errors, because some of the executor nodes still
reference bits that were moved elsewhere.

E.g. there's no targetlist in PlanState anymore, yet nodeGatherMerge and
nodeTableFuncscan do this:

gm_state->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) gm_state);

Some of the nodes also assign to ps.qual values that are (List *), but
that field was switched to ExprState. That needs fixing, I guess.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#42Andres Freund
andres@anarazel.de
In reply to: Tomas Vondra (#41)
4 attachment(s)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-12 05:40:51 +0100, Tomas Vondra wrote:

I wanted to do a bit of testing and benchmarking on this, but 0004 seems to
be a bit broken.

Well, "broken" in the sense that it's already outdated, because other
stuff that got merged.

The patch does not apply anymore - there are some conflicts
in execQual.c, but I think I fixed those. But then I ran into a bunch of
compile-time errors, because some of the executor nodes still reference bits
that were moved elsewhere.

Updated patch attached. Note that this patch has two changes I've not
yet evaluated performance-wise.

Questions:
- New code calls UpdateDomainConstraintRef at the beginning of
expression evaluation, not for each row. That allows to generate
"proper programs" for constraint checking, which is quite useful for
performance. But it's a behaviour change.
- I have *not* changed that, but I'm less than convinced of the pattern
of "runtime" get_cached_rowtype calls.

Changes:
- comments
- fix minor bug in BooleanTest brought to ligth thanks to the new
regression tests (No, IS TRUE isn't the same IS NOT FALSE)
- fix (theoretical?) issue with get_cached_rowtype() being called once
for deforming and once for forming a tuple for FieldStores.
- fixed/implemented an optimization XXX around bool AND/OR because the
solution actually makes the code easier.

What's basically missing here is:
- pgindent run and minimizing resulting damage
- A higher level explanation of how the interpreted expression
evaluation works. This patch implements "fairly standard" approach,
but it's imo sufficiently
- There's one remaining FIXME that needs to be fixed.
- I sometimes see a minor performance regression (~1.2%) in tpch-h's Q17
that might or might not be code layout related.
- continue pass to ensure all worthwhile comments from execQual.c are
retained.

Could be delayed:
- add regression tests for track_functions - I've manually verified it
works, and we previously didn't have tests either.

Greetings,

Andres Freund

Attachments:

0001-Add-expression-dependencies-on-composite-type-whole-.patchtext/x-patch; charset=us-asciiDownload
From 74a8321af1143c1392042628d61d1143de3656ec Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Fri, 3 Feb 2017 11:33:16 -0800
Subject: [PATCH 1/4] Add expression dependencies on composite type / whole row
 components.

This allows, in a later commit, to have fewer checks during expression
evaluation.
---
 src/backend/catalog/dependency.c          | 145 ++++++++++++++++++++++--------
 src/test/regress/expected/create_view.out |  21 +----
 src/test/regress/expected/rangefuncs.out  |  76 +++++++++++++---
 src/test/regress/sql/create_view.sql      |   6 +-
 src/test/regress/sql/rangefuncs.sql       |  24 ++++-
 5 files changed, 198 insertions(+), 74 deletions(-)

diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fc088b2165..304209dd7c 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -206,6 +206,9 @@ static bool object_address_present_add_flags(const ObjectAddress *object,
 static bool stack_address_present_add_flags(const ObjectAddress *object,
 								int flags,
 								ObjectAddressStack *stack);
+static void add_type_addresses(Oid typeid, bool include_components, ObjectAddresses *addrs);
+static void add_type_component_addresses(Oid typeid, ObjectAddresses *addrs);
+static void add_class_component_addresses(Oid relid, ObjectAddresses *addrs);
 static void DeleteInitPrivs(const ObjectAddress *object);
 
 
@@ -1364,10 +1367,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,
  * whereas 'behavior' is used for everything else.
  *
  * NOTE: the caller should ensure that a whole-table dependency on the
- * specified relation is created separately, if one is needed.  In particular,
- * a whole-row Var "relation.*" will not cause this routine to emit any
- * dependency item.  This is appropriate behavior for subexpressions of an
- * ordinary query, so other cases need to cope as necessary.
+ * specified relation is created separately, if one is needed.  E.g. SELECT
+ * FROM tbl will not cause this routine to emit any dependency items.
  */
 void
 recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
@@ -1484,19 +1485,23 @@ find_expr_references_walker(Node *node,
 			elog(ERROR, "invalid varno %d", var->varno);
 		rte = rt_fetch(var->varno, rtable);
 
-		/*
-		 * A whole-row Var references no specific columns, so adds no new
-		 * dependency.  (We assume that there is a whole-table dependency
-		 * arising from each underlying rangetable entry.  While we could
-		 * record such a dependency when finding a whole-row Var that
-		 * references a relation directly, it's quite unclear how to extend
-		 * that to whole-row Vars for JOINs, so it seems better to leave the
-		 * responsibility with the range table.  Note that this poses some
-		 * risks for identifying dependencies of stand-alone expressions:
-		 * whole-table references may need to be created separately.)
-		 */
 		if (var->varattno == InvalidAttrNumber)
-			return false;
+		{
+			/*
+			 * A whole-row Var essentially references all current columns, so
+			 * add dependencies for them - that allow adding new columns to
+			 * the type, but not removing or altering the type of existing
+			 * columns.
+			 *
+			 * That does not obviate the need for a whole-table dependency
+			 * arising from each underlying rangetable entry - this would
+			 * e.g. not do the right thing for column-less tables.  Note that
+			 * this requires some care for identifying dependencies of
+			 * stand-alone expressions: whole-table references may need to be
+			 * created separately.
+			 */
+			add_class_component_addresses(rte->relid, context->addrs);
+		}
 		if (rte->rtekind == RTE_RELATION)
 		{
 			/* If it's a plain relation, reference this column */
@@ -1529,8 +1534,7 @@ find_expr_references_walker(Node *node,
 		Oid			objoid;
 
 		/* A constant must depend on the constant's datatype */
-		add_object_address(OCLASS_TYPE, con->consttype, 0,
-						   context->addrs);
+		add_type_addresses(con->consttype, true, context->addrs);
 
 		/*
 		 * We must also depend on the constant's collation: it could be
@@ -1580,8 +1584,7 @@ find_expr_references_walker(Node *node,
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TYPEOID,
 											  ObjectIdGetDatum(objoid)))
-						add_object_address(OCLASS_TYPE, objoid, 0,
-										   context->addrs);
+						add_type_addresses(objoid, false, context->addrs);
 					break;
 				case REGCONFIGOID:
 					objoid = DatumGetObjectId(con->constvalue);
@@ -1625,8 +1628,7 @@ find_expr_references_walker(Node *node,
 		Param	   *param = (Param *) node;
 
 		/* A parameter must depend on the parameter's datatype */
-		add_object_address(OCLASS_TYPE, param->paramtype, 0,
-						   context->addrs);
+		add_type_addresses(param->paramtype, true, context->addrs);
 		/* and its collation, just as for Consts */
 		if (OidIsValid(param->paramcollid) &&
 			param->paramcollid != DEFAULT_COLLATION_OID)
@@ -1639,6 +1641,9 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_PROC, funcexpr->funcid, 0,
 						   context->addrs);
+		/* dependency on type itself already exists via function */
+		add_type_component_addresses(funcexpr->funcresulttype, context->addrs);
+
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, OpExpr))
@@ -1647,6 +1652,9 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
 						   context->addrs);
+		/* dependency on type itself already exists via function */
+		add_type_component_addresses(opexpr->opresulttype, context->addrs);
+
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, DistinctExpr))
@@ -1655,6 +1663,13 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_OPERATOR, distinctexpr->opno, 0,
 						   context->addrs);
+		/*
+		 * Dependency on type itself already exists via function. Be paranoid
+		 * and add deps to return type components (unlikely to matter due to
+		 * return type, but ...)
+		 */
+		add_type_component_addresses(distinctexpr->opresulttype,
+									 context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, NullIfExpr))
@@ -1663,6 +1678,7 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
 						   context->addrs);
+		/* can't add new type dependencies */
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, ScalarArrayOpExpr))
@@ -1679,6 +1695,7 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_PROC, aggref->aggfnoid, 0,
 						   context->addrs);
+		add_type_component_addresses(aggref->aggtype, context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, WindowFunc))
@@ -1687,6 +1704,7 @@ find_expr_references_walker(Node *node,
 
 		add_object_address(OCLASS_PROC, wfunc->winfnoid, 0,
 						   context->addrs);
+		add_type_component_addresses(wfunc->wintype, context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, SubPlan))
@@ -1699,8 +1717,8 @@ find_expr_references_walker(Node *node,
 		RelabelType *relab = (RelabelType *) node;
 
 		/* since there is no function dependency, need to depend on type */
-		add_object_address(OCLASS_TYPE, relab->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(relab->resulttype, false, context->addrs);
+
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(relab->resultcollid) &&
 			relab->resultcollid != DEFAULT_COLLATION_OID)
@@ -1712,8 +1730,7 @@ find_expr_references_walker(Node *node,
 		CoerceViaIO *iocoerce = (CoerceViaIO *) node;
 
 		/* since there is no exposed function, need to depend on type */
-		add_object_address(OCLASS_TYPE, iocoerce->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(iocoerce->resulttype, true, context->addrs);
 	}
 	else if (IsA(node, ArrayCoerceExpr))
 	{
@@ -1722,8 +1739,7 @@ find_expr_references_walker(Node *node,
 		if (OidIsValid(acoerce->elemfuncid))
 			add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0,
 							   context->addrs);
-		add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(acoerce->resulttype, true, context->addrs);
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, ConvertRowtypeExpr))
@@ -1731,8 +1747,7 @@ find_expr_references_walker(Node *node,
 		ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
 
 		/* since there is no function dependency, need to depend on type */
-		add_object_address(OCLASS_TYPE, cvt->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(cvt->resulttype, true, context->addrs);
 	}
 	else if (IsA(node, CollateExpr))
 	{
@@ -1745,8 +1760,7 @@ find_expr_references_walker(Node *node,
 	{
 		RowExpr    *rowexpr = (RowExpr *) node;
 
-		add_object_address(OCLASS_TYPE, rowexpr->row_typeid, 0,
-						   context->addrs);
+		add_type_addresses(rowexpr->row_typeid, true, context->addrs);
 	}
 	else if (IsA(node, RowCompareExpr))
 	{
@@ -1769,8 +1783,7 @@ find_expr_references_walker(Node *node,
 	{
 		CoerceToDomain *cd = (CoerceToDomain *) node;
 
-		add_object_address(OCLASS_TYPE, cd->resulttype, 0,
-						   context->addrs);
+		add_type_addresses(cd->resulttype, true, context->addrs);
 	}
 	else if (IsA(node, OnConflictExpr))
 	{
@@ -1898,13 +1911,13 @@ find_expr_references_walker(Node *node,
 
 		/*
 		 * Add refs for any datatypes and collations used in a column
-		 * definition list for a RECORD function.  (For other cases, it should
-		 * be enough to depend on the function itself.)
+		 * definition list for a RECORD function.  Additional dependencies
+		 * will possibly be added when recursing to the contained function
+		 * expression.
 		 */
 		foreach(ct, rtfunc->funccoltypes)
 		{
-			add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0,
-							   context->addrs);
+			add_type_addresses(lfirst_oid(ct), true, context->addrs);
 		}
 		foreach(ct, rtfunc->funccolcollations)
 		{
@@ -2228,6 +2241,62 @@ object_address_present_add_flags(const ObjectAddress *object,
 }
 
 /*
+ * Add ObjectAddresses entry for typeid and, if include_components = true and
+ * and the type is a composite type, for it's columns.
+ */
+static void
+add_type_addresses(Oid typeid, bool include_components, ObjectAddresses *addrs)
+{
+	add_object_address(OCLASS_TYPE, typeid, 0, addrs);
+
+	if (include_components)
+	{
+		add_type_component_addresses(typeid, addrs);
+	}
+}
+
+/*
+ * Add ObjectAddresses entry for the type's columns if it's a composite type.
+ *
+ * Besides being a helper for add_type_addresses it can make sense to add
+ * dependencies on the components if, as e.g. the case for a function return
+ * type, there already exists a dependency on the type, but not the typ's
+ * components.
+ */
+static void
+add_type_component_addresses(Oid typeid, ObjectAddresses *addrs)
+{
+	Oid typerelid = get_typ_typrelid(typeid);
+
+	if (typerelid != InvalidOid)
+	{
+		add_class_component_addresses(typerelid, addrs);
+	}
+}
+
+/*
+ * Add ObjectAddresses entries for the relation's columns.
+ */
+static void
+add_class_component_addresses(Oid relid, ObjectAddresses *addrs)
+{
+	Relation rel = relation_open(relid, NoLock);
+	TupleDesc tupDesc = RelationGetDescr(rel);
+	int i;
+
+	for (i = 0; i < tupDesc->natts; i++)
+	{
+		Form_pg_attribute att = tupDesc->attrs[i];
+
+		if (att->attisdropped)
+			continue;
+
+		add_object_address(OCLASS_CLASS, relid, att->attnum, addrs);
+	}
+	relation_close(rel, NoLock);
+}
+
+/*
  * Similar to above, except we search an ObjectAddressStack.
  */
 static bool
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ce0c8cedf8..379c070ca6 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -1429,24 +1429,11 @@ select * from tt14v;
  foo | baz | quux
 (1 row)
 
--- this perhaps should be rejected, but it isn't:
+-- this will be rejected as the column is referenced in the view:
 alter table tt14t drop column f3;
--- f3 is still in the view but will read as nulls
-select pg_get_viewdef('tt14v', true);
-         pg_get_viewdef         
---------------------------------
-  SELECT t.f1,                 +
-     t.f3,                     +
-     t.f4                      +
-    FROM tt14f() t(f1, f3, f4);
-(1 row)
-
-select * from tt14v;
- f1  | f3 |  f4  
------+----+------
- foo |    | quux
-(1 row)
-
+ERROR:  cannot drop table tt14t column f3 because other objects depend on it
+DETAIL:  view tt14v depends on table tt14t column f3
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
 -- check display of whole-row variables in some corner cases
 create type nestedcomposite as (x int8_tbl);
 create view tt15v as select row(i)::nestedcomposite from int8_tbl i;
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 526a4aed0a..33b462126a 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1899,9 +1899,13 @@ SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
  id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
--- check that we can cope with post-parsing changes in rowtypes
 create temp view usersview as
 SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
+create temp view usersview2 as
+SELECT * FROM (SELECT get_users()) f;
+create temp view usersview3 as
+SELECT * FROM (SELECT users FROM users) f;
+-- check that rowtypes referenced in views can't be dropped / changed
 select * from usersview;
  userid | seq | email  | moredrop | enabled | generate_series | ordinality 
 --------+-----+--------+----------+---------+-----------------+------------
@@ -1909,27 +1913,79 @@ select * from usersview;
  id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
+select * from usersview2;
+      get_users      
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
 alter table users drop column moredrop;
+ERROR:  cannot drop table users column moredrop because other objects depend on it
+DETAIL:  view usersview depends on table users column moredrop
+view usersview2 depends on table users column moredrop
+view usersview3 depends on table users column moredrop
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+alter table users alter column seq type numeric;
+ERROR:  cannot alter type of a column used by a view or rule
+DETAIL:  rule _RETURN on view usersview3 depends on column "seq"
 select * from usersview;
  userid | seq | email  | moredrop | enabled | generate_series | ordinality 
 --------+-----+--------+----------+---------+-----------------+------------
- id     |   1 | email  |          | t       |              10 |          1
- id2    |   2 | email2 |          | t       |              11 |          2
+ id     |   1 | email  |       11 | t       |              10 |          1
+ id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
+select * from usersview2;
+      get_users      
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
+select * from usersview3;
+        users        
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
+-- check that column additions are handled properly
 alter table users add column junk text;
 select * from usersview;
  userid | seq | email  | moredrop | enabled | generate_series | ordinality 
 --------+-----+--------+----------+---------+-----------------+------------
- id     |   1 | email  |          | t       |              10 |          1
- id2    |   2 | email2 |          | t       |              11 |          2
+ id     |   1 | email  |       11 | t       |              10 |          1
+ id2    |   2 | email2 |       12 | t       |              11 |          2
 (2 rows)
 
-alter table users alter column seq type numeric;
-select * from usersview;  -- expect clean failure
-ERROR:  attribute 2 has wrong type
-DETAIL:  Table has type numeric, but query expects integer.
-drop view usersview;
+select * from usersview2;
+      get_users       
+----------------------
+ (id,1,email,11,t,)
+ (id2,2,email2,12,t,)
+(2 rows)
+
+select * from usersview3;
+        users         
+----------------------
+ (id,1,email,11,t,)
+ (id2,2,email2,12,t,)
+(2 rows)
+
+-- check that cascade is handled properly
+alter table users drop column moredrop CASCADE;
+NOTICE:  drop cascades to 3 other objects
+DETAIL:  drop cascades to view usersview
+drop cascades to view usersview2
+drop cascades to view usersview3
+-- should all be gone now
+\dv usersview*
+      List of relations
+ Schema | Name | Type | Owner 
+--------+------+------+-------
+(0 rows)
+
 drop function get_first_user();
 drop function get_users();
 drop table users;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index c27f1034e1..da4236766c 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -483,13 +483,9 @@ create view tt14v as select t.* from tt14f() t;
 select pg_get_viewdef('tt14v', true);
 select * from tt14v;
 
--- this perhaps should be rejected, but it isn't:
+-- this will be rejected as the column is referenced in the view:
 alter table tt14t drop column f3;
 
--- f3 is still in the view but will read as nulls
-select pg_get_viewdef('tt14v', true);
-select * from tt14v;
-
 -- check display of whole-row variables in some corner cases
 
 create type nestedcomposite as (x int8_tbl);
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 09ac8fbdb4..902457f5f1 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -551,19 +551,35 @@ SELECT * FROM get_users() WITH ORDINALITY;   -- make sure ordinality copes
 SELECT * FROM ROWS FROM(generate_series(10,11), get_users()) WITH ORDINALITY;
 SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
 
--- check that we can cope with post-parsing changes in rowtypes
 create temp view usersview as
 SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
 
+create temp view usersview2 as
+SELECT * FROM (SELECT get_users()) f;
+
+create temp view usersview3 as
+SELECT * FROM (SELECT users FROM users) f;
+
+-- check that rowtypes referenced in views can't be dropped / changed
 select * from usersview;
+select * from usersview2;
 alter table users drop column moredrop;
+alter table users alter column seq type numeric;
 select * from usersview;
+select * from usersview2;
+select * from usersview3;
+
+-- check that column additions are handled properly
 alter table users add column junk text;
 select * from usersview;
-alter table users alter column seq type numeric;
-select * from usersview;  -- expect clean failure
+select * from usersview2;
+select * from usersview3;
+
+-- check that cascade is handled properly
+alter table users drop column moredrop CASCADE;
+-- should all be gone now
+\dv usersview*
 
-drop view usersview;
 drop function get_first_user();
 drop function get_users();
 drop table users;
-- 
2.11.0.22.g8d7a455.dirty

0002-Make-get_last_attnums-more-generic.patchtext/x-patch; charset=us-asciiDownload
From 850767aa755f90ceedf48b504fadb49ddb562aa7 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Mon, 5 Dec 2016 18:10:04 -0800
Subject: [PATCH 2/4] Make get_last_attnums more generic.

---
 src/backend/executor/execUtils.c | 48 +++++++++++++++++++++++++++++-----------
 src/include/executor/executor.h  |  4 ++++
 2 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 3d6a3801c0..d205101b89 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -47,7 +47,14 @@
 #include "utils/rel.h"
 
 
-static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
+typedef struct LastLastAttnumInfo
+{
+	AttrNumber last_outer;
+	AttrNumber last_inner;
+	AttrNumber last_scan;
+} LastAttnumInfo;
+
+
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -580,7 +587,10 @@ ExecBuildProjectionInfo(List *targetList,
 			/* Not a simple variable, add it to generic targetlist */
 			exprlist = lappend(exprlist, gstate);
 			/* Examine expr to include contained Vars in lastXXXVar counts */
-			get_last_attnums((Node *) variable, projInfo);
+			ExecGetLastAttnums((Node *) variable,
+							   &projInfo->pi_lastOuterVar,
+							   &projInfo->pi_lastInnerVar,
+							   &projInfo->pi_lastScanVar);
 		}
 	}
 	projInfo->pi_targetlist = exprlist;
@@ -591,13 +601,13 @@ ExecBuildProjectionInfo(List *targetList,
 }
 
 /*
- * get_last_attnums: expression walker for ExecBuildProjectionInfo
+ * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
  *	Update the lastXXXVar counts to be at least as large as the largest
  *	attribute numbers found in the expression
  */
 static bool
-get_last_attnums(Node *node, ProjectionInfo *projInfo)
+get_last_attnums_walker(Node *node, LastAttnumInfo *info)
 {
 	if (node == NULL)
 		return false;
@@ -609,21 +619,17 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo)
 		switch (variable->varno)
 		{
 			case INNER_VAR:
-				if (projInfo->pi_lastInnerVar < attnum)
-					projInfo->pi_lastInnerVar = attnum;
+				info->last_inner = Max(info->last_inner, attnum);
 				break;
 
 			case OUTER_VAR:
-				if (projInfo->pi_lastOuterVar < attnum)
-					projInfo->pi_lastOuterVar = attnum;
+				info->last_outer = Max(info->last_outer, attnum);
 				break;
 
 				/* INDEX_VAR is handled by default case */
 
 			default:
-				if (projInfo->pi_lastScanVar < attnum)
-					projInfo->pi_lastScanVar = attnum;
-				break;
+				info->last_scan = Max(info->last_scan, attnum);
 		}
 		return false;
 	}
@@ -640,10 +646,26 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo)
 		return false;
 	if (IsA(node, GroupingFunc))
 		return false;
-	return expression_tree_walker(node, get_last_attnums,
-								  (void *) projInfo);
+	return expression_tree_walker(node, get_last_attnums_walker,
+								  (void *) info);
 }
 
+void
+ExecGetLastAttnums(Node *node, int *last_outer, int *last_inner,
+				   int *last_scan)
+{
+	LastAttnumInfo info = {0,0,0};
+
+	get_last_attnums_walker(node, &info);
+	if (last_outer && *last_outer < info.last_outer)
+		*last_outer = info.last_outer;
+	if (last_inner && *last_inner < info.last_inner)
+		*last_inner = info.last_inner;
+	if (last_scan && *last_scan < info.last_scan)
+		*last_scan = info.last_scan;
+}
+
+
 /* ----------------
  *		ExecAssignProjectionInfo
  *
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 02dbe7b228..32e1838e15 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -354,6 +354,10 @@ extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
 extern void ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc);
 extern void ExecAssignResultTypeFromTL(PlanState *planstate);
 extern TupleDesc ExecGetResultType(PlanState *planstate);
+extern void ExecGetLastAttnums(Node *node,
+							   int *last_outer,
+							   int *last_inner,
+							   int *last_scan);
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
-- 
2.11.0.22.g8d7a455.dirty

0003-Add-autoconf-test-for-computed-goto-support.patchtext/x-patch; charset=us-asciiDownload
From ff0d49fd9d40e13402cf40884b240672817a5728 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 7 Feb 2017 14:16:15 -0800
Subject: [PATCH 3/4] Add autoconf test for computed goto support.

Author: Andres Freund
---
 config/c-compiler.m4          | 24 ++++++++++++++++++++++++
 configure                     | 34 ++++++++++++++++++++++++++++++++++
 configure.in                  |  1 +
 src/include/pg_config.h.in    |  3 +++
 src/include/pg_config.h.win32 |  3 +++
 5 files changed, 65 insertions(+)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 7d901e1f1a..f56d8852f0 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -491,3 +491,27 @@ if test x"$Ac_cachevar" = x"yes"; then
 fi
 undefine([Ac_cachevar])dnl
 ])# PGAC_SSE42_CRC32_INTRINSICS
+
+
+
+# PGAC_C_COMPUTED_GOTO
+# -----------------------
+# Check if the C compiler knows computed gotos (gcc extension, also
+# available in at least clang).  If so define HAVE_COMPUTED_GOTO
+#
+# Checking whether computed gotos are supported syntax-wise ought to
+# be enough, as the syntax is otherwise illegal.
+AC_DEFUN([PGAC_C_COMPUTED_GOTO],
+[AC_CACHE_CHECK(for computed goto support, pgac_cv__computed_goto,
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[[void *labeladdrs[] = {&&my_label};
+  goto *labeladdrs[0];
+  my_label:
+  return 1;
+]])],
+[pgac_cv__computed_goto=yes],
+[pgac_cv__computed_goto=no])])
+if test x"$pgac_cv__computed_goto" = xyes ; then
+AC_DEFINE(HAVE__COMPUTED_GOTO, 1,
+  [Define to 1 if your compiler handles computed gotos.])
+fi])# PGAC_C_COMPUTED_GOTO
diff --git a/configure b/configure
index b5cdebb510..fafef4638e 100755
--- a/configure
+++ b/configure
@@ -11564,6 +11564,40 @@ if test x"$pgac_cv__va_args" = xyes ; then
 $as_echo "#define HAVE__VA_ARGS 1" >>confdefs.h
 
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for computed goto support" >&5
+$as_echo_n "checking for computed goto support... " >&6; }
+if ${pgac_cv__computed_goto+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+void *labeladdrs[] = {&&my_label};
+  goto *labeladdrs[0];
+  my_label:
+  return 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv__computed_goto=yes
+else
+  pgac_cv__computed_goto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__computed_goto" >&5
+$as_echo "$pgac_cv__computed_goto" >&6; }
+if test x"$pgac_cv__computed_goto" = xyes ; then
+
+$as_echo "#define HAVE__COMPUTED_GOTO 1" >>confdefs.h
+
+fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
 if ${ac_cv_struct_tm+:} false; then :
diff --git a/configure.in b/configure.in
index 1d99cda1d8..9aa9203c40 100644
--- a/configure.in
+++ b/configure.in
@@ -1323,6 +1323,7 @@ PGAC_C_BUILTIN_BSWAP64
 PGAC_C_BUILTIN_CONSTANT_P
 PGAC_C_BUILTIN_UNREACHABLE
 PGAC_C_VA_ARGS
+PGAC_C_COMPUTED_GOTO
 PGAC_STRUCT_TIMEZONE
 PGAC_UNION_SEMUN
 PGAC_STRUCT_SOCKADDR_UN
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5bcd8a1160..94de1a75a2 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -687,6 +687,9 @@
 /* Define to 1 if your compiler understands __builtin_unreachable. */
 #undef HAVE__BUILTIN_UNREACHABLE
 
+/* Define to 1 if your compiler handles computed gotos. */
+#undef HAVE__COMPUTED_GOTO
+
 /* Define to 1 if you have __cpuid. */
 #undef HAVE__CPUID
 
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 3e4132cd82..c5441855bd 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -514,6 +514,9 @@
 /* Define to 1 if your compiler understands __builtin_unreachable. */
 /* #undef HAVE__BUILTIN_UNREACHABLE */
 
+/* Define to 1 if your compiler handles computed gotos. */
+#undef HAVE__COMPUTED_GOTO
+
 /* Define to 1 if you have __cpuid. */
 #define HAVE__CPUID 1
 
-- 
2.11.0.22.g8d7a455.dirty

0004-WIP-Faster-expression-evaluation-and-targetlist-proj.patchtext/x-patch; charset=us-asciiDownload
From 82be56001e184b125f16fd6007ad5f9926245490 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Mon, 5 Dec 2016 18:10:04 -0800
Subject: [PATCH 4/4] WIP: Faster expression evaluation and targetlist
 projection.

This reimplements expression evaluation as a an opcode based
interpreter, instead of doing tree-traversal. This both saves on
once-per-execution overhead of expression initalization, but more
importantly makes execution faster.  It also has the distinct
advantage of making JITing of expression evaluation easier.
---
 contrib/postgres_fdw/postgres_fdw.c       |    2 +-
 src/backend/bootstrap/bootstrap.c         |    2 +-
 src/backend/catalog/index.c               |   43 +-
 src/backend/catalog/partition.c           |    4 +-
 src/backend/catalog/toasting.c            |    2 +-
 src/backend/commands/analyze.c            |   10 +-
 src/backend/commands/copy.c               |    2 +-
 src/backend/commands/explain.c            |    2 +-
 src/backend/commands/indexcmds.c          |    4 +-
 src/backend/commands/prepare.c            |    4 +-
 src/backend/commands/tablecmds.c          |   22 +-
 src/backend/commands/trigger.c            |    8 +-
 src/backend/executor/Makefile             |    6 +-
 src/backend/executor/execExpr.c           | 2279 +++++++++++++++
 src/backend/executor/execIndexing.c       |   20 +-
 src/backend/executor/execInterpExpr.c     | 2888 ++++++++++++++++++
 src/backend/executor/execMain.c           |   32 +-
 src/backend/executor/execQual.c           | 4540 +----------------------------
 src/backend/executor/execScan.c           |    4 +-
 src/backend/executor/execUtils.c          |  133 +-
 src/backend/executor/nodeAgg.c            |   20 +-
 src/backend/executor/nodeBitmapHeapscan.c |   17 +-
 src/backend/executor/nodeCtescan.c        |    8 +-
 src/backend/executor/nodeCustom.c         |    7 +-
 src/backend/executor/nodeForeignscan.c    |   15 +-
 src/backend/executor/nodeFunctionscan.c   |   18 +-
 src/backend/executor/nodeGather.c         |    8 +-
 src/backend/executor/nodeGatherMerge.c    |    9 +-
 src/backend/executor/nodeGroup.c          |   12 +-
 src/backend/executor/nodeHash.c           |   12 +-
 src/backend/executor/nodeHashjoin.c       |   43 +-
 src/backend/executor/nodeIndexonlyscan.c  |   15 +-
 src/backend/executor/nodeIndexscan.c      |   24 +-
 src/backend/executor/nodeMergejoin.c      |   33 +-
 src/backend/executor/nodeModifyTable.c    |   40 +-
 src/backend/executor/nodeNestloop.c       |   23 +-
 src/backend/executor/nodeProjectSet.c     |   52 +-
 src/backend/executor/nodeResult.c         |   16 +-
 src/backend/executor/nodeSamplescan.c     |   15 +-
 src/backend/executor/nodeSeqscan.c        |    8 +-
 src/backend/executor/nodeSubplan.c        |   91 +-
 src/backend/executor/nodeSubqueryscan.c   |    8 +-
 src/backend/executor/nodeTableFuncscan.c  |   21 +-
 src/backend/executor/nodeTidscan.c        |   37 +-
 src/backend/executor/nodeValuesscan.c     |   10 +-
 src/backend/executor/nodeWindowAgg.c      |    8 +-
 src/backend/executor/nodeWorktablescan.c  |    8 +-
 src/backend/optimizer/util/clauses.c      |   12 +-
 src/backend/utils/adt/domains.c           |   18 +-
 src/backend/utils/adt/ruleutils.c         |    4 +-
 src/backend/utils/adt/xml.c               |   37 +-
 src/backend/utils/cache/typcache.c        |   10 +-
 src/include/executor/execExpr.h           |  455 +++
 src/include/executor/execdebug.h          |   21 -
 src/include/executor/executor.h           |   95 +-
 src/include/executor/nodeSubplan.h        |    3 +
 src/include/nodes/execnodes.h             |  562 +---
 src/include/nodes/nodes.h                 |   29 +-
 src/include/utils/xml.h                   |    2 +-
 src/pl/plpgsql/src/pl_exec.c              |    5 +-
 src/test/regress/expected/case.out        |    2 +-
 src/test/regress/sql/case.sql             |    2 +-
 62 files changed, 6363 insertions(+), 5479 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c
 create mode 100644 src/backend/executor/execInterpExpr.c
 create mode 100644 src/include/executor/execExpr.h

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 990313a597..975aa88c5c 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3414,7 +3414,7 @@ prepare_query_params(PlanState *node,
 	 * benefit, and it'd require postgres_fdw to know more than is desirable
 	 * about Param evaluation.)
 	 */
-	*param_exprs = (List *) ExecInitExpr((Expr *) fdw_exprs, node);
+	*param_exprs = ExecInitExprList(fdw_exprs, node);
 
 	/* Allocate buffer for text form of query parameters. */
 	*param_values = (const char **) palloc0(numParams * sizeof(char *));
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 6511c6064b..6cfce4f8dd 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1084,7 +1084,7 @@ index_register(Oid heap,
 	/* predicate will likely be null, but may as well copy it */
 	newind->il_info->ii_Predicate = (List *)
 		copyObject(indexInfo->ii_Predicate);
-	newind->il_info->ii_PredicateState = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 8d42a347ea..d6be0915a7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1658,7 +1658,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -1774,9 +1774,8 @@ FormIndexDatum(IndexInfo *indexInfo,
 		indexInfo->ii_ExpressionsState == NIL)
 	{
 		/* First time through, set up expression evaluation state */
-		indexInfo->ii_ExpressionsState = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
-							estate);
+		indexInfo->ii_ExpressionsState =
+			ExecPrepareExprList(indexInfo->ii_Expressions, estate);
 		/* Check caller has set up context correctly */
 		Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
 	}
@@ -2208,7 +2207,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2247,9 +2246,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2552,9 +2549,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
@@ -2619,7 +2616,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2646,7 +2643,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2672,9 +2669,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2699,9 +2694,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
@@ -2732,7 +2727,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2962,7 +2957,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2992,9 +2987,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3121,9 +3114,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate, econtext))
 					continue;
 			}
 
@@ -3177,7 +3170,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index e01ef864f0..1e75777acb 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1618,8 +1618,8 @@ FormPartitionKeyDatum(PartitionDispatch pd,
 			   GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
 
 		/* First time through, set up expression evaluation state */
-		pd->keystate = (List *) ExecPrepareExpr((Expr *) pd->key->partexprs,
-												estate);
+		pd->keystate = ExecPrepareExprList(pd->key->partexprs,
+													estate);
 	}
 
 	partexpr_item = list_head(pd->keystate);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 0e4231668d..29756eb14e 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index b91df986c5..d124366db2 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -703,7 +703,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState  *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -729,9 +729,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = castNode(List,
-							 ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-											 estate));
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -754,9 +752,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate, econtext))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3102ab18c5..7295853e15 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -3406,7 +3406,7 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
 		Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory);
 
 		values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext,
-										 &nulls[defmap[i]]);
+										  &nulls[defmap[i]]);
 	}
 
 	return true;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c9b55ead3d..55d09c0e29 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2890,7 +2890,7 @@ ExplainSubPlans(List *plans, List *ancestors,
 	foreach(lst, plans)
 	{
 		SubPlanState *sps = (SubPlanState *) lfirst(lst);
-		SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
+		SubPlan    *sp = sps->subplan;
 
 		/*
 		 * There can be multiple SubPlan nodes referencing the same physical
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 72bb06c760..fd0aeb06b9 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -179,7 +179,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -556,7 +556,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 1cf0d2b971..46a362132c 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -391,7 +391,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 	}
 
 	/* Prepare the expressions for execution */
-	exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
+	exprstates = ExecPrepareExprList(params, estate);
 
 	paramLI = (ParamListInfo)
 		palloc(offsetof(ParamListInfoData, params) +
@@ -407,7 +407,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 	i = 0;
 	foreach(l, exprstates)
 	{
-		ExprState  *n = lfirst(l);
+		ExprState  *n = (ExprState *) lfirst(l);
 		ParamExternData *prm = &paramLI->params[i];
 
 		prm->ptype = param_types[i];
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1ddb72d164..07d0bb7af0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -185,7 +185,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState  *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4262,7 +4262,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 	CommandId	mycid;
 	BulkInsertState bistate;
 	int			hi_options;
-	List	   *partqualstate = NIL;
+	ExprState  *partqualstate = NULL;
 
 	/*
 	 * Open the relation(s).  We have surely already locked the existing
@@ -4315,8 +4315,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4331,9 +4330,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 	if (tab->partition_constraint)
 	{
 		needscan = true;
-		partqualstate = (List *)
-			ExecPrepareExpr((Expr *) tab->partition_constraint,
-							estate);
+		partqualstate = ExecPrepareCheck(tab->partition_constraint, estate);
 	}
 
 	foreach(l, tab->newvals)
@@ -4508,7 +4505,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate, econtext))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -4524,7 +4521,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				}
 			}
 
-			if (partqualstate && !ExecQual(partqualstate, econtext, true))
+			if (partqualstate && !ExecCheck(partqualstate, econtext))
 				ereport(ERROR,
 						(errcode(ERRCODE_CHECK_VIOLATION),
 					errmsg("partition constraint is violated by some row")));
@@ -7786,7 +7783,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState  *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7817,8 +7814,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7838,7 +7834,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate, econtext))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d80bff671c..a13cce05ca 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3057,7 +3057,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -3078,7 +3078,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -3089,7 +3089,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -3137,7 +3137,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate, econtext))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index d281906cd5..2c68e18a5b 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,9 +12,9 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
-       execReplication.o execScan.o execTuples.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execInterpExpr.o execGrouping.o \
+       execIndexing.o execJunk.o execMain.o execParallel.o execProcnode.o \
+       execQual.o execReplication.o execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
        nodeBitmapHeapscan.o nodeBitmapIndexscan.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000000..f6ff937943
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,2279 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.c
+ *	  Support for evaluating expressions.
+ *
+ *	Expression evaluation now works by first converting expression trees
+ *	(which went through planning first) into an ExprState using ExecInitExpr()
+ *	et al. This converts the tree into a opcode based program (ExprEvalStep
+ *	representing individual instructions); allocated as an array of stesps.
+ *
+ *	The ExprEvalStep representation is designed to be usable for interpreting
+ *	the expression, as well as compiling into native code. Thus, if possible,
+ *	as much complexity as possible should be handed by ExecInitExpr() (and
+ *	helpers), instead of handled at execution time where both interpreted and
+ *	compiled versions would need to deal with the complexity. Additionally
+ *	checks for initialization at run time have a small but noticeable cost at
+ *	every execution.
+ *
+ *	The next step is preparing the ExprState for execution, using
+ *	ExecInstantiateExpr(). This is internally done by ExecInitExpr() and other
+ *	functions that prepare for expression evaluation.  ExecInstantiateExpr()
+ *	initializes the expression tree for the relevant method chosen to evaluate
+ *	the expression.
+ *
+ *	Note that a lot of the more complex expression evaluation steps, which are
+ *	less performance critical than some of the simpler and more common ones,
+ *	are implemented as separate functions outside the fast-path of interpreted
+ *	expression, like e.g. ExecEvalRow(), so that the implementation can be
+ *	shared between interpreted and compiled expression evaluation.  That means
+ *	that ExecInstantiateExpr() always has to initialize the expression for
+ *	evaluation by execInterpExpr.c.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/execExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/execExpr.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+
+
+/*
+ * Support for building execution state.
+ */
+static void ExecInstantiateExpr(ExprState *state);
+static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state, Datum *resv, bool *resnull);
+static void ExprEvalPushStep(ExprState *es, ExprEvalStep *s);
+static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+						  PlanState *parent, ExprState *state,  Datum *resv, bool *resnull);
+static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent, ExprState *state, Datum *resv, bool *resnull);
+static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent, ExprState *state, Datum *resv, bool *resnull);
+static void ExecInitExprSlots(ExprState *state, Node *node);
+
+/* support functions */
+static bool isAssignmentIndirectionExpr(Expr *expr);
+
+
+/*
+ * ExecInitExpr: prepare an expression tree for execution
+ *
+ * This function builds and returns an ExprState implementing the given
+ * Expr node tree.  The return ExprState can then be handed to ExecEvalExpr
+ * for execution.  Because the Expr tree itself is read-only as far as
+ * ExecInitExpr and ExecEvalExpr are concerned, several different executions
+ * of the same plan tree can occur concurrently.
+ *
+ * This must be called in a memory context that will last as long as repeated
+ * executions of the expression are needed.  Typically the context will be
+ * the same as the per-query context of the associated ExprContext.
+ *
+ * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
+ * lists of such nodes held by the parent PlanState.
+ *
+ * Note: there is no ExecEndExpr function; we assume that any resource
+ * cleanup needed will be handled by just releasing the memory context
+ * in which the state tree is built.  Functions that require additional
+ * cleanup work can register a shutdown callback in the ExprContext.
+ *
+ *	'node' is the root of the expression tree to examine
+ *	'parent' is the PlanState node that owns the expression.
+ *
+ * 'parent' may be NULL if we are preparing an expression that is not
+ * associated with a plan tree.  (If so, it can't have aggs or subplans.)
+ * This case should usually come through ExecPrepareExpr, not directly here.
+ */
+ExprState *
+ExecInitExpr(Expr *node, PlanState *parent)
+{
+	ExprState *state = makeNode(ExprState);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->expr = node;
+	ExecInitExprSlots(state, (Node *) node);
+
+	ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecInstantiateExpr(state);
+
+	return state;
+}
+
+/*
+ * ExecInitQual: prepare a qual for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that returns true iff none of the subexpressions are false.  (We
+ * also return true if the list is empty.)
+ *
+ * If some of the subexpressions yield NULL, then the result of the
+ * conjunction is false.  This makes this routine primarily useful for
+ * evaluating WHERE clauses, since SQL specifies that tuples with null WHERE
+ * results do not get selected.
+ */
+ExprState *
+ExecInitQual(List *qual, PlanState *parent)
+{
+	ExprState *state = makeNode(ExprState);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	List *adjust_bailout = NIL;
+
+	/* short-circuit (here and in ExecQual) for empty restriction list */
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	/*
+	 * ExecQual() needs to return false for expression returning NULL. That
+	 * allows to short-circuit the evaluation the first time a NULL is
+	 * encountered.  As qual evaluation is a hot-path this warrants using a
+	 * special opcode for qual evaluation that's simpler than BOOL_AND (which
+	 * has more complex NULL handling).
+	 */
+	state->expr = (Expr *) qual;
+	ExecInitExprSlots(state, (Node *) qual);
+
+	scratch.opcode = EEO_QUAL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+
+	foreach (lc, qual)
+	{
+		Expr *node = (Expr *) lfirst(lc);
+
+		/* first evaluate expression */
+		ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+		/* then check whether it's false or NULL */
+		scratch.d.qualexpr.jumpdone = -1;
+		ExprEvalPushStep(state, &scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+	}
+
+	/* adjust early bail out jump target */
+	foreach (lc, adjust_bailout)
+	{
+		ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+		Assert(as->d.qualexpr.jumpdone == -1);
+		as->d.qualexpr.jumpdone = state->steps_len;
+	}
+
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecInstantiateExpr(state);
+
+	return state;
+}
+
+/*
+ * ExecInitCheck: prepare a check constraint for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that returns true iff none of the subexpressions are false.  (We
+ * also return true if the list is empty.)
+ *
+ * If some of the subexpressions yield NULL, then the result of the
+ * conjunction is true, since SQL specifies that NULL constraint conditions
+ * are not failures.
+ */
+ExprState *
+ExecInitCheck(List *qual, PlanState *parent)
+{
+	Expr *expr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) == 1)
+		expr = linitial(qual);
+	else
+	{
+		/*
+		 * Just whip-up a boolean AND expression, that behaves just as needed.
+		 * It'd be valid to implement short-circuiting behaviour on NULLs, but
+		 * that doesn't seem warranted.
+		 */
+		expr = makeBoolExpr(AND_EXPR, qual, -1);
+	}
+
+	return ExecInitExpr(expr, parent);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ *
+ * This is implemented by internally building an ExprState that performs the
+ * projection. That way faster implementations of expression evaluation, e.g
+ * compiled to native code, can evaluate the whole projection in one go.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle->expr, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			switch (variable->varno)
+			{
+			case INNER_VAR: /* get the tuple from the inner node */
+				scratch.opcode = EEO_ASSIGN_INNER_VAR;
+			break;
+
+			case OUTER_VAR: /* get the tuple from the outer node */
+				scratch.opcode = EEO_ASSIGN_OUTER_VAR;
+				break;
+
+				/* INDEX_VAR is handled by default case */
+			default:				/* get the tuple from the relation being
+									 * scanned */
+				scratch.opcode = EEO_ASSIGN_SCAN_VAR;
+			break;
+			}
+
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			/*
+			 * We can't directly point into the result slot for the contained
+			 * expression, as the result slot (and the exprstate for that
+			 * matter) can change below us. So we instead evaluate into a
+			 * temporary value and then move.
+			 */
+			ExecInitExprRec(tle->expr, parent, state, &state->resvalue, &state->resnull);
+			if (get_typlen(exprType((Node *) tle->expr)) == -1)
+				scratch.opcode = EEO_ASSIGN_TMP_UNEXPAND;
+			else
+				scratch.opcode = EEO_ASSIGN_TMP;
+			scratch.d.assign_tmp.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+	}
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecInstantiateExpr(state);
+
+	return projInfo;
+}
+
+/*
+ * Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
+ */
+List *
+ExecInitExprList(List *nodes, PlanState *parent)
+{
+	List	   *result = NIL;
+	ListCell   *lc;
+
+	foreach (lc, nodes)
+	{
+		Expr   *e = lfirst(lc);
+
+		result = lappend(result, ExecInitExpr(e, parent));
+	}
+
+	return result;
+}
+
+/*
+ * ExecPrepareExpr --- initialize for expression execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState *
+ExecPrepareExpr(Expr *node, EState *estate)
+{
+	ExprState  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	node = expression_planner(node);
+
+	result = ExecInitExpr(node, NULL);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+/*
+ * ExecPrepareCheck -- initialize qual for execution outside a normal Plan
+ * tree context.
+ *
+ * See ExecPrepareExpr() and ExecInitQual() for details.
+ */
+ExprState *
+ExecPrepareCheck(List *qual, EState *estate)
+{
+	ExprState  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitCheck(qual, NULL);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+/*
+ * Call ExecPrepareExpr() on each member of nodes, and return list of
+ * ExprStates.
+ *
+ * See ExecPrepareExpr() for details.
+ */
+List *
+ExecPrepareExprList(List *nodes, EState *estate)
+{
+	List	   *result = NIL;
+	ListCell   *lc;
+
+	foreach (lc, nodes)
+	{
+		Expr   *e = lfirst(lc);
+
+		result = lappend(result, ExecPrepareExpr(e, estate));
+	}
+
+	return result;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ * Projects a tuple based on projection info and stores it in the specified
+ * tuple table slot.
+ *
+ * Note: the result is always a virtual tuple; therefore it
+ * may reference the contents of the exprContext's scan tuples
+ * and/or temporary results constructed in the exprContext.
+ * If the caller wishes the result to be valid longer than that
+ * data will be valid, he must call ExecMaterializeSlot on the
+ * result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext = projInfo->pi_exprContext;
+	ExprState *state;
+
+	state = &projInfo->pi_state;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr(state, econtext, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+/*
+ * ExecCheck - evaluate a check constraint prepared with ExecInitCheck
+ * (possibly via ExecPrepareCheck).
+ */
+bool
+ExecCheck(ExprState *state, ExprContext *econtext)
+{
+	bool isnull;
+	Datum ret;
+
+	ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/*
+ * Prepare an expression for execution.
+ */
+static void
+ExecInstantiateExpr(ExprState *state)
+{
+	ExecInstantiateInterpretedExpr(state);
+}
+
+/*
+ * Add evaluation of node to ExprState, possibly recursing into
+ * sub-expressions of node.
+ */
+static void
+ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	/*
+	 * Guard against stack overflow due to overly complex expressions. Because
+	 * expression evaluation is not recursive, but expression planning is,
+	 * we'll hit stack limits due to recursion either here, or when recursing
+	 * into a separate expression evaluation inside a function call.  This
+	 * lets us avoid repeatedly doing the quite expensive stack depth checks
+	 * during expression evaluation.
+	 */
+	check_stack_depth();
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+
+	/* cases should be ordered as they are in enum NodeTag */
+	switch (nodeTag(node))
+	{
+		case T_Var:
+			{
+				Var *variable = (Var *) node;
+
+				/* varattno == InvalidAttrNumber means it's a whole-row Var */
+				if (variable->varattno == InvalidAttrNumber)
+				{
+					ExecInitWholeRowVar(&scratch, variable, parent, state, resv, resnull);
+
+					scratch.opcode = EEO_WHOLEROW;
+				}
+				else if (variable->varattno <= 0)
+				{
+					scratch.d.var.attnum = variable->varattno;
+					switch (variable->varno)
+					{
+					case INNER_VAR:
+						scratch.opcode = EEO_INNER_SYSVAR;
+						break;
+					case OUTER_VAR:
+						scratch.opcode = EEO_OUTER_SYSVAR;
+						break;
+					default:
+						scratch.opcode = EEO_SCAN_SYSVAR;
+						break;
+					}
+				}
+				else
+				{
+					switch (variable->varno)
+					{
+						case INNER_VAR:
+							scratch.opcode = EEO_INNER_VAR;
+							break;
+						case OUTER_VAR:
+							scratch.opcode = EEO_OUTER_VAR;
+							break;
+						default:
+							scratch.opcode = EEO_SCAN_VAR;
+							break;
+					}
+					scratch.d.var.attnum = variable->varattno - 1;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_Param:
+			{
+				Param *param = (Param *) node;
+
+				switch (param->paramkind)
+				{
+					case PARAM_EXEC:
+						{
+							scratch.opcode = EEO_PARAM_EXEC;
+							scratch.d.param.paramid = param->paramid;
+							scratch.d.param.paramtype = InvalidOid;
+							break;
+						}
+					case PARAM_EXTERN:
+						{
+							scratch.opcode = EEO_PARAM_EXTERN;
+							scratch.d.param.paramid = param->paramid;
+							scratch.d.param.paramtype = param->paramtype;
+							break;
+						}
+					default:
+						elog(ERROR, "unrecognized paramkind: %d",
+							 (int) ((Param *) node)->paramkind);
+						break;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_Aggref:
+			{
+				Aggref *aggref = (Aggref *) node;
+				AggrefExprState *astate = makeNode(AggrefExprState);
+
+				scratch.opcode = EEO_AGGREF;
+				scratch.d.aggref.astate = astate;
+				astate->aggref = aggref;
+				if (parent && IsA(parent, AggState))
+				{
+					AggState   *aggstate = (AggState *) parent;
+
+					aggstate->aggs = lcons(astate, aggstate->aggs);
+					aggstate->numaggs++;
+				}
+				else
+				{
+					/* planner messed up */
+					elog(ERROR, "Aggref found in non-Agg plan node");
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_GroupingFunc:
+			{
+				GroupingFunc *grp_node = (GroupingFunc *) node;
+				Agg		   *agg = NULL;
+
+				if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
+					elog(ERROR, "parent of GROUPING is not Agg node");
+
+				scratch.opcode = EEO_GROUPING_FUNC;
+				scratch.d.grouping_func.parent = (AggState *) parent;
+
+				agg = (Agg *) (parent->plan);
+
+				if (agg->groupingSets)
+					scratch.d.grouping_func.clauses = grp_node->cols;
+				else
+					scratch.d.grouping_func.clauses = NIL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_WindowFunc:
+			{
+				WindowFunc *wfunc = (WindowFunc *) node;
+				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
+
+				wfstate->wfunc = wfunc;
+
+				if (parent && IsA(parent, WindowAggState))
+				{
+					WindowAggState *winstate = (WindowAggState *) parent;
+					int			nfuncs;
+
+					winstate->funcs = lcons(wfstate, winstate->funcs);
+					nfuncs = ++winstate->numfuncs;
+					if (wfunc->winagg)
+						winstate->numaggs++;
+
+					/* for now intialize agg using old style expressions */
+					wfstate->args = NIL;
+					wfstate->args = ExecInitExprList(wfunc->args, parent);
+					wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
+													  parent);
+
+					/*
+					 * Complain if the windowfunc's arguments contain any
+					 * windowfuncs; nested window functions are semantically
+					 * nonsensical.  (This should have been caught earlier,
+					 * but we defend against it here anyway.)
+					 */
+					if (nfuncs != winstate->numfuncs)
+						ereport(ERROR,
+								(errcode(ERRCODE_WINDOWING_ERROR),
+								 errmsg("window function calls cannot be nested")));
+				}
+				else
+				{
+					/* planner messed up */
+					elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
+				}
+
+				scratch.opcode = EEO_WINDOW_FUNC;
+				scratch.d.window_func.wfstate = wfstate;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ArrayRef:
+			{
+				ArrayRef   *aref = (ArrayRef *) node;
+
+				ExecInitArrayRef(&scratch, aref, parent, state, resv, resnull);
+				break;
+			}
+
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				ExecInitFunc(&scratch, node, func->args, func->funcid, func->inputcollid,
+							 parent, state, resv, resnull);
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+							 parent, state, resv, resnull);
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_DistinctExpr:
+			{
+				DistinctExpr   *op = (DistinctExpr *) node;
+
+				ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+							 parent, state, resv, resnull);
+
+				/* Can't use normal function call, override opcode for DISTINCT */
+				/*
+				 * XXX: historically we've not called the function usage
+				 * pgstat infrastructure - that seems inconsistent given that
+				 * we do so for normal function *and* operator evaluation
+				 */
+				scratch.opcode = EEO_DISTINCT;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_NullIfExpr:
+			{
+				NullIfExpr   *op = (NullIfExpr *) node;
+
+				ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+							 parent, state, resv, resnull);
+
+				/* Can't use normal function call, override opcode for NULL() */
+				/*
+				 * XXX: historically we've not called the function usage
+				 * pgstat infrastructure - that seems inconsistent given that
+				 * we do so for normal function *and* operator evaluation
+				 */
+				scratch.opcode = EEO_NULLIF;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ScalarArrayOpExpr:
+			{
+				ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+				FmgrInfo	*finfo;
+				FunctionCallInfo fcinfo;
+
+				finfo = palloc0(sizeof(FmgrInfo));
+				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fmgr_info(opexpr->opfuncid, finfo);
+				fmgr_info_set_expr((Node *) node, finfo);
+				InitFunctionCallInfoData(*fcinfo, finfo, 2,
+										 opexpr->inputcollid, NULL, NULL);
+
+				scratch.opcode = EEO_SCALARARRAYOP;
+				scratch.d.scalararrayop.opexpr = opexpr;
+				scratch.d.scalararrayop.finfo = finfo;
+				scratch.d.scalararrayop.fcinfo_data = fcinfo;
+				scratch.d.scalararrayop.fn_addr = fcinfo->flinfo->fn_addr;
+
+				Assert(fcinfo->nargs == 2);
+				Assert(list_length(opexpr->args) == 2);
+
+				/* evaluate scalar directly into function argument */
+				ExecInitExprRec((Expr *) linitial(opexpr->args), parent, state,
+								&fcinfo->arg[0], &fcinfo->argnull[0]);
+
+				/* evaluate array argument into our return value, overwrite later */
+				ExecInitExprRec((Expr *) lsecond(opexpr->args), parent, state,
+								resv, resnull);
+
+				scratch.d.scalararrayop.element_type = InvalidOid;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				int nargs = list_length(boolexpr->args);
+				int off = 0;
+
+				/* allocate scratch memory used by all steps */
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				/*
+				 * For each argument evaluate the argument itself, then
+				 * perform the bool operation's appropriate handling.
+				 */
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							Assert(list_length(boolexpr->args) >= 2);
+
+							if (off == 0)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else if(off + 1 == nargs)
+								scratch.opcode = EEO_BOOL_AND_STEP_LAST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							Assert(list_length(boolexpr->args) >= 2);
+
+							if (off == 0)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else if (off + 1 == nargs)
+								scratch.opcode = EEO_BOOL_OR_STEP_LAST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					ExecInitExprRec(arg, parent, state,
+									scratch.d.boolexpr.value,
+									scratch.d.boolexpr.isnull);
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+					off++;
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_SubPlan:
+			{
+				SubPlan    *subplan = (SubPlan *) node;
+				SubPlanState *sstate;
+
+				if (!parent)
+					elog(ERROR, "SubPlan found with no parent plan");
+
+				sstate = ExecInitSubPlan(subplan, parent);
+
+				/* Add SubPlanState nodes to parent->subPlan */
+				parent->subPlan = lappend(parent->subPlan, sstate);
+
+				scratch.opcode = EEO_SUBPLAN;
+				scratch.d.subplan.sstate = sstate;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_AlternativeSubPlan:
+			{
+				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
+				AlternativeSubPlanState *asstate;
+
+				if (!parent)
+					elog(ERROR, "AlternativeSubPlan found with no parent plan");
+
+				asstate = ExecInitAlternativeSubPlan(asplan, parent);
+
+				scratch.opcode = EEO_ALTERNATIVE_SUBPLAN;
+				scratch.d.alternative_subplan.asstate = asstate;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_FieldSelect:
+			{
+				FieldSelect *fselect = (FieldSelect *) node;
+
+
+				/* evaluate argument */
+				ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
+
+				scratch.opcode = EEO_FIELDSELECT;
+				scratch.d.fieldselect.fieldnum = fselect->fieldnum;
+				scratch.d.fieldselect.resulttype = fselect->resulttype;
+				scratch.d.fieldselect.argdesc = NULL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_FieldStore:
+			{
+				FieldStore *fstore = (FieldStore *) node;
+				ListCell *l1, *l2;
+				Datum *values;
+				bool *nulls;
+				TupleDesc *descp;
+
+				/* FIXME: properly size workspace */
+				values = (Datum *) palloc(sizeof(Datum) * MaxTupleAttributeNumber);
+				nulls = (bool *) palloc(sizeof(bool) * MaxTupleAttributeNumber);
+				descp =  (TupleDesc *) palloc(sizeof(TupleDesc));
+				*descp = NULL;
+
+				/* prepare argument evaluation */
+				ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
+
+				/* first deform the input tuple */
+				scratch.opcode = EEO_FIELDSTORE_DEFORM;
+				scratch.d.fieldstore.argdesc = descp;
+				scratch.d.fieldstore.fstore = fstore;
+				scratch.d.fieldstore.values = values;
+				scratch.d.fieldstore.nulls = nulls;
+				ExprEvalPushStep(state, &scratch);
+
+				/* evaluate new values, one step for each arg */
+				forboth(l1, fstore->newvals, l2, fstore->fieldnums)
+				{
+					Expr	   *e = (Expr *) lfirst(l1);
+					AttrNumber	fieldnum = lfirst_int(l2);
+					Datum *save_innermost_caseval = NULL;
+					bool *save_innermost_casenull = NULL;
+
+					/*
+					 * Use the CaseTestExpr mechanism to pass down the old value
+					 * of the field being replaced; this is needed in case the
+					 * newval is itself a FieldStore or ArrayRef that has to
+					 * obtain and modify the old value.  It's safe to reuse the
+					 * CASE mechanism because there cannot be a CASE between here
+					 * and where the value would be needed, and a field assignment
+					 * can't be within a CASE either.  (So saving and restoring
+					 * the caseValue is just paranoia, but let's do it anyway.)
+					 */
+					save_innermost_caseval = state->innermost_caseval;
+					save_innermost_casenull = state->innermost_casenull;
+					state->innermost_caseval = &values[fieldnum - 1];
+					state->innermost_casenull = &nulls[fieldnum - 1];
+
+					ExecInitExprRec(e, parent, state,
+									&values[fieldnum - 1],
+									&nulls[fieldnum - 1]);
+
+					state->innermost_caseval = save_innermost_caseval;
+					state->innermost_casenull = save_innermost_casenull;
+				}
+
+				/* then form result tuple */
+				scratch.opcode = EEO_FIELDSTORE_FORM;
+				scratch.d.fieldstore.fstore = fstore;
+				scratch.d.fieldstore.argdesc = descp;
+				scratch.d.fieldstore.values = values;
+				scratch.d.fieldstore.nulls = nulls;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_RelabelType:
+			{
+				/* at runtime relabel doesn't need to do anything */
+				RelabelType *relabel = (RelabelType *) node;
+
+				ExecInitExprRec(relabel->arg, parent, state, resv, resnull);
+				break;
+			}
+
+		case T_CoerceViaIO:
+			{
+				CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+				Oid			iofunc;
+				bool		typisvarlena;
+
+				/* evaluate argument */
+				ExecInitExprRec(iocoerce->arg, parent, state, resv, resnull);
+
+				/*
+				 * Compute coercion by preparing both output / input calls, to
+				 * be evaluated inside a single evaluation step for speed -
+				 * this can be a very common operation.
+				 */
+				scratch.opcode = EEO_IOCOERCE;
+
+				/* lookup the input type's output function */
+				scratch.d.iocoerce.finfo_out = palloc0(sizeof(*scratch.d.iocoerce.finfo_out));
+				scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_out));
+
+				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+								  &iofunc, &typisvarlena);
+				fmgr_info(iofunc, scratch.d.iocoerce.finfo_out);
+				fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out);
+				InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out,
+										 scratch.d.iocoerce.finfo_out,
+										 1, InvalidOid, NULL, NULL);
+
+				/* lookup the result type's input function */
+				scratch.d.iocoerce.finfo_in = palloc0(sizeof(*scratch.d.iocoerce.finfo_in));
+				scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_in));
+
+				getTypeInputInfo(iocoerce->resulttype, &iofunc,
+								 &scratch.d.iocoerce.intypioparam);
+				fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
+				fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
+				InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
+										 scratch.d.iocoerce.finfo_in,
+										 3, InvalidOid, NULL, NULL);
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ArrayCoerceExpr:
+			{
+				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+
+				/* evaluate argument */
+				ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
+
+				scratch.opcode = EEO_ARRAYCOERCE;
+				scratch.d.arraycoerce.coerceexpr = acoerce;
+				scratch.d.arraycoerce.resultelemtype =
+					get_element_type(acoerce->resulttype);
+				if (scratch.d.arraycoerce.resultelemtype == InvalidOid)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("target type is not an array")));
+				/* Arrays over domains aren't supported yet */
+				Assert(getBaseType(scratch.d.arraycoerce.resultelemtype) ==
+					   scratch.d.arraycoerce.resultelemtype);
+				scratch.d.arraycoerce.elemfunc =
+					(FmgrInfo *) palloc(sizeof(FmgrInfo));
+				scratch.d.arraycoerce.elemfunc->fn_oid =
+					InvalidOid;	/* not initialized */
+				scratch.d.arraycoerce.amstate =
+					(ArrayMapState *) palloc0(sizeof(ArrayMapState));
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ConvertRowtypeExpr:
+			{
+				ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+
+				/* evaluate argument */
+				ExecInitExprRec(convert->arg, parent, state, resv, resnull);
+
+				/* and push conversion step */
+				scratch.opcode = EEO_CONVERT_ROWTYPE;
+				scratch.d.convert_rowtype.convert = convert;
+				scratch.d.convert_rowtype.indesc = NULL;
+				scratch.d.convert_rowtype.outdesc = NULL;
+				scratch.d.convert_rowtype.map = NULL;
+				scratch.d.convert_rowtype.initialized = false;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		/* note that CaseWhen expressions are handled within this block */
+		case T_CaseExpr:
+			{
+				CaseExpr *caseExpr = (CaseExpr *) node;
+				ListCell   *clause;
+				List *adjust_bailout = NIL;
+				ListCell *lc;
+				Datum *casevalue = palloc0(sizeof(Datum));
+				bool *caseisnull = palloc0(sizeof(bool));
+				Datum *save_innermost_caseval = NULL;
+				bool *save_innermost_casenull = NULL;
+				Datum *caseval = NULL;
+				bool *casenull = NULL;
+
+				/* arg == NULL -> CASE WHEN foo */
+				/* arg != NULL -> CASE foo WHEN blarg */
+				if (caseExpr->arg != NULL)
+				{
+					caseval = palloc0(sizeof(Datum));
+					casenull = palloc0(sizeof(bool));
+
+					ExecInitExprRec(caseExpr->arg, parent, state,
+									caseval, casenull);
+				}
+
+				foreach(clause, caseExpr->args)
+				{
+					CaseWhen *when = (CaseWhen *) lfirst(clause);
+					int whenstep;
+
+					/* evaluate condition */
+					save_innermost_caseval = state->innermost_caseval;
+					save_innermost_casenull = state->innermost_casenull;
+					state->innermost_caseval = caseval;
+					state->innermost_casenull = casenull;
+
+					ExecInitExprRec(when->expr, parent, state,
+									casevalue, caseisnull);
+
+					state->innermost_caseval = save_innermost_caseval;
+					state->innermost_casenull = save_innermost_casenull;
+
+					scratch.opcode = EEO_CASE_WHEN_STEP;
+					scratch.d.casewhen.value = casevalue;
+					scratch.d.casewhen.isnull = caseisnull;
+					scratch.d.casewhen.jumpfalse = -1; /* computed later */
+					ExprEvalPushStep(state, &scratch);
+					whenstep = state->steps_len - 1;
+
+					/* evaluate result */
+					ExecInitExprRec(when->result, parent, state, resv, resnull);
+
+					scratch.opcode = EEO_CASE_THEN_STEP;
+					scratch.d.casewhen.value = casevalue;
+					scratch.d.casewhen.isnull = caseisnull;
+					scratch.d.casethen.jumpdone = -1; /* computed later */
+					ExprEvalPushStep(state, &scratch);
+					/*
+					 * Don't know "address" of jump target yet, compute once the
+					 * whole case expression is built.
+					 */
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+
+					/* adjust jump target for WHEN step, for the !match case */
+					state->steps[whenstep].d.casewhen.jumpfalse = state->steps_len;
+				}
+
+				if (caseExpr->defresult)
+				{
+					/* evaluate result, directly into result datum */
+					ExecInitExprRec(caseExpr->defresult, parent, state,
+									resv, resnull);
+				}
+				else
+				{
+					/* statically return NULL */
+					scratch.opcode = EEO_CONST;
+					scratch.d.constval.isnull = true;
+					scratch.d.constval.value = 0;
+					ExprEvalPushStep(state, &scratch);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.casethen.jumpdone == -1);
+					as->d.casethen.jumpdone = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_CaseTestExpr:
+			{
+				CaseTestExpr *casetestexpr = (CaseTestExpr *) node;
+
+				scratch.d.casetest.value = state->innermost_caseval;
+				scratch.d.casetest.isnull = state->innermost_casenull;
+
+				/* only check for extended datums if possible */
+				if (get_typlen(casetestexpr->typeId) == -1)
+					scratch.opcode = EEO_CASE_TESTVAL_UNEXPAND;
+				else
+					scratch.opcode = EEO_CASE_TESTVAL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_ArrayExpr:
+			{
+				ArrayExpr  *arrayexpr = (ArrayExpr *) node;
+				int nelems = list_length(arrayexpr->elements);
+				ListCell *lc;
+				int elemoff;
+
+				/*
+				 * Evaluate by computing each element, and then forming the
+				 * array.
+				 */
+				scratch.opcode = EEO_ARRAYEXPR;
+				scratch.d.arrayexpr.arrayexpr = arrayexpr;
+				scratch.d.arrayexpr.nelems = nelems;
+				scratch.d.arrayexpr.elemvalues =
+					(Datum *) palloc(sizeof(Datum) * nelems);
+				scratch.d.arrayexpr.elemnulls =
+					(bool *) palloc(sizeof(bool) * nelems);
+
+				/* do one-time catalog lookup for type info */
+				get_typlenbyvalalign(arrayexpr->element_typeid,
+									 &scratch.d.arrayexpr.elemlength,
+									 &scratch.d.arrayexpr.elembyval,
+									 &scratch.d.arrayexpr.elemalign);
+
+				/* prepare to evaluate all arguments */
+				elemoff = 0;
+				foreach(lc, arrayexpr->elements)
+				{
+					Expr	   *e = (Expr *) lfirst(lc);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.arrayexpr.elemvalues[elemoff],
+									&scratch.d.arrayexpr.elemnulls[elemoff]);
+					elemoff++;
+				}
+
+				/* and then to collect collect all into an array */
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_RowExpr:
+			{
+				RowExpr    *rowexpr = (RowExpr *) node;
+				int nelems = list_length(rowexpr->args);
+				TupleDesc tupdesc;
+				Form_pg_attribute *attrs;
+				int			i;
+				ListCell   *l;
+
+				/*
+				 * Evaluate by first building datums for each field, and then
+				 * a final step forming the composite datum.
+				 */
+				scratch.opcode = EEO_ROW;
+
+				/* space for the individual field datums */
+				scratch.d.row.elemvalues =
+					(Datum *) palloc(sizeof(Datum) * nelems);
+				scratch.d.row.elemnulls =
+					(bool *) palloc(sizeof(bool) * nelems);
+
+				/* Build tupdesc to describe result tuples */
+				if (rowexpr->row_typeid == RECORDOID)
+				{
+					/* generic record, use types of given expressions */
+					tupdesc = ExecTypeFromExprList(rowexpr->args);
+				}
+				else
+				{
+					/* it's been cast to a named type, use that */
+					tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
+				}
+
+				scratch.d.row.tupdesc = tupdesc;
+
+				/* In either case, adopt RowExpr's column aliases */
+				ExecTypeSetColNames(tupdesc, rowexpr->colnames);
+				/* Bless the tupdesc in case it's now of type RECORD */
+				BlessTupleDesc(tupdesc);
+
+				/* Set up evaluation, skipping any deleted columns */
+				attrs = tupdesc->attrs;
+				i = 0;
+				foreach(l, rowexpr->args)
+				{
+					Expr	   *e = (Expr *) lfirst(l);
+
+					if (!attrs[i]->attisdropped)
+					{
+						/*
+						 * Guard against ALTER COLUMN TYPE on rowtype since
+						 * the RowExpr was created.  XXX should we check
+						 * typmod too?	Not sure we can be sure it'll be the
+						 * same.
+						 */
+						if (exprType((Node *) e) != attrs[i]->atttypid)
+							ereport(ERROR,
+									(errcode(ERRCODE_DATATYPE_MISMATCH),
+									 errmsg("ROW() column has type %s instead of type %s",
+											format_type_be(exprType((Node *) e)),
+											format_type_be(attrs[i]->atttypid))));
+					}
+					else
+					{
+						/*
+						 * Ignore original expression and insert a NULL. We
+						 * don't really care what type of NULL it is, so
+						 * always make an int4 NULL.
+						 */
+						e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
+					}
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.row.elemvalues[i],
+									&scratch.d.row.elemnulls[i]);
+					i++;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_RowCompareExpr:
+			{
+				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+				int			nopers = list_length(rcexpr->opnos);
+				List *adjust_bailout = NIL;
+				ListCell *l_left_expr, *l_right_expr, *l_opno,
+					*l_opfamily, *l_inputcollid;
+				ListCell *lc;
+				int off;
+
+				/*
+				 * Iterate over each field, prepare comparisons. To handle
+				 * NULL results, prepare jumps to after the expression. If
+				 * expression yields a != 0 result, jump to the final step.
+				 */
+				Assert(list_length(rcexpr->largs) == nopers);
+				Assert(list_length(rcexpr->rargs) == nopers);
+
+				off = 0;
+				for (off = 0,
+						 l_left_expr = list_head(rcexpr->largs),
+						 l_right_expr = list_head(rcexpr->rargs),
+						 l_opno = list_head(rcexpr->opnos),
+						 l_opfamily = list_head(rcexpr->opfamilies),
+						 l_inputcollid = list_head(rcexpr->inputcollids);
+					 off < nopers;
+					 off++,
+						 l_left_expr = lnext(l_left_expr),
+						 l_right_expr = lnext(l_right_expr),
+						 l_opno = lnext(l_opno),
+						 l_opfamily = lnext(l_opfamily),
+						 l_inputcollid = lnext(l_inputcollid))
+				{
+					Expr	   *left_expr = (Expr *) lfirst(l_left_expr);
+					Expr	   *right_expr = (Expr *) lfirst(l_right_expr);
+					Oid			opno = lfirst_oid(l_opno);
+					Oid			opfamily = lfirst_oid(l_opfamily);
+					Oid			inputcollid = lfirst_oid(l_inputcollid);
+					int			strategy;
+					Oid			lefttype;
+					Oid			righttype;
+					Oid			proc;
+					FmgrInfo	*finfo;
+					FunctionCallInfo fcinfo;
+
+					get_op_opfamily_properties(opno, opfamily, false,
+											   &strategy,
+											   &lefttype,
+											   &righttype);
+					proc = get_opfamily_proc(opfamily,
+											 lefttype,
+											 righttype,
+											 BTORDER_PROC);
+
+					/* Set up the primary fmgr lookup information */
+					finfo = palloc0(sizeof(FmgrInfo));
+					fcinfo = palloc0(sizeof(FunctionCallInfoData));
+					fmgr_info(proc, finfo);
+					fmgr_info_set_expr((Node *) node, finfo);
+					InitFunctionCallInfoData(*fcinfo, finfo, 2, inputcollid, NULL, NULL);
+
+					/*
+					 * If we enforced permissions checks on index support
+					 * functions, we'd need to make a check here.  But the
+					 * index support machinery doesn't do that, and thus
+					 * neither does this code.
+					 */
+
+					/* evaluate left and right expression directly into fcinfo */
+					ExecInitExprRec(left_expr, parent, state,
+									&fcinfo->arg[0], &fcinfo->argnull[0]);
+					ExecInitExprRec(right_expr, parent, state,
+									&fcinfo->arg[1], &fcinfo->argnull[1]);
+
+					scratch.opcode = EEO_ROWCOMPARE_STEP;
+
+					/* jump targets computed later */
+					scratch.d.rowcompare_step.jumpnull = -1;
+					scratch.d.rowcompare_step.jumpdone = -1;
+
+					scratch.d.rowcompare_step.finfo = finfo;
+					scratch.d.rowcompare_step.fcinfo_data = fcinfo;
+					scratch.d.rowcompare_step.fn_addr = fcinfo->flinfo->fn_addr;
+
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* and then compare the last result */
+				scratch.opcode = EEO_ROWCOMPARE_FINAL;
+				scratch.d.rowcompare_final.rctype = rcexpr->rctype;
+				ExprEvalPushStep(state, &scratch);
+
+				/* adjust early bail out jump targets */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.rowcompare_step.jumpdone == -1);
+					Assert(as->d.rowcompare_step.jumpnull == -1);
+
+					/* jump to comparison evaluation */
+					as->d.rowcompare_step.jumpdone = state->steps_len - 1;
+					/* jump to the following expression */
+					as->d.rowcompare_step.jumpnull = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_CoalesceExpr:
+			{
+				CoalesceExpr *coalesce = (CoalesceExpr *) node;
+				List *adjust_bailout = NIL;
+				ListCell   *lc;
+
+				Assert(list_length(coalesce->args) > 0);
+
+				/*
+				 * Prepare evaluation of all coalesced arguments, after each
+				 * push a step that short-circuits if not null.
+				 */
+				foreach(lc, coalesce->args)
+				{
+					Expr	   *e = (Expr *) lfirst(lc);
+
+					/* evaluate result, directly into result datum */
+					ExecInitExprRec(e, parent, state, resv, resnull);
+
+					/* then push step checking for NULLs */
+					scratch.opcode = EEO_COALESCE;
+					scratch.d.coalesce.jumpdone = -1; /* adjust later */
+					ExprEvalPushStep(state, &scratch);
+
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/*
+				 * No need to add a constant NULL return - we only can get to the
+				 * end of the expression if a NULL already is being returned.
+				 */
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->d.coalesce.jumpdone == -1);
+					as->d.coalesce.jumpdone = state->steps_len;
+				}
+
+				break;
+			}
+
+		case T_MinMaxExpr:
+			{
+				MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+				int nelems = list_length(minmaxexpr->args);
+				TypeCacheEntry *typentry;
+				FmgrInfo	*finfo;
+				FunctionCallInfo fcinfo;
+				ListCell *lc;
+				int off;
+
+				/* Look up the btree comparison function for the datatype */
+				typentry = lookup_type_cache(minmaxexpr->minmaxtype,
+											 TYPECACHE_CMP_PROC);
+				if (!OidIsValid(typentry->cmp_proc))
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_FUNCTION),
+							 errmsg("could not identify a comparison function for type %s",
+									format_type_be(minmaxexpr->minmaxtype))));
+
+				/*
+				 * If we enforced permissions checks on index support
+				 * functions, we'd need to make a check here.  But the index
+				 * support machinery doesn't do that, and thus neither does
+				 * this code.
+				 */
+				finfo = palloc0(sizeof(FmgrInfo));
+				fcinfo = palloc0(sizeof(FunctionCallInfoData));
+				fmgr_info(typentry->cmp_proc, finfo);
+				fmgr_info_set_expr((Node *) node, finfo);
+				InitFunctionCallInfoData(*fcinfo, finfo, 2,
+										 minmaxexpr->inputcollid, NULL, NULL);
+
+				scratch.opcode = EEO_MINMAX;
+				/* allocate space to store arguments */
+				scratch.d.minmax.values =
+					(Datum *) palloc(sizeof(Datum) * nelems);
+				scratch.d.minmax.nulls =
+					(bool *) palloc(sizeof(bool) * nelems);
+				scratch.d.minmax.nelems = nelems;
+				scratch.d.minmax.op = minmaxexpr->op;
+
+				scratch.d.minmax.finfo = finfo;
+				scratch.d.minmax.fcinfo_data = fcinfo;
+
+				/* evaluate expressions into minmax->values/nulls */
+				off = 0;
+				foreach(lc, minmaxexpr->args)
+				{
+					Expr	   *e = (Expr *) lfirst(lc);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.minmax.values[off],
+									&scratch.d.minmax.nulls[off]);
+					off++;
+				}
+
+				/* and push the final comparison */
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_SQLValueFunction:
+			{
+				SQLValueFunction *svf = (SQLValueFunction *) node;
+
+				scratch.opcode = EEO_SQLVALUEFUNCTION;
+				scratch.d.sqlvaluefunction.svf = svf;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_XmlExpr:
+			{
+				XmlExpr    *xexpr = (XmlExpr *) node;
+				ListCell   *arg;
+				int nnamed = list_length(xexpr->named_args);
+				int nargs = list_length(xexpr->args);
+				int off;
+
+				scratch.opcode = EEO_XMLEXPR;
+				scratch.d.xmlexpr.xexpr = xexpr;
+
+				/* allocate space for storing all the arguments */
+				if (nnamed)
+				{
+					scratch.d.xmlexpr.named_argvalue =
+						(Datum *) palloc(sizeof(Datum) * nnamed);
+					scratch.d.xmlexpr.named_argnull =
+						(bool *) palloc(sizeof(bool) * nnamed);
+				}
+				else
+				{
+					scratch.d.xmlexpr.named_argvalue = NULL;
+					scratch.d.xmlexpr.named_argnull = NULL;
+				}
+
+				if (nargs)
+				{
+					scratch.d.xmlexpr.argvalue =
+						(Datum *) palloc(sizeof(Datum) * nargs);
+					scratch.d.xmlexpr.argnull =
+						(bool *) palloc(sizeof(bool) * nargs);
+				}
+				else
+				{
+					scratch.d.xmlexpr.argvalue = NULL;
+					scratch.d.xmlexpr.argnull = NULL;
+				}
+
+				/* prepare argument execution */
+				off = 0;
+				foreach(arg, xexpr->named_args)
+				{
+					Expr	   *e = (Expr *) lfirst(arg);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.xmlexpr.named_argvalue[off],
+									&scratch.d.xmlexpr.named_argnull[off]);
+					off++;
+				}
+
+				off = 0;
+				foreach(arg, xexpr->args)
+				{
+					Expr	   *e = (Expr *) lfirst(arg);
+
+					ExecInitExprRec(e, parent, state,
+									&scratch.d.xmlexpr.argvalue[off],
+									&scratch.d.xmlexpr.argnull[off]);
+					off++;
+				}
+
+				/* and evaluate the actual XML expression */
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_NullTest:
+			{
+				NullTest   *ntest = (NullTest *) node;
+
+				if (ntest->nulltesttype == IS_NULL)
+				{
+					if (ntest->argisrow)
+						scratch.opcode = EEO_NULLTEST_ROWISNULL;
+					else
+						scratch.opcode = EEO_NULLTEST_ISNULL;
+				}
+				else if (ntest->nulltesttype == IS_NOT_NULL)
+				{
+					if (ntest->argisrow)
+						scratch.opcode = EEO_NULLTEST_ROWISNOTNULL;
+					else
+						scratch.opcode = EEO_NULLTEST_ISNOTNULL;
+				}
+				else
+				{
+					elog(ERROR, "unrecognized nulltesttype: %d",
+						 (int) ntest->nulltesttype);
+				}
+
+				/* first evaluate argument */
+				ExecInitExprRec(ntest->arg, parent, state,
+								resv, resnull);
+
+				/* then push the test of that argument */
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_BooleanTest:
+			{
+				BooleanTest *btest = (BooleanTest *) node;
+
+				/*
+				 * Evaluate argument, directly into result datum. That's a bit
+				 * debatable, because the types will be different, but it's
+				 * efficient...  The evaluation step will then store an actual
+				 * boolean.
+				 */
+				ExecInitExprRec(btest->arg, parent, state, resv, resnull);
+
+				switch (btest->booltesttype)
+				{
+					case IS_TRUE:
+						scratch.opcode = EEO_BOOLTEST_IS_TRUE;
+						break;
+					case IS_NOT_TRUE:
+						scratch.opcode = EEO_BOOLTEST_IS_NOT_TRUE;
+						break;
+					case IS_FALSE:
+						scratch.opcode = EEO_BOOLTEST_IS_FALSE;
+						break;
+					case IS_NOT_FALSE:
+						scratch.opcode = EEO_BOOLTEST_IS_NOT_FALSE;
+						break;
+					case IS_UNKNOWN:
+						scratch.opcode = EEO_BOOLTEST_IS_UNKNOWN;
+						break;
+					case IS_NOT_UNKNOWN:
+						scratch.opcode = EEO_BOOLTEST_IS_NOT_UNKNOWN;
+						break;
+					default:
+						elog(ERROR, "unrecognized booltesttype: %d",
+							 (int) btest->booltesttype);
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_CoerceToDomain:
+			{
+				CoerceToDomain *ctest = (CoerceToDomain *) node;
+				Datum *save_innermost_domainval = NULL;
+				bool *save_innermost_domainnull = NULL;
+				DomainConstraintRef *constraint_ref =
+					palloc(sizeof(DomainConstraintRef));
+				ListCell   *l;
+
+				scratch.d.domaincheck.resulttype = ctest->resulttype;
+				scratch.d.domaincheck.checkvalue = (Datum *) palloc(sizeof(Datum));
+				scratch.d.domaincheck.checknull = (bool *) palloc(sizeof(bool));
+
+				/* evaluate argument */
+				ExecInitExprRec(ctest->arg, parent, state, resv, resnull);
+
+				/*
+				 * XXX: In contrast to the old implementation we're evaluating
+				 * the set of to-be-checked constraints at query start - that
+				 * seems perfectly sensible to me.  But perhaps there's a
+				 * reason the previous implementation did what it did? ISTM
+				 * that was just a side-effect of using the typecache (which
+				 * is longer lived than a single query).
+				 */
+
+				/* Make sure we have up-to-date constraints */
+				InitDomainConstraintRef(ctest->resulttype,
+										constraint_ref,
+										CurrentMemoryContext);
+				UpdateDomainConstraintRef(constraint_ref);
+
+				/*
+				 * Set up value to be returned by CoerceToDomainValue nodes. We
+				 * must save and restore innermost_domainval/null fields, in case
+				 * this node is itself within a check expression for another
+				 * domain.
+				 */
+				save_innermost_domainval = state->innermost_domainval;
+				save_innermost_domainnull = state->innermost_domainnull;
+				state->innermost_domainval = resv;
+				state->innermost_domainnull = resnull;
+
+				foreach(l, constraint_ref->constraints)
+				{
+					DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+					scratch.d.domaincheck.constraintname = con->name;
+
+					switch (con->constrainttype)
+					{
+						case DOM_CONSTRAINT_NOTNULL:
+							scratch.opcode = EEO_DOMAIN_NOTNULL;
+							ExprEvalPushStep(state, &scratch);
+							break;
+						case DOM_CONSTRAINT_CHECK:
+							/* evaluate check expression value */
+							ExecInitExprRec(con->check_expr, parent, state,
+											scratch.d.domaincheck.checkvalue,
+											scratch.d.domaincheck.checknull);
+
+							/* and then check result value */
+							scratch.opcode = EEO_DOMAIN_CHECK;
+							ExprEvalPushStep(state, &scratch);
+							break;
+						default:
+							elog(ERROR, "unrecognized constraint type: %d",
+								 (int) con->constrainttype);
+							break;
+					}
+				}
+
+				state->innermost_domainval = save_innermost_domainval;
+				state->innermost_domainnull = save_innermost_domainnull;
+
+				break;
+			}
+
+		case T_CoerceToDomainValue:
+			{
+				CoerceToDomainValue *domainval = (CoerceToDomainValue *) node;
+
+				/*
+				 * Share implementation with case testval, but different pointers.
+				 */
+				scratch.d.casetest.value = state->innermost_domainval;
+				scratch.d.casetest.isnull = state->innermost_domainnull;
+
+				if (get_typlen(domainval->typeId) == -1)
+					scratch.opcode = EEO_DOMAIN_TESTVAL_UNEXPAND;
+				else
+					scratch.opcode = EEO_DOMAIN_TESTVAL;
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		case T_CurrentOfExpr:
+			{
+				scratch.opcode = EEO_CURRENTOFEXPR;
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+}
+
+static void
+ExprEvalPushStep(ExprState *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static void
+ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprState *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info(funcid, scratch->d.func.finfo);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else
+		{
+			ExecInitExprRec(arg, parent, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]);
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict && nargs > 0)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict && nargs > 0)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+}
+
+static void
+ExecInitExprSlots(ExprState *state, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_INNER_FETCHSOME;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_OUTER_FETCHSOME;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_SCAN_FETCHSOME;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+static void
+ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+	scratch->d.wholerow.tupdesc = NULL;
+	scratch->d.wholerow.junkFilter = NULL;
+	scratch->d.wholerow.var = variable;
+	scratch->d.wholerow.first = true;
+
+	/*
+	 * If the input tuple came from a subquery, it might contain "resjunk"
+	 * columns (such as GROUP BY or ORDER BY columns), which we don't want to
+	 * keep in the whole-row result.  We can get rid of such columns by
+	 * passing the tuple through a JunkFilter --- but to make one, we have to
+	 * lay our hands on the subquery's targetlist.  Fortunately, there are not
+	 * very many cases where this can happen, and we can identify all of them
+	 * by examining our parent PlanState.  We assume this is not an issue in
+	 * standalone expressions that don't have parent plans.  (Whole-row Vars
+	 * can occur in such expressions, but they will always be referencing
+	 * table rows.)
+	 */
+
+	if (parent)
+	{
+		PlanState  *subplan = NULL;
+
+		switch (nodeTag(parent))
+		{
+			case T_SubqueryScanState:
+				subplan = ((SubqueryScanState *) parent)->subplan;
+				break;
+			case T_CteScanState:
+				subplan = ((CteScanState *) parent)->cteplanstate;
+				break;
+			default:
+				break;
+		}
+
+		if (subplan)
+		{
+			bool		junk_filter_needed = false;
+			ListCell   *tlist;
+
+			/* Detect whether subplan tlist actually has any junk columns */
+			foreach(tlist, subplan->plan->targetlist)
+			{
+				TargetEntry *tle = (TargetEntry *) lfirst(tlist);
+
+				if (tle->resjunk)
+				{
+					junk_filter_needed = true;
+					break;
+				}
+			}
+
+			if (junk_filter_needed)
+			{
+				/* If so, build the junkfilter in the query memory context */
+				scratch->d.wholerow.junkFilter =
+					ExecInitJunkFilter(subplan->plan->targetlist,
+									   ExecGetResultType(subplan)->tdhasoid,
+									   ExecInitExtraTupleSlot(parent->state));
+			}
+		}
+	}
+}
+
+static void
+ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
+				 ExprState *state, Datum *resv, bool *resnull)
+{
+	List *adjust_bailout = NIL;
+	ListCell *lc;
+	bool		isAssignment = (aref->refassgnexpr != NULL);
+	ArrayRefState *arefstate = palloc(sizeof(ArrayRefState));
+	int i;
+
+	if (list_length(aref->refupperindexpr) >= MAXDIM)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+						list_length(aref->refupperindexpr), MAXDIM)));
+
+	if (list_length(aref->reflowerindexpr) >= MAXDIM)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+						list_length(aref->refupperindexpr), MAXDIM)));
+
+	if (list_length(aref->reflowerindexpr) > 0 &&
+		list_length(aref->refupperindexpr) != list_length(aref->reflowerindexpr))
+		elog(ERROR, "upper and lower index lists are not same length");
+
+	arefstate->isassignment = isAssignment;
+	arefstate->refattrlength = get_typlen(aref->refarraytype);
+	arefstate->refelemtype = aref->refelemtype;
+	get_typlenbyvalalign(aref->refelemtype,
+						 &arefstate->refelemlength,
+						 &arefstate->refelembyval,
+						 &arefstate->refelemalign);
+
+	/* evaluate input */
+	ExecInitExprRec(aref->refexpr, parent, state,
+					resv, resnull);
+
+	/*
+	 * If refexpr yields NULL, and it's a fetch, then result is
+	 * NULL. In the assignment case, we'll create an empty input.
+	 */
+	if (!isAssignment)
+	{
+		scratch->opcode = EEO_ARRAYREF_CHECKINPUT;
+		scratch->d.arrayref.state = arefstate;
+		scratch->d.arrayref.jumpdone = -1; /* adjust later */
+		ExprEvalPushStep(state, scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+	}
+
+	/* evaluate upper subscripts */
+	i = 0;
+	foreach(lc, aref->refupperindexpr)
+	{
+		Expr  *e = (Expr *) lfirst(lc);
+
+		if (!e)
+		{
+			arefstate->upperprovided[i] = false;
+			i++;
+			continue;
+		}
+
+		arefstate->upperprovided[i] = true;
+
+		ExecInitExprRec(e, parent, state,
+						&arefstate->upper[i], &arefstate->uppernull[i]);
+
+		scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+		scratch->d.arrayref_checksubscript.state = arefstate;
+		scratch->d.arrayref_checksubscript.off = i;
+		scratch->d.arrayref_checksubscript.isupper = true;
+		scratch->d.arrayref_checksubscript.jumpdone = -1; /* adjust later */
+		ExprEvalPushStep(state, scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+		i++;
+	}
+	arefstate->numupper = i;
+
+	/* evaluate lower subscripts */
+	i = 0;
+	foreach(lc, aref->reflowerindexpr)
+	{
+		Expr  *e = (Expr *) lfirst(lc);
+
+		if (!e)
+		{
+			arefstate->lowerprovided[i] = false;
+			i++;
+			continue;
+		}
+
+		arefstate->lowerprovided[i] = true;
+
+		ExecInitExprRec(e, parent, state,
+						&arefstate->lower[i], &arefstate->lowernull[i]);
+
+		scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+		scratch->d.arrayref_checksubscript.state = arefstate;
+		scratch->d.arrayref_checksubscript.off = i;
+		scratch->d.arrayref_checksubscript.isupper = false;
+		scratch->d.arrayref_checksubscript.jumpdone = -1; /* adjust later */
+		ExprEvalPushStep(state, scratch);
+		adjust_bailout = lappend_int(adjust_bailout,
+									 state->steps_len - 1);
+		i++;
+	}
+	arefstate->numlower = i;
+
+	if (isAssignment)
+	{
+		Datum *save_innermost_caseval = NULL;
+		bool *save_innermost_casenull = NULL;
+
+		/*
+		 * We might have a nested-assignment situation, in which the
+		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
+		 * obtain and modify the previous value of the array element or
+		 * slice being replaced.  If so, we have to extract that value
+		 * from the array and pass it down via the econtext's caseValue.
+		 * It's safe to reuse the CASE mechanism because there cannot be a
+		 * CASE between here and where the value would be needed, and an
+		 * array assignment can't be within a CASE either.  (So saving and
+		 * restoring the caseValue is just paranoia, but let's do it
+		 * anyway.)
+		 *
+		 * Since fetching the old element might be a nontrivial expense, do it
+		 * only if the argument appears to actually need it.
+		 */
+		if (isAssignmentIndirectionExpr(aref->refassgnexpr))
+		{
+			scratch->opcode = EEO_ARRAYREF_OLD;
+			scratch->d.arrayref.state = arefstate;
+			ExprEvalPushStep(state, scratch);
+		}
+
+		save_innermost_caseval = state->innermost_caseval;
+		save_innermost_casenull = state->innermost_casenull;
+		state->innermost_caseval = &arefstate->prevvalue;
+		state->innermost_casenull = &arefstate->prevnull;
+
+		/* evaluate replacement value */
+		ExecInitExprRec(aref->refassgnexpr, parent, state,
+						&arefstate->replacevalue, &arefstate->replacenull);
+
+		state->innermost_caseval = save_innermost_caseval;
+		state->innermost_casenull = save_innermost_casenull;
+
+		scratch->opcode = EEO_ARRAYREF_ASSIGN;
+		scratch->d.arrayref.state = arefstate;
+		ExprEvalPushStep(state, scratch);
+	}
+	else
+	{
+		scratch->opcode = EEO_ARRAYREF_FETCH;
+		scratch->d.arrayref.state = arefstate;
+		ExprEvalPushStep(state, scratch);
+	}
+
+	/* adjust early bail out jump target */
+	foreach (lc, adjust_bailout)
+	{
+		ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+		if (as->opcode == EEO_ARRAYREF_CHECKSUBSCRIPT)
+		{
+			Assert(as->d.arrayref_checksubscript.jumpdone == -1);
+			as->d.arrayref_checksubscript.jumpdone = state->steps_len;
+		}
+		else
+		{
+			Assert(as->d.arrayref.jumpdone == -1);
+			as->d.arrayref.jumpdone = state->steps_len;
+		}
+	}
+}
+
+/*
+ * Helper for preparing ArrayRef expressions for evaluation: is expr a nested
+ * FieldStore or ArrayRef that might need the old element value passed down?
+ *
+ * (We could use this in FieldStore too, but in that case passing the old
+ * value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(Expr *expr)
+{
+	if (expr == NULL)
+		return false;			/* just paranoia */
+	if (IsA(expr, FieldStore))
+	{
+		FieldStore *fstore = (FieldStore *) expr;
+
+		if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+			return true;
+	}
+	else if (IsA(expr, ArrayRef))
+	{
+		ArrayRef   *arrayRef = (ArrayRef *) expr;
+
+		if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
+			return true;
+	}
+	return false;
+}
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 5242dee006..108060ac0f 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState  *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
@@ -551,23 +549,21 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState  *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate, econtext))
 				continue;
 		}
 
diff --git a/src/backend/executor/execInterpExpr.c b/src/backend/executor/execInterpExpr.c
new file mode 100644
index 0000000000..38b6acb8d8
--- /dev/null
+++ b/src/backend/executor/execInterpExpr.c
@@ -0,0 +1,2888 @@
+/*-------------------------------------------------------------------------
+ *
+ * execInterpExpr.c
+ *	  Opcode based expression evaluation.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/execInterprExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "executor/execExpr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/lsyscache.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+
+/*
+ * Use computed goto based opcode dispatch when computed gotos are
+ * available. But allow to disable locally in this file for development.
+ */
+#ifdef HAVE__COMPUTED_GOTO
+#	define EEO_USE_COMPUTED_GOTO
+#endif /* HAVE__COMPUTED_GOTO */
+
+/*
+ * Macros for opcode dispatch.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+static void **dispatch_table = NULL;
+#	define EEO_OPCODE(opc) dispatch_table[opc]
+#	define EEO_SWITCH(d)
+#	define EEO_DISPATCH_DIRECT(op) goto *((void *) op->opcode)
+#	define EEO_CASE(name) CASE_##name
+#else
+#	define EEO_OPCODE(opc) opc
+#	define EEO_SWITCH(d) switch ((ExprEvalOp) d)
+#	define EEO_DISPATCH_DIRECT(op) goto starteval
+#	define EEO_CASE(name) case name
+#endif /* EEO_USE_COMPUTED_GOTO */
+
+#define EEO_DISPATCH(op) \
+	do \
+	{ \
+		op++; \
+		EEO_DISPATCH_DIRECT(op); \
+	} \
+	while (0)
+
+static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
+static void ExecPrepareInterp(void);
+
+/* support functions */
+static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull);
+static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
+									TupleDesc *cache_field, ExprContext *econtext);
+static void ShutdownTupleDescRef(Datum arg);
+
+/* fast-path evaluation functions */
+static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull);
+
+
+/*
+ * Prepare ExprState for interpreted execution.
+ */
+void
+ExecInstantiateInterpretedExpr(ExprState *state)
+{
+	ExecPrepareInterp();
+
+	Assert(state->steps_len >= 1);
+	Assert(ExecEvalStepOp(state, &state->steps[state->steps_len - 1]) == EEO_DONE);
+
+	/*
+	 * Fast-path for very simple expressions. "starting up" the full
+	 * interpreter is a measurable overhead for these.  Plain Vars and Const
+	 * seem to be the only ones where the intrinsic cost is small enough that
+	 * the overhead of ExecInterpExpr matters.  For more complex expressions
+	 * it's cheaper to use ExecInterpExpr anyways.
+	 */
+	if (state->steps_len == 3)
+	{
+		ExprEvalOp step0 = ExecEvalStepOp(state, &state->steps[0]);
+		ExprEvalOp step1 = ExecEvalStepOp(state, &state->steps[1]);
+
+		if (step0 == EEO_INNER_FETCHSOME && step1 == EEO_INNER_VAR)
+		{
+			state->cb = ExecJustInnerVar;
+			return;
+		}
+		else if (step0 == EEO_OUTER_FETCHSOME && step1 == EEO_OUTER_VAR)
+		{
+			state->cb = ExecJustOuterVar;
+			return;
+		}
+		else if (step0 == EEO_SCAN_FETCHSOME && step1 == EEO_SCAN_VAR)
+		{
+			state->cb = ExecJustScanVar;
+			return;
+		}
+	}
+	else if (state->steps_len == 2 &&
+			 ExecEvalStepOp(state, &state->steps[0]) == EEO_CONST)
+	{
+		state->cb = ExecJustConst;
+		return;
+	}
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+	{
+		int off;
+
+		for (off = 0; off < state->steps_len; off++)
+		{
+			ExprEvalStep *op = &state->steps[off];
+
+			op->opcode = (size_t) EEO_OPCODE(op->opcode);
+		}
+
+		state->flags |= EEO_FLAG_JUMP_THREADED;
+	}
+#endif /* defined(EEO_USE_COMPUTED_GOTO) */
+
+	state->cb = ExecInterpExpr;
+}
+
+
+static Datum
+ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep   *op;
+	const TupleTableSlot *resultslot;
+	TupleTableSlot *innerslot;
+	TupleTableSlot *outerslot;
+	TupleTableSlot *scanslot;
+
+	/*
+	 * This array has to be in the same order as ExprEvalOp.
+	 */
+#if defined(EEO_USE_COMPUTED_GOTO)
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_DONE,
+		&&CASE_EEO_INNER_FETCHSOME,
+		&&CASE_EEO_OUTER_FETCHSOME,
+		&&CASE_EEO_SCAN_FETCHSOME,
+		&&CASE_EEO_INNER_VAR,
+		&&CASE_EEO_OUTER_VAR,
+		&&CASE_EEO_SCAN_VAR,
+		&&CASE_EEO_ASSIGN_INNER_VAR,
+		&&CASE_EEO_ASSIGN_OUTER_VAR,
+		&&CASE_EEO_ASSIGN_SCAN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_ASSIGN_TMP_UNEXPAND,
+		&&CASE_EEO_INNER_SYSVAR,
+		&&CASE_EEO_OUTER_SYSVAR,
+		&&CASE_EEO_SCAN_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_AND_STEP_LAST,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_LAST,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_QUAL,
+		&&CASE_EEO_NULLTEST_ISNULL,
+		&&CASE_EEO_NULLTEST_ISNOTNULL,
+		&&CASE_EEO_NULLTEST_ROWISNULL,
+		&&CASE_EEO_NULLTEST_ROWISNOTNULL,
+		&&CASE_EEO_PARAM_EXEC,
+		&&CASE_EEO_PARAM_EXTERN,
+		&&CASE_EEO_CASE_WHEN_STEP,
+		&&CASE_EEO_CASE_THEN_STEP,
+		&&CASE_EEO_CASE_TESTVAL,
+		&&CASE_EEO_CASE_TESTVAL_UNEXPAND,
+		&&CASE_EEO_COALESCE,
+		&&CASE_EEO_BOOLTEST_IS_TRUE,
+		&&CASE_EEO_BOOLTEST_IS_NOT_TRUE,
+		&&CASE_EEO_BOOLTEST_IS_FALSE,
+		&&CASE_EEO_BOOLTEST_IS_NOT_FALSE,
+		&&CASE_EEO_BOOLTEST_IS_UNKNOWN,
+		&&CASE_EEO_BOOLTEST_IS_NOT_UNKNOWN,
+		&&CASE_EEO_WHOLEROW,
+		&&CASE_EEO_IOCOERCE,
+		&&CASE_EEO_DISTINCT,
+		&&CASE_EEO_NULLIF,
+		&&CASE_EEO_SQLVALUEFUNCTION,
+		&&CASE_EEO_CURRENTOFEXPR,
+		&&CASE_EEO_ARRAYEXPR,
+		&&CASE_EEO_ARRAYCOERCE,
+		&&CASE_EEO_ROW,
+		&&CASE_EEO_ROWCOMPARE_STEP,
+		&&CASE_EEO_ROWCOMPARE_FINAL,
+		&&CASE_EEO_MINMAX,
+		&&CASE_EEO_FIELDSELECT,
+		&&CASE_EEO_FIELDSTORE_DEFORM,
+		&&CASE_EEO_FIELDSTORE_FORM,
+		&&CASE_EEO_ARRAYREF_CHECKINPUT,
+		&&CASE_EEO_ARRAYREF_CHECKSUBSCRIPT,
+		&&CASE_EEO_ARRAYREF_OLD,
+		&&CASE_EEO_ARRAYREF_ASSIGN,
+		&&CASE_EEO_ARRAYREF_FETCH,
+		&&CASE_EEO_CONVERT_ROWTYPE,
+		&&CASE_EEO_SCALARARRAYOP,
+		&&CASE_EEO_DOMAIN_TESTVAL,
+		&&CASE_EEO_DOMAIN_TESTVAL_UNEXPAND,
+		&&CASE_EEO_DOMAIN_NOTNULL,
+		&&CASE_EEO_DOMAIN_CHECK,
+		&&CASE_EEO_XMLEXPR,
+		&&CASE_EEO_AGGREF,
+		&&CASE_EEO_GROUPING_FUNC,
+		&&CASE_EEO_WINDOW_FUNC,
+		&&CASE_EEO_SUBPLAN,
+		&&CASE_EEO_ALTERNATIVE_SUBPLAN,
+		&&CASE_EEO_LAST
+	};
+
+	StaticAssertStmt(EEO_LAST + 1 == lengthof(dispatch_table),
+					 "dispatch_table out of whack with ExprEvalOp");
+#endif
+
+#ifdef EEO_USE_COMPUTED_GOTO
+	if (unlikely(state == NULL))
+	{
+		return PointerGetDatum(dispatch_table);
+	}
+#endif
+
+	/* setup state */
+	op = state->steps;
+	resultslot = state->resultslot;
+	innerslot = econtext->ecxt_innertuple;
+	outerslot = econtext->ecxt_outertuple;
+	scanslot = econtext->ecxt_scantuple;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+	EEO_DISPATCH_DIRECT(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_DONE):
+			goto out;
+
+		EEO_CASE(EEO_INNER_FETCHSOME):
+			/* XXX: worthwhile to check tts_nvalid inline first? */
+			slot_getsomeattrs(innerslot, op->d.fetch.last_var);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_OUTER_FETCHSOME):
+			slot_getsomeattrs(outerslot, op->d.fetch.last_var);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_SCAN_FETCHSOME):
+			slot_getsomeattrs(scanslot, op->d.fetch.last_var);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_INNER_VAR):
+			{
+				int attnum = op->d.var.attnum;
+
+				/*
+				 * Can't assert tts_nvalid, as wholerow var evaluation or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = innerslot->tts_isnull[attnum];
+				*op->resvalue = innerslot->tts_values[attnum];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_OUTER_VAR):
+			{
+				int attnum = op->d.var.attnum;
+
+				/* See EEO_INNER_VAR comments */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = outerslot->tts_isnull[attnum];
+				*op->resvalue = outerslot->tts_values[attnum];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SCAN_VAR):
+			{
+				int attnum = op->d.var.attnum;
+
+				/* See EEO_INNER_VAR comments */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = scanslot->tts_isnull[attnum];
+				*op->resvalue = scanslot->tts_values[attnum];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_ASSIGN_INNER_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_OUTER_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_SCAN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ASSIGN_TMP_UNEXPAND):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				if (!resultslot->tts_isnull[resultnum] &&
+					resultslot->tts_tupleDescriptor->attrs[resultnum]->attlen == -1)
+					resultslot->tts_values[resultnum] = MakeExpandedObjectReadOnlyInternal(resultslot->tts_values[resultnum]);
+
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_INNER_SYSVAR):
+			{
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
+												innerslot->tts_tupleDescriptor,
+												op->resnull);
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_OUTER_SYSVAR):
+			{
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+												scanslot->tts_tupleDescriptor,
+												op->resnull);
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SCAN_SYSVAR):
+			{
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+												scanslot->tts_tupleDescriptor,
+												op->resnull);
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+			}
+strictfail:
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+			/*
+			 * Fallthrough (can't be last - ANDs have two arguments at least).
+			 */
+
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_AND_STEP_LAST):
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->resnull = true;
+				*op->resvalue = 0;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+
+				/*
+				 * No point jumping early to jumpdone - would be same target
+				 * (as this is the last argument to the AND expression),
+				 * except more expensive.
+				 */
+			}
+			else if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = true;
+				*op->resvalue = 0;
+			}
+			else
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+			/*
+			 * Fallthrough (can't be last - ORs have two arguments at least).
+			 */
+
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_OR_STEP_LAST):
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->resnull = true;
+				*op->resvalue = 0;
+			}
+			else if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+
+				/*
+				 * No point jumping to jumpdone - would be same target (as
+				 * this is the last argument to the AND expression), except
+				 * more expensive.
+				 */
+			}
+			else if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = true;
+				*op->resvalue = 0;
+			}
+			else
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_QUAL):
+			/* special case for ExecQual() */
+
+			if (*op->resnull ||
+				!DatumGetBool(*op->resvalue))
+			{
+				/* bail out early */
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				op = &state->steps[op->d.qualexpr.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ISNULL):
+			*op->resvalue = BoolGetDatum(*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ISNOTNULL):
+			*op->resvalue = BoolGetDatum(!*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ROWISNULL):
+			/* out of line implementation: too large */
+			ExecEvalRowNull(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_NULLTEST_ROWISNOTNULL):
+			/* out of line implementation: too large */
+			ExecEvalRowNotNull(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_PARAM_EXEC):
+			ExecEvalParamExec(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_PARAM_EXTERN):
+			/* out of line implementation: too large */
+			ExecEvalParamExtern(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CASE_WHEN_STEP):
+			if (!*op->d.casewhen.isnull
+				&& DatumGetBool(*op->d.casewhen.value))
+			{
+				EEO_DISPATCH(op);
+			}
+			else
+			{
+				op = &state->steps[op->d.casewhen.jumpfalse];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+		EEO_CASE(EEO_CASE_THEN_STEP):
+			/* results already placed in correct place during preceding steps */
+			op = &state->steps[op->d.casethen.jumpdone];
+			EEO_DISPATCH_DIRECT(op);
+
+		EEO_CASE(EEO_CASE_TESTVAL):
+			/*
+			 * Normally upper parts in the expression tree have setup the
+			 * values to be returned here, but some parts of the system
+			 * currently misuse {caseValue,domainValue}_{datum,isNull} to set
+			 * run-time data.  So if no values have been set-up, use
+			 * ExprContext's.  This isn't pretty, but also not *that* ugly,
+			 * and this is unlikely to be performance sensitive enoiugh to
+			 * worry about a branch.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->caseValue_datum;
+				*op->resnull = econtext->caseValue_isNull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CASE_TESTVAL_UNEXPAND):
+			/*
+			 * See EEO_CASE_TESTVAL comment.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->caseValue_datum;
+				*op->resnull = econtext->caseValue_isNull;
+			}
+
+			/* Since caseValue_datum may be read multiple times, force to R/O */
+			if (!*op->resnull)
+			{
+				*op->resvalue = MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_TESTVAL):
+			/*
+			 * See EEO_CASE_TESTVAL comment.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->domainValue_datum;
+				*op->resnull = econtext->domainValue_isNull;
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_TESTVAL_UNEXPAND):
+			/*
+			 * See EEO_CASE_TESTVAL comment.
+			 */
+			if (op->d.casetest.value)
+			{
+				*op->resvalue = *op->d.casetest.value;
+				*op->resnull = *op->d.casetest.isnull;
+			}
+			else
+			{
+				*op->resvalue = econtext->domainValue_datum;
+				*op->resnull = econtext->domainValue_isNull;
+			}
+
+			/* Since domainValue_datum may be read multiple times, force to R/O */
+			if (!*op->resnull)
+			{
+				*op->resvalue = MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_COALESCE):
+			if (!*op->resnull)
+			{
+				op = &state->steps[op->d.coalesce.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_TRUE):
+			if (*op->resnull)
+				*op->resvalue = false;
+			else
+				*op->resvalue = *op->resvalue;
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_NOT_FALSE):
+			if (*op->resnull)
+				*op->resvalue = true;
+			else
+				*op->resvalue = *op->resvalue;
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_FALSE):
+			if (*op->resnull)
+				*op->resvalue = false;
+			else
+				*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_NOT_TRUE):
+			if (*op->resnull)
+				*op->resvalue = true;
+			else
+				*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_UNKNOWN):
+			*op->resvalue = BoolGetDatum(*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_BOOLTEST_IS_NOT_UNKNOWN):
+			*op->resvalue = BoolGetDatum(!*op->resnull);
+			*op->resnull = false;
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_WHOLEROW):
+			/* too complex for an inline implementation */
+			ExecEvalWholeRowVar(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_IOCOERCE):
+			{
+				char *str;
+
+				/* call output function (similar to OutputFunctionCall) */
+				if (*op->resnull)
+				{
+					/* output functions are not called on nulls */
+					str = NULL;
+				}
+				else
+				{
+					FunctionCallInfo fcinfo_out;
+
+					fcinfo_out = op->d.iocoerce.fcinfo_data_out;
+					fcinfo_out->arg[0] = *op->resvalue;
+					fcinfo_out->argnull[0] = false;
+
+					str = DatumGetPointer(FunctionCallInvoke(fcinfo_out));
+					Assert(!fcinfo_out->isnull);
+				}
+
+				/* call input function (similar to InputFunctionCall) */
+				if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
+				{
+					FunctionCallInfo fcinfo_in;
+
+					fcinfo_in = op->d.iocoerce.fcinfo_data_in;
+					fcinfo_in->arg[0] = PointerGetDatum(str);
+					fcinfo_in->argnull[0] = BoolGetDatum(*op->resnull);
+					fcinfo_in->arg[1] = op->d.iocoerce.intypioparam;
+					fcinfo_in->argnull[1] = false;
+					fcinfo_in->arg[2] = -1;
+					fcinfo_in->argnull[2] = false;
+
+					fcinfo_in->isnull = false;
+					*op->resvalue = FunctionCallInvoke(fcinfo_in);
+
+					/* Should get null result if and only if str is NULL */
+					if (str == NULL)
+					{
+						Assert(*op->resnull);
+						Assert(fcinfo_in->isnull);
+					}
+					else
+					{
+						Assert(!*op->resnull);
+						Assert(!fcinfo_in->isnull);
+					}
+
+				}
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_DISTINCT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				/* check function arguments for NULLness */
+				if (fcinfo->argnull[0] && fcinfo->argnull[1])
+				{
+					/* Both NULL? Then is not distinct... */
+					*op->resnull = false;
+					*op->resvalue = BoolGetDatum(false);
+				}
+				else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+				{
+					/* Only one is NULL? Then is distinct... */
+					*op->resnull = false;
+					*op->resvalue = BoolGetDatum(true);
+				}
+				else
+				{
+					fcinfo->isnull = false;
+					*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+					/* Must invert result of "=" */
+					*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+					*op->resnull = fcinfo->isnull;
+				}
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_NULLIF):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				/* if either argument is NULL they can't be equal */
+				if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+				{
+					bool result;
+
+					fcinfo->isnull = false;
+					result = (op->d.func.fn_addr)(fcinfo);
+
+					/* if the arguments are equal return null */
+					if (!fcinfo->isnull && DatumGetBool(result))
+					{
+						*op->resnull = true;
+						*op->resvalue = true;
+						EEO_DISPATCH(op);
+					}
+				}
+				*op->resnull = fcinfo->argnull[0];
+				*op->resvalue = fcinfo->arg[0];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SQLVALUEFUNCTION):
+			/*
+			 * Doesn't seem worthwhile to have an inline implementation
+			 * efficency-wise.
+			 */
+			ExecEvalSQLValueFunction(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CURRENTOFEXPR):
+			/* error invocation uses space, and shouldn't ever occur */
+			ExecEvalCurrentOfExpr(state, op);
+
+		EEO_CASE(EEO_ARRAYEXPR):
+			/* too complex for an inline implementation */
+			ExecEvalArrayExpr(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ARRAYCOERCE):
+			/* too complex for an inline implementation */
+			ExecEvalArrayCoerce(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ROW):
+			/* too complex for an inline implementation */
+			ExecEvalRow(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ROWCOMPARE_STEP):
+			{
+				FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
+
+				/* force NULL result */
+				if (op->d.rowcompare_step.finfo->fn_strict &&
+					(fcinfo->argnull[0] || fcinfo->argnull[1]))
+				{
+					*op->resnull = true;
+					op = &state->steps[op->d.rowcompare_step.jumpnull];
+					EEO_DISPATCH_DIRECT(op);
+				}
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.rowcompare_step.fn_addr)(fcinfo);
+
+				/* force NULL result */
+				if (fcinfo->isnull)
+				{
+					*op->resnull = true;
+					op = &state->steps[op->d.rowcompare_step.jumpnull];
+					EEO_DISPATCH_DIRECT(op);
+				}
+
+				/* no need to compare remaining columns */
+				if (DatumGetInt32(*op->resvalue) != 0)
+				{
+					op = &state->steps[op->d.rowcompare_step.jumpdone];
+					EEO_DISPATCH_DIRECT(op);
+				}
+
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_ROWCOMPARE_FINAL):
+			{
+				int32		cmpresult = DatumGetInt32(*op->resvalue);
+				RowCompareType rctype = op->d.rowcompare_final.rctype;
+
+				*op->resnull = false;
+				switch (rctype)
+				{
+					/* EQ and NE cases aren't allowed here */
+				case ROWCOMPARE_LT:
+					*op->resvalue = BoolGetDatum(cmpresult < 0);
+					break;
+				case ROWCOMPARE_LE:
+					*op->resvalue = BoolGetDatum(cmpresult <= 0);
+					break;
+				case ROWCOMPARE_GE:
+					*op->resvalue = BoolGetDatum(cmpresult >= 0);
+					break;
+				case ROWCOMPARE_GT:
+					*op->resvalue = BoolGetDatum(cmpresult > 0);
+					break;
+				default:
+					Assert(false);
+					break;
+				}
+
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_MINMAX):
+			/* too complex for an inline implementation */
+			ExecEvalMinMax(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FIELDSELECT):
+			/* too complex for an inline implementation */
+			ExecEvalFieldSelect(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FIELDSTORE_DEFORM):
+			/* too complex for an inline implementation */
+			ExecEvalFieldStoreDeForm(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_FIELDSTORE_FORM):
+			/* too complex for an inline implementation */
+			ExecEvalFieldStoreForm(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ARRAYREF_CHECKINPUT):
+			Assert(!op->d.arrayref.state->isassignment);
+			/*
+			 * If refexpr yields NULL, and it's a fetch, then result is NULL.
+			 */
+			if (*op->resnull &&
+				!op->d.arrayref.state->isassignment)
+			{
+				op = &state->steps[op->d.arrayref.jumpdone];
+				EEO_DISPATCH_DIRECT(op);
+			}
+
+			EEO_DISPATCH(op);
+
+		/*
+		 * Check whether a subscript is NULL and handle that.
+		 */
+		EEO_CASE(EEO_ARRAYREF_CHECKSUBSCRIPT):
+			if (ExecEvalArrayRefCheckSubscript(state, op))
+			{
+				op++;
+			}
+			else
+			{
+				op = &state->steps[op->d.arrayref_checksubscript.jumpdone];
+			}
+			EEO_DISPATCH_DIRECT(op);
+
+		/*
+		 * Fetch the old value in an arrayref, if referenced (via a
+		 * CaseTestExpr) inside the assignment expression.
+		 */
+		EEO_CASE(EEO_ARRAYREF_OLD):
+			ExecEvalArrayRefOld(state, op);
+			EEO_DISPATCH(op);
+
+		/*
+		 * Perform ArrayRef assignment
+		 */
+		EEO_CASE(EEO_ARRAYREF_ASSIGN):
+			ExecEvalArrayRefAssign(state, op);
+			EEO_DISPATCH(op);
+
+		/*
+		 * Fetch subset of an array.
+		 */
+		EEO_CASE(EEO_ARRAYREF_FETCH):
+			ExecEvalArrayRefFetch(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_CONVERT_ROWTYPE):
+			ExecEvalConvertRowtype(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_SCALARARRAYOP):
+			/* too complex for an inline implementation */
+			ExecEvalScalarArrayOp(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_NOTNULL):
+			/* too complex for an inline implementation */
+			ExecEvalConstraintNotNull(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_DOMAIN_CHECK):
+			/* too complex for an inline implementation */
+			ExecEvalConstraintCheck(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_XMLEXPR):
+			/* too complex for an inline implementation */
+			ExecEvalXmlExpr(state, op);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_AGGREF):
+			{
+				AggrefExprState *aggref = op->d.aggref.astate;
+				Assert(econtext->ecxt_aggvalues != NULL);
+
+				*op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
+				*op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_GROUPING_FUNC):
+			/* too complex/uncommon for an inline implementation */
+			ExecEvalGroupingFunc(state, op);
+
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_WINDOW_FUNC):
+			{
+				WindowFuncExprState *wfunc = op->d.window_func.wfstate;
+				Assert(econtext->ecxt_aggvalues != NULL);
+
+				*op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
+				*op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
+				EEO_DISPATCH(op);
+			}
+
+		EEO_CASE(EEO_SUBPLAN):
+			/* too complex for an inline implementation */
+			ExecEvalSubPlan(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_ALTERNATIVE_SUBPLAN):
+			/* too complex for an inline implementation */
+			ExecEvalAlternativeSubPlan(state, op, econtext);
+			EEO_DISPATCH(op);
+
+		EEO_CASE(EEO_LAST):
+		{
+			Assert(false);
+		}
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+static Datum
+ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[1];
+	int attnum = op->d.var.attnum + 1;
+	TupleTableSlot *slot = econtext->ecxt_outertuple;
+	return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[1];
+	int attnum = op->d.var.attnum + 1;
+	TupleTableSlot *slot = econtext->ecxt_innertuple;
+	return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[1];
+	int attnum = op->d.var.attnum + 1;
+	TupleTableSlot *slot = econtext->ecxt_scantuple;
+	return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	ExprEvalStep *op = &state->steps[0];
+	*isnull = op->d.constval.isnull;
+	return op->d.constval.value;
+}
+
+static void
+ExecPrepareInterp(void)
+{
+	static bool prepared = false;
+	if (prepared)
+		return;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+	if (dispatch_table == NULL)
+		dispatch_table = (void **) DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL));
+#endif
+
+	prepared = true;
+}
+
+ExprEvalOp
+ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
+{
+#if defined(EEO_USE_COMPUTED_GOTO)
+	if (state->flags & EEO_FLAG_JUMP_THREADED)
+	{
+		int		i;
+		for (i = 0; i < EEO_LAST; i++)
+		{
+			if ((void *) op->opcode == dispatch_table[i])
+			{
+				return (ExprEvalOp) i;
+			}
+		}
+		elog(ERROR, "unknown opcode");
+	}
+#endif
+	return (ExprEvalOp) op->opcode;
+}
+
+/*
+ * Computes the value for a PARAM_EXEC parameter.
+ */
+void
+ExecEvalParamExec(ExprState *state, ExprEvalStep *op,  ExprContext *econtext)
+{
+	ParamExecData *prm;
+
+	prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]);
+	if (unlikely(prm->execPlan != NULL))
+	{
+		/*
+		 * XXX: Worthwhile to extract this into a separate opcode?
+		 */
+		ExecSetParamPlan(prm->execPlan, econtext);
+		/* ExecSetParamPlan should have processed this param... */
+		Assert(prm->execPlan == NULL);
+	}
+	*op->resvalue = prm->value;
+	*op->resnull = prm->isnull;
+}
+
+/*
+ * Computes the value for a PARAM_EXTERN parameter.
+ */
+void
+ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,  ExprContext *econtext)
+{
+
+	ParamListInfo paramInfo = econtext->ecxt_param_list_info;
+	int			paramId = op->d.param.paramid;
+
+	if (likely(paramInfo &&
+			   paramId > 0 && paramId <= paramInfo->numParams))
+	{
+		ParamExternData *prm = &paramInfo->params[paramId - 1];
+
+		/* give hook a chance in case parameter is dynamic */
+		/* XXX: separate opcode */
+		if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
+			(*paramInfo->paramFetch) (paramInfo, paramId);
+
+		if (likely(OidIsValid(prm->ptype)))
+		{
+			/* safety check in case hook did something unexpected */
+			/* XXX: assert instead? */
+			if (unlikely(prm->ptype != op->d.param.paramtype))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
+								paramId,
+								format_type_be(prm->ptype),
+								format_type_be(op->d.param.paramtype))));
+			*op->resvalue = prm->value;
+			*op->resnull = prm->isnull;
+			return;
+		}
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_UNDEFINED_OBJECT),
+			 errmsg("no value found for parameter %d", paramId)));
+}
+
+/*
+ * Evaluate SQLValueFunction expressions.
+ */
+void
+ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
+{
+	FunctionCallInfoData fcinfo;
+	SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
+
+	*op->resnull = false;
+
+	/*
+	 * Note: current_schema() can return NULL.  current_user() etc currently
+	 * cannot, but might as well code those cases the same way for safety.
+	 */
+	switch (svf->op)
+	{
+		case SVFOP_CURRENT_DATE:
+			*op->resvalue = DateADTGetDatum(GetSQLCurrentDate());
+			break;
+		case SVFOP_CURRENT_TIME:
+		case SVFOP_CURRENT_TIME_N:
+			*op->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
+			break;
+		case SVFOP_CURRENT_TIMESTAMP:
+		case SVFOP_CURRENT_TIMESTAMP_N:
+			*op->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
+			break;
+		case SVFOP_LOCALTIME:
+		case SVFOP_LOCALTIME_N:
+			*op->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
+			break;
+		case SVFOP_LOCALTIMESTAMP:
+		case SVFOP_LOCALTIMESTAMP_N:
+			*op->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
+			break;
+		case SVFOP_CURRENT_ROLE:
+		case SVFOP_CURRENT_USER:
+		case SVFOP_USER:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_user(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+		case SVFOP_SESSION_USER:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = session_user(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+		case SVFOP_CURRENT_CATALOG:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_database(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+		case SVFOP_CURRENT_SCHEMA:
+			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+			*op->resvalue = current_schema(&fcinfo);
+			*op->resnull = fcinfo.isnull;
+			break;
+	}
+}
+
+void
+ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
+{
+	/*
+	 * The planner should convert CURRENT OF into a TidScan qualification, or
+	 * some other special handling in a ForeignScan node.  So we have to be
+	 * able to do ExecInitExpr on a CurrentOfExpr, but we shouldn't ever
+	 * actually execute it.  If we get here, we suppose we must be dealing
+	 * with CURRENT OF on a foreign table whose FDW doesn't handle it, and
+	 * complain accordingly.
+	 */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("WHERE CURRENT OF is not supported for this table type")));
+}
+
+/*
+ * Evaluate NullTest / IS NULL for rows.
+ */
+void
+ExecEvalRowNull(ExprState *state, ExprEvalStep *op)
+{
+	ExecEvalRowNullInt(state, op, true);
+}
+
+/*
+ * Evaluate NullTest / IS NOT NULL for rows.
+ */
+void
+ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op)
+{
+	ExecEvalRowNullInt(state, op, false);
+}
+
+static void
+ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull)
+{
+	Datum		value = *op->resvalue;
+	bool		isnull = *op->resnull;
+	HeapTupleHeader tuple;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupDesc;
+	HeapTupleData tmptup;
+	int			att;
+
+	*op->resnull = false;
+
+	if (isnull && checkisnull)
+	{
+		*op->resvalue = BoolGetDatum(true);
+		return;
+	}
+	else if (isnull)
+	{
+		*op->resvalue = BoolGetDatum(false);
+		return;
+	}
+
+	/*
+	 * The SQL standard defines IS [NOT] NULL for a non-null rowtype
+	 * argument as:
+	 *
+	 * "R IS NULL" is true if every field is the null value.
+	 *
+	 * "R IS NOT NULL" is true if no field is the null value.
+	 *
+	 * This definition is (apparently intentionally) not recursive; so our
+	 * tests on the fields are primitive attisnull tests, not recursive
+	 * checks to see if they are all-nulls or no-nulls rowtypes.
+	 *
+	 * The standard does not consider the possibility of zero-field rows,
+	 * but here we consider them to vacuously satisfy both predicates.
+	 */
+
+	tuple = DatumGetHeapTupleHeader(value);
+
+	tupType = HeapTupleHeaderGetTypeId(tuple);
+	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+	/* Lookup tupdesc if first time through or if type changes */
+	tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+	/*
+	 * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
+	 */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	tmptup.t_data = tuple;
+
+	for (att = 1; att <= tupDesc->natts; att++)
+	{
+		/* ignore dropped columns */
+		if (tupDesc->attrs[att - 1]->attisdropped)
+			continue;
+		if (heap_attisnull(&tmptup, att))
+		{
+			/* null field disproves IS NOT NULL */
+			if (!checkisnull)
+			{
+				ReleaseTupleDesc(tupDesc);
+				*op->resvalue = BoolGetDatum(false);
+				return;
+			}
+		}
+		else
+		{
+			/* non-null field disproves IS NULL */
+			if (checkisnull)
+			{
+				ReleaseTupleDesc(tupDesc);
+				*op->resvalue = BoolGetDatum(false);
+				return;
+			}
+		}
+	}
+
+	ReleaseTupleDesc(tupDesc);
+	*op->resvalue = BoolGetDatum(true);
+}
+
+
+/*
+ * Evaluate ARRAY[] expressions.
+ */
+void
+ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
+{
+	ArrayType  *result;
+	ArrayExpr * arrayExpr = op->d.arrayexpr.arrayexpr;
+	Oid			element_type = arrayExpr->element_typeid;
+	int			ndims = 0;
+	int			dims[MAXDIM];
+	int			lbs[MAXDIM];
+	int			nelems = op->d.arrayexpr.nelems;
+
+	/* Set default values for result flags: non-null */
+	*op->resnull = false;
+
+	if (!arrayExpr->multidims)
+	{
+		/* Elements are presumably of scalar type */
+		Datum	   *dvalues = op->d.arrayexpr.elemvalues;
+		bool	   *dnulls = op->d.arrayexpr.elemnulls;
+
+		ndims = 1;
+
+		/* Shouldn't happen here, but if length is 0, return empty array */
+		if (nelems == 0)
+		{
+			*op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+			return;
+		}
+
+		/* setup for 1-D array of the given length */
+		dims[0] = nelems;
+		lbs[0] = 1;
+
+		result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
+									element_type,
+									op->d.arrayexpr.elemlength,
+									op->d.arrayexpr.elembyval,
+									op->d.arrayexpr.elemalign);
+	}
+	else
+	{
+		/* Must be nested array expressions */
+		int			nbytes = 0;
+		int			nitems = 0;
+		int			outer_nelems = 0;
+		int			elem_ndims = 0;
+		int		   *elem_dims = NULL;
+		int		   *elem_lbs = NULL;
+		bool		firstone = true;
+		bool		havenulls = false;
+		bool		haveempty = false;
+		char	  **subdata;
+		bits8	  **subbitmaps;
+		int		   *subbytes;
+		int		   *subnitems;
+		int32		dataoffset;
+		char	   *dat;
+		int			iitem;
+		int			elemoff;
+		int			i;
+
+		subdata = (char **) palloc(nelems * sizeof(char *));
+		subbitmaps = (bits8 **) palloc(nelems * sizeof(bits8 *));
+		subbytes = (int *) palloc(nelems * sizeof(int));
+		subnitems = (int *) palloc(nelems * sizeof(int));
+
+		/* loop through and get data area from each element */
+		for (elemoff = 0; elemoff < nelems; elemoff++)
+		{
+			bool		eisnull;
+			Datum		arraydatum;
+			ArrayType  *array;
+			int			this_ndims;
+
+			arraydatum = op->d.arrayexpr.elemvalues[elemoff];
+			eisnull = op->d.arrayexpr.elemnulls[elemoff];
+
+			/* temporarily ignore null subarrays */
+			if (eisnull)
+			{
+				haveempty = true;
+				continue;
+			}
+
+			array = DatumGetArrayTypeP(arraydatum);
+
+			/* run-time double-check on element type */
+			if (element_type != ARR_ELEMTYPE(array))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("cannot merge incompatible arrays"),
+						 errdetail("Array with element type %s cannot be "
+						 "included in ARRAY construct with element type %s.",
+								   format_type_be(ARR_ELEMTYPE(array)),
+								   format_type_be(element_type))));
+
+			this_ndims = ARR_NDIM(array);
+			/* temporarily ignore zero-dimensional subarrays */
+			if (this_ndims <= 0)
+			{
+				haveempty = true;
+				continue;
+			}
+
+			if (firstone)
+			{
+				/* Get sub-array details from first member */
+				elem_ndims = this_ndims;
+				ndims = elem_ndims + 1;
+				if (ndims <= 0 || ndims > MAXDIM)
+					ereport(ERROR,
+							(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+						  errmsg("number of array dimensions (%d) exceeds " \
+								 "the maximum allowed (%d)", ndims, MAXDIM)));
+
+				elem_dims = (int *) palloc(elem_ndims * sizeof(int));
+				memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
+				elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
+				memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
+
+				firstone = false;
+			}
+			else
+			{
+				/* Check other sub-arrays are compatible */
+				if (elem_ndims != this_ndims ||
+					memcmp(elem_dims, ARR_DIMS(array),
+						   elem_ndims * sizeof(int)) != 0 ||
+					memcmp(elem_lbs, ARR_LBOUND(array),
+						   elem_ndims * sizeof(int)) != 0)
+					ereport(ERROR,
+							(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+							 errmsg("multidimensional arrays must have array "
+									"expressions with matching dimensions")));
+			}
+
+			subdata[outer_nelems] = ARR_DATA_PTR(array);
+			subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
+			subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
+			nbytes += subbytes[outer_nelems];
+			subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
+													 ARR_DIMS(array));
+			nitems += subnitems[outer_nelems];
+			havenulls |= ARR_HASNULL(array);
+			outer_nelems++;
+		}
+
+		/*
+		 * If all items were null or empty arrays, return an empty array;
+		 * otherwise, if some were and some weren't, raise error.  (Note: we
+		 * must special-case this somehow to avoid trying to generate a 1-D
+		 * array formed from empty arrays.  It's not ideal...)
+		 */
+		if (haveempty)
+		{
+			if (ndims == 0)		/* didn't find any nonempty array */
+			{
+				*op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+				return;
+			}
+			ereport(ERROR,
+					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+					 errmsg("multidimensional arrays must have array "
+							"expressions with matching dimensions")));
+		}
+
+		/* setup for multi-D array */
+		dims[0] = outer_nelems;
+		lbs[0] = 1;
+		for (i = 1; i < ndims; i++)
+		{
+			dims[i] = elem_dims[i - 1];
+			lbs[i] = elem_lbs[i - 1];
+		}
+
+		if (havenulls)
+		{
+			dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+			nbytes += dataoffset;
+		}
+		else
+		{
+			dataoffset = 0;		/* marker for no null bitmap */
+			nbytes += ARR_OVERHEAD_NONULLS(ndims);
+		}
+
+		result = (ArrayType *) palloc(nbytes);
+		SET_VARSIZE(result, nbytes);
+		result->ndim = ndims;
+		result->dataoffset = dataoffset;
+		result->elemtype = element_type;
+		memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+		memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+
+		dat = ARR_DATA_PTR(result);
+		iitem = 0;
+		for (i = 0; i < outer_nelems; i++)
+		{
+			memcpy(dat, subdata[i], subbytes[i]);
+			dat += subbytes[i];
+			if (havenulls)
+				array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
+								  subbitmaps[i], 0,
+								  subnitems[i]);
+			iitem += subnitems[i];
+		}
+	}
+
+	*op->resvalue = PointerGetDatum(result);
+}
+
+
+
+/*
+ * Evaluate an ArrayCoerceExpr expression.
+ */
+void
+ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
+{
+	ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
+	Datum		result;
+	FunctionCallInfoData locfcinfo;
+
+	/* input is in *step->resvalue/resnull */
+	result = *op->resvalue;
+	if (*op->resnull)
+	{
+		return;			/* nothing to do */
+	}
+
+	/*
+	 * If it's binary-compatible, modify the element type in the array header,
+	 * but otherwise leave the array as we received it.
+	 */
+	if (!OidIsValid(acoerce->elemfuncid))
+	{
+		/* Detoast input array if necessary, and copy in any case */
+		ArrayType  *array = DatumGetArrayTypePCopy(result);
+
+		ARR_ELEMTYPE(array) = op->d.arraycoerce.resultelemtype;
+		*op->resvalue = PointerGetDatum(array);
+		return;
+	}
+
+	/* Initialize function cache if first time through */
+	if (op->d.arraycoerce.elemfunc->fn_oid == InvalidOid)
+	{
+		AclResult	aclresult;
+
+		/* Check permission to call function */
+		aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
+									 ACL_EXECUTE);
+		if (aclresult != ACLCHECK_OK)
+			aclcheck_error(aclresult, ACL_KIND_PROC,
+						   get_func_name(acoerce->elemfuncid));
+		InvokeFunctionExecuteHook(acoerce->elemfuncid);
+
+		/* Set up the primary fmgr lookup information */
+		fmgr_info(acoerce->elemfuncid, op->d.arraycoerce.elemfunc);
+		fmgr_info_set_expr((Node *) acoerce, op->d.arraycoerce.elemfunc);
+	}
+
+	/*
+	 * Use array_map to apply the function to each array element.
+	 *
+	 * We pass on the desttypmod and isExplicit flags whether or not the
+	 * function wants them.
+	 *
+	 * Note: coercion functions are assumed to not use collation.
+	 */
+	InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3,
+							 InvalidOid, NULL, NULL);
+	locfcinfo.arg[0] = result;
+	locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
+	locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
+	locfcinfo.argnull[0] = false;
+	locfcinfo.argnull[1] = false;
+	locfcinfo.argnull[2] = false;
+
+	*op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype,
+							  op->d.arraycoerce.amstate);
+}
+
+/*
+ * Evaluate ROW() expressions.
+ *
+ * The individual columns have already been evaluated into
+ * op->d.row.elemvalues/nulls.
+ */
+void
+ExecEvalRow(ExprState *state, ExprEvalStep *op)
+{
+	HeapTuple	tuple;
+
+	/* Set default values for result flag: non-null */
+	*op->resnull = false;
+
+	/* build tuple from evaluated field values */
+	tuple = heap_form_tuple(op->d.row.tupdesc,
+							op->d.row.elemvalues,
+							op->d.row.elemnulls);
+
+	*op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+/*
+ * Evaluate GREATEST()/LEAST() type expression (note this is not MIN(),
+ * MAX()).
+ *
+ * All of the to-be-compared expressions have already been evaluated into
+ * ->op->d.minmax.values/nulls.
+ */
+void
+ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
+{
+	int off;
+	Datum *values = op->d.minmax.values;
+	bool *nulls = op->d.minmax.nulls;
+	FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data;
+	MinMaxOp	operator = op->d.minmax.op;
+
+	/* set at initialization */
+	Assert(fcinfo->argnull[0] == false);
+	Assert(fcinfo->argnull[1] == false);
+
+	*op->resnull = true;
+
+	for (off = 0; off < op->d.minmax.nelems; off++)
+	{
+		/* ignore NULL inputs */
+		if (nulls[off])
+			continue;
+
+		if (*op->resnull)
+		{
+			/* first nonnull input, adopt value */
+			*op->resvalue = values[off];
+			*op->resnull = false;
+		}
+		else
+		{
+			int cmpresult;
+
+			/* apply comparison function */
+			fcinfo->isnull = false;
+			fcinfo->arg[0] = *op->resvalue;
+			fcinfo->arg[1] = values[off];
+			Assert(!fcinfo->isnull);
+
+			cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
+
+			if (cmpresult > 0 && operator == IS_LEAST)
+				*op->resvalue = values[off];
+			else if (cmpresult < 0 && operator == IS_GREATEST)
+				*op->resvalue = values[off];
+		}
+	}
+}
+
+/*
+ * Evaluate a FieldSelect node.
+ */
+void
+ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	AttrNumber	fieldnum = op->d.fieldselect.fieldnum;
+	Datum		tupDatum;
+	HeapTupleHeader tuple;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupDesc;
+	Form_pg_attribute attr;
+	HeapTupleData tmptup;
+
+	tupDatum = *op->resvalue;
+
+	/* this test covers the isDone exception too: */
+	if (*op->resnull)
+		return;
+
+	tuple = DatumGetHeapTupleHeader(tupDatum);
+
+	tupType = HeapTupleHeaderGetTypeId(tuple);
+	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+	/* Lookup tupdesc if first time through or if type changes */
+	tupDesc = get_cached_rowtype(tupType, tupTypmod,
+								 &op->d.fieldselect.argdesc,
+								 econtext);
+
+	/*
+	 * Find field's attr record.  Note we don't support system columns here: a
+	 * datum tuple doesn't have valid values for most of the interesting
+	 * system columns anyway.
+	 */
+	if (fieldnum <= 0)			/* should never happen */
+		elog(ERROR, "unsupported reference to system column %d in FieldSelect",
+			 fieldnum);
+	if (fieldnum > tupDesc->natts)		/* should never happen */
+		elog(ERROR, "attribute number %d exceeds number of columns %d",
+			 fieldnum, tupDesc->natts);
+	attr = tupDesc->attrs[fieldnum - 1];
+
+	/* Check for dropped column, and force a NULL result if so */
+	if (attr->attisdropped)
+	{
+		*op->resnull = true;
+		return;
+	}
+
+	/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
+	/* As in ExecEvalScalarVar, we should but can't check typmod */
+	if (op->d.fieldselect.resulttype != attr->atttypid)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("attribute %d has wrong type", fieldnum),
+				 errdetail("Table has type %s, but query expects %s.",
+						   format_type_be(attr->atttypid),
+						   format_type_be(op->d.fieldselect.resulttype))));
+
+	/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	tmptup.t_data = tuple;
+
+	*op->resvalue = heap_getattr(&tmptup,
+								 fieldnum,
+								 tupDesc,
+								 op->resnull);
+}
+
+/*
+ * Deform tuple before evaluating the individual new values as part of a
+ * FieldStore expression.
+ */
+void
+ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	/* Lookup tupdesc if first time through or after rescan */
+	get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
+					   op->d.fieldstore.argdesc, econtext);
+
+	if (*op->resnull)
+	{
+		/* Convert null input tuple into an all-nulls row */
+		memset(op->d.fieldstore.nulls, true,
+			   MaxTupleAttributeNumber * sizeof(bool));
+	}
+	else
+	{
+		/*
+		 * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
+		 * set all the fields in the struct just in case.
+		 */
+		Datum		tupDatum = *op->resvalue;
+		HeapTupleHeader tuphdr;
+		HeapTupleData tmptup;
+
+		tuphdr = DatumGetHeapTupleHeader(tupDatum);
+		tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
+		ItemPointerSetInvalid(&(tmptup.t_self));
+		tmptup.t_tableOid = InvalidOid;
+		tmptup.t_data = tuphdr;
+
+		heap_deform_tuple(&tmptup, *op->d.fieldstore.argdesc,
+						  op->d.fieldstore.values,
+						  op->d.fieldstore.nulls);
+	}
+}
+
+/*
+ * Compute the new wholerow datum after each individual row part of a
+ * FieldStore expression has been evaluated.
+ */
+void
+ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	HeapTuple	tuple;
+
+	/* Result is never null */
+	*op->resnull = false;
+
+	tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
+							op->d.fieldstore.values,
+							op->d.fieldstore.nulls);
+
+	*op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+void
+ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref.state;
+
+	if (*op->resnull)
+	{
+		arefstate->prevnull = true;
+		arefstate->prevvalue = (Datum) 0;
+	}
+	else if (arefstate->numlower == 0)
+	{
+		arefstate->prevvalue =
+			array_get_element(*op->resvalue,
+							  arefstate->numupper,
+							  arefstate->upperindex,
+							  arefstate->refattrlength,
+							  arefstate->refelemlength,
+							  arefstate->refelembyval,
+							  arefstate->refelemalign,
+							  &arefstate->prevnull);
+	}
+	else
+	{
+		/* this is currently unreachable */
+		arefstate->prevvalue =
+			array_get_slice(*op->resvalue,
+							arefstate->numupper,
+							arefstate->upperindex,
+							arefstate->lowerindex,
+							arefstate->upperprovided,
+							arefstate->lowerprovided,
+							arefstate->refattrlength,
+							arefstate->refelemlength,
+							arefstate->refelembyval,
+							arefstate->refelemalign);
+	}
+}
+
+void
+ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref.state;
+
+	/*
+	 * For an assignment to a fixed-length array type, both the
+	 * original array and the value to be assigned into it must be
+	 * non-NULL, else we punt and return the original array.
+	 */
+	if (arefstate->refattrlength > 0 &&
+		(*op->resnull || arefstate->replacenull))
+	{
+		return;
+	}
+
+	/*
+	 * For assignment to varlena arrays, we handle a NULL original array
+	 * by substituting an empty (zero-dimensional) array; insertion of the
+	 * new element will result in a singleton array value.  It does not
+	 * matter whether the new element is NULL.
+	 */
+	if (*op->resnull)
+	{
+		*op->resvalue = PointerGetDatum(construct_empty_array(arefstate->refelemtype));
+		*op->resnull = false;
+	}
+
+	if (arefstate->numlower == 0)
+	{
+		*op->resvalue =
+			array_set_element(*op->resvalue, arefstate->numupper,
+							  arefstate->upperindex,
+							  arefstate->replacevalue,
+							  arefstate->replacenull,
+							  arefstate->refattrlength,
+							  arefstate->refelemlength,
+							  arefstate->refelembyval,
+							  arefstate->refelemalign);
+	}
+	else
+	{
+		*op->resvalue =
+			array_set_slice(*op->resvalue, arefstate->numupper,
+							arefstate->upperindex,
+							arefstate->lowerindex,
+							arefstate->upperprovided,
+							arefstate->lowerprovided,
+							arefstate->replacevalue,
+							arefstate->replacenull,
+							arefstate->refattrlength,
+							arefstate->refelemlength,
+							arefstate->refelembyval,
+							arefstate->refelemalign);
+	}
+}
+
+void
+ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref.state;
+
+	if (arefstate->numlower == 0)
+	{
+
+		*op->resvalue =
+			array_get_element(*op->resvalue,
+							  arefstate->numupper,
+							  arefstate->upperindex,
+							  arefstate->refattrlength,
+							  arefstate->refelemlength,
+							  arefstate->refelembyval,
+							  arefstate->refelemalign,
+							  op->resnull);
+	}
+	else
+	{
+		*op->resvalue =
+			array_get_slice(*op->resvalue,
+							arefstate->numupper,
+							arefstate->upperindex,
+							arefstate->lowerindex,
+							arefstate->upperprovided,
+							arefstate->lowerprovided,
+							arefstate->refattrlength,
+							arefstate->refelemlength,
+							arefstate->refelembyval,
+							arefstate->refelemalign);
+	}
+}
+
+bool
+ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op)
+{
+	ArrayRefState *arefstate = op->d.arrayref_checksubscript.state;
+	int off = op->d.arrayref_checksubscript.off;
+	bool *nulls;
+	Datum *values;
+	int *indexes;
+
+	if (op->d.arrayref_checksubscript.isupper)
+	{
+		nulls = arefstate->uppernull;
+		values = arefstate->upper;
+		indexes = arefstate->upperindex;
+	}
+	else
+	{
+		nulls = arefstate->lowernull;
+		values = arefstate->lower;
+		indexes = arefstate->lowerindex;
+	}
+
+	/* If any index expr yields NULL, result is NULL or error */
+	if (nulls[off])
+	{
+		if (arefstate->isassignment)
+			ereport(ERROR,
+					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					 errmsg("array subscript in assignment must not be null")));
+		*op->resnull = true;
+		return false;
+	}
+
+	/* convert datum to int */
+	indexes[off] = values[off];
+
+	return true;
+}
+
+void
+ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert;
+	HeapTuple	result;
+	Datum		tupDatum;
+	HeapTupleHeader tuple;
+	HeapTupleData tmptup;
+	TupleDesc indesc, outdesc;
+
+	tupDatum = *op->resvalue;
+
+	/* this test covers the isDone exception too: */
+	if (*op->resnull)
+		return;
+
+	tuple = DatumGetHeapTupleHeader(tupDatum);
+
+	/* Lookup tupdescs if first time through or after rescan */
+	if (op->d.convert_rowtype.indesc == NULL)
+	{
+		get_cached_rowtype(exprType((Node *) convert->arg), -1,
+						   &op->d.convert_rowtype.indesc,
+						   econtext);
+		op->d.convert_rowtype.initialized = false;
+	}
+	if (op->d.convert_rowtype.outdesc == NULL)
+	{
+		get_cached_rowtype(convert->resulttype, -1,
+						   &op->d.convert_rowtype.outdesc,
+						   econtext);
+		op->d.convert_rowtype.initialized = false;
+	}
+
+	indesc = op->d.convert_rowtype.indesc;
+	outdesc = op->d.convert_rowtype.outdesc;
+
+	/*
+	 * We used to be able to assert that incoming tuples are marked with
+	 * exactly the rowtype of cstate->indesc.  However, now that
+	 * ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
+	 * due to inserting aliases, we can only make this weak test:
+	 */
+	Assert(HeapTupleHeaderGetTypeId(tuple) == indesc->tdtypeid ||
+		   HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
+
+	/* if first time through, initialize conversion map */
+	if (!op->d.convert_rowtype.initialized)
+	{
+		MemoryContext old_cxt;
+
+		/* allocate map in long-lived memory context */
+		old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+		/* prepare map from old to new attribute numbers */
+		op->d.convert_rowtype.map =
+			convert_tuples_by_name(indesc, outdesc,
+								   gettext_noop("could not convert row type"));
+		op->d.convert_rowtype.initialized = true;
+
+		MemoryContextSwitchTo(old_cxt);
+	}
+
+	/*
+	 * No-op if no conversion needed (not clear this can happen here).
+	 */
+	if (op->d.convert_rowtype.map == NULL)
+		return;
+
+	/*
+	 * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
+	 */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	tmptup.t_data = tuple;
+
+	result = do_convert_tuple(&tmptup, op->d.convert_rowtype.map);
+
+	*op->resvalue = HeapTupleGetDatum(result);
+
+}
+
+/*
+ * ExecEvalScalarArrayOp
+ *
+ * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
+ * and we combine the results across all array elements using OR and AND
+ * (for ANY and ALL respectively).  Of course we short-circuit as soon as
+ * the result is known.
+ */
+void
+ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
+{
+	ScalarArrayOpExpr *opexpr = op->d.scalararrayop.opexpr;
+	bool		useOr = opexpr->useOr;
+	FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
+	bool strictfunc = op->d.scalararrayop.finfo->fn_strict;
+	ArrayType  *arr;
+	int			nitems;
+	Datum		result;
+	bool		resultnull;
+	int			i;
+	int16		typlen;
+	bool		typbyval;
+	char		typalign;
+	char	   *s;
+	bits8	   *bitmap;
+	int			bitmask;
+
+	/*
+	 * If the array is NULL then we return NULL --- it's not very meaningful
+	 * to do anything else, even if the operator isn't strict.
+	 */
+	if (*op->resnull)
+	{
+		return;
+	}
+
+	/* no "bad" nulls, okay to fetch and detoast the array */
+	arr = DatumGetArrayTypeP(*op->resvalue);
+
+	/*
+	 * If the array is empty, we return either FALSE or TRUE per the useOr
+	 * flag.  This is correct even if the scalar is NULL; since we would
+	 * evaluate the operator zero times, it matters not whether it would want
+	 * to return NULL.
+	 */
+	nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+	if (nitems <= 0)
+	{
+		*op->resnull = false;
+		*op->resvalue = BoolGetDatum(!useOr);
+		return;
+	}
+
+	/*
+	 * If the scalar is NULL, and the function is strict, return NULL; no
+	 * point in iterating the loop.
+	 */
+	if (fcinfo->argnull[0] && strictfunc)
+	{
+		*op->resnull = true;
+		return;
+	}
+
+	/*
+	 * We arrange to look up info about the element type only once per series
+	 * of calls, assuming the element type doesn't change underneath us.
+	 */
+	if (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr))
+	{
+		get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+							 &op->d.scalararrayop.typlen,
+							 &op->d.scalararrayop.typbyval,
+							 &op->d.scalararrayop.typalign);
+		op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr);
+	}
+
+	typlen = op->d.scalararrayop.typlen;
+	typbyval = op->d.scalararrayop.typbyval;
+	typalign = op->d.scalararrayop.typalign;
+
+	result = BoolGetDatum(!useOr);
+	resultnull = false;
+
+	/* Loop over the array elements */
+	s = (char *) ARR_DATA_PTR(arr);
+	bitmap = ARR_NULLBITMAP(arr);
+	bitmask = 1;
+
+	for (i = 0; i < nitems; i++)
+	{
+		Datum		elt;
+		Datum		thisresult;
+
+		/* Get array element, checking for NULL */
+		if (bitmap && (*bitmap & bitmask) == 0)
+		{
+			fcinfo->arg[1] = (Datum) 0;
+			fcinfo->argnull[1] = true;
+		}
+		else
+		{
+			elt = fetch_att(s, typbyval, typlen);
+			s = att_addlength_pointer(s, typlen, s);
+			s = (char *) att_align_nominal(s, typalign);
+			fcinfo->arg[1] = elt;
+			fcinfo->argnull[1] = false;
+		}
+
+		/* Call comparison function */
+		if (fcinfo->argnull[1] && strictfunc)
+		{
+			fcinfo->isnull = true;
+			thisresult = (Datum) 0;
+		}
+		else
+		{
+			fcinfo->isnull = false;
+			thisresult = (op->d.scalararrayop.fn_addr)(fcinfo);
+		}
+
+		/* Combine results per OR or AND semantics */
+		if (fcinfo->isnull)
+			resultnull = true;
+		else if (useOr)
+		{
+			if (DatumGetBool(thisresult))
+			{
+				result = BoolGetDatum(true);
+				resultnull = false;
+				break;			/* needn't look at any more elements */
+			}
+		}
+		else
+		{
+			if (!DatumGetBool(thisresult))
+			{
+				result = BoolGetDatum(false);
+				resultnull = false;
+				break;			/* needn't look at any more elements */
+			}
+		}
+
+		/* advance bitmap pointer if any */
+		if (bitmap)
+		{
+			bitmask <<= 1;
+			if (bitmask == 0x100)
+			{
+				bitmap++;
+				bitmask = 1;
+			}
+		}
+	}
+
+	*op->resnull = resultnull;
+	*op->resvalue = result;
+}
+
+void
+ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
+{
+	if (*op->resnull)
+		ereport(ERROR,
+				(errcode(ERRCODE_NOT_NULL_VIOLATION),
+				 errmsg("domain %s does not allow null values",
+						format_type_be(op->d.domaincheck.resulttype)),
+				 errdatatype(op->d.domaincheck.resulttype)));
+}
+
+void
+ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
+{
+	if (!*op->d.domaincheck.checknull &&
+		!DatumGetBool(*op->d.domaincheck.checkvalue))
+		ereport(ERROR,
+				(errcode(ERRCODE_CHECK_VIOLATION),
+				 errmsg("value for domain %s violates check constraint \"%s\"",
+						format_type_be(op->d.domaincheck.resulttype),
+						op->d.domaincheck.constraintname),
+				 errdomainconstraint(op->d.domaincheck.resulttype,
+									 op->d.domaincheck.constraintname)));
+}
+
+/*
+ * Evaluate the various forms of XmlExpr.
+ */
+void
+ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
+{
+	XmlExpr    *xexpr = op->d.xmlexpr.xexpr;
+	Datum		value;
+	ListCell   *arg;
+	ListCell   *narg;
+	int i;
+
+	*op->resnull = true;		/* until we get a result */
+	*op->resvalue = (Datum) 0;
+
+	switch (xexpr->op)
+	{
+		case IS_XMLCONCAT:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+				List	   *values = NIL;
+
+				for (i = 0; i < list_length(xexpr->args); i++)
+				{
+					if (!argnull[i])
+						values = lappend(values, DatumGetPointer(argvalue[i]));
+				}
+
+				if (list_length(values) > 0)
+				{
+					*op->resnull = false;
+					*op->resvalue = PointerGetDatum(xmlconcat(values));
+				}
+			}
+			break;
+
+		case IS_XMLFOREST:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.named_argvalue;
+				bool	   *argnull = op->d.xmlexpr.named_argnull;
+				StringInfoData buf;
+
+				initStringInfo(&buf);
+
+				i = 0;
+				forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
+				{
+					char	   *argname = strVal(lfirst(narg));
+					Expr	   *e = (Expr *) lfirst(arg);
+
+					if (!argnull[i])
+					{
+						value = argvalue[i];
+						appendStringInfo(&buf, "<%s>%s</%s>",
+										 argname,
+										 map_sql_value_to_xml_value(value, exprType((Node *) e), true),
+										 argname);
+						*op->resnull = false;
+					}
+					i++;
+				}
+
+				if (*op->resnull)
+				{
+					pfree(buf.data);
+				}
+				else
+				{
+					text	   *result;
+
+					result = cstring_to_text_with_len(buf.data, buf.len);
+					pfree(buf.data);
+
+					*op->resvalue = PointerGetDatum(result);
+				}
+			}
+			break;
+
+		case IS_XMLELEMENT:
+			*op->resnull = false;
+			*op->resvalue = PointerGetDatum(
+				xmlelement(xexpr,
+						   op->d.xmlexpr.named_argnull,
+						   op->d.xmlexpr.named_argvalue,
+						   op->d.xmlexpr.argnull,
+						   op->d.xmlexpr.argvalue));
+			break;
+		case IS_XMLPARSE:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+				text	   *data;
+				bool		preserve_whitespace;
+
+				/* arguments are known to be text, bool */
+				Assert(list_length(xexpr->args) == 2);
+
+				if (argnull[0])
+					return;
+				value = argvalue[0];
+				data = DatumGetTextP(value);
+
+				if (argnull[1])		/* probably can't happen */
+					return;
+				value = argvalue[1];
+				preserve_whitespace = DatumGetBool(value);
+
+				*op->resnull = false;
+				*op->resvalue = PointerGetDatum(
+					xmlparse(data, xexpr->xmloption, preserve_whitespace));
+			}
+			break;
+
+		case IS_XMLPI:
+			{
+				text	   *arg;
+				bool		isnull;
+
+				/* optional argument is known to be text */
+				Assert(list_length(xexpr->args) <= 1);
+
+				if (xexpr->args)
+				{
+					isnull = op->d.xmlexpr.argnull[0];
+					if (isnull)
+						arg = NULL;
+					else
+						arg = DatumGetTextP(op->d.xmlexpr.argvalue[0]);
+
+				}
+				else
+				{
+					arg = NULL;
+					isnull = false;
+				}
+
+				*op->resvalue = PointerGetDatum(xmlpi(xexpr->name, arg,
+													  isnull, op->resnull));
+			}
+			break;
+
+		case IS_XMLROOT:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+				xmltype    *data;
+				text	   *version;
+				int			standalone;
+
+				/* arguments are known to be xml, text, int */
+				Assert(list_length(xexpr->args) == 3);
+
+				if (argnull[0])
+					return;
+				data = DatumGetXmlP(argvalue[0]);
+
+				if (argnull[1])
+					version = NULL;
+				else
+					version = DatumGetTextP(argvalue[1]);
+
+				Assert(!argnull[2]); /* always present */
+				standalone = DatumGetInt32(argvalue[2]);
+
+				*op->resnull = false;
+				*op->resvalue = PointerGetDatum(xmlroot(data,
+														version,
+														standalone));
+			}
+			break;
+
+		case IS_XMLSERIALIZE:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+
+				/* argument type is known to be xml */
+				Assert(list_length(xexpr->args) == 1);
+
+				if (argnull[0])
+					return;
+
+				value = argvalue[0];
+				*op->resnull = false;
+				*op->resvalue = PointerGetDatum(
+					xmltotext_with_xmloption(DatumGetXmlP(value),
+											 xexpr->xmloption));
+			}
+			break;
+
+		case IS_DOCUMENT:
+			{
+				Datum	   *argvalue = op->d.xmlexpr.argvalue;
+				bool	   *argnull = op->d.xmlexpr.argnull;
+
+				/* optional argument is known to be xml */
+				Assert(list_length(xexpr->args) == 1);
+
+				if (argnull[0])
+					break;
+
+				value = argvalue[0];
+				*op->resnull = false;
+				*op->resvalue =
+					BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+			}
+			break;
+		default:
+			elog(ERROR, "unrecognized XML operation");
+	}
+}
+
+/*
+ * ExecEvalGroupingFunc
+ *
+ * Computes a bitmask with a bit for each (unevaluated) argument expression
+ * (rightmost arg is least significant bit).
+ *
+ * A bit is set if the corresponding expression is NOT part of the set of
+ * grouping expressions in the current grouping set.
+ */
+void
+ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op)
+{
+	int			result = 0;
+	int			attnum = 0;
+	Bitmapset  *grouped_cols = op->d.grouping_func.parent->grouped_cols;
+	ListCell   *lc;
+
+	*op->resnull = false;
+
+	foreach(lc, op->d.grouping_func.clauses)
+	{
+		attnum = lfirst_int(lc);
+
+		result = result << 1;
+
+		if (!bms_is_member(attnum, grouped_cols))
+			result = result | 1;
+	}
+
+	*op->resvalue = Int32GetDatum(result);
+}
+
+/*
+ * Hand off evaluation of a subplan to nodeSubplan.c
+ */
+void
+ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	SubPlanState *sstate = op->d.subplan.sstate;
+
+	*op->resvalue = ExecSubPlan(sstate, econtext, op->resnull);
+}
+
+/*
+ * Hand off evaluation of an alternative subplan to nodeSubplan.c
+ */
+void
+ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	AlternativeSubPlanState *asstate = op->d.alternative_subplan.asstate;
+
+	*op->resvalue = ExecAlternativeSubPlan(asstate, econtext, op->resnull);
+}
+
+/*
+ * Evaluate a wholerow Var expression.
+ */
+void
+ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	Var		   *variable = op->d.wholerow.var;
+	TupleTableSlot *slot;
+	TupleDesc	output_tupdesc;
+	MemoryContext oldcontext;
+	bool		needslow = false;
+	HeapTupleHeader dtuple;
+	HeapTuple	tuple;
+
+	/* This was checked by ExecInitExpr */
+	Assert(variable->varattno == InvalidAttrNumber);
+
+	/* Get the input slot we want */
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = econtext->ecxt_scantuple;
+			break;
+	}
+
+
+	/* Apply the junkfilter if any */
+	if (op->d.wholerow.junkFilter != NULL)
+		slot = ExecFilterJunk(op->d.wholerow.junkFilter, slot);
+
+	/*
+	 * First time through, perform initialization.
+	 *
+	 * XXX: It'd be great if this could be moved to the the expression
+	 * initialization phase, but due to using slots that's currently not
+	 * feasible.
+	 */
+	if (op->d.wholerow.first)
+	{
+		/*
+		 * If the Var identifies a named composite type, we must check that the
+		 * actual tuple type is compatible with it.
+		 */
+		if (variable->vartype != RECORDOID)
+		{
+			TupleDesc	var_tupdesc;
+			TupleDesc	slot_tupdesc;
+			int			i;
+
+			/*
+			 * We really only care about numbers of attributes and data types.
+			 * Also, we can ignore type mismatch on columns that are dropped in
+			 * the destination type, so long as (1) the physical storage matches
+			 * or (2) the actual column value is NULL.  Case (1) is helpful in
+			 * some cases involving out-of-date cached plans, while case (2) is
+			 * expected behavior in situations such as an INSERT into a table with
+			 * dropped columns (the planner typically generates an INT4 NULL
+			 * regardless of the dropped column type).  If we find a dropped
+			 * column and cannot verify that case (1) holds, we have to use
+			 * ExecEvalWholeRowSlow to check (2) for each row.
+			 */
+			var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+
+			slot_tupdesc = slot->tts_tupleDescriptor;
+
+			if (var_tupdesc->natts != slot_tupdesc->natts)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("table row type and query-specified row type do not match"),
+						 errdetail_plural("Table row contains %d attribute, but query expects %d.",
+										  "Table row contains %d attributes, but query expects %d.",
+										  slot_tupdesc->natts,
+										  slot_tupdesc->natts,
+										  var_tupdesc->natts)));
+
+			for (i = 0; i < var_tupdesc->natts; i++)
+			{
+				Form_pg_attribute vattr = var_tupdesc->attrs[i];
+				Form_pg_attribute sattr = slot_tupdesc->attrs[i];
+
+				if (vattr->atttypid == sattr->atttypid)
+					continue;		/* no worries */
+				if (!vattr->attisdropped)
+					ereport(ERROR,
+							(errcode(ERRCODE_DATATYPE_MISMATCH),
+							 errmsg("table row type and query-specified row type do not match"),
+							 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
+									   format_type_be(sattr->atttypid),
+									   i + 1,
+									   format_type_be(vattr->atttypid))));
+
+				if (vattr->attlen != sattr->attlen ||
+					vattr->attalign != sattr->attalign)
+					needslow = true;	/* need runtime check for null */
+			}
+
+			/*
+			 * Use the variable's declared rowtype as the descriptor for the
+			 * output values, modulo possibly assigning new column names below. In
+			 * particular, we *must* absorb any attisdropped markings.
+			 */
+			oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+			output_tupdesc = CreateTupleDescCopy(var_tupdesc);
+			MemoryContextSwitchTo(oldcontext);
+
+			ReleaseTupleDesc(var_tupdesc);
+		}
+		else
+		{
+			/*
+			 * In the RECORD case, we use the input slot's rowtype as the
+			 * descriptor for the output values, modulo possibly assigning new
+			 * column names below.
+			 */
+			oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+			output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+			MemoryContextSwitchTo(oldcontext);
+		}
+
+		/*
+		 * Construct a tuple descriptor for the composite values we'll produce,
+		 * and make sure its record type is "blessed".  The main reason to do this
+		 * is to be sure that operations such as row_to_json() will see the
+		 * desired column names when they look up the descriptor from the type
+		 * information embedded in the composite values.
+		 *
+		 * We already got the correct physical datatype info above, but now we
+		 * should try to find the source RTE and adopt its column aliases, in case
+		 * they are different from the original rowtype's names.  For example, in
+		 * "SELECT foo(t) FROM tab t(x,y)", the first two columns in the composite
+		 * output should be named "x" and "y" regardless of tab's column names.
+		 *
+		 * If we can't locate the RTE, assume the column names we've got are OK.
+		 * (As of this writing, the only cases where we can't locate the RTE are
+		 * in execution of trigger WHEN clauses, and then the Var will have the
+		 * trigger's relation's rowtype, so its names are fine.)  Also, if the
+		 * creator of the RTE didn't bother to fill in an eref field, assume our
+		 * column names are OK.  (This happens in COPY, and perhaps other places.)
+		 */
+		if (econtext->ecxt_estate &&
+			variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
+		{
+			RangeTblEntry *rte = rt_fetch(variable->varno,
+										  econtext->ecxt_estate->es_range_table);
+
+			if (rte->eref)
+				ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
+		}
+
+		/* Bless the tupdesc if needed, and save it in the execution state */
+		op->d.wholerow.tupdesc = BlessTupleDesc(output_tupdesc);
+
+		op->d.wholerow.first = false;
+	}
+
+	if (needslow)
+	{
+		TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+		TupleDesc var_tupdesc = op->d.wholerow.tupdesc;
+		int i;
+
+		tuple = ExecFetchSlotTuple(slot);
+
+
+		/* Check to see if any dropped attributes are non-null */
+		for (i = 0; i < var_tupdesc->natts; i++)
+		{
+			Form_pg_attribute vattr = var_tupdesc->attrs[i];
+			Form_pg_attribute sattr = tupleDesc->attrs[i];
+
+			if (!vattr->attisdropped)
+				continue;			/* already checked non-dropped cols */
+			if (heap_attisnull(tuple, i + 1))
+				continue;			/* null is always okay */
+			if (vattr->attlen != sattr->attlen ||
+				vattr->attalign != sattr->attalign)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("table row type and query-specified row type do not match"),
+						 errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
+								   i + 1)));
+		}
+	}
+
+	/*
+	 * Copy the slot tuple and make sure any toasted fields get detoasted.
+	 */
+	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
+
+	/*
+	 * Label the datum with the composite type info we identified before.
+	 */
+	HeapTupleHeaderSetTypeId(dtuple, op->d.wholerow.tupdesc->tdtypeid);
+	HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod);
+
+	*op->resnull = false;
+	*op->resvalue = PointerGetDatum(dtuple);
+}
+
+/*
+ * Callback function to release a tupdesc refcount at expression tree shutdown
+ */
+static void
+ShutdownTupleDescRef(Datum arg)
+{
+	TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
+
+	if (*cache_field)
+		ReleaseTupleDesc(*cache_field);
+	*cache_field = NULL;
+}
+
+/*
+ * get_cached_rowtype: utility function to lookup a rowtype tupdesc
+ *
+ * type_id, typmod: identity of the rowtype
+ * cache_field: where to cache the TupleDesc pointer in expression state node
+ *		(field must be initialized to NULL)
+ * econtext: expression context we are executing in
+ *
+ * NOTE: because the shutdown callback will be called during plan rescan,
+ * must be prepared to re-do this during any node execution; cannot call
+ * just once during expression initialization
+ */
+static TupleDesc
+get_cached_rowtype(Oid type_id, int32 typmod,
+				   TupleDesc *cache_field, ExprContext *econtext)
+{
+	TupleDesc	tupDesc = *cache_field;
+
+	/* Do lookup if no cached value or if requested type changed */
+	if (tupDesc == NULL ||
+		type_id != tupDesc->tdtypeid ||
+		typmod != tupDesc->tdtypmod)
+	{
+		tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
+
+		if (*cache_field)
+		{
+			/* Release old tupdesc; but callback is already registered */
+			ReleaseTupleDesc(*cache_field);
+		}
+		else
+		{
+			/* Need to register shutdown callback to release tupdesc */
+			RegisterExprContextCallback(econtext,
+										ShutdownTupleDescRef,
+										PointerGetDatum(cache_field));
+		}
+		*cache_field = tupDesc;
+	}
+	return tupDesc;
+}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index f5cd65d8a0..ede675fa61 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1246,7 +1246,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1689,7 +1689,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1701,13 +1700,15 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState **) palloc(ncheck * sizeof(ExprState *));
 		for (i = 0; i < ncheck; i++)
 		{
-			/* ExecQual wants implicit-AND form */
+			List	   *qual;
+
+			/* ExecPrepareQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] =
+				ExecPrepareCheck(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1724,14 +1725,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState  *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
-		 * is not to be treated as a failure.  Therefore, tell ExecQual to
-		 * return TRUE for NULL.
+		 * is not to be treated as a failure.  Therefore, use ExecCheck not
+		 * ExecQual.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual, econtext))
 			return check[i].ccname;
 	}
 
@@ -1759,8 +1760,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 	{
 		List	   *qual = resultRelInfo->ri_PartitionCheck;
 
-		resultRelInfo->ri_PartitionCheckExpr = (List *)
-			ExecPrepareExpr((Expr *) qual, estate);
+		resultRelInfo->ri_PartitionCheckExpr = ExecPrepareCheck(qual, estate);
 	}
 
 	/*
@@ -1776,7 +1776,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 	 * As in case of the catalogued constraints, we treat a NULL result as
 	 * success here, not a failure.
 	 */
-	return ExecQual(resultRelInfo->ri_PartitionCheckExpr, econtext, true);
+	return ExecCheck(resultRelInfo->ri_PartitionCheckExpr, econtext);
 }
 
 /*
@@ -1956,11 +1956,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * is visible (in the case of a view) or that it passes the
 		 * 'with-check' policy (in the case of row security). If the qual
 		 * evaluates to NULL or FALSE, then the new tuple won't be included in
-		 * the view or doesn't pass the 'with-check' policy for the table.  We
-		 * need ExecQual to return FALSE for NULL to handle the view case (the
-		 * opposite of what we do above for CHECK constraints).
+		 * the view or doesn't pass the 'with-check' policy for the table.
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr, econtext))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 4ff0188bcc..d250627bbe 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * execQual.c
- *	  Routines to evaluate qualification and targetlist expressions
+ *	  Former Routines to evaluate qualification and targetlist expressions
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -12,28 +12,6 @@
  *
  *-------------------------------------------------------------------------
  */
-/*
- *	 INTERFACE ROUTINES
- *		ExecEvalExpr	- (now a macro) evaluate an expression, return a datum
- *		ExecEvalExprSwitchContext - same, but switch into eval memory context
- *		ExecQual		- return true/false if qualification is satisfied
- *		ExecProject		- form a new tuple by projecting the given tuple
- *
- *	 NOTES
- *		The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar,
- *		are hotspots. Making these faster will speed up the entire system.
- *
- *		ExecProject() is used to make tuple projections.  Rather then
- *		trying to speed it up, the execution plan should be pre-processed
- *		to facilitate attribute sharing between nodes wherever possible,
- *		instead of doing needless copying.  -cim 5/31/91
- *
- *		During expression evaluation, we check_stack_depth only in
- *		ExecMakeFunctionResultSet/ExecMakeFunctionResultNoSets rather than at
- *		every single node.  This is a compromise that trades off precision of
- *		the stack limit setting to gain speed.
- */
-
 #include "postgres.h"
 
 #include "access/htup_details.h"
@@ -62,1091 +40,18 @@
 
 
 /* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
-				 ExprContext *econtext,
-				 bool *isNull);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
-static Datum ExecEvalAggref(AggrefExprState *aggref,
-			   ExprContext *econtext,
-			   bool *isNull);
-static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc,
-				   ExprContext *econtext,
-				   bool *isNull);
-static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull);
-static Datum ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull);
-static Datum ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate,
-					 ExprContext *econtext,
-					 bool *isNull);
-static Datum ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate,
-					 ExprContext *econtext,
-					 bool *isNull);
-static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-			  bool *isNull);
-static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull);
-static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-					bool *isNull);
-static void init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-			MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF);
-static void ShutdownFuncExpr(Datum arg);
-static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
-				   TupleDesc *cache_field, ExprContext *econtext);
-static void ShutdownTupleDescRef(Datum arg);
+static void init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+			MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF);
+static void ShutdownSetExpr(Datum arg);
 static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 				 List *argList, ExprContext *econtext);
-static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
+static void ExecPrepareTuplestoreResult(SetExprState *sexpr,
 							ExprContext *econtext,
 							Tuplestorestate *resultStore,
 							TupleDesc resultDesc);
 static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
-static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
-							 ExprContext *econtext,
-							 bool *isNull);
-static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
-			 bool *isNull);
-static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
-			 bool *isNull);
-static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
-				 bool *isNull);
-static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
-					  ExprContext *econtext,
-					  bool *isNull);
-static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-		   bool *isNull);
-static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-					   ExprContext *econtext,
-					   bool *isNull);
-static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-			 bool *isNull);
-static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
-					 ExprContext *econtext,
-					 bool *isNull);
-static Datum ExecEvalArray(ArrayExprState *astate,
-			  ExprContext *econtext,
-			  bool *isNull);
-static Datum ExecEvalRow(RowExprState *rstate,
-			ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
-				   ExprContext *econtext,
-				   bool *isNull);
-static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
-				 ExprContext *econtext,
-				 bool *isNull);
-static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
-			   ExprContext *econtext,
-			   bool *isNull);
-static Datum ExecEvalSQLValueFunction(ExprState *svfExpr,
-						 ExprContext *econtext,
-						 bool *isNull);
-static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-			bool *isNull);
-static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
-			   ExprContext *econtext,
-			   bool *isNull);
-static Datum ExecEvalNullTest(NullTestState *nstate,
-				 ExprContext *econtext,
-				 bool *isNull);
-static Datum ExecEvalBooleanTest(GenericExprState *bstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
-					   ExprContext *econtext,
-					   bool *isNull);
-static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
-							ExprContext *econtext,
-							bool *isNull);
-static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalFieldStore(FieldStoreState *fstate,
-				   ExprContext *econtext,
-				   bool *isNull);
-static Datum ExecEvalRelabelType(GenericExprState *exprstate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
-					ExprContext *econtext,
-					bool *isNull);
-static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
-						ExprContext *econtext,
-						bool *isNull);
-static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull);
-static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
-						 ExprContext *econtext,
-						 bool *isNull);
 
 
-/* ----------------------------------------------------------------
- *		ExecEvalExpr routines
- *
- *		Recursively evaluate a targetlist or qualification expression.
- *
- * Each of the following routines having the signature
- *		Datum ExecEvalFoo(ExprState *expression,
- *						  ExprContext *econtext,
- *						  bool *isNull);
- * is responsible for evaluating one type or subtype of ExprState node.
- * They are normally called via the ExecEvalExpr macro, which makes use of
- * the function pointer set up when the ExprState node was built by
- * ExecInitExpr.  (In some cases, we change this pointer later to avoid
- * re-executing one-time overhead.)
- *
- * Note: for notational simplicity we declare these functions as taking the
- * specific type of ExprState that they work on.  This requires casting when
- * assigning the function pointer in ExecInitExpr.  Be careful that the
- * function signature is declared correctly, because the cast suppresses
- * automatic checking!
- *
- *
- * All these functions share this calling convention:
- *
- * Inputs:
- *		expression: the expression state tree to evaluate
- *		econtext: evaluation context information
- *
- * Outputs:
- *		return value: Datum value of result
- *		*isNull: set to TRUE if result is NULL (actual return value is
- *				 meaningless if so); set to FALSE if non-null result
- *
- * The caller should already have switched into the temporary memory
- * context econtext->ecxt_per_tuple_memory.  The convenience entry point
- * ExecEvalExprSwitchContext() is provided for callers who don't prefer to
- * do the switch in an outer loop.  We do not do the switch in these routines
- * because it'd be a waste of cycles during nested expression evaluation.
- * ----------------------------------------------------------------
- */
-
-
-/*----------
- *	  ExecEvalArrayRef
- *
- *	   This function takes an ArrayRef and returns the extracted Datum
- *	   if it's a simple reference, or the modified array value if it's
- *	   an array assignment (i.e., array element or slice insertion).
- *
- * NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
- *----------
- */
-static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
-				 ExprContext *econtext,
-				 bool *isNull)
-{
-	ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
-	Datum		array_source;
-	bool		isAssignment = (arrayRef->refassgnexpr != NULL);
-	bool		eisnull;
-	ListCell   *l;
-	int			i = 0,
-				j = 0;
-	IntArray	upper,
-				lower;
-	bool		upperProvided[MAXDIM],
-				lowerProvided[MAXDIM];
-	int		   *lIndex;
-
-	array_source = ExecEvalExpr(astate->refexpr,
-								econtext,
-								isNull);
-
-	/*
-	 * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
-	 * assignment case, we'll cons up something below.
-	 */
-	if (*isNull)
-	{
-		if (!isAssignment)
-			return (Datum) NULL;
-	}
-
-	foreach(l, astate->refupperindexpr)
-	{
-		ExprState  *eltstate = (ExprState *) lfirst(l);
-
-		if (i >= MAXDIM)
-			ereport(ERROR,
-					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-					 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
-							i + 1, MAXDIM)));
-
-		if (eltstate == NULL)
-		{
-			/* Slice bound is omitted, so use array's upper bound */
-			Assert(astate->reflowerindexpr != NIL);
-			upperProvided[i++] = false;
-			continue;
-		}
-		upperProvided[i] = true;
-
-		upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
-													 econtext,
-													 &eisnull));
-		/* If any index expr yields NULL, result is NULL or error */
-		if (eisnull)
-		{
-			if (isAssignment)
-				ereport(ERROR,
-						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-				  errmsg("array subscript in assignment must not be null")));
-			*isNull = true;
-			return (Datum) NULL;
-		}
-	}
-
-	if (astate->reflowerindexpr != NIL)
-	{
-		foreach(l, astate->reflowerindexpr)
-		{
-			ExprState  *eltstate = (ExprState *) lfirst(l);
-
-			if (j >= MAXDIM)
-				ereport(ERROR,
-						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-						 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
-								j + 1, MAXDIM)));
-
-			if (eltstate == NULL)
-			{
-				/* Slice bound is omitted, so use array's lower bound */
-				lowerProvided[j++] = false;
-				continue;
-			}
-			lowerProvided[j] = true;
-
-			lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
-														 econtext,
-														 &eisnull));
-			/* If any index expr yields NULL, result is NULL or error */
-			if (eisnull)
-			{
-				if (isAssignment)
-					ereport(ERROR,
-							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-							 errmsg("array subscript in assignment must not be null")));
-				*isNull = true;
-				return (Datum) NULL;
-			}
-		}
-		/* this can't happen unless parser messed up */
-		if (i != j)
-			elog(ERROR, "upper and lower index lists are not same length");
-		lIndex = lower.indx;
-	}
-	else
-		lIndex = NULL;
-
-	if (isAssignment)
-	{
-		Datum		sourceData;
-		Datum		save_datum;
-		bool		save_isNull;
-
-		/*
-		 * We might have a nested-assignment situation, in which the
-		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
-		 * obtain and modify the previous value of the array element or slice
-		 * being replaced.  If so, we have to extract that value from the
-		 * array and pass it down via the econtext's caseValue.  It's safe to
-		 * reuse the CASE mechanism because there cannot be a CASE between
-		 * here and where the value would be needed, and an array assignment
-		 * can't be within a CASE either.  (So saving and restoring the
-		 * caseValue is just paranoia, but let's do it anyway.)
-		 *
-		 * Since fetching the old element might be a nontrivial expense, do it
-		 * only if the argument appears to actually need it.
-		 */
-		save_datum = econtext->caseValue_datum;
-		save_isNull = econtext->caseValue_isNull;
-
-		if (isAssignmentIndirectionExpr(astate->refassgnexpr))
-		{
-			if (*isNull)
-			{
-				/* whole array is null, so any element or slice is too */
-				econtext->caseValue_datum = (Datum) 0;
-				econtext->caseValue_isNull = true;
-			}
-			else if (lIndex == NULL)
-			{
-				econtext->caseValue_datum =
-					array_get_element(array_source, i,
-									  upper.indx,
-									  astate->refattrlength,
-									  astate->refelemlength,
-									  astate->refelembyval,
-									  astate->refelemalign,
-									  &econtext->caseValue_isNull);
-			}
-			else
-			{
-				/* this is currently unreachable */
-				econtext->caseValue_datum =
-					array_get_slice(array_source, i,
-									upper.indx, lower.indx,
-									upperProvided, lowerProvided,
-									astate->refattrlength,
-									astate->refelemlength,
-									astate->refelembyval,
-									astate->refelemalign);
-				econtext->caseValue_isNull = false;
-			}
-		}
-		else
-		{
-			/* argument shouldn't need caseValue, but for safety set it null */
-			econtext->caseValue_datum = (Datum) 0;
-			econtext->caseValue_isNull = true;
-		}
-
-		/*
-		 * Evaluate the value to be assigned into the array.
-		 */
-		sourceData = ExecEvalExpr(astate->refassgnexpr,
-								  econtext,
-								  &eisnull);
-
-		econtext->caseValue_datum = save_datum;
-		econtext->caseValue_isNull = save_isNull;
-
-		/*
-		 * For an assignment to a fixed-length array type, both the original
-		 * array and the value to be assigned into it must be non-NULL, else
-		 * we punt and return the original array.
-		 */
-		if (astate->refattrlength > 0)	/* fixed-length array? */
-			if (eisnull || *isNull)
-				return array_source;
-
-		/*
-		 * For assignment to varlena arrays, we handle a NULL original array
-		 * by substituting an empty (zero-dimensional) array; insertion of the
-		 * new element will result in a singleton array value.  It does not
-		 * matter whether the new element is NULL.
-		 */
-		if (*isNull)
-		{
-			array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
-			*isNull = false;
-		}
-
-		if (lIndex == NULL)
-			return array_set_element(array_source, i,
-									 upper.indx,
-									 sourceData,
-									 eisnull,
-									 astate->refattrlength,
-									 astate->refelemlength,
-									 astate->refelembyval,
-									 astate->refelemalign);
-		else
-			return array_set_slice(array_source, i,
-								   upper.indx, lower.indx,
-								   upperProvided, lowerProvided,
-								   sourceData,
-								   eisnull,
-								   astate->refattrlength,
-								   astate->refelemlength,
-								   astate->refelembyval,
-								   astate->refelemalign);
-	}
-
-	if (lIndex == NULL)
-		return array_get_element(array_source, i,
-								 upper.indx,
-								 astate->refattrlength,
-								 astate->refelemlength,
-								 astate->refelembyval,
-								 astate->refelemalign,
-								 isNull);
-	else
-		return array_get_slice(array_source, i,
-							   upper.indx, lower.indx,
-							   upperProvided, lowerProvided,
-							   astate->refattrlength,
-							   astate->refelemlength,
-							   astate->refelembyval,
-							   astate->refelemalign);
-}
-
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
-	if (exprstate == NULL)
-		return false;			/* just paranoia */
-	if (IsA(exprstate, FieldStoreState))
-	{
-		FieldStore *fstore = (FieldStore *) exprstate->expr;
-
-		if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
-			return true;
-	}
-	else if (IsA(exprstate, ArrayRefExprState))
-	{
-		ArrayRef   *arrayRef = (ArrayRef *) exprstate->expr;
-
-		if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
-			return true;
-	}
-	return false;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalAggref
- *
- *		Returns a Datum whose value is the value of the precomputed
- *		aggregate found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
-			   bool *isNull)
-{
-	if (econtext->ecxt_aggvalues == NULL)		/* safety check */
-		elog(ERROR, "no aggregates in this expression context");
-
-	*isNull = econtext->ecxt_aggnulls[aggref->aggno];
-	return econtext->ecxt_aggvalues[aggref->aggno];
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWindowFunc
- *
- *		Returns a Datum whose value is the value of the precomputed
- *		window function found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
-				   bool *isNull)
-{
-	if (econtext->ecxt_aggvalues == NULL)		/* safety check */
-		elog(ERROR, "no window functions in this expression context");
-
-	*isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
-	return econtext->ecxt_aggvalues[wfunc->wfuncno];
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalScalarVar
- *
- *		Returns a Datum whose value is the value of a scalar (not whole-row)
- *		range variable with respect to given expression context.
- *
- * Note: ExecEvalScalarVar is executed only the first time through in a given
- * plan; it changes the ExprState's function pointer to pass control directly
- * to ExecEvalScalarVarFast after making one-time checks.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull)
-{
-	Var		   *variable = (Var *) exprstate->expr;
-	TupleTableSlot *slot;
-	AttrNumber	attnum;
-
-	/* Get the input slot and attribute number we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	attnum = variable->varattno;
-
-	/* This was checked by ExecInitExpr */
-	Assert(attnum != InvalidAttrNumber);
-
-	/*
-	 * If it's a user attribute, check validity (bogus system attnums will be
-	 * caught inside slot_getattr).  What we have to check for here is the
-	 * possibility of an attribute having been changed in type since the plan
-	 * tree was created.  Ideally the plan will get invalidated and not
-	 * re-used, but just in case, we keep these defenses.  Fortunately it's
-	 * sufficient to check once on the first time through.
-	 *
-	 * Note: we allow a reference to a dropped attribute.  slot_getattr will
-	 * force a NULL result in such cases.
-	 *
-	 * Note: ideally we'd check typmod as well as typid, but that seems
-	 * impractical at the moment: in many cases the tupdesc will have been
-	 * generated by ExecTypeFromTL(), and that can't guarantee to generate an
-	 * accurate typmod in all cases, because some expression node types don't
-	 * carry typmod.
-	 */
-	if (attnum > 0)
-	{
-		TupleDesc	slot_tupdesc = slot->tts_tupleDescriptor;
-		Form_pg_attribute attr;
-
-		if (attnum > slot_tupdesc->natts)		/* should never happen */
-			elog(ERROR, "attribute number %d exceeds number of columns %d",
-				 attnum, slot_tupdesc->natts);
-
-		attr = slot_tupdesc->attrs[attnum - 1];
-
-		/* can't check type if dropped, since atttypid is probably 0 */
-		if (!attr->attisdropped)
-		{
-			if (variable->vartype != attr->atttypid)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("attribute %d has wrong type", attnum),
-						 errdetail("Table has type %s, but query expects %s.",
-								   format_type_be(attr->atttypid),
-								   format_type_be(variable->vartype))));
-		}
-	}
-
-	/* Skip the checking on future executions of node */
-	exprstate->evalfunc = ExecEvalScalarVarFast;
-
-	/* Fetch the value from the slot */
-	return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalScalarVarFast
- *
- *		Returns a Datum for a scalar variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull)
-{
-	Var		   *variable = (Var *) exprstate->expr;
-	TupleTableSlot *slot;
-	AttrNumber	attnum;
-
-	/* Get the input slot and attribute number we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	attnum = variable->varattno;
-
-	/* Fetch the value from the slot */
-	return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWholeRowVar
- *
- *		Returns a Datum whose value is the value of a whole-row range
- *		variable with respect to given expression context.
- *
- * Note: ExecEvalWholeRowVar is executed only the first time through in a
- * given plan; it changes the ExprState's function pointer to pass control
- * directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making
- * one-time checks.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					bool *isNull)
-{
-	Var		   *variable = (Var *) wrvstate->xprstate.expr;
-	TupleTableSlot *slot;
-	TupleDesc	output_tupdesc;
-	MemoryContext oldcontext;
-	bool		needslow = false;
-
-	/* This was checked by ExecInitExpr */
-	Assert(variable->varattno == InvalidAttrNumber);
-
-	/* Get the input slot we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	/*
-	 * If the input tuple came from a subquery, it might contain "resjunk"
-	 * columns (such as GROUP BY or ORDER BY columns), which we don't want to
-	 * keep in the whole-row result.  We can get rid of such columns by
-	 * passing the tuple through a JunkFilter --- but to make one, we have to
-	 * lay our hands on the subquery's targetlist.  Fortunately, there are not
-	 * very many cases where this can happen, and we can identify all of them
-	 * by examining our parent PlanState.  We assume this is not an issue in
-	 * standalone expressions that don't have parent plans.  (Whole-row Vars
-	 * can occur in such expressions, but they will always be referencing
-	 * table rows.)
-	 */
-	if (wrvstate->parent)
-	{
-		PlanState  *subplan = NULL;
-
-		switch (nodeTag(wrvstate->parent))
-		{
-			case T_SubqueryScanState:
-				subplan = ((SubqueryScanState *) wrvstate->parent)->subplan;
-				break;
-			case T_CteScanState:
-				subplan = ((CteScanState *) wrvstate->parent)->cteplanstate;
-				break;
-			default:
-				break;
-		}
-
-		if (subplan)
-		{
-			bool		junk_filter_needed = false;
-			ListCell   *tlist;
-
-			/* Detect whether subplan tlist actually has any junk columns */
-			foreach(tlist, subplan->plan->targetlist)
-			{
-				TargetEntry *tle = (TargetEntry *) lfirst(tlist);
-
-				if (tle->resjunk)
-				{
-					junk_filter_needed = true;
-					break;
-				}
-			}
-
-			/* If so, build the junkfilter in the query memory context */
-			if (junk_filter_needed)
-			{
-				oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-				wrvstate->wrv_junkFilter =
-					ExecInitJunkFilter(subplan->plan->targetlist,
-									   ExecGetResultType(subplan)->tdhasoid,
-							ExecInitExtraTupleSlot(wrvstate->parent->state));
-				MemoryContextSwitchTo(oldcontext);
-			}
-		}
-	}
-
-	/* Apply the junkfilter if any */
-	if (wrvstate->wrv_junkFilter != NULL)
-		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-	/*
-	 * If the Var identifies a named composite type, we must check that the
-	 * actual tuple type is compatible with it.
-	 */
-	if (variable->vartype != RECORDOID)
-	{
-		TupleDesc	var_tupdesc;
-		TupleDesc	slot_tupdesc;
-		int			i;
-
-		/*
-		 * We really only care about numbers of attributes and data types.
-		 * Also, we can ignore type mismatch on columns that are dropped in
-		 * the destination type, so long as (1) the physical storage matches
-		 * or (2) the actual column value is NULL.  Case (1) is helpful in
-		 * some cases involving out-of-date cached plans, while case (2) is
-		 * expected behavior in situations such as an INSERT into a table with
-		 * dropped columns (the planner typically generates an INT4 NULL
-		 * regardless of the dropped column type).  If we find a dropped
-		 * column and cannot verify that case (1) holds, we have to use
-		 * ExecEvalWholeRowSlow to check (2) for each row.
-		 */
-		var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
-
-		slot_tupdesc = slot->tts_tupleDescriptor;
-
-		if (var_tupdesc->natts != slot_tupdesc->natts)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("table row type and query-specified row type do not match"),
-					 errdetail_plural("Table row contains %d attribute, but query expects %d.",
-				   "Table row contains %d attributes, but query expects %d.",
-									  slot_tupdesc->natts,
-									  slot_tupdesc->natts,
-									  var_tupdesc->natts)));
-
-		for (i = 0; i < var_tupdesc->natts; i++)
-		{
-			Form_pg_attribute vattr = var_tupdesc->attrs[i];
-			Form_pg_attribute sattr = slot_tupdesc->attrs[i];
-
-			if (vattr->atttypid == sattr->atttypid)
-				continue;		/* no worries */
-			if (!vattr->attisdropped)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("table row type and query-specified row type do not match"),
-						 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
-								   format_type_be(sattr->atttypid),
-								   i + 1,
-								   format_type_be(vattr->atttypid))));
-
-			if (vattr->attlen != sattr->attlen ||
-				vattr->attalign != sattr->attalign)
-				needslow = true;	/* need runtime check for null */
-		}
-
-		/*
-		 * Use the variable's declared rowtype as the descriptor for the
-		 * output values, modulo possibly assigning new column names below. In
-		 * particular, we *must* absorb any attisdropped markings.
-		 */
-		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-		output_tupdesc = CreateTupleDescCopy(var_tupdesc);
-		MemoryContextSwitchTo(oldcontext);
-
-		ReleaseTupleDesc(var_tupdesc);
-	}
-	else
-	{
-		/*
-		 * In the RECORD case, we use the input slot's rowtype as the
-		 * descriptor for the output values, modulo possibly assigning new
-		 * column names below.
-		 */
-		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-		output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
-		MemoryContextSwitchTo(oldcontext);
-	}
-
-	/*
-	 * Construct a tuple descriptor for the composite values we'll produce,
-	 * and make sure its record type is "blessed".  The main reason to do this
-	 * is to be sure that operations such as row_to_json() will see the
-	 * desired column names when they look up the descriptor from the type
-	 * information embedded in the composite values.
-	 *
-	 * We already got the correct physical datatype info above, but now we
-	 * should try to find the source RTE and adopt its column aliases, in case
-	 * they are different from the original rowtype's names.  For example, in
-	 * "SELECT foo(t) FROM tab t(x,y)", the first two columns in the composite
-	 * output should be named "x" and "y" regardless of tab's column names.
-	 *
-	 * If we can't locate the RTE, assume the column names we've got are OK.
-	 * (As of this writing, the only cases where we can't locate the RTE are
-	 * in execution of trigger WHEN clauses, and then the Var will have the
-	 * trigger's relation's rowtype, so its names are fine.)  Also, if the
-	 * creator of the RTE didn't bother to fill in an eref field, assume our
-	 * column names are OK.  (This happens in COPY, and perhaps other places.)
-	 */
-	if (econtext->ecxt_estate &&
-		variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
-	{
-		RangeTblEntry *rte = rt_fetch(variable->varno,
-									  econtext->ecxt_estate->es_range_table);
-
-		if (rte->eref)
-			ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
-	}
-
-	/* Bless the tupdesc if needed, and save it in the execution state */
-	wrvstate->wrv_tupdesc = BlessTupleDesc(output_tupdesc);
-
-	/* Skip all the above on future executions of node */
-	if (needslow)
-		wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowSlow;
-	else
-		wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowFast;
-
-	/* Fetch the value */
-	return (*wrvstate->xprstate.evalfunc) ((ExprState *) wrvstate, econtext,
-										   isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWholeRowFast
- *
- *		Returns a Datum for a whole-row variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					 bool *isNull)
-{
-	Var		   *variable = (Var *) wrvstate->xprstate.expr;
-	TupleTableSlot *slot;
-	HeapTupleHeader dtuple;
-
-	*isNull = false;
-
-	/* Get the input slot we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	/* Apply the junkfilter if any */
-	if (wrvstate->wrv_junkFilter != NULL)
-		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-	/*
-	 * Copy the slot tuple and make sure any toasted fields get detoasted.
-	 */
-	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
-	/*
-	 * Label the datum with the composite type info we identified before.
-	 */
-	HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
-	HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
-	return PointerGetDatum(dtuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalWholeRowSlow
- *
- *		Returns a Datum for a whole-row variable, in the "slow" case where
- *		we can't just copy the subplan's output.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					 bool *isNull)
-{
-	Var		   *variable = (Var *) wrvstate->xprstate.expr;
-	TupleTableSlot *slot;
-	HeapTuple	tuple;
-	TupleDesc	tupleDesc;
-	TupleDesc	var_tupdesc;
-	HeapTupleHeader dtuple;
-	int			i;
-
-	*isNull = false;
-
-	/* Get the input slot we want */
-	switch (variable->varno)
-	{
-		case INNER_VAR: /* get the tuple from the inner node */
-			slot = econtext->ecxt_innertuple;
-			break;
-
-		case OUTER_VAR: /* get the tuple from the outer node */
-			slot = econtext->ecxt_outertuple;
-			break;
-
-			/* INDEX_VAR is handled by default case */
-
-		default:				/* get the tuple from the relation being
-								 * scanned */
-			slot = econtext->ecxt_scantuple;
-			break;
-	}
-
-	/* Apply the junkfilter if any */
-	if (wrvstate->wrv_junkFilter != NULL)
-		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-	tuple = ExecFetchSlotTuple(slot);
-	tupleDesc = slot->tts_tupleDescriptor;
-
-	/* wrv_tupdesc is a good enough representation of the Var's rowtype */
-	Assert(variable->vartype != RECORDOID);
-	var_tupdesc = wrvstate->wrv_tupdesc;
-
-	/* Check to see if any dropped attributes are non-null */
-	for (i = 0; i < var_tupdesc->natts; i++)
-	{
-		Form_pg_attribute vattr = var_tupdesc->attrs[i];
-		Form_pg_attribute sattr = tupleDesc->attrs[i];
-
-		if (!vattr->attisdropped)
-			continue;			/* already checked non-dropped cols */
-		if (heap_attisnull(tuple, i + 1))
-			continue;			/* null is always okay */
-		if (vattr->attlen != sattr->attlen ||
-			vattr->attalign != sattr->attalign)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("table row type and query-specified row type do not match"),
-					 errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
-							   i + 1)));
-	}
-
-	/*
-	 * Copy the slot tuple and make sure any toasted fields get detoasted.
-	 */
-	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
-	/*
-	 * Label the datum with the composite type info we identified before.
-	 */
-	HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
-	HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
-	return PointerGetDatum(dtuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalConst
- *
- *		Returns the value of a constant.
- *
- *		Note that for pass-by-ref datatypes, we return a pointer to the
- *		actual constant node.  This is one of the reasons why functions
- *		must treat their input arguments as read-only.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-			  bool *isNull)
-{
-	Const	   *con = (Const *) exprstate->expr;
-
-	*isNull = con->constisnull;
-	return con->constvalue;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalParamExec
- *
- *		Returns the value of a PARAM_EXEC parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull)
-{
-	Param	   *expression = (Param *) exprstate->expr;
-	int			thisParamId = expression->paramid;
-	ParamExecData *prm;
-
-	/*
-	 * PARAM_EXEC params (internal executor parameters) are stored in the
-	 * ecxt_param_exec_vals array, and can be accessed by array index.
-	 */
-	prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
-	if (prm->execPlan != NULL)
-	{
-		/* Parameter not evaluated yet, so go do it */
-		ExecSetParamPlan(prm->execPlan, econtext);
-		/* ExecSetParamPlan should have processed this param... */
-		Assert(prm->execPlan == NULL);
-	}
-	*isNull = prm->isnull;
-	return prm->value;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalParamExtern
- *
- *		Returns the value of a PARAM_EXTERN parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-					bool *isNull)
-{
-	Param	   *expression = (Param *) exprstate->expr;
-	int			thisParamId = expression->paramid;
-	ParamListInfo paramInfo = econtext->ecxt_param_list_info;
-
-	/*
-	 * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
-	 */
-	if (paramInfo &&
-		thisParamId > 0 && thisParamId <= paramInfo->numParams)
-	{
-		ParamExternData *prm = &paramInfo->params[thisParamId - 1];
-
-		/* give hook a chance in case parameter is dynamic */
-		if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
-			(*paramInfo->paramFetch) (paramInfo, thisParamId);
-
-		if (OidIsValid(prm->ptype))
-		{
-			/* safety check in case hook did something unexpected */
-			if (prm->ptype != expression->paramtype)
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
-								thisParamId,
-								format_type_be(prm->ptype),
-								format_type_be(expression->paramtype))));
-
-			*isNull = prm->isnull;
-			return prm->value;
-		}
-	}
-
-	ereport(ERROR,
-			(errcode(ERRCODE_UNDEFINED_OBJECT),
-			 errmsg("no value found for parameter %d", thisParamId)));
-	return (Datum) 0;			/* keep compiler quiet */
-}
-
-
-/* ----------------------------------------------------------------
- *		ExecEvalOper / ExecEvalFunc support routines
- * ----------------------------------------------------------------
- */
-
 /*
  *		GetAttributeByName
  *		GetAttributeByNum
@@ -1268,11 +173,11 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
 }
 
 /*
- * init_fcache - initialize a FuncExprState node during first use
+ * init_sexpr - initialize a SetExprState node during first use
  */
 static void
-init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-			MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
+init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+		   MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
 {
 	AclResult	aclresult;
 
@@ -1288,7 +193,7 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
 	 * declared in pg_proc?
 	 */
-	if (list_length(fcache->args) > FUNC_MAX_ARGS)
+	if (list_length(sexpr->args) > FUNC_MAX_ARGS)
 		ereport(ERROR,
 				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
 			 errmsg_plural("cannot pass more than %d argument to a function",
@@ -1297,45 +202,45 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 						   FUNC_MAX_ARGS)));
 
 	/* Set up the primary fmgr lookup information */
-	fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
-	fmgr_info_set_expr((Node *) fcache->xprstate.expr, &(fcache->func));
+	fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
+	fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
 
 	/* Initialize the function call parameter struct as well */
-	InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func),
-							 list_length(fcache->args),
+	InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+							 list_length(sexpr->args),
 							 input_collation, NULL, NULL);
 
 	/* If function returns set, check if that's allowed by caller */
-	if (fcache->func.fn_retset && !allowSRF)
+	if (sexpr->func.fn_retset && !allowSRF)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("set-valued function called in context that cannot accept a set")));
 
-	/* Otherwise, ExecInitExpr should have marked the fcache correctly */
-	Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
+	/* Otherwise, ExecInitExpr should have marked the sexpr correctly */
+	Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
 
 	/* If function returns set, prepare expected tuple descriptor */
-	if (fcache->func.fn_retset && needDescForSRF)
+	if (sexpr->func.fn_retset && needDescForSRF)
 	{
 		TypeFuncClass functypclass;
 		Oid			funcrettype;
 		TupleDesc	tupdesc;
 		MemoryContext oldcontext;
 
-		functypclass = get_expr_result_type(fcache->func.fn_expr,
+		functypclass = get_expr_result_type(sexpr->func.fn_expr,
 											&funcrettype,
 											&tupdesc);
 
-		/* Must save tupdesc in fcache's context */
-		oldcontext = MemoryContextSwitchTo(fcacheCxt);
+		/* Must save tupdesc in sexpr's context */
+		oldcontext = MemoryContextSwitchTo(sexprCxt);
 
 		if (functypclass == TYPEFUNC_COMPOSITE)
 		{
 			/* Composite data type, e.g. a table's row type */
 			Assert(tupdesc);
 			/* Must copy it out of typcache for safety */
-			fcache->funcResultDesc = CreateTupleDescCopy(tupdesc);
-			fcache->funcReturnsTuple = true;
+			sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
+			sexpr->funcReturnsTuple = true;
 		}
 		else if (functypclass == TYPEFUNC_SCALAR)
 		{
@@ -1347,110 +252,55 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 							   funcrettype,
 							   -1,
 							   0);
-			fcache->funcResultDesc = tupdesc;
-			fcache->funcReturnsTuple = false;
+			sexpr->funcResultDesc = tupdesc;
+			sexpr->funcReturnsTuple = false;
 		}
 		else if (functypclass == TYPEFUNC_RECORD)
 		{
 			/* This will work if function doesn't need an expectedDesc */
-			fcache->funcResultDesc = NULL;
-			fcache->funcReturnsTuple = true;
+			sexpr->funcResultDesc = NULL;
+			sexpr->funcReturnsTuple = true;
 		}
 		else
 		{
 			/* Else, we will fail if function needs an expectedDesc */
-			fcache->funcResultDesc = NULL;
+			sexpr->funcResultDesc = NULL;
 		}
 
 		MemoryContextSwitchTo(oldcontext);
 	}
 	else
-		fcache->funcResultDesc = NULL;
+		sexpr->funcResultDesc = NULL;
 
 	/* Initialize additional state */
-	fcache->funcResultStore = NULL;
-	fcache->funcResultSlot = NULL;
-	fcache->shutdown_reg = false;
+	sexpr->funcResultStore = NULL;
+	sexpr->funcResultSlot = NULL;
+	sexpr->shutdown_reg = false;
 }
 
 /*
- * callback function in case a FuncExpr returning a set needs to be shut down
- * before it has been run to completion
+ * callback function in case a SetExpr needs to be shut down before it has
+ * been run to completion
  */
 static void
-ShutdownFuncExpr(Datum arg)
+ShutdownSetExpr(Datum arg)
 {
-	FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg);
+	SetExprState *sexpr = (SetExprState *) DatumGetPointer(arg);
 
 	/* If we have a slot, make sure it's let go of any tuplestore pointer */
-	if (fcache->funcResultSlot)
-		ExecClearTuple(fcache->funcResultSlot);
+	if (sexpr->funcResultSlot)
+		ExecClearTuple(sexpr->funcResultSlot);
 
 	/* Release any open tuplestore */
-	if (fcache->funcResultStore)
-		tuplestore_end(fcache->funcResultStore);
-	fcache->funcResultStore = NULL;
+	if (sexpr->funcResultStore)
+		tuplestore_end(sexpr->funcResultStore);
+	sexpr->funcResultStore = NULL;
 
 	/* Clear any active set-argument state */
-	fcache->setArgsValid = false;
+	sexpr->setArgsValid = false;
 
 	/* execUtils will deregister the callback... */
-	fcache->shutdown_reg = false;
-}
-
-/*
- * get_cached_rowtype: utility function to lookup a rowtype tupdesc
- *
- * type_id, typmod: identity of the rowtype
- * cache_field: where to cache the TupleDesc pointer in expression state node
- *		(field must be initialized to NULL)
- * econtext: expression context we are executing in
- *
- * NOTE: because the shutdown callback will be called during plan rescan,
- * must be prepared to re-do this during any node execution; cannot call
- * just once during expression initialization
- */
-static TupleDesc
-get_cached_rowtype(Oid type_id, int32 typmod,
-				   TupleDesc *cache_field, ExprContext *econtext)
-{
-	TupleDesc	tupDesc = *cache_field;
-
-	/* Do lookup if no cached value or if requested type changed */
-	if (tupDesc == NULL ||
-		type_id != tupDesc->tdtypeid ||
-		typmod != tupDesc->tdtypmod)
-	{
-		tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
-
-		if (*cache_field)
-		{
-			/* Release old tupdesc; but callback is already registered */
-			ReleaseTupleDesc(*cache_field);
-		}
-		else
-		{
-			/* Need to register shutdown callback to release tupdesc */
-			RegisterExprContextCallback(econtext,
-										ShutdownTupleDescRef,
-										PointerGetDatum(cache_field));
-		}
-		*cache_field = tupDesc;
-	}
-	return tupDesc;
-}
-
-/*
- * Callback function to release a tupdesc refcount at expression tree shutdown
- */
-static void
-ShutdownTupleDescRef(Datum arg)
-{
-	TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
-
-	if (*cache_field)
-		ReleaseTupleDesc(*cache_field);
-	*cache_field = NULL;
+	sexpr->shutdown_reg = false;
 }
 
 /*
@@ -1487,27 +337,27 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
  * returned the expected tuple descriptor.
  */
 static void
-ExecPrepareTuplestoreResult(FuncExprState *fcache,
+ExecPrepareTuplestoreResult(SetExprState *sexpr,
 							ExprContext *econtext,
 							Tuplestorestate *resultStore,
 							TupleDesc resultDesc)
 {
-	fcache->funcResultStore = resultStore;
+	sexpr->funcResultStore = resultStore;
 
-	if (fcache->funcResultSlot == NULL)
+	if (sexpr->funcResultSlot == NULL)
 	{
 		/* Create a slot so we can read data out of the tuplestore */
 		TupleDesc	slotDesc;
 		MemoryContext oldcontext;
 
-		oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
+		oldcontext = MemoryContextSwitchTo(sexpr->func.fn_mcxt);
 
 		/*
 		 * If we were not able to determine the result rowtype from context,
 		 * and the function didn't return a tupdesc, we have to fail.
 		 */
-		if (fcache->funcResultDesc)
-			slotDesc = fcache->funcResultDesc;
+		if (sexpr->funcResultDesc)
+			slotDesc = sexpr->funcResultDesc;
 		else if (resultDesc)
 		{
 			/* don't assume resultDesc is long-lived */
@@ -1522,7 +372,7 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
 			slotDesc = NULL;	/* keep compiler quiet */
 		}
 
-		fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
+		sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
 		MemoryContextSwitchTo(oldcontext);
 	}
 
@@ -1532,8 +382,8 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
 	 */
 	if (resultDesc)
 	{
-		if (fcache->funcResultDesc)
-			tupledesc_match(fcache->funcResultDesc, resultDesc);
+		if (sexpr->funcResultDesc)
+			tupledesc_match(sexpr->funcResultDesc, resultDesc);
 
 		/*
 		 * If it is a dynamically-allocated TupleDesc, free it: it is
@@ -1545,12 +395,12 @@ ExecPrepareTuplestoreResult(FuncExprState *fcache,
 	}
 
 	/* Register cleanup callback if we didn't already */
-	if (!fcache->shutdown_reg)
+	if (!sexpr->shutdown_reg)
 	{
 		RegisterExprContextCallback(econtext,
-									ShutdownFuncExpr,
-									PointerGetDatum(fcache));
-		fcache->shutdown_reg = true;
+									ShutdownSetExpr,
+									PointerGetDatum(sexpr));
+		sexpr->shutdown_reg = true;
 	}
 }
 
@@ -1612,7 +462,7 @@ tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
  * functions (the planner is supposed to have separated evaluation for those).
  */
 Datum
-ExecMakeFunctionResultSet(FuncExprState *fcache,
+ExecMakeFunctionResultSet(SetExprState *fcache,
 						  ExprContext *econtext,
 						  bool *isNull,
 						  ExprDoneCond *isDone)
@@ -1631,34 +481,6 @@ restart:
 	check_stack_depth();
 
 	/*
-	 * Initialize function cache if first time through.  The expression node
-	 * could be either a FuncExpr or an OpExpr.
-	 */
-	if (fcache->func.fn_oid == InvalidOid)
-	{
-		if (IsA(fcache->xprstate.expr, FuncExpr))
-		{
-			FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-			init_fcache(func->funcid, func->inputcollid, fcache,
-						econtext->ecxt_per_query_memory, true, true);
-		}
-		else if (IsA(fcache->xprstate.expr, OpExpr))
-		{
-			OpExpr	   *op = (OpExpr *) fcache->xprstate.expr;
-
-			init_fcache(op->opfuncid, op->inputcollid, fcache,
-						econtext->ecxt_per_query_memory, true, true);
-		}
-		else
-			elog(ERROR, "unrecognized node type: %d",
-				 (int) nodeTag(fcache->xprstate.expr));
-
-		/* shouldn't get here otherwise */
-		Assert(fcache->func.fn_retset);
-	}
-
-	/*
 	 * If a previous call of the function returned a set result in the form of
 	 * a tuplestore, continue reading rows from the tuplestore until it's
 	 * empty.
@@ -1774,7 +596,7 @@ restart:
 				if (!fcache->shutdown_reg)
 				{
 					RegisterExprContextCallback(econtext,
-												ShutdownFuncExpr,
+												ShutdownSetExpr,
 												PointerGetDatum(fcache));
 					fcache->shutdown_reg = true;
 				}
@@ -1812,63 +634,100 @@ restart:
 }
 
 /*
- *		ExecMakeFunctionResultNoSets
- *
- * Evaluate a function or operator node with a non-set-returning function.
- * Assumes init_fcache() already done.  Hand-tuned for speed.
+ * Prepare function call in nodeProjectSet.c (taretlist SRF) for execution.
  */
-static Datum
-ExecMakeFunctionResultNoSets(FuncExprState *fcache,
-							 ExprContext *econtext,
-							 bool *isNull)
+SetExprState *
+ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent)
 {
-	ListCell   *arg;
-	Datum		result;
-	FunctionCallInfo fcinfo;
-	PgStat_FunctionCallUsage fcusage;
-	int			i;
+	SetExprState *state = makeNode(SetExprState);
+	ListCell *lc;
 
-	/* Guard against stack overflow due to overly complex expressions */
-	check_stack_depth();
-
-	/* inlined, simplified version of ExecEvalFuncArgs */
-	fcinfo = &fcache->fcinfo_data;
-	i = 0;
-	foreach(arg, fcache->args)
-	{
-		ExprState  *argstate = (ExprState *) lfirst(arg);
-
-		fcinfo->arg[i] = ExecEvalExpr(argstate,
-									  econtext,
-									  &fcinfo->argnull[i]);
-		i++;
-	}
+	state->funcReturnsSet = true;
+	state->expr = expr;
+	state->func.fn_oid = InvalidOid;
 
 	/*
-	 * If function is strict, and there are any NULL arguments, skip calling
-	 * the function and return NULL.
+	 * Initialize metadata.  The expression node could be either a FuncExpr or
+	 * an OpExpr.
 	 */
-	if (fcache->func.fn_strict)
+	if (IsA(expr, FuncExpr))
 	{
-		while (--i >= 0)
+		FuncExpr   *func = (FuncExpr *) expr;
+
+		foreach(lc, func->args)
 		{
-			if (fcinfo->argnull[i])
-			{
-				*isNull = true;
-				return (Datum) 0;
-			}
+			state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
 		}
+
+		init_sexpr(func->funcid, func->inputcollid, state,
+				   econtext->ecxt_per_query_memory, true, true);
+	}
+	else if (IsA(expr, OpExpr))
+	{
+		OpExpr	   *op = (OpExpr *) expr;
+
+		foreach(lc, op->args)
+		{
+			state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+		}
+
+		init_sexpr(op->opfuncid, op->inputcollid, state,
+				   econtext->ecxt_per_query_memory, true, true);
+	}
+	else
+		elog(ERROR, "unrecognized node type: %d",
+			 (int) nodeTag(expr));
+
+	/* shouldn't get here otherwise */
+	Assert(state->func.fn_retset);
+
+	return state;
+}
+
+/*
+ * Prepare function call in nodeFunctionscan.c (FROM function/ROWS FROM) for
+ * execution.
+ */
+SetExprState *
+ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent)
+{
+	SetExprState *state = makeNode(SetExprState);
+	ListCell *lc;
+
+	state->funcReturnsSet = false;
+	state->expr = expr;
+	state->func.fn_oid = InvalidOid;
+
+	/*
+	 * Normally the passed expression tree will be a FuncExpr, since the
+	 * grammar only allows a function call at the top level of a table
+	 * function reference.  However, if the function doesn't return set then
+	 * the planner might have replaced the function call via constant-folding
+	 * or inlining.  So if we see any other kind of expression node, execute
+	 * it via the general ExecEvalExpr() code; the only difference is that we
+	 * don't get a chance to pass a special ReturnSetInfo to any functions
+	 * buried in the expression.
+	 */
+	if (IsA(expr, FuncExpr))
+	{
+		FuncExpr   *func = (FuncExpr *) expr;
+
+		state->funcReturnsSet = func->funcretset;
+
+		foreach(lc, func->args)
+		{
+			state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+		}
+
+		init_sexpr(func->funcid, func->inputcollid, state,
+				   econtext->ecxt_per_query_memory, func->funcretset, false);
+	}
+	else
+	{
+		state->elidedFuncState = ExecInitExpr(expr, parent);
 	}
 
-	pgstat_init_function_usage(fcinfo, &fcusage);
-
-	fcinfo->isnull = false;
-	result = FunctionCallInvoke(fcinfo);
-	*isNull = fcinfo->isnull;
-
-	pgstat_end_function_usage(&fcusage, true);
-
-	return result;
+	return state;
 }
 
 
@@ -1879,7 +738,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
  * object.
  */
 Tuplestorestate *
-ExecMakeTableFunctionResult(ExprState *funcexpr,
+ExecMakeTableFunctionResult(SetExprState *setexpr,
 							ExprContext *econtext,
 							MemoryContext argContext,
 							TupleDesc expectedDesc,
@@ -1896,12 +755,11 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	HeapTupleData tmptup;
 	MemoryContext callerContext;
 	MemoryContext oldcontext;
-	bool		direct_function_call;
 	bool		first_time = true;
 
 	callerContext = CurrentMemoryContext;
 
-	funcrettype = exprType((Node *) funcexpr->expr);
+	funcrettype = exprType((Node *) setexpr->expr);
 
 	returnsTuple = type_is_rowtype(funcrettype);
 
@@ -1924,7 +782,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	rsinfo.setDesc = NULL;
 
 	/*
-	 * Normally the passed expression tree will be a FuncExprState, since the
+	 * Normally the passed expression tree will be a SetExprState, since the
 	 * grammar only allows a function call at the top level of a table
 	 * function reference.  However, if the function doesn't return set then
 	 * the planner might have replaced the function call via constant-folding
@@ -1933,30 +791,15 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	 * don't get a chance to pass a special ReturnSetInfo to any functions
 	 * buried in the expression.
 	 */
-	if (funcexpr && IsA(funcexpr, FuncExprState) &&
-		IsA(funcexpr->expr, FuncExpr))
+	if (!setexpr->elidedFuncState)
 	{
-		FuncExprState *fcache = (FuncExprState *) funcexpr;
-
 		/*
 		 * This path is similar to ExecMakeFunctionResultSet.
 		 */
-		direct_function_call = true;
-
-		/*
-		 * Initialize function cache if first time through
-		 */
-		if (fcache->func.fn_oid == InvalidOid)
-		{
-			FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-			init_fcache(func->funcid, func->inputcollid, fcache,
-						econtext->ecxt_per_query_memory, true, false);
-		}
-		returnsSet = fcache->func.fn_retset;
-		InitFunctionCallInfoData(fcinfo, &(fcache->func),
-								 list_length(fcache->args),
-								 fcache->fcinfo_data.fncollation,
+		returnsSet = setexpr->funcReturnsSet;
+		InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+								 list_length(setexpr->args),
+								 setexpr->fcinfo_data.fncollation,
 								 NULL, (Node *) &rsinfo);
 
 		/*
@@ -1971,7 +814,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		 */
 		MemoryContextReset(argContext);
 		oldcontext = MemoryContextSwitchTo(argContext);
-		ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
+		ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
 		MemoryContextSwitchTo(oldcontext);
 
 		/*
@@ -1979,7 +822,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		 * calling the function and act like it returned NULL (or an empty
 		 * set, in the returns-set case).
 		 */
-		if (fcache->func.fn_strict)
+		if (setexpr->func.fn_strict)
 		{
 			int			i;
 
@@ -1992,8 +835,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	}
 	else
 	{
-		/* Treat funcexpr as a generic expression */
-		direct_function_call = false;
+		/* Treat setexpr as a generic expression */
 		InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
 	}
 
@@ -2020,7 +862,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		ResetExprContext(econtext);
 
 		/* Call the function or expression one time */
-		if (direct_function_call)
+		if (!setexpr->elidedFuncState)
 		{
 			pgstat_init_function_usage(&fcinfo, &fcusage);
 
@@ -2033,7 +875,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 		}
 		else
 		{
-			result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull);
+			result =
+				ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
 			rsinfo.isDone = ExprSingleResult;
 		}
 
@@ -2211,2911 +1054,6 @@ no_function_result:
 	return rsinfo.setResult;
 }
 
-
-/* ----------------------------------------------------------------
- *		ExecEvalFunc
- *		ExecEvalOper
- *
- *		Evaluate the functional result of a list of arguments by calling the
- *		function manager.
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- *		ExecEvalFunc
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFunc(FuncExprState *fcache,
-			 ExprContext *econtext,
-			 bool *isNull)
-{
-	/* This is called only the first time through */
-	FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-	/* Initialize function lookup info */
-	init_fcache(func->funcid, func->inputcollid, fcache,
-				econtext->ecxt_per_query_memory, false, false);
-
-	/* Change the evalfunc pointer to save a few cycles in additional calls */
-	fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-	return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalOper
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOper(FuncExprState *fcache,
-			 ExprContext *econtext,
-			 bool *isNull)
-{
-	/* This is called only the first time through */
-	OpExpr	   *op = (OpExpr *) fcache->xprstate.expr;
-
-	/* Initialize function lookup info */
-	init_fcache(op->opfuncid, op->inputcollid, fcache,
-				econtext->ecxt_per_query_memory, false, false);
-
-	/* Change the evalfunc pointer to save a few cycles in additional calls */
-	fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-	return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalDistinct
- *
- * IS DISTINCT FROM must evaluate arguments to determine whether
- * they are NULL; if either is NULL then the result is already
- * known. If neither is NULL, then proceed to evaluate the
- * function. Note that this is *always* derived from the equals
- * operator, but since we need special processing of the arguments
- * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalDistinct(FuncExprState *fcache,
-				 ExprContext *econtext,
-				 bool *isNull)
-{
-	Datum		result;
-	FunctionCallInfo fcinfo;
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	/*
-	 * Initialize function cache if first time through
-	 */
-	if (fcache->func.fn_oid == InvalidOid)
-	{
-		DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
-
-		init_fcache(op->opfuncid, op->inputcollid, fcache,
-					econtext->ecxt_per_query_memory, false, false);
-	}
-
-	/*
-	 * Evaluate arguments
-	 */
-	fcinfo = &fcache->fcinfo_data;
-	ExecEvalFuncArgs(fcinfo, fcache->args, econtext);
-	Assert(fcinfo->nargs == 2);
-
-	if (fcinfo->argnull[0] && fcinfo->argnull[1])
-	{
-		/* Both NULL? Then is not distinct... */
-		result = BoolGetDatum(FALSE);
-	}
-	else if (fcinfo->argnull[0] || fcinfo->argnull[1])
-	{
-		/* Only one is NULL? Then is distinct... */
-		result = BoolGetDatum(TRUE);
-	}
-	else
-	{
-		fcinfo->isnull = false;
-		result = FunctionCallInvoke(fcinfo);
-		*isNull = fcinfo->isnull;
-		/* Must invert result of "=" */
-		result = BoolGetDatum(!DatumGetBool(result));
-	}
-
-	return result;
-}
-
-/*
- * ExecEvalScalarArrayOp
- *
- * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
- * and we combine the results across all array elements using OR and AND
- * (for ANY and ALL respectively).  Of course we short-circuit as soon as
- * the result is known.
- */
-static Datum
-ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
-					  ExprContext *econtext,
-					  bool *isNull)
-{
-	ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
-	bool		useOr = opexpr->useOr;
-	ArrayType  *arr;
-	int			nitems;
-	Datum		result;
-	bool		resultnull;
-	FunctionCallInfo fcinfo;
-	int			i;
-	int16		typlen;
-	bool		typbyval;
-	char		typalign;
-	char	   *s;
-	bits8	   *bitmap;
-	int			bitmask;
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	/*
-	 * Initialize function cache if first time through
-	 */
-	if (sstate->fxprstate.func.fn_oid == InvalidOid)
-	{
-		init_fcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
-					econtext->ecxt_per_query_memory, false, false);
-	}
-
-	/*
-	 * Evaluate arguments
-	 */
-	fcinfo = &sstate->fxprstate.fcinfo_data;
-	ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext);
-	Assert(fcinfo->nargs == 2);
-
-	/*
-	 * If the array is NULL then we return NULL --- it's not very meaningful
-	 * to do anything else, even if the operator isn't strict.
-	 */
-	if (fcinfo->argnull[1])
-	{
-		*isNull = true;
-		return (Datum) 0;
-	}
-	/* Else okay to fetch and detoast the array */
-	arr = DatumGetArrayTypeP(fcinfo->arg[1]);
-
-	/*
-	 * If the array is empty, we return either FALSE or TRUE per the useOr
-	 * flag.  This is correct even if the scalar is NULL; since we would
-	 * evaluate the operator zero times, it matters not whether it would want
-	 * to return NULL.
-	 */
-	nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
-	if (nitems <= 0)
-		return BoolGetDatum(!useOr);
-
-	/*
-	 * If the scalar is NULL, and the function is strict, return NULL; no
-	 * point in iterating the loop.
-	 */
-	if (fcinfo->argnull[0] && sstate->fxprstate.func.fn_strict)
-	{
-		*isNull = true;
-		return (Datum) 0;
-	}
-
-	/*
-	 * We arrange to look up info about the element type only once per series
-	 * of calls, assuming the element type doesn't change underneath us.
-	 */
-	if (sstate->element_type != ARR_ELEMTYPE(arr))
-	{
-		get_typlenbyvalalign(ARR_ELEMTYPE(arr),
-							 &sstate->typlen,
-							 &sstate->typbyval,
-							 &sstate->typalign);
-		sstate->element_type = ARR_ELEMTYPE(arr);
-	}
-	typlen = sstate->typlen;
-	typbyval = sstate->typbyval;
-	typalign = sstate->typalign;
-
-	result = BoolGetDatum(!useOr);
-	resultnull = false;
-
-	/* Loop over the array elements */
-	s = (char *) ARR_DATA_PTR(arr);
-	bitmap = ARR_NULLBITMAP(arr);
-	bitmask = 1;
-
-	for (i = 0; i < nitems; i++)
-	{
-		Datum		elt;
-		Datum		thisresult;
-
-		/* Get array element, checking for NULL */
-		if (bitmap && (*bitmap & bitmask) == 0)
-		{
-			fcinfo->arg[1] = (Datum) 0;
-			fcinfo->argnull[1] = true;
-		}
-		else
-		{
-			elt = fetch_att(s, typbyval, typlen);
-			s = att_addlength_pointer(s, typlen, s);
-			s = (char *) att_align_nominal(s, typalign);
-			fcinfo->arg[1] = elt;
-			fcinfo->argnull[1] = false;
-		}
-
-		/* Call comparison function */
-		if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict)
-		{
-			fcinfo->isnull = true;
-			thisresult = (Datum) 0;
-		}
-		else
-		{
-			fcinfo->isnull = false;
-			thisresult = FunctionCallInvoke(fcinfo);
-		}
-
-		/* Combine results per OR or AND semantics */
-		if (fcinfo->isnull)
-			resultnull = true;
-		else if (useOr)
-		{
-			if (DatumGetBool(thisresult))
-			{
-				result = BoolGetDatum(true);
-				resultnull = false;
-				break;			/* needn't look at any more elements */
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(thisresult))
-			{
-				result = BoolGetDatum(false);
-				resultnull = false;
-				break;			/* needn't look at any more elements */
-			}
-		}
-
-		/* advance bitmap pointer if any */
-		if (bitmap)
-		{
-			bitmask <<= 1;
-			if (bitmask == 0x100)
-			{
-				bitmap++;
-				bitmask = 1;
-			}
-		}
-	}
-
-	*isNull = resultnull;
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalNot
- *		ExecEvalOr
- *		ExecEvalAnd
- *
- *		Evaluate boolean expressions, with appropriate short-circuiting.
- *
- *		The query planner reformulates clause expressions in the
- *		qualification to conjunctive normal form.  If we ever get
- *		an AND to evaluate, we can be sure that it's not a top-level
- *		clause in the qualification, but appears lower (as a function
- *		argument, for example), or in the target list.  Not that you
- *		need to know this, mind you...
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-			bool *isNull)
-{
-	ExprState  *clause = linitial(notclause->args);
-	Datum		expr_value;
-
-	expr_value = ExecEvalExpr(clause, econtext, isNull);
-
-	/*
-	 * if the expression evaluates to null, then we just cascade the null back
-	 * to whoever called us.
-	 */
-	if (*isNull)
-		return expr_value;
-
-	/*
-	 * evaluation of 'not' is simple.. expr is false, then return 'true' and
-	 * vice versa.
-	 */
-	return BoolGetDatum(!DatumGetBool(expr_value));
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalOr
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-		   bool *isNull)
-{
-	List	   *clauses = orExpr->args;
-	ListCell   *clause;
-	bool		AnyNull;
-
-	AnyNull = false;
-
-	/*
-	 * If any of the clauses is TRUE, the OR result is TRUE regardless of the
-	 * states of the rest of the clauses, so we can stop evaluating and return
-	 * TRUE immediately.  If none are TRUE and one or more is NULL, we return
-	 * NULL; otherwise we return FALSE.  This makes sense when you interpret
-	 * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we
-	 * aren't sure about some of the other inputs. If all the known inputs are
-	 * FALSE, but we have one or more "don't knows", then we have to report
-	 * that we "don't know" what the OR's result should be --- perhaps one of
-	 * the "don't knows" would have been TRUE if we'd known its value.  Only
-	 * when all the inputs are known to be FALSE can we state confidently that
-	 * the OR's result is FALSE.
-	 */
-	foreach(clause, clauses)
-	{
-		ExprState  *clausestate = (ExprState *) lfirst(clause);
-		Datum		clause_value;
-
-		clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
-		/*
-		 * if we have a non-null true result, then return it.
-		 */
-		if (*isNull)
-			AnyNull = true;		/* remember we got a null */
-		else if (DatumGetBool(clause_value))
-			return clause_value;
-	}
-
-	/* AnyNull is true if at least one clause evaluated to NULL */
-	*isNull = AnyNull;
-	return BoolGetDatum(false);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalAnd
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-			bool *isNull)
-{
-	List	   *clauses = andExpr->args;
-	ListCell   *clause;
-	bool		AnyNull;
-
-	AnyNull = false;
-
-	/*
-	 * If any of the clauses is FALSE, the AND result is FALSE regardless of
-	 * the states of the rest of the clauses, so we can stop evaluating and
-	 * return FALSE immediately.  If none are FALSE and one or more is NULL,
-	 * we return NULL; otherwise we return TRUE.  This makes sense when you
-	 * interpret NULL as "don't know", using the same sort of reasoning as for
-	 * OR, above.
-	 */
-
-	foreach(clause, clauses)
-	{
-		ExprState  *clausestate = (ExprState *) lfirst(clause);
-		Datum		clause_value;
-
-		clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
-		/*
-		 * if we have a non-null false result, then return it.
-		 */
-		if (*isNull)
-			AnyNull = true;		/* remember we got a null */
-		else if (!DatumGetBool(clause_value))
-			return clause_value;
-	}
-
-	/* AnyNull is true if at least one clause evaluated to NULL */
-	*isNull = AnyNull;
-	return BoolGetDatum(!AnyNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalConvertRowtype
- *
- *		Evaluate a rowtype coercion operation.  This may require
- *		rearranging field positions.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-					   ExprContext *econtext,
-					   bool *isNull)
-{
-	ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
-	HeapTuple	result;
-	Datum		tupDatum;
-	HeapTupleHeader tuple;
-	HeapTupleData tmptup;
-
-	tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull);
-
-	/* this test covers the isDone exception too: */
-	if (*isNull)
-		return tupDatum;
-
-	tuple = DatumGetHeapTupleHeader(tupDatum);
-
-	/* Lookup tupdescs if first time through or after rescan */
-	if (cstate->indesc == NULL)
-	{
-		get_cached_rowtype(exprType((Node *) convert->arg), -1,
-						   &cstate->indesc, econtext);
-		cstate->initialized = false;
-	}
-	if (cstate->outdesc == NULL)
-	{
-		get_cached_rowtype(convert->resulttype, -1,
-						   &cstate->outdesc, econtext);
-		cstate->initialized = false;
-	}
-
-	/*
-	 * We used to be able to assert that incoming tuples are marked with
-	 * exactly the rowtype of cstate->indesc.  However, now that
-	 * ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
-	 * due to inserting aliases, we can only make this weak test:
-	 */
-	Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid ||
-		   HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
-
-	/* if first time through, initialize conversion map */
-	if (!cstate->initialized)
-	{
-		MemoryContext old_cxt;
-
-		/* allocate map in long-lived memory context */
-		old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-
-		/* prepare map from old to new attribute numbers */
-		cstate->map = convert_tuples_by_name(cstate->indesc,
-											 cstate->outdesc,
-								 gettext_noop("could not convert row type"));
-		cstate->initialized = true;
-
-		MemoryContextSwitchTo(old_cxt);
-	}
-
-	/*
-	 * No-op if no conversion needed (not clear this can happen here).
-	 */
-	if (cstate->map == NULL)
-		return tupDatum;
-
-	/*
-	 * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
-	 */
-	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-	tmptup.t_data = tuple;
-
-	result = do_convert_tuple(&tmptup, cstate->map);
-
-	return HeapTupleGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCase
- *
- *		Evaluate a CASE clause. Will have boolean expressions
- *		inside the WHEN clauses, and will have expressions
- *		for results.
- *		- thomas 1998-11-09
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-			 bool *isNull)
-{
-	List	   *clauses = caseExpr->args;
-	ListCell   *clause;
-	Datum		save_datum;
-	bool		save_isNull;
-
-	/*
-	 * If there's a test expression, we have to evaluate it and save the value
-	 * where the CaseTestExpr placeholders can find it.  We must save and
-	 * restore prior setting of econtext's caseValue fields, in case this node
-	 * is itself within a larger CASE.  Furthermore, don't assign to the
-	 * econtext fields until after returning from evaluation of the test
-	 * expression.  We used to pass &econtext->caseValue_isNull to the
-	 * recursive call, but that leads to aliasing that variable within said
-	 * call, which can (and did) produce bugs when the test expression itself
-	 * contains a CASE.
-	 *
-	 * If there's no test expression, we don't actually need to save and
-	 * restore these fields; but it's less code to just do so unconditionally.
-	 */
-	save_datum = econtext->caseValue_datum;
-	save_isNull = econtext->caseValue_isNull;
-
-	if (caseExpr->arg)
-	{
-		Datum		arg_value;
-		bool		arg_isNull;
-
-		arg_value = ExecEvalExpr(caseExpr->arg,
-								 econtext,
-								 &arg_isNull);
-		/* Since caseValue_datum may be read multiple times, force to R/O */
-		econtext->caseValue_datum =
-			MakeExpandedObjectReadOnly(arg_value,
-									   arg_isNull,
-									   caseExpr->argtyplen);
-		econtext->caseValue_isNull = arg_isNull;
-	}
-
-	/*
-	 * we evaluate each of the WHEN clauses in turn, as soon as one is true we
-	 * return the corresponding result. If none are true then we return the
-	 * value of the default clause, or NULL if there is none.
-	 */
-	foreach(clause, clauses)
-	{
-		CaseWhenState *wclause = lfirst(clause);
-		Datum		clause_value;
-		bool		clause_isNull;
-
-		clause_value = ExecEvalExpr(wclause->expr,
-									econtext,
-									&clause_isNull);
-
-		/*
-		 * if we have a true test, then we return the result, since the case
-		 * statement is satisfied.  A NULL result from the test is not
-		 * considered true.
-		 */
-		if (DatumGetBool(clause_value) && !clause_isNull)
-		{
-			econtext->caseValue_datum = save_datum;
-			econtext->caseValue_isNull = save_isNull;
-			return ExecEvalExpr(wclause->result,
-								econtext,
-								isNull);
-		}
-	}
-
-	econtext->caseValue_datum = save_datum;
-	econtext->caseValue_isNull = save_isNull;
-
-	if (caseExpr->defresult)
-	{
-		return ExecEvalExpr(caseExpr->defresult,
-							econtext,
-							isNull);
-	}
-
-	*isNull = true;
-	return (Datum) 0;
-}
-
-/*
- * ExecEvalCaseTestExpr
- *
- * Return the value stored by CASE.
- */
-static Datum
-ExecEvalCaseTestExpr(ExprState *exprstate,
-					 ExprContext *econtext,
-					 bool *isNull)
-{
-	*isNull = econtext->caseValue_isNull;
-	return econtext->caseValue_datum;
-}
-
-/*
- * ExecEvalGroupingFuncExpr
- *
- * Return a bitmask with a bit for each (unevaluated) argument expression
- * (rightmost arg is least significant bit).
- *
- * A bit is set if the corresponding expression is NOT part of the set of
- * grouping expressions in the current grouping set.
- */
-static Datum
-ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
-						 ExprContext *econtext,
-						 bool *isNull)
-{
-	int			result = 0;
-	int			attnum = 0;
-	Bitmapset  *grouped_cols = gstate->aggstate->grouped_cols;
-	ListCell   *lc;
-
-	*isNull = false;
-
-	foreach(lc, (gstate->clauses))
-	{
-		attnum = lfirst_int(lc);
-
-		result = result << 1;
-
-		if (!bms_is_member(attnum, grouped_cols))
-			result = result | 1;
-	}
-
-	return (Datum) result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalArray - ARRAY[] expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
-			  bool *isNull)
-{
-	ArrayExpr  *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
-	ArrayType  *result;
-	ListCell   *element;
-	Oid			element_type = arrayExpr->element_typeid;
-	int			ndims = 0;
-	int			dims[MAXDIM];
-	int			lbs[MAXDIM];
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	if (!arrayExpr->multidims)
-	{
-		/* Elements are presumably of scalar type */
-		int			nelems;
-		Datum	   *dvalues;
-		bool	   *dnulls;
-		int			i = 0;
-
-		ndims = 1;
-		nelems = list_length(astate->elements);
-
-		/* Shouldn't happen here, but if length is 0, return empty array */
-		if (nelems == 0)
-			return PointerGetDatum(construct_empty_array(element_type));
-
-		dvalues = (Datum *) palloc(nelems * sizeof(Datum));
-		dnulls = (bool *) palloc(nelems * sizeof(bool));
-
-		/* loop through and build array of datums */
-		foreach(element, astate->elements)
-		{
-			ExprState  *e = (ExprState *) lfirst(element);
-
-			dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i]);
-			i++;
-		}
-
-		/* setup for 1-D array of the given length */
-		dims[0] = nelems;
-		lbs[0] = 1;
-
-		result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
-									element_type,
-									astate->elemlength,
-									astate->elembyval,
-									astate->elemalign);
-	}
-	else
-	{
-		/* Must be nested array expressions */
-		int			nbytes = 0;
-		int			nitems = 0;
-		int			outer_nelems = 0;
-		int			elem_ndims = 0;
-		int		   *elem_dims = NULL;
-		int		   *elem_lbs = NULL;
-		bool		firstone = true;
-		bool		havenulls = false;
-		bool		haveempty = false;
-		char	  **subdata;
-		bits8	  **subbitmaps;
-		int		   *subbytes;
-		int		   *subnitems;
-		int			i;
-		int32		dataoffset;
-		char	   *dat;
-		int			iitem;
-
-		i = list_length(astate->elements);
-		subdata = (char **) palloc(i * sizeof(char *));
-		subbitmaps = (bits8 **) palloc(i * sizeof(bits8 *));
-		subbytes = (int *) palloc(i * sizeof(int));
-		subnitems = (int *) palloc(i * sizeof(int));
-
-		/* loop through and get data area from each element */
-		foreach(element, astate->elements)
-		{
-			ExprState  *e = (ExprState *) lfirst(element);
-			bool		eisnull;
-			Datum		arraydatum;
-			ArrayType  *array;
-			int			this_ndims;
-
-			arraydatum = ExecEvalExpr(e, econtext, &eisnull);
-			/* temporarily ignore null subarrays */
-			if (eisnull)
-			{
-				haveempty = true;
-				continue;
-			}
-
-			array = DatumGetArrayTypeP(arraydatum);
-
-			/* run-time double-check on element type */
-			if (element_type != ARR_ELEMTYPE(array))
-				ereport(ERROR,
-						(errcode(ERRCODE_DATATYPE_MISMATCH),
-						 errmsg("cannot merge incompatible arrays"),
-						 errdetail("Array with element type %s cannot be "
-						 "included in ARRAY construct with element type %s.",
-								   format_type_be(ARR_ELEMTYPE(array)),
-								   format_type_be(element_type))));
-
-			this_ndims = ARR_NDIM(array);
-			/* temporarily ignore zero-dimensional subarrays */
-			if (this_ndims <= 0)
-			{
-				haveempty = true;
-				continue;
-			}
-
-			if (firstone)
-			{
-				/* Get sub-array details from first member */
-				elem_ndims = this_ndims;
-				ndims = elem_ndims + 1;
-				if (ndims <= 0 || ndims > MAXDIM)
-					ereport(ERROR,
-							(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-						  errmsg("number of array dimensions (%d) exceeds " \
-								 "the maximum allowed (%d)", ndims, MAXDIM)));
-
-				elem_dims = (int *) palloc(elem_ndims * sizeof(int));
-				memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
-				elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
-				memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
-
-				firstone = false;
-			}
-			else
-			{
-				/* Check other sub-arrays are compatible */
-				if (elem_ndims != this_ndims ||
-					memcmp(elem_dims, ARR_DIMS(array),
-						   elem_ndims * sizeof(int)) != 0 ||
-					memcmp(elem_lbs, ARR_LBOUND(array),
-						   elem_ndims * sizeof(int)) != 0)
-					ereport(ERROR,
-							(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-							 errmsg("multidimensional arrays must have array "
-									"expressions with matching dimensions")));
-			}
-
-			subdata[outer_nelems] = ARR_DATA_PTR(array);
-			subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
-			subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
-			nbytes += subbytes[outer_nelems];
-			subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
-													 ARR_DIMS(array));
-			nitems += subnitems[outer_nelems];
-			havenulls |= ARR_HASNULL(array);
-			outer_nelems++;
-		}
-
-		/*
-		 * If all items were null or empty arrays, return an empty array;
-		 * otherwise, if some were and some weren't, raise error.  (Note: we
-		 * must special-case this somehow to avoid trying to generate a 1-D
-		 * array formed from empty arrays.  It's not ideal...)
-		 */
-		if (haveempty)
-		{
-			if (ndims == 0)		/* didn't find any nonempty array */
-				return PointerGetDatum(construct_empty_array(element_type));
-			ereport(ERROR,
-					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-					 errmsg("multidimensional arrays must have array "
-							"expressions with matching dimensions")));
-		}
-
-		/* setup for multi-D array */
-		dims[0] = outer_nelems;
-		lbs[0] = 1;
-		for (i = 1; i < ndims; i++)
-		{
-			dims[i] = elem_dims[i - 1];
-			lbs[i] = elem_lbs[i - 1];
-		}
-
-		if (havenulls)
-		{
-			dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
-			nbytes += dataoffset;
-		}
-		else
-		{
-			dataoffset = 0;		/* marker for no null bitmap */
-			nbytes += ARR_OVERHEAD_NONULLS(ndims);
-		}
-
-		result = (ArrayType *) palloc(nbytes);
-		SET_VARSIZE(result, nbytes);
-		result->ndim = ndims;
-		result->dataoffset = dataoffset;
-		result->elemtype = element_type;
-		memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
-		memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
-
-		dat = ARR_DATA_PTR(result);
-		iitem = 0;
-		for (i = 0; i < outer_nelems; i++)
-		{
-			memcpy(dat, subdata[i], subbytes[i]);
-			dat += subbytes[i];
-			if (havenulls)
-				array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
-								  subbitmaps[i], 0,
-								  subnitems[i]);
-			iitem += subnitems[i];
-		}
-	}
-
-	return PointerGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalRow - ROW() expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRow(RowExprState *rstate,
-			ExprContext *econtext,
-			bool *isNull)
-{
-	HeapTuple	tuple;
-	Datum	   *values;
-	bool	   *isnull;
-	int			natts;
-	ListCell   *arg;
-	int			i;
-
-	/* Set non-null as default */
-	*isNull = false;
-
-	/* Allocate workspace */
-	natts = rstate->tupdesc->natts;
-	values = (Datum *) palloc0(natts * sizeof(Datum));
-	isnull = (bool *) palloc(natts * sizeof(bool));
-
-	/* preset to nulls in case rowtype has some later-added columns */
-	memset(isnull, true, natts * sizeof(bool));
-
-	/* Evaluate field values */
-	i = 0;
-	foreach(arg, rstate->args)
-	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-
-		values[i] = ExecEvalExpr(e, econtext, &isnull[i]);
-		i++;
-	}
-
-	tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
-
-	pfree(values);
-	pfree(isnull);
-
-	return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalRowCompare - ROW() comparison-op ROW()
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRowCompare(RowCompareExprState *rstate,
-				   ExprContext *econtext,
-				   bool *isNull)
-{
-	bool		result;
-	RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype;
-	int32		cmpresult = 0;
-	ListCell   *l;
-	ListCell   *r;
-	int			i;
-
-	*isNull = true;				/* until we get a result */
-
-	i = 0;
-	forboth(l, rstate->largs, r, rstate->rargs)
-	{
-		ExprState  *le = (ExprState *) lfirst(l);
-		ExprState  *re = (ExprState *) lfirst(r);
-		FunctionCallInfoData locfcinfo;
-
-		InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2,
-								 rstate->collations[i],
-								 NULL, NULL);
-		locfcinfo.arg[0] = ExecEvalExpr(le, econtext,
-										&locfcinfo.argnull[0]);
-		locfcinfo.arg[1] = ExecEvalExpr(re, econtext,
-										&locfcinfo.argnull[1]);
-		if (rstate->funcs[i].fn_strict &&
-			(locfcinfo.argnull[0] || locfcinfo.argnull[1]))
-			return (Datum) 0;	/* force NULL result */
-		locfcinfo.isnull = false;
-		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
-		if (locfcinfo.isnull)
-			return (Datum) 0;	/* force NULL result */
-		if (cmpresult != 0)
-			break;				/* no need to compare remaining columns */
-		i++;
-	}
-
-	switch (rctype)
-	{
-			/* EQ and NE cases aren't allowed here */
-		case ROWCOMPARE_LT:
-			result = (cmpresult < 0);
-			break;
-		case ROWCOMPARE_LE:
-			result = (cmpresult <= 0);
-			break;
-		case ROWCOMPARE_GE:
-			result = (cmpresult >= 0);
-			break;
-		case ROWCOMPARE_GT:
-			result = (cmpresult > 0);
-			break;
-		default:
-			elog(ERROR, "unrecognized RowCompareType: %d", (int) rctype);
-			result = 0;			/* keep compiler quiet */
-			break;
-	}
-
-	*isNull = false;
-	return BoolGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCoalesce
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
-				 bool *isNull)
-{
-	ListCell   *arg;
-
-	/* Simply loop through until something NOT NULL is found */
-	foreach(arg, coalesceExpr->args)
-	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-
-		value = ExecEvalExpr(e, econtext, isNull);
-		if (!*isNull)
-			return value;
-	}
-
-	/* Else return NULL */
-	*isNull = true;
-	return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalMinMax
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
-			   bool *isNull)
-{
-	Datum		result = (Datum) 0;
-	MinMaxExpr *minmax = (MinMaxExpr *) minmaxExpr->xprstate.expr;
-	Oid			collation = minmax->inputcollid;
-	MinMaxOp	op = minmax->op;
-	FunctionCallInfoData locfcinfo;
-	ListCell   *arg;
-
-	*isNull = true;				/* until we get a result */
-
-	InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2,
-							 collation, NULL, NULL);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-
-	foreach(arg, minmaxExpr->args)
-	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-		bool		valueIsNull;
-		int32		cmpresult;
-
-		value = ExecEvalExpr(e, econtext, &valueIsNull);
-		if (valueIsNull)
-			continue;			/* ignore NULL inputs */
-
-		if (*isNull)
-		{
-			/* first nonnull input, adopt value */
-			result = value;
-			*isNull = false;
-		}
-		else
-		{
-			/* apply comparison function */
-			locfcinfo.arg[0] = result;
-			locfcinfo.arg[1] = value;
-			locfcinfo.isnull = false;
-			cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
-			if (locfcinfo.isnull)		/* probably should not happen */
-				continue;
-			if (cmpresult > 0 && op == IS_LEAST)
-				result = value;
-			else if (cmpresult < 0 && op == IS_GREATEST)
-				result = value;
-		}
-	}
-
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalSQLValueFunction
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalSQLValueFunction(ExprState *svfExpr,
-						 ExprContext *econtext,
-						 bool *isNull)
-{
-	Datum		result = (Datum) 0;
-	SQLValueFunction *svf = (SQLValueFunction *) svfExpr->expr;
-	FunctionCallInfoData fcinfo;
-
-	*isNull = false;
-
-	/*
-	 * Note: current_schema() can return NULL.  current_user() etc currently
-	 * cannot, but might as well code those cases the same way for safety.
-	 */
-	switch (svf->op)
-	{
-		case SVFOP_CURRENT_DATE:
-			result = DateADTGetDatum(GetSQLCurrentDate());
-			break;
-		case SVFOP_CURRENT_TIME:
-		case SVFOP_CURRENT_TIME_N:
-			result = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
-			break;
-		case SVFOP_CURRENT_TIMESTAMP:
-		case SVFOP_CURRENT_TIMESTAMP_N:
-			result = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
-			break;
-		case SVFOP_LOCALTIME:
-		case SVFOP_LOCALTIME_N:
-			result = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
-			break;
-		case SVFOP_LOCALTIMESTAMP:
-		case SVFOP_LOCALTIMESTAMP_N:
-			result = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
-			break;
-		case SVFOP_CURRENT_ROLE:
-		case SVFOP_CURRENT_USER:
-		case SVFOP_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = current_user(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-		case SVFOP_SESSION_USER:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = session_user(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-		case SVFOP_CURRENT_CATALOG:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = current_database(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-		case SVFOP_CURRENT_SCHEMA:
-			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-			result = current_schema(&fcinfo);
-			*isNull = fcinfo.isnull;
-			break;
-	}
-
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalXml
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-			bool *isNull)
-{
-	XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
-	Datum		value;
-	bool		isnull;
-	ListCell   *arg;
-	ListCell   *narg;
-
-	*isNull = true;				/* until we get a result */
-
-	switch (xexpr->op)
-	{
-		case IS_XMLCONCAT:
-			{
-				List	   *values = NIL;
-
-				foreach(arg, xmlExpr->args)
-				{
-					ExprState  *e = (ExprState *) lfirst(arg);
-
-					value = ExecEvalExpr(e, econtext, &isnull);
-					if (!isnull)
-						values = lappend(values, DatumGetPointer(value));
-				}
-
-				if (list_length(values) > 0)
-				{
-					*isNull = false;
-					return PointerGetDatum(xmlconcat(values));
-				}
-				else
-					return (Datum) 0;
-			}
-			break;
-
-		case IS_XMLFOREST:
-			{
-				StringInfoData buf;
-
-				initStringInfo(&buf);
-				forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
-				{
-					ExprState  *e = (ExprState *) lfirst(arg);
-					char	   *argname = strVal(lfirst(narg));
-
-					value = ExecEvalExpr(e, econtext, &isnull);
-					if (!isnull)
-					{
-						appendStringInfo(&buf, "<%s>%s</%s>",
-										 argname,
-										 map_sql_value_to_xml_value(value, exprType((Node *) e->expr), true),
-										 argname);
-						*isNull = false;
-					}
-				}
-
-				if (*isNull)
-				{
-					pfree(buf.data);
-					return (Datum) 0;
-				}
-				else
-				{
-					text	   *result;
-
-					result = cstring_to_text_with_len(buf.data, buf.len);
-					pfree(buf.data);
-
-					return PointerGetDatum(result);
-				}
-			}
-			break;
-
-		case IS_XMLELEMENT:
-			*isNull = false;
-			return PointerGetDatum(xmlelement(xmlExpr, econtext));
-			break;
-
-		case IS_XMLPARSE:
-			{
-				ExprState  *e;
-				text	   *data;
-				bool		preserve_whitespace;
-
-				/* arguments are known to be text, bool */
-				Assert(list_length(xmlExpr->args) == 2);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-				data = DatumGetTextP(value);
-
-				e = (ExprState *) lsecond(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)		/* probably can't happen */
-					return (Datum) 0;
-				preserve_whitespace = DatumGetBool(value);
-
-				*isNull = false;
-
-				return PointerGetDatum(xmlparse(data,
-												xexpr->xmloption,
-												preserve_whitespace));
-			}
-			break;
-
-		case IS_XMLPI:
-			{
-				ExprState  *e;
-				text	   *arg;
-
-				/* optional argument is known to be text */
-				Assert(list_length(xmlExpr->args) <= 1);
-
-				if (xmlExpr->args)
-				{
-					e = (ExprState *) linitial(xmlExpr->args);
-					value = ExecEvalExpr(e, econtext, &isnull);
-					if (isnull)
-						arg = NULL;
-					else
-						arg = DatumGetTextP(value);
-				}
-				else
-				{
-					arg = NULL;
-					isnull = false;
-				}
-
-				return PointerGetDatum(xmlpi(xexpr->name, arg, isnull, isNull));
-			}
-			break;
-
-		case IS_XMLROOT:
-			{
-				ExprState  *e;
-				xmltype    *data;
-				text	   *version;
-				int			standalone;
-
-				/* arguments are known to be xml, text, int */
-				Assert(list_length(xmlExpr->args) == 3);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-				data = DatumGetXmlP(value);
-
-				e = (ExprState *) lsecond(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					version = NULL;
-				else
-					version = DatumGetTextP(value);
-
-				e = (ExprState *) lthird(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				standalone = DatumGetInt32(value);
-
-				*isNull = false;
-
-				return PointerGetDatum(xmlroot(data,
-											   version,
-											   standalone));
-			}
-			break;
-
-		case IS_XMLSERIALIZE:
-			{
-				ExprState  *e;
-
-				/* argument type is known to be xml */
-				Assert(list_length(xmlExpr->args) == 1);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-
-				*isNull = false;
-
-				return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption));
-			}
-			break;
-
-		case IS_DOCUMENT:
-			{
-				ExprState  *e;
-
-				/* optional argument is known to be xml */
-				Assert(list_length(xmlExpr->args) == 1);
-
-				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull);
-				if (isnull)
-					return (Datum) 0;
-				else
-				{
-					*isNull = false;
-					return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
-				}
-			}
-			break;
-	}
-
-	elog(ERROR, "unrecognized XML operation");
-	return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalNullIf
- *
- * Note that this is *always* derived from the equals operator,
- * but since we need special processing of the arguments
- * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNullIf(FuncExprState *nullIfExpr,
-			   ExprContext *econtext,
-			   bool *isNull)
-{
-	Datum		result;
-	FunctionCallInfo fcinfo;
-
-	/*
-	 * Initialize function cache if first time through
-	 */
-	if (nullIfExpr->func.fn_oid == InvalidOid)
-	{
-		NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
-
-		init_fcache(op->opfuncid, op->inputcollid, nullIfExpr,
-					econtext->ecxt_per_query_memory, false, false);
-	}
-
-	/*
-	 * Evaluate arguments
-	 */
-	fcinfo = &nullIfExpr->fcinfo_data;
-	ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext);
-	Assert(fcinfo->nargs == 2);
-
-	/* if either argument is NULL they can't be equal */
-	if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
-	{
-		fcinfo->isnull = false;
-		result = FunctionCallInvoke(fcinfo);
-		/* if the arguments are equal return null */
-		if (!fcinfo->isnull && DatumGetBool(result))
-		{
-			*isNull = true;
-			return (Datum) 0;
-		}
-	}
-
-	/* else return first argument */
-	*isNull = fcinfo->argnull[0];
-	return fcinfo->arg[0];
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalNullTest
- *
- *		Evaluate a NullTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNullTest(NullTestState *nstate,
-				 ExprContext *econtext,
-				 bool *isNull)
-{
-	NullTest   *ntest = (NullTest *) nstate->xprstate.expr;
-	Datum		result;
-
-	result = ExecEvalExpr(nstate->arg, econtext, isNull);
-
-	if (ntest->argisrow && !(*isNull))
-	{
-		/*
-		 * The SQL standard defines IS [NOT] NULL for a non-null rowtype
-		 * argument as:
-		 *
-		 * "R IS NULL" is true if every field is the null value.
-		 *
-		 * "R IS NOT NULL" is true if no field is the null value.
-		 *
-		 * This definition is (apparently intentionally) not recursive; so our
-		 * tests on the fields are primitive attisnull tests, not recursive
-		 * checks to see if they are all-nulls or no-nulls rowtypes.
-		 *
-		 * The standard does not consider the possibility of zero-field rows,
-		 * but here we consider them to vacuously satisfy both predicates.
-		 */
-		HeapTupleHeader tuple;
-		Oid			tupType;
-		int32		tupTypmod;
-		TupleDesc	tupDesc;
-		HeapTupleData tmptup;
-		int			att;
-
-		tuple = DatumGetHeapTupleHeader(result);
-
-		tupType = HeapTupleHeaderGetTypeId(tuple);
-		tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
-		/* Lookup tupdesc if first time through or if type changes */
-		tupDesc = get_cached_rowtype(tupType, tupTypmod,
-									 &nstate->argdesc, econtext);
-
-		/*
-		 * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
-		 */
-		tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-		tmptup.t_data = tuple;
-
-		for (att = 1; att <= tupDesc->natts; att++)
-		{
-			/* ignore dropped columns */
-			if (tupDesc->attrs[att - 1]->attisdropped)
-				continue;
-			if (heap_attisnull(&tmptup, att))
-			{
-				/* null field disproves IS NOT NULL */
-				if (ntest->nulltesttype == IS_NOT_NULL)
-					return BoolGetDatum(false);
-			}
-			else
-			{
-				/* non-null field disproves IS NULL */
-				if (ntest->nulltesttype == IS_NULL)
-					return BoolGetDatum(false);
-			}
-		}
-
-		return BoolGetDatum(true);
-	}
-	else
-	{
-		/* Simple scalar-argument case, or a null rowtype datum */
-		switch (ntest->nulltesttype)
-		{
-			case IS_NULL:
-				if (*isNull)
-				{
-					*isNull = false;
-					return BoolGetDatum(true);
-				}
-				else
-					return BoolGetDatum(false);
-			case IS_NOT_NULL:
-				if (*isNull)
-				{
-					*isNull = false;
-					return BoolGetDatum(false);
-				}
-				else
-					return BoolGetDatum(true);
-			default:
-				elog(ERROR, "unrecognized nulltesttype: %d",
-					 (int) ntest->nulltesttype);
-				return (Datum) 0;		/* keep compiler quiet */
-		}
-	}
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalBooleanTest
- *
- *		Evaluate a BooleanTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalBooleanTest(GenericExprState *bstate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
-	Datum		result;
-
-	result = ExecEvalExpr(bstate->arg, econtext, isNull);
-
-	switch (btest->booltesttype)
-	{
-		case IS_TRUE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(false);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(true);
-			else
-				return BoolGetDatum(false);
-		case IS_NOT_TRUE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(true);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(false);
-			else
-				return BoolGetDatum(true);
-		case IS_FALSE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(false);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(false);
-			else
-				return BoolGetDatum(true);
-		case IS_NOT_FALSE:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(true);
-			}
-			else if (DatumGetBool(result))
-				return BoolGetDatum(true);
-			else
-				return BoolGetDatum(false);
-		case IS_UNKNOWN:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(true);
-			}
-			else
-				return BoolGetDatum(false);
-		case IS_NOT_UNKNOWN:
-			if (*isNull)
-			{
-				*isNull = false;
-				return BoolGetDatum(false);
-			}
-			else
-				return BoolGetDatum(true);
-		default:
-			elog(ERROR, "unrecognized booltesttype: %d",
-				 (int) btest->booltesttype);
-			return (Datum) 0;	/* keep compiler quiet */
-	}
-}
-
-/*
- * ExecEvalCoerceToDomain
- *
- * Test the provided data against the domain constraint(s).  If the data
- * passes the constraint specifications, pass it through (return the
- * datum) otherwise throw an error.
- */
-static Datum
-ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
-					   bool *isNull)
-{
-	CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
-	Datum		result;
-	ListCell   *l;
-
-	result = ExecEvalExpr(cstate->arg, econtext, isNull);
-
-	/* Make sure we have up-to-date constraints */
-	UpdateDomainConstraintRef(cstate->constraint_ref);
-
-	foreach(l, cstate->constraint_ref->constraints)
-	{
-		DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
-
-		switch (con->constrainttype)
-		{
-			case DOM_CONSTRAINT_NOTNULL:
-				if (*isNull)
-					ereport(ERROR,
-							(errcode(ERRCODE_NOT_NULL_VIOLATION),
-							 errmsg("domain %s does not allow null values",
-									format_type_be(ctest->resulttype)),
-							 errdatatype(ctest->resulttype)));
-				break;
-			case DOM_CONSTRAINT_CHECK:
-				{
-					Datum		conResult;
-					bool		conIsNull;
-					Datum		save_datum;
-					bool		save_isNull;
-
-					/*
-					 * Set up value to be returned by CoerceToDomainValue
-					 * nodes. We must save and restore prior setting of
-					 * econtext's domainValue fields, in case this node is
-					 * itself within a check expression for another domain.
-					 *
-					 * Also, if we are working with a read-write expanded
-					 * datum, be sure that what we pass to CHECK expressions
-					 * is a read-only pointer; else called functions might
-					 * modify or even delete the expanded object.
-					 */
-					save_datum = econtext->domainValue_datum;
-					save_isNull = econtext->domainValue_isNull;
-
-					econtext->domainValue_datum =
-						MakeExpandedObjectReadOnly(result, *isNull,
-									 cstate->constraint_ref->tcache->typlen);
-					econtext->domainValue_isNull = *isNull;
-
-					conResult = ExecEvalExpr(con->check_expr, econtext,
-											 &conIsNull);
-
-					if (!conIsNull &&
-						!DatumGetBool(conResult))
-						ereport(ERROR,
-								(errcode(ERRCODE_CHECK_VIOLATION),
-								 errmsg("value for domain %s violates check constraint \"%s\"",
-										format_type_be(ctest->resulttype),
-										con->name),
-								 errdomainconstraint(ctest->resulttype,
-													 con->name)));
-					econtext->domainValue_datum = save_datum;
-					econtext->domainValue_isNull = save_isNull;
-
-					break;
-				}
-			default:
-				elog(ERROR, "unrecognized constraint type: %d",
-					 (int) con->constrainttype);
-				break;
-		}
-	}
-
-	/* If all has gone well (constraints did not fail) return the datum */
-	return result;
-}
-
-/*
- * ExecEvalCoerceToDomainValue
- *
- * Return the value stored by CoerceToDomain.
- */
-static Datum
-ExecEvalCoerceToDomainValue(ExprState *exprstate,
-							ExprContext *econtext,
-							bool *isNull)
-{
-	*isNull = econtext->domainValue_isNull;
-	return econtext->domainValue_datum;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalFieldSelect
- *
- *		Evaluate a FieldSelect node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldSelect(FieldSelectState *fstate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
-	AttrNumber	fieldnum = fselect->fieldnum;
-	Datum		result;
-	Datum		tupDatum;
-	HeapTupleHeader tuple;
-	Oid			tupType;
-	int32		tupTypmod;
-	TupleDesc	tupDesc;
-	Form_pg_attribute attr;
-	HeapTupleData tmptup;
-
-	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
-	if (*isNull)
-		return tupDatum;
-
-	tuple = DatumGetHeapTupleHeader(tupDatum);
-
-	tupType = HeapTupleHeaderGetTypeId(tuple);
-	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
-	/* Lookup tupdesc if first time through or if type changes */
-	tupDesc = get_cached_rowtype(tupType, tupTypmod,
-								 &fstate->argdesc, econtext);
-
-	/*
-	 * Find field's attr record.  Note we don't support system columns here: a
-	 * datum tuple doesn't have valid values for most of the interesting
-	 * system columns anyway.
-	 */
-	if (fieldnum <= 0)			/* should never happen */
-		elog(ERROR, "unsupported reference to system column %d in FieldSelect",
-			 fieldnum);
-	if (fieldnum > tupDesc->natts)		/* should never happen */
-		elog(ERROR, "attribute number %d exceeds number of columns %d",
-			 fieldnum, tupDesc->natts);
-	attr = tupDesc->attrs[fieldnum - 1];
-
-	/* Check for dropped column, and force a NULL result if so */
-	if (attr->attisdropped)
-	{
-		*isNull = true;
-		return (Datum) 0;
-	}
-
-	/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
-	/* As in ExecEvalScalarVar, we should but can't check typmod */
-	if (fselect->resulttype != attr->atttypid)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("attribute %d has wrong type", fieldnum),
-				 errdetail("Table has type %s, but query expects %s.",
-						   format_type_be(attr->atttypid),
-						   format_type_be(fselect->resulttype))));
-
-	/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
-	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-	tmptup.t_data = tuple;
-
-	result = heap_getattr(&tmptup,
-						  fieldnum,
-						  tupDesc,
-						  isNull);
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalFieldStore
- *
- *		Evaluate a FieldStore node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldStore(FieldStoreState *fstate,
-				   ExprContext *econtext,
-				   bool *isNull)
-{
-	FieldStore *fstore = (FieldStore *) fstate->xprstate.expr;
-	HeapTuple	tuple;
-	Datum		tupDatum;
-	TupleDesc	tupDesc;
-	Datum	   *values;
-	bool	   *isnull;
-	Datum		save_datum;
-	bool		save_isNull;
-	ListCell   *l1,
-			   *l2;
-
-	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
-	/* Lookup tupdesc if first time through or after rescan */
-	tupDesc = get_cached_rowtype(fstore->resulttype, -1,
-								 &fstate->argdesc, econtext);
-
-	/* Allocate workspace */
-	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
-	isnull = (bool *) palloc(tupDesc->natts * sizeof(bool));
-
-	if (!*isNull)
-	{
-		/*
-		 * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
-		 * set all the fields in the struct just in case.
-		 */
-		HeapTupleHeader tuphdr;
-		HeapTupleData tmptup;
-
-		tuphdr = DatumGetHeapTupleHeader(tupDatum);
-		tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
-		ItemPointerSetInvalid(&(tmptup.t_self));
-		tmptup.t_tableOid = InvalidOid;
-		tmptup.t_data = tuphdr;
-
-		heap_deform_tuple(&tmptup, tupDesc, values, isnull);
-	}
-	else
-	{
-		/* Convert null input tuple into an all-nulls row */
-		memset(isnull, true, tupDesc->natts * sizeof(bool));
-	}
-
-	/* Result is never null */
-	*isNull = false;
-
-	save_datum = econtext->caseValue_datum;
-	save_isNull = econtext->caseValue_isNull;
-
-	forboth(l1, fstate->newvals, l2, fstore->fieldnums)
-	{
-		ExprState  *newval = (ExprState *) lfirst(l1);
-		AttrNumber	fieldnum = lfirst_int(l2);
-
-		Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
-
-		/*
-		 * Use the CaseTestExpr mechanism to pass down the old value of the
-		 * field being replaced; this is needed in case the newval is itself a
-		 * FieldStore or ArrayRef that has to obtain and modify the old value.
-		 * It's safe to reuse the CASE mechanism because there cannot be a
-		 * CASE between here and where the value would be needed, and a field
-		 * assignment can't be within a CASE either.  (So saving and restoring
-		 * the caseValue is just paranoia, but let's do it anyway.)
-		 */
-		econtext->caseValue_datum = values[fieldnum - 1];
-		econtext->caseValue_isNull = isnull[fieldnum - 1];
-
-		values[fieldnum - 1] = ExecEvalExpr(newval,
-											econtext,
-											&isnull[fieldnum - 1]);
-	}
-
-	econtext->caseValue_datum = save_datum;
-	econtext->caseValue_isNull = save_isNull;
-
-	tuple = heap_form_tuple(tupDesc, values, isnull);
-
-	pfree(values);
-	pfree(isnull);
-
-	return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalRelabelType
- *
- *		Evaluate a RelabelType node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRelabelType(GenericExprState *exprstate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	return ExecEvalExpr(exprstate->arg, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCoerceViaIO
- *
- *		Evaluate a CoerceViaIO node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
-					ExprContext *econtext,
-					bool *isNull)
-{
-	Datum		result;
-	Datum		inputval;
-	char	   *string;
-
-	inputval = ExecEvalExpr(iostate->arg, econtext, isNull);
-
-	if (*isNull)
-		string = NULL;			/* output functions are not called on nulls */
-	else
-		string = OutputFunctionCall(&iostate->outfunc, inputval);
-
-	result = InputFunctionCall(&iostate->infunc,
-							   string,
-							   iostate->intypioparam,
-							   -1);
-
-	/* The input function cannot change the null/not-null status */
-	return result;
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalArrayCoerceExpr
- *
- *		Evaluate an ArrayCoerceExpr node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
-						ExprContext *econtext,
-						bool *isNull)
-{
-	ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
-	Datum		result;
-	FunctionCallInfoData locfcinfo;
-
-	result = ExecEvalExpr(astate->arg, econtext, isNull);
-
-	if (*isNull)
-		return result;			/* nothing to do */
-
-	/*
-	 * If it's binary-compatible, modify the element type in the array header,
-	 * but otherwise leave the array as we received it.
-	 */
-	if (!OidIsValid(acoerce->elemfuncid))
-	{
-		/* Detoast input array if necessary, and copy in any case */
-		ArrayType  *array = DatumGetArrayTypePCopy(result);
-
-		ARR_ELEMTYPE(array) = astate->resultelemtype;
-		PG_RETURN_ARRAYTYPE_P(array);
-	}
-
-	/* Initialize function cache if first time through */
-	if (astate->elemfunc.fn_oid == InvalidOid)
-	{
-		AclResult	aclresult;
-
-		/* Check permission to call function */
-		aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
-									 ACL_EXECUTE);
-		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult, ACL_KIND_PROC,
-						   get_func_name(acoerce->elemfuncid));
-		InvokeFunctionExecuteHook(acoerce->elemfuncid);
-
-		/* Set up the primary fmgr lookup information */
-		fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
-					  econtext->ecxt_per_query_memory);
-		fmgr_info_set_expr((Node *) acoerce, &(astate->elemfunc));
-	}
-
-	/*
-	 * Use array_map to apply the function to each array element.
-	 *
-	 * We pass on the desttypmod and isExplicit flags whether or not the
-	 * function wants them.
-	 *
-	 * Note: coercion functions are assumed to not use collation.
-	 */
-	InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
-							 InvalidOid, NULL, NULL);
-	locfcinfo.arg[0] = result;
-	locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
-	locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
-	locfcinfo.argnull[0] = false;
-	locfcinfo.argnull[1] = false;
-	locfcinfo.argnull[2] = false;
-
-	return array_map(&locfcinfo, astate->resultelemtype, astate->amstate);
-}
-
-/* ----------------------------------------------------------------
- *		ExecEvalCurrentOfExpr
- *
- * The planner should convert CURRENT OF into a TidScan qualification, or some
- * other special handling in a ForeignScan node.  So we have to be able to do
- * ExecInitExpr on a CurrentOfExpr, but we shouldn't ever actually execute it.
- * If we get here, we suppose we must be dealing with CURRENT OF on a foreign
- * table whose FDW doesn't handle it, and complain accordingly.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull)
-{
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-		   errmsg("WHERE CURRENT OF is not supported for this table type")));
-	return 0;					/* keep compiler quiet */
-}
-
-
-/*
- * ExecEvalExprSwitchContext
- *
- * Same as ExecEvalExpr, but get into the right allocation context explicitly.
- */
-Datum
-ExecEvalExprSwitchContext(ExprState *expression,
-						  ExprContext *econtext,
-						  bool *isNull)
-{
-	Datum		retDatum;
-	MemoryContext oldContext;
-
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-	retDatum = ExecEvalExpr(expression, econtext, isNull);
-	MemoryContextSwitchTo(oldContext);
-	return retDatum;
-}
-
-
-/*
- * ExecInitExpr: prepare an expression tree for execution
- *
- * This function builds and returns an ExprState tree paralleling the given
- * Expr node tree.  The ExprState tree can then be handed to ExecEvalExpr
- * for execution.  Because the Expr tree itself is read-only as far as
- * ExecInitExpr and ExecEvalExpr are concerned, several different executions
- * of the same plan tree can occur concurrently.
- *
- * This must be called in a memory context that will last as long as repeated
- * executions of the expression are needed.  Typically the context will be
- * the same as the per-query context of the associated ExprContext.
- *
- * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
- * lists of such nodes held by the parent PlanState. Otherwise, we do very
- * little initialization here other than building the state-node tree.  Any
- * nontrivial work associated with initializing runtime info for a node should
- * happen during the first actual evaluation of that node.  (This policy lets
- * us avoid work if the node is never actually evaluated.)
- *
- * Note: there is no ExecEndExpr function; we assume that any resource
- * cleanup needed will be handled by just releasing the memory context
- * in which the state tree is built.  Functions that require additional
- * cleanup work can register a shutdown callback in the ExprContext.
- *
- *	'node' is the root of the expression tree to examine
- *	'parent' is the PlanState node that owns the expression.
- *
- * 'parent' may be NULL if we are preparing an expression that is not
- * associated with a plan tree.  (If so, it can't have aggs or subplans.)
- * This case should usually come through ExecPrepareExpr, not directly here.
- */
-ExprState *
-ExecInitExpr(Expr *node, PlanState *parent)
-{
-	ExprState  *state;
-
-	if (node == NULL)
-		return NULL;
-
-	/* Guard against stack overflow due to overly complex expressions */
-	check_stack_depth();
-
-	switch (nodeTag(node))
-	{
-		case T_Var:
-			/* varattno == InvalidAttrNumber means it's a whole-row Var */
-			if (((Var *) node)->varattno == InvalidAttrNumber)
-			{
-				WholeRowVarExprState *wstate = makeNode(WholeRowVarExprState);
-
-				wstate->parent = parent;
-				wstate->wrv_tupdesc = NULL;
-				wstate->wrv_junkFilter = NULL;
-				state = (ExprState *) wstate;
-				state->evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowVar;
-			}
-			else
-			{
-				state = makeNode(ExprState);
-				state->evalfunc = ExecEvalScalarVar;
-			}
-			break;
-		case T_Const:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalConst;
-			break;
-		case T_Param:
-			state = makeNode(ExprState);
-			switch (((Param *) node)->paramkind)
-			{
-				case PARAM_EXEC:
-					state->evalfunc = ExecEvalParamExec;
-					break;
-				case PARAM_EXTERN:
-					state->evalfunc = ExecEvalParamExtern;
-					break;
-				default:
-					elog(ERROR, "unrecognized paramkind: %d",
-						 (int) ((Param *) node)->paramkind);
-					break;
-			}
-			break;
-		case T_CoerceToDomainValue:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalCoerceToDomainValue;
-			break;
-		case T_CaseTestExpr:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalCaseTestExpr;
-			break;
-		case T_Aggref:
-			{
-				AggrefExprState *astate = makeNode(AggrefExprState);
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
-				if (parent && IsA(parent, AggState))
-				{
-					AggState   *aggstate = (AggState *) parent;
-
-					aggstate->aggs = lcons(astate, aggstate->aggs);
-					aggstate->numaggs++;
-				}
-				else
-				{
-					/* planner messed up */
-					elog(ERROR, "Aggref found in non-Agg plan node");
-				}
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_GroupingFunc:
-			{
-				GroupingFunc *grp_node = (GroupingFunc *) node;
-				GroupingFuncExprState *grp_state = makeNode(GroupingFuncExprState);
-				Agg		   *agg = NULL;
-
-				if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
-					elog(ERROR, "parent of GROUPING is not Agg node");
-
-				grp_state->aggstate = (AggState *) parent;
-
-				agg = (Agg *) (parent->plan);
-
-				if (agg->groupingSets)
-					grp_state->clauses = grp_node->cols;
-				else
-					grp_state->clauses = NIL;
-
-				state = (ExprState *) grp_state;
-				state->evalfunc = (ExprStateEvalFunc) ExecEvalGroupingFuncExpr;
-			}
-			break;
-		case T_WindowFunc:
-			{
-				WindowFunc *wfunc = (WindowFunc *) node;
-				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
-
-				wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
-				if (parent && IsA(parent, WindowAggState))
-				{
-					WindowAggState *winstate = (WindowAggState *) parent;
-					int			nfuncs;
-
-					winstate->funcs = lcons(wfstate, winstate->funcs);
-					nfuncs = ++winstate->numfuncs;
-					if (wfunc->winagg)
-						winstate->numaggs++;
-
-					wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
-														  parent);
-					wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
-													  parent);
-
-					/*
-					 * Complain if the windowfunc's arguments contain any
-					 * windowfuncs; nested window functions are semantically
-					 * nonsensical.  (This should have been caught earlier,
-					 * but we defend against it here anyway.)
-					 */
-					if (nfuncs != winstate->numfuncs)
-						ereport(ERROR,
-								(errcode(ERRCODE_WINDOWING_ERROR),
-						  errmsg("window function calls cannot be nested")));
-				}
-				else
-				{
-					/* planner messed up */
-					elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
-				}
-				state = (ExprState *) wfstate;
-			}
-			break;
-		case T_ArrayRef:
-			{
-				ArrayRef   *aref = (ArrayRef *) node;
-				ArrayRefExprState *astate = makeNode(ArrayRefExprState);
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
-				astate->refupperindexpr = (List *)
-					ExecInitExpr((Expr *) aref->refupperindexpr, parent);
-				astate->reflowerindexpr = (List *)
-					ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
-				astate->refexpr = ExecInitExpr(aref->refexpr, parent);
-				astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
-													parent);
-				/* do one-time catalog lookups for type info */
-				astate->refattrlength = get_typlen(aref->refarraytype);
-				get_typlenbyvalalign(aref->refelemtype,
-									 &astate->refelemlength,
-									 &astate->refelembyval,
-									 &astate->refelemalign);
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_FuncExpr:
-			{
-				FuncExpr   *funcexpr = (FuncExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) funcexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = funcexpr->funcretset;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_OpExpr:
-			{
-				OpExpr	   *opexpr = (OpExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) opexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = opexpr->opretset;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_DistinctExpr:
-			{
-				DistinctExpr *distinctexpr = (DistinctExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) distinctexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = false; /* not supported */
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_NullIfExpr:
-			{
-				NullIfExpr *nullifexpr = (NullIfExpr *) node;
-				FuncExprState *fstate = makeNode(FuncExprState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
-				fstate->args = (List *)
-					ExecInitExpr((Expr *) nullifexpr->args, parent);
-				fstate->func.fn_oid = InvalidOid;		/* not initialized */
-				fstate->funcReturnsSet = false; /* not supported */
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_ScalarArrayOpExpr:
-			{
-				ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
-				ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
-
-				sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
-				sstate->fxprstate.args = (List *)
-					ExecInitExpr((Expr *) opexpr->args, parent);
-				sstate->fxprstate.func.fn_oid = InvalidOid;		/* not initialized */
-				sstate->fxprstate.funcReturnsSet = false;		/* not supported */
-				sstate->element_type = InvalidOid;		/* ditto */
-				state = (ExprState *) sstate;
-			}
-			break;
-		case T_BoolExpr:
-			{
-				BoolExpr   *boolexpr = (BoolExpr *) node;
-				BoolExprState *bstate = makeNode(BoolExprState);
-
-				switch (boolexpr->boolop)
-				{
-					case AND_EXPR:
-						bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAnd;
-						break;
-					case OR_EXPR:
-						bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOr;
-						break;
-					case NOT_EXPR:
-						bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNot;
-						break;
-					default:
-						elog(ERROR, "unrecognized boolop: %d",
-							 (int) boolexpr->boolop);
-						break;
-				}
-				bstate->args = (List *)
-					ExecInitExpr((Expr *) boolexpr->args, parent);
-				state = (ExprState *) bstate;
-			}
-			break;
-		case T_SubPlan:
-			{
-				SubPlan    *subplan = (SubPlan *) node;
-				SubPlanState *sstate;
-
-				if (!parent)
-					elog(ERROR, "SubPlan found with no parent plan");
-
-				sstate = ExecInitSubPlan(subplan, parent);
-
-				/* Add SubPlanState nodes to parent->subPlan */
-				parent->subPlan = lappend(parent->subPlan, sstate);
-
-				state = (ExprState *) sstate;
-			}
-			break;
-		case T_AlternativeSubPlan:
-			{
-				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
-				AlternativeSubPlanState *asstate;
-
-				if (!parent)
-					elog(ERROR, "AlternativeSubPlan found with no parent plan");
-
-				asstate = ExecInitAlternativeSubPlan(asplan, parent);
-
-				state = (ExprState *) asstate;
-			}
-			break;
-		case T_FieldSelect:
-			{
-				FieldSelect *fselect = (FieldSelect *) node;
-				FieldSelectState *fstate = makeNode(FieldSelectState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
-				fstate->arg = ExecInitExpr(fselect->arg, parent);
-				fstate->argdesc = NULL;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_FieldStore:
-			{
-				FieldStore *fstore = (FieldStore *) node;
-				FieldStoreState *fstate = makeNode(FieldStoreState);
-
-				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
-				fstate->arg = ExecInitExpr(fstore->arg, parent);
-				fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
-				fstate->argdesc = NULL;
-				state = (ExprState *) fstate;
-			}
-			break;
-		case T_RelabelType:
-			{
-				RelabelType *relabel = (RelabelType *) node;
-				GenericExprState *gstate = makeNode(GenericExprState);
-
-				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
-				gstate->arg = ExecInitExpr(relabel->arg, parent);
-				state = (ExprState *) gstate;
-			}
-			break;
-		case T_CoerceViaIO:
-			{
-				CoerceViaIO *iocoerce = (CoerceViaIO *) node;
-				CoerceViaIOState *iostate = makeNode(CoerceViaIOState);
-				Oid			iofunc;
-				bool		typisvarlena;
-
-				iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
-				iostate->arg = ExecInitExpr(iocoerce->arg, parent);
-				/* lookup the result type's input function */
-				getTypeInputInfo(iocoerce->resulttype, &iofunc,
-								 &iostate->intypioparam);
-				fmgr_info(iofunc, &iostate->infunc);
-				/* lookup the input type's output function */
-				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
-								  &iofunc, &typisvarlena);
-				fmgr_info(iofunc, &iostate->outfunc);
-				state = (ExprState *) iostate;
-			}
-			break;
-		case T_ArrayCoerceExpr:
-			{
-				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
-				ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
-				astate->arg = ExecInitExpr(acoerce->arg, parent);
-				astate->resultelemtype = get_element_type(acoerce->resulttype);
-				if (astate->resultelemtype == InvalidOid)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("target type is not an array")));
-				/* Arrays over domains aren't supported yet */
-				Assert(getBaseType(astate->resultelemtype) ==
-					   astate->resultelemtype);
-				astate->elemfunc.fn_oid = InvalidOid;	/* not initialized */
-				astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_ConvertRowtypeExpr:
-			{
-				ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
-				ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
-				cstate->arg = ExecInitExpr(convert->arg, parent);
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_CaseExpr:
-			{
-				CaseExpr   *caseexpr = (CaseExpr *) node;
-				CaseExprState *cstate = makeNode(CaseExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
-				cstate->arg = ExecInitExpr(caseexpr->arg, parent);
-				foreach(l, caseexpr->args)
-				{
-					CaseWhen   *when = castNode(CaseWhen, lfirst(l));
-					CaseWhenState *wstate = makeNode(CaseWhenState);
-
-					wstate->xprstate.evalfunc = NULL;	/* not used */
-					wstate->xprstate.expr = (Expr *) when;
-					wstate->expr = ExecInitExpr(when->expr, parent);
-					wstate->result = ExecInitExpr(when->result, parent);
-					outlist = lappend(outlist, wstate);
-				}
-				cstate->args = outlist;
-				cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
-				if (caseexpr->arg)
-					cstate->argtyplen = get_typlen(exprType((Node *) caseexpr->arg));
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_ArrayExpr:
-			{
-				ArrayExpr  *arrayexpr = (ArrayExpr *) node;
-				ArrayExprState *astate = makeNode(ArrayExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArray;
-				foreach(l, arrayexpr->elements)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				astate->elements = outlist;
-				/* do one-time catalog lookup for type info */
-				get_typlenbyvalalign(arrayexpr->element_typeid,
-									 &astate->elemlength,
-									 &astate->elembyval,
-									 &astate->elemalign);
-				state = (ExprState *) astate;
-			}
-			break;
-		case T_RowExpr:
-			{
-				RowExpr    *rowexpr = (RowExpr *) node;
-				RowExprState *rstate = makeNode(RowExprState);
-				Form_pg_attribute *attrs;
-				List	   *outlist = NIL;
-				ListCell   *l;
-				int			i;
-
-				rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow;
-				/* Build tupdesc to describe result tuples */
-				if (rowexpr->row_typeid == RECORDOID)
-				{
-					/* generic record, use types of given expressions */
-					rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
-				}
-				else
-				{
-					/* it's been cast to a named type, use that */
-					rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
-				}
-				/* In either case, adopt RowExpr's column aliases */
-				ExecTypeSetColNames(rstate->tupdesc, rowexpr->colnames);
-				/* Bless the tupdesc in case it's now of type RECORD */
-				BlessTupleDesc(rstate->tupdesc);
-				/* Set up evaluation, skipping any deleted columns */
-				Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
-				attrs = rstate->tupdesc->attrs;
-				i = 0;
-				foreach(l, rowexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					if (!attrs[i]->attisdropped)
-					{
-						/*
-						 * Guard against ALTER COLUMN TYPE on rowtype since
-						 * the RowExpr was created.  XXX should we check
-						 * typmod too?	Not sure we can be sure it'll be the
-						 * same.
-						 */
-						if (exprType((Node *) e) != attrs[i]->atttypid)
-							ereport(ERROR,
-									(errcode(ERRCODE_DATATYPE_MISMATCH),
-									 errmsg("ROW() column has type %s instead of type %s",
-										format_type_be(exprType((Node *) e)),
-									   format_type_be(attrs[i]->atttypid))));
-					}
-					else
-					{
-						/*
-						 * Ignore original expression and insert a NULL. We
-						 * don't really care what type of NULL it is, so
-						 * always make an int4 NULL.
-						 */
-						e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
-					}
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-					i++;
-				}
-				rstate->args = outlist;
-				state = (ExprState *) rstate;
-			}
-			break;
-		case T_RowCompareExpr:
-			{
-				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
-				RowCompareExprState *rstate = makeNode(RowCompareExprState);
-				int			nopers = list_length(rcexpr->opnos);
-				List	   *outlist;
-				ListCell   *l;
-				ListCell   *l2;
-				ListCell   *l3;
-				int			i;
-
-				rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
-				Assert(list_length(rcexpr->largs) == nopers);
-				outlist = NIL;
-				foreach(l, rcexpr->largs)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				rstate->largs = outlist;
-				Assert(list_length(rcexpr->rargs) == nopers);
-				outlist = NIL;
-				foreach(l, rcexpr->rargs)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				rstate->rargs = outlist;
-				Assert(list_length(rcexpr->opfamilies) == nopers);
-				rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
-				rstate->collations = (Oid *) palloc(nopers * sizeof(Oid));
-				i = 0;
-				forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids)
-				{
-					Oid			opno = lfirst_oid(l);
-					Oid			opfamily = lfirst_oid(l2);
-					Oid			inputcollid = lfirst_oid(l3);
-					int			strategy;
-					Oid			lefttype;
-					Oid			righttype;
-					Oid			proc;
-
-					get_op_opfamily_properties(opno, opfamily, false,
-											   &strategy,
-											   &lefttype,
-											   &righttype);
-					proc = get_opfamily_proc(opfamily,
-											 lefttype,
-											 righttype,
-											 BTORDER_PROC);
-
-					/*
-					 * If we enforced permissions checks on index support
-					 * functions, we'd need to make a check here.  But the
-					 * index support machinery doesn't do that, and neither
-					 * does this code.
-					 */
-					fmgr_info(proc, &(rstate->funcs[i]));
-					rstate->collations[i] = inputcollid;
-					i++;
-				}
-				state = (ExprState *) rstate;
-			}
-			break;
-		case T_CoalesceExpr:
-			{
-				CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
-				CoalesceExprState *cstate = makeNode(CoalesceExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoalesce;
-				foreach(l, coalesceexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				cstate->args = outlist;
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_MinMaxExpr:
-			{
-				MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
-				MinMaxExprState *mstate = makeNode(MinMaxExprState);
-				List	   *outlist = NIL;
-				ListCell   *l;
-				TypeCacheEntry *typentry;
-
-				mstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalMinMax;
-				foreach(l, minmaxexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(l);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				mstate->args = outlist;
-				/* Look up the btree comparison function for the datatype */
-				typentry = lookup_type_cache(minmaxexpr->minmaxtype,
-											 TYPECACHE_CMP_PROC);
-				if (!OidIsValid(typentry->cmp_proc))
-					ereport(ERROR,
-							(errcode(ERRCODE_UNDEFINED_FUNCTION),
-							 errmsg("could not identify a comparison function for type %s",
-									format_type_be(minmaxexpr->minmaxtype))));
-
-				/*
-				 * If we enforced permissions checks on index support
-				 * functions, we'd need to make a check here.  But the index
-				 * support machinery doesn't do that, and neither does this
-				 * code.
-				 */
-				fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
-				state = (ExprState *) mstate;
-			}
-			break;
-		case T_SQLValueFunction:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalSQLValueFunction;
-			break;
-		case T_XmlExpr:
-			{
-				XmlExpr    *xexpr = (XmlExpr *) node;
-				XmlExprState *xstate = makeNode(XmlExprState);
-				List	   *outlist;
-				ListCell   *arg;
-
-				xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml;
-				outlist = NIL;
-				foreach(arg, xexpr->named_args)
-				{
-					Expr	   *e = (Expr *) lfirst(arg);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				xstate->named_args = outlist;
-
-				outlist = NIL;
-				foreach(arg, xexpr->args)
-				{
-					Expr	   *e = (Expr *) lfirst(arg);
-					ExprState  *estate;
-
-					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
-				}
-				xstate->args = outlist;
-
-				state = (ExprState *) xstate;
-			}
-			break;
-		case T_NullTest:
-			{
-				NullTest   *ntest = (NullTest *) node;
-				NullTestState *nstate = makeNode(NullTestState);
-
-				nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
-				nstate->arg = ExecInitExpr(ntest->arg, parent);
-				nstate->argdesc = NULL;
-				state = (ExprState *) nstate;
-			}
-			break;
-		case T_BooleanTest:
-			{
-				BooleanTest *btest = (BooleanTest *) node;
-				GenericExprState *gstate = makeNode(GenericExprState);
-
-				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
-				gstate->arg = ExecInitExpr(btest->arg, parent);
-				state = (ExprState *) gstate;
-			}
-			break;
-		case T_CoerceToDomain:
-			{
-				CoerceToDomain *ctest = (CoerceToDomain *) node;
-				CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
-
-				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
-				cstate->arg = ExecInitExpr(ctest->arg, parent);
-				/* We spend an extra palloc to reduce header inclusions */
-				cstate->constraint_ref = (DomainConstraintRef *)
-					palloc(sizeof(DomainConstraintRef));
-				InitDomainConstraintRef(ctest->resulttype,
-										cstate->constraint_ref,
-										CurrentMemoryContext);
-				state = (ExprState *) cstate;
-			}
-			break;
-		case T_CurrentOfExpr:
-			state = makeNode(ExprState);
-			state->evalfunc = ExecEvalCurrentOfExpr;
-			break;
-		case T_TargetEntry:
-			{
-				TargetEntry *tle = (TargetEntry *) node;
-				GenericExprState *gstate = makeNode(GenericExprState);
-
-				gstate->xprstate.evalfunc = NULL;		/* not used */
-				gstate->arg = ExecInitExpr(tle->expr, parent);
-				state = (ExprState *) gstate;
-			}
-			break;
-		case T_List:
-			{
-				List	   *outlist = NIL;
-				ListCell   *l;
-
-				foreach(l, (List *) node)
-				{
-					outlist = lappend(outlist,
-									  ExecInitExpr((Expr *) lfirst(l),
-												   parent));
-				}
-				/* Don't fall through to the "common" code below */
-				return (ExprState *) outlist;
-			}
-		default:
-			elog(ERROR, "unrecognized node type: %d",
-				 (int) nodeTag(node));
-			state = NULL;		/* keep compiler quiet */
-			break;
-	}
-
-	/* Common code for all state-node types */
-	state->expr = node;
-
-	return state;
-}
-
-/*
- * ExecPrepareExpr --- initialize for expression execution outside a normal
- * Plan tree context.
- *
- * This differs from ExecInitExpr in that we don't assume the caller is
- * already running in the EState's per-query context.  Also, we run the
- * passed expression tree through expression_planner() to prepare it for
- * execution.  (In ordinary Plan trees the regular planning process will have
- * made the appropriate transformations on expressions, but for standalone
- * expressions this won't have happened.)
- */
-ExprState *
-ExecPrepareExpr(Expr *node, EState *estate)
-{
-	ExprState  *result;
-	MemoryContext oldcontext;
-
-	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
-
-	node = expression_planner(node);
-
-	result = ExecInitExpr(node, NULL);
-
-	MemoryContextSwitchTo(oldcontext);
-
-	return result;
-}
-
-
-/* ----------------------------------------------------------------
- *					 ExecQual / ExecTargetList / ExecProject
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5137,177 +1075,11 @@ ExecCleanTargetListLength(List *targetlist)
 
 	foreach(tl, targetlist)
 	{
-		TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
+		TargetEntry *curTle = (TargetEntry *) lfirst(tl);
 
+		Assert(IsA(curTle, TargetEntry));
 		if (!curTle->resjunk)
 			len++;
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.
- *
- * tupdesc must describe the rowtype of the expected result.
- *
- * Results are stored into the passed values and isnull arrays.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static void
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-	}
-
-	MemoryContextSwitchTo(oldContext);
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace.
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		ExecTargetList(projInfo->pi_targetlist,
-					   slot->tts_tupleDescriptor,
-					   econtext,
-					   slot->tts_values,
-					   slot->tts_isnull);
-	}
-
-	/*
-	 * Mark the result slot as containing a valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 65196795d7..138a596743 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState *qual;
 	ProjectionInfo *projInfo;
 
 	/*
@@ -187,7 +187,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual, econtext))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index d205101b89..5bc89f8554 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -470,136 +470,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -680,9 +550,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index aa08152350..2f3355636d 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1639,7 +1639,7 @@ project_aggregates(AggState *aggstate)
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual, econtext))
 	{
 		/*
 		 * Form and return projection tuple using the aggregate results and
@@ -2506,13 +2506,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	 * under SQL semantics anyway (and it's forbidden by the spec). Because
 	 * that is true, we don't need to worry about evaluating the aggs in any
 	 * particular order.
+	 * FIXME: adjust comment (now added in projection)
 	 */
-	aggstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) aggstate);
 
 	/*
 	 * Initialize child nodes.
@@ -2724,7 +2721,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	foreach(l, aggstate->aggs)
 	{
 		AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
-		Aggref	   *aggref = (Aggref *) aggrefstate->xprstate.expr;
+		Aggref	   *aggref = aggrefstate->aggref;
 		AggStatePerAgg peragg;
 		AggStatePerTrans pertrans;
 		int			existing_aggno;
@@ -3024,11 +3021,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	/* and then create a projection for that targetlist */
 	aggstate->evaldesc = ExecTypeFromTL(combined_inputeval, false);
 	aggstate->evalslot = ExecInitExtraTupleSlot(estate);
-	combined_inputeval = (List *) ExecInitExpr((Expr *) combined_inputeval,
-											   (PlanState *) aggstate);
 	aggstate->evalproj = ExecBuildProjectionInfo(combined_inputeval,
 												 aggstate->tmpcontext,
 												 aggstate->evalslot,
+												 &aggstate->ss.ps,
 												 NULL);
 	ExecSetSlotDescriptor(aggstate->evalslot, aggstate->evaldesc);
 
@@ -3206,8 +3202,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 	naggs = aggstate->numaggs;
 	pertrans->aggfilter = ExecInitExpr(aggref->aggfilter,
 									   (PlanState *) aggstate);
-	pertrans->aggdirectargs = (List *) ExecInitExpr((Expr *) aggref->aggdirectargs,
-													(PlanState *) aggstate);
+	pertrans->aggdirectargs = ExecInitExprList(aggref->aggdirectargs,
+											   (PlanState *) aggstate);
 
 	/*
 	 * Complain if the aggregate's arguments contain any aggregates; nested
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 2e9ff7d1b9..19eb1755be 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -319,7 +319,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -654,7 +654,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig, econtext);
 }
 
 /* ----------------------------------------------------------------
@@ -837,15 +837,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 8f4e0f527e..bed7949c5a 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,12 +242,8 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index d464748290..57a4ce3218 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -49,11 +49,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	ExecAssignExprContext(estate, &css->ss.ps);
 
 	/* initialize child expressions */
-	css->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
-					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
 					 (PlanState *) css);
 
 	/* tuple table initialization */
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 3b6d1390eb..9ae1561404 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals, econtext);
 }
 
 /* ----------------------------------------------------------------
@@ -155,15 +155,10 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 972022784d..250efcb449 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -35,7 +35,7 @@
  */
 typedef struct FunctionScanPerFuncState
 {
-	ExprState  *funcexpr;		/* state of the expression being evaluated */
+	SetExprState *setexpr;		/* state of the expression being evaluated */
 	TupleDesc	tupdesc;		/* desc of the function result type */
 	int			colcount;		/* expected number of result columns */
 	Tuplestorestate *tstore;	/* holds the function result set */
@@ -92,7 +92,7 @@ FunctionNext(FunctionScanState *node)
 		if (tstore == NULL)
 		{
 			node->funcstates[0].tstore = tstore =
-				ExecMakeTableFunctionResult(node->funcstates[0].funcexpr,
+				ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
 											node->ss.ps.ps_ExprContext,
 											node->argcontext,
 											node->funcstates[0].tupdesc,
@@ -151,7 +151,7 @@ FunctionNext(FunctionScanState *node)
 		if (fs->tstore == NULL)
 		{
 			fs->tstore =
-				ExecMakeTableFunctionResult(fs->funcexpr,
+				ExecMakeTableFunctionResult(fs->setexpr,
 											node->ss.ps.ps_ExprContext,
 											node->argcontext,
 											fs->tupdesc,
@@ -340,11 +340,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
 					 (PlanState *) scanstate);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
@@ -361,7 +358,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 		Oid			funcrettype;
 		TupleDesc	tupdesc;
 
-		fs->funcexpr = ExecInitExpr((Expr *) funcexpr, (PlanState *) scanstate);
+		fs->setexpr =
+			ExecInitTableFunctionResult((Expr *) funcexpr,
+										scanstate->ss.ps.ps_ExprContext,
+										&scanstate->ss.ps);
 
 		/*
 		 * Don't allocate the tuplestores; the actual calls to the functions
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 32c97d390e..1e5b1b7675 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -81,12 +81,8 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	gatherstate->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) gatherstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 62a6b1866d..15659d1d2c 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -86,12 +86,9 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	gm_state->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) gm_state);
-	gm_state->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gm_state);
+	gm_state->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 &gm_state->ps);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index 66c095bc72..af9ba4905e 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -85,7 +85,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual, econtext))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -139,7 +139,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual, econtext))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -188,12 +188,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	grpstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) grpstate);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index e695d8834b..cfc6b96093 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -190,12 +190,8 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	hashstate->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) hashstate);
 
 	/*
 	 * initialize child nodes
@@ -1063,7 +1059,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState  *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1097,7 +1093,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses, econtext))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index c50d93f43d..1aa133925f 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState  *joinqual;
+	ExprState  *otherqual;
 	ExprContext *econtext;
 	HashJoinTable hashtable;
 	TupleTableSlot *outerTupleSlot;
@@ -275,7 +275,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual, econtext))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -294,8 +294,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual, econtext))
 						return ExecProject(node->js.ps.ps_ProjInfo);
 					else
 						InstrCountFiltered2(node, 1);
@@ -322,8 +321,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual, econtext))
 						return ExecProject(node->js.ps.ps_ProjInfo);
 					else
 						InstrCountFiltered2(node, 1);
@@ -350,8 +348,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual, econtext))
 					return ExecProject(node->js.ps.ps_ProjInfo);
 				else
 					InstrCountFiltered2(node, 1);
@@ -411,19 +408,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	hjstate->js.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->join.plan.targetlist,
-					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses, (PlanState *) hjstate);
 
 	/*
 	 * initialize child nodes
@@ -517,13 +508,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
-		OpExpr	   *hclause = castNode(OpExpr, fstate->xprstate.expr);
+		OpExpr	   *hclause =  castNode(OpExpr, lfirst(l));
+
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index db7f2e120e..5550f6c0a4 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -211,7 +211,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -488,15 +488,10 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	 * Note: we don't initialize all of the indexorderby expression, only the
 	 * sub-parts corresponding to runtime keys (see below).
 	 */
-	indexstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual, (PlanState *) indexstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index cb6aff9137..5afd02e09d 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -149,7 +149,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -295,7 +295,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig, econtext))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -415,7 +415,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig, econtext);
 }
 
 
@@ -921,18 +921,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	 * would be nice to improve that.  (Problem is that any SubPlans present
 	 * in the expression must be found now...)
 	 */
-	indexstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
-	indexstate->indexorderbyorig = (List *)
-		ExecInitExpr((Expr *) node->indexorderbyorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
+	indexstate->indexorderbyorig =
+		ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 105e2dcedb..62784af304 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState  *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual, econtext))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -483,14 +483,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState  *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual, econtext))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -598,8 +598,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState  *joinqual;
+	ExprState  *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -785,8 +785,8 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL ||
+							  ExecQual(joinqual, econtext));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -808,8 +808,8 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL ||
+								  ExecQual(otherqual, econtext));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1455,16 +1455,11 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	mergestate->js.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->join.plan.targetlist,
-					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 95e158970c..127a64eb31 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1151,7 +1151,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState  *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1270,7 +1270,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere, econtext))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1645,7 +1645,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	mtstate = makeNode(ModifyTableState);
 	mtstate->ps.plan = (Plan *) node;
 	mtstate->ps.state = estate;
-	mtstate->ps.targetlist = NIL;		/* not actually used */
 
 	mtstate->operation = operation;
 	mtstate->canSetTag = node->canSetTag;
@@ -1765,8 +1764,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i]);
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1805,7 +1804,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			foreach(ll, mapped_wcoList)
 			{
 				WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-				ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
+				ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
 											   mtstate->mt_plans[i]);
 
 				wcoExprs = lappend(wcoExprs, wcoExpr);
@@ -1839,8 +1838,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1849,11 +1849,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
 									 resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
@@ -1870,17 +1868,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		for (i = 0; i < mtstate->mt_num_partitions; i++)
 		{
 			Relation	partrel = resultRelInfo->ri_RelationDesc;
-			List	   *rlist,
-					   *rliststate;
+			List	   *rlist;
 
 			/* varno = node->nominalRelation */
 			rlist = map_partition_varattnos(returningList,
 											node->nominalRelation,
 											partrel, rel);
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1905,7 +1901,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1931,11 +1926,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
@@ -1943,10 +1937,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		{
 			ExprState  *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
 									&mtstate->ps);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index cac7ba1b9b..53977e0b32 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState  *joinqual;
+	ExprState  *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -176,7 +176,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual, econtext))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -207,7 +207,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual, econtext))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -225,7 +225,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual, econtext))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -282,16 +282,11 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	nlstate->js.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->join.plan.targetlist,
-					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
index eae0f1dad9..60862cf142 100644
--- a/src/backend/executor/nodeProjectSet.c
+++ b/src/backend/executor/nodeProjectSet.c
@@ -24,6 +24,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeProjectSet.h"
+#include "nodes/nodeFuncs.h"
 #include "utils/memutils.h"
 
 
@@ -122,7 +123,6 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 	bool		hassrf PG_USED_FOR_ASSERTS_ONLY = false;
 	bool		hasresult;
 	int			argno;
-	ListCell   *lc;
 
 	ExecClearTuple(resultSlot);
 
@@ -133,10 +133,9 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 	node->pending_srf_tuples = false;
 
 	hasresult = false;
-	argno = 0;
-	foreach(lc, node->ps.targetlist)
+	for (argno = 0; argno < node->nelems; argno++)
 	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(lc);
+		Node *elem = node->elems[argno];
 		ExprDoneCond *isdone = &node->elemdone[argno];
 		Datum	   *result = &resultSlot->tts_values[argno];
 		bool	   *isnull = &resultSlot->tts_isnull[argno];
@@ -151,13 +150,12 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 			*isnull = true;
 			hassrf = true;
 		}
-		else if (IsA(gstate->arg, FuncExprState) &&
-				 ((FuncExprState *) gstate->arg)->funcReturnsSet)
+		else if (IsA(elem, SetExprState))
 		{
 			/*
 			 * Evaluate SRF - possibly continuing previously started output.
 			 */
-			*result = ExecMakeFunctionResultSet((FuncExprState *) gstate->arg,
+			*result = ExecMakeFunctionResultSet((SetExprState *) elem,
 												econtext, isnull, isdone);
 
 			if (*isdone != ExprEndResult)
@@ -169,11 +167,10 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 		else
 		{
 			/* Non-SRF tlist expression, just evaluate normally. */
-			*result = ExecEvalExpr(gstate->arg, econtext, isnull);
+			*result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
 			*isdone = ExprSingleResult;
 		}
 
-		argno++;
 	}
 
 	/* ProjectSet should not be used if there's no SRFs */
@@ -204,6 +201,8 @@ ProjectSetState *
 ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 {
 	ProjectSetState *state;
+	ListCell *lc;
+	int off;
 
 	/* check for unsupported flags */
 	Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
@@ -229,12 +228,6 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 	 */
 	ExecInitResultTupleSlot(estate, &state->ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	state->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) state);
 	Assert(node->plan.qual == NIL);
 
 	/*
@@ -254,9 +247,38 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 
 	/* Create workspace for per-SRF is-done state */
 	state->nelems = list_length(node->plan.targetlist);
+	state->elems = (Node **)
+		palloc(sizeof(Node *) * state->nelems);
 	state->elemdone = (ExprDoneCond *)
 		palloc(sizeof(ExprDoneCond) * state->nelems);
 
+	/*
+	 * Build expressions to evaluate targetlist. Can't use
+	 * ExecBuildProjectionInfo here, since that doesn't deal with
+	 * SRFs. Instead evaluate all expressions individually, using
+	 * ExecInitFunctionResultSet where applicable.
+	 */
+	off = 0;
+	foreach(lc, node->plan.targetlist)
+	{
+		TargetEntry *te = (TargetEntry *) lfirst(lc);
+		Expr *expr = te->expr;
+
+		if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
+			(IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
+		{
+			state->elems[off] = (Node *)
+				ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
+										  &state->ps);
+		}
+		else
+		{
+			state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
+			Assert(!expression_returns_set((Node *) expr));
+		}
+
+		off++;
+	}
 	return state;
 }
 
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index b5b50b21e9..a753a53419 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -77,9 +77,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual, econtext);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -209,14 +207,10 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	resstate->ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) resstate);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index d38265e810..0247bd2347 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -164,19 +164,12 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
-	scanstate->args = (List *)
-		ExecInitExpr((Expr *) tsc->args,
-					 (PlanState *) scanstate);
+	scanstate->args = ExecInitExprList(tsc->args, (PlanState *) scanstate);
 	scanstate->repeatable =
-		ExecInitExpr(tsc->repeatable,
-					 (PlanState *) scanstate);
+		ExecInitExpr(tsc->repeatable, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index e61895de0a..5680464fa2 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -188,12 +188,8 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 8f419a13ac..01383fdc33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -39,12 +39,6 @@
 #include "utils/memutils.h"
 
 
-static Datum ExecSubPlan(SubPlanState *node,
-			ExprContext *econtext,
-			bool *isNull);
-static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node,
-					   ExprContext *econtext,
-					   bool *isNull);
 static Datum ExecHashSubPlan(SubPlanState *node,
 				ExprContext *econtext,
 				bool *isNull);
@@ -64,12 +58,12 @@ static bool slotNoNulls(TupleTableSlot *slot);
  * This is the main entry point for execution of a regular SubPlan.
  * ----------------------------------------------------------------
  */
-static Datum
+Datum
 ExecSubPlan(SubPlanState *node,
 			ExprContext *econtext,
 			bool *isNull)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 
 	/* Set non-null as default */
 	*isNull = false;
@@ -95,7 +89,7 @@ ExecHashSubPlan(SubPlanState *node,
 				ExprContext *econtext,
 				bool *isNull)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	TupleTableSlot *slot;
 
@@ -217,7 +211,7 @@ ExecScanSubPlan(SubPlanState *node,
 				ExprContext *econtext,
 				bool *isNull)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	SubLinkType subLinkType = subplan->subLinkType;
 	MemoryContext oldcontext;
@@ -462,7 +456,7 @@ ExecScanSubPlan(SubPlanState *node,
 static void
 buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	int			ncols = list_length(subplan->paramIds);
 	ExprContext *innerecontext = node->innerecontext;
@@ -694,8 +688,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 	SubPlanState *sstate = makeNode(SubPlanState);
 	EState	   *estate = parent->state;
 
-	sstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecSubPlan;
-	sstate->xprstate.expr = (Expr *) subplan;
+	sstate->subplan = subplan;
 
 	/* Link the SubPlanState to already-initialized subplan */
 	sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates,
@@ -706,7 +699,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 
 	/* Initialize subexpressions */
 	sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
-	sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent);
+	sstate->args = ExecInitExprList(subplan->args, parent);
 
 	/*
 	 * initialize my state
@@ -763,9 +756,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		TupleTableSlot *slot;
 		List	   *oplist,
 				   *lefttlist,
-				   *righttlist,
-				   *leftptlist,
-				   *rightptlist;
+				   *righttlist;
 		ListCell   *l;
 
 		/* We need a memory context to hold the hash table(s) */
@@ -800,27 +791,27 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * We also extract the combining operators themselves to initialize
 		 * the equality and hashing functions for the hash tables.
 		 */
-		if (IsA(sstate->testexpr->expr, OpExpr))
+		if (IsA(subplan->testexpr, OpExpr))
 		{
 			/* single combining operator */
-			oplist = list_make1(sstate->testexpr);
+			oplist = list_make1(subplan->testexpr);
 		}
-		else if (and_clause((Node *) sstate->testexpr->expr))
+		else if (and_clause((Node *) subplan->testexpr))
 		{
 			/* multiple combining operators */
-			oplist = castNode(BoolExprState, sstate->testexpr)->args;
+			Assert(IsA(subplan->testexpr, BoolExpr));
+			oplist = castNode(BoolExpr, subplan->testexpr)->args;
 		}
 		else
 		{
 			/* shouldn't see anything else in a hashable subplan */
 			elog(ERROR, "unrecognized testexpr type: %d",
-				 (int) nodeTag(sstate->testexpr->expr));
+				 (int) nodeTag(subplan->testexpr));
 			oplist = NIL;		/* keep compiler quiet */
 		}
 		Assert(list_length(oplist) == ncols);
 
 		lefttlist = righttlist = NIL;
-		leftptlist = rightptlist = NIL;
 		sstate->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
 		sstate->tab_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
 		sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
@@ -828,45 +819,30 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		i = 1;
 		foreach(l, oplist)
 		{
-			FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
-			OpExpr	   *opexpr = castNode(OpExpr, fstate->xprstate.expr);
-			ExprState  *exstate;
+			OpExpr	   *opexpr = castNode(OpExpr, lfirst(l));
 			Expr	   *expr;
 			TargetEntry *tle;
-			GenericExprState *tlestate;
 			Oid			rhs_eq_oper;
 			Oid			left_hashfn;
 			Oid			right_hashfn;
 
-			Assert(list_length(fstate->args) == 2);
+			Assert(list_length(opexpr->args) == 2);
 
 			/* Process lefthand argument */
-			exstate = (ExprState *) linitial(fstate->args);
-			expr = exstate->expr;
+			expr = (Expr *) linitial(opexpr->args);
 			tle = makeTargetEntry(expr,
 								  i,
 								  NULL,
 								  false);
-			tlestate = makeNode(GenericExprState);
-			tlestate->xprstate.expr = (Expr *) tle;
-			tlestate->xprstate.evalfunc = NULL;
-			tlestate->arg = exstate;
-			lefttlist = lappend(lefttlist, tlestate);
-			leftptlist = lappend(leftptlist, tle);
+			lefttlist = lappend(lefttlist, tle);
 
 			/* Process righthand argument */
-			exstate = (ExprState *) lsecond(fstate->args);
-			expr = exstate->expr;
+			expr = (Expr *) lsecond(opexpr->args);
 			tle = makeTargetEntry(expr,
 								  i,
 								  NULL,
 								  false);
-			tlestate = makeNode(GenericExprState);
-			tlestate->xprstate.expr = (Expr *) tle;
-			tlestate->xprstate.evalfunc = NULL;
-			tlestate->arg = exstate;
-			righttlist = lappend(righttlist, tlestate);
-			rightptlist = lappend(rightptlist, tle);
+			righttlist = lappend(righttlist, tle);
 
 			/* Lookup the equality function (potentially cross-type) */
 			fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
@@ -898,20 +874,22 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
 		 */
-		tupDesc = ExecTypeFromTL(leftptlist, false);
+		tupDesc = ExecTypeFromTL(lefttlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
 		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+												   parent->ps_ExprContext,
 												   slot,
+												   parent,
 												   NULL);
 
-		tupDesc = ExecTypeFromTL(rightptlist, false);
+		tupDesc = ExecTypeFromTL(righttlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
 		sstate->projRight = ExecBuildProjectionInfo(righttlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
@@ -934,7 +912,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 void
 ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 {
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	PlanState  *planstate = node->planstate;
 	SubLinkType subLinkType = subplan->subLinkType;
 	MemoryContext oldcontext;
@@ -1111,7 +1089,7 @@ void
 ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
 {
 	PlanState  *planstate = node->planstate;
-	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	SubPlan    *subplan = node->subplan;
 	EState	   *estate = parent->state;
 	ListCell   *l;
 
@@ -1162,16 +1140,21 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
 	SubPlan    *subplan2;
 	Cost		cost1;
 	Cost		cost2;
+	ListCell   *lc;
 
-	asstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecAlternativeSubPlan;
-	asstate->xprstate.expr = (Expr *) asplan;
+	asstate->subplan = asplan;
 
 	/*
 	 * Initialize subplans.  (Can we get away with only initializing the one
 	 * we're going to use?)
 	 */
-	asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
-											  parent);
+	foreach(lc, asplan->subplans)
+	{
+		SubPlanState *sp = ExecInitSubPlan(lfirst(lc), parent);
+		asstate->subplans =
+			lappend(asstate->subplans, sp);
+		parent->subPlan = lappend(parent->subPlan, sp);
+	}
 
 	/*
 	 * Select the one to be used.  For this, we need an estimate of the number
@@ -1209,7 +1192,7 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
  * Note: in future we might consider changing to different subplans on the
  * fly, in case the original rowcount estimate turns out to be way off.
  */
-static Datum
+Datum
 ExecAlternativeSubPlan(AlternativeSubPlanState *node,
 					   ExprContext *econtext,
 					   bool *isNull)
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 230a96f9d2..ae184700a6 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -120,12 +120,8 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	subquerystate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c
index 628f1ba074..8e589d916f 100644
--- a/src/backend/executor/nodeTableFuncscan.c
+++ b/src/backend/executor/nodeTableFuncscan.c
@@ -139,12 +139,9 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 &scanstate->ss.ps);
 
 	/*
 	 * tuple table initialization
@@ -179,16 +176,16 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
 
 	scanstate->ns_names = tf->ns_names;
 
-	scanstate->ns_uris = (List *)
-		ExecInitExpr((Expr *) tf->ns_uris, (PlanState *) scanstate);
+	scanstate->ns_uris =
+		ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
 	scanstate->docexpr =
 		ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
 	scanstate->rowexpr =
 		ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
-	scanstate->colexprs = (List *)
-		ExecInitExpr((Expr *) tf->colexprs, (PlanState *) scanstate);
-	scanstate->coldefexprs = (List *)
-		ExecInitExpr((Expr *) tf->coldefexprs, (PlanState *) scanstate);
+	scanstate->colexprs =
+		ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
+	scanstate->coldefexprs =
+		ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
 
 	scanstate->notnulls = tf->notnulls;
 
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 13ed886577..1763be5cf0 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -52,7 +52,8 @@ static TupleTableSlot *TidNext(TidScanState *node);
 static void
 TidListCreate(TidScanState *tidstate)
 {
-	List	   *evalList = tidstate->tss_tidquals;
+	TidScan	   *node = (TidScan *) tidstate->ss.ps.plan;
+	List	   *evalList = node->tidquals;
 	ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
 	BlockNumber nblocks;
 	ItemPointerData *tidList;
@@ -81,28 +82,29 @@ TidListCreate(TidScanState *tidstate)
 
 	foreach(l, evalList)
 	{
-		ExprState  *exstate = (ExprState *) lfirst(l);
-		Expr	   *expr = exstate->expr;
+		Expr	   *expr = (Expr*) lfirst(l);
 		ItemPointer itemptr;
 		bool		isNull;
 
 		if (is_opclause(expr))
 		{
-			FuncExprState *fexstate = (FuncExprState *) exstate;
+			FuncExpr   *fex = (FuncExpr *) expr;
+			ExprState  *exprstate;
 			Node	   *arg1;
 			Node	   *arg2;
 
 			arg1 = get_leftop(expr);
 			arg2 = get_rightop(expr);
 			if (IsCTIDVar(arg1))
-				exstate = (ExprState *) lsecond(fexstate->args);
+				exprstate = ExecInitExpr((Expr *) lsecond(fex->args),
+										  &tidstate->ss.ps);
 			else if (IsCTIDVar(arg2))
-				exstate = (ExprState *) linitial(fexstate->args);
-			else
+				exprstate = ExecInitExpr((Expr *) linitial(fex->args),
+										  &tidstate->ss.ps);
 				elog(ERROR, "could not identify CTID variable");
 
 			itemptr = (ItemPointer)
-				DatumGetPointer(ExecEvalExprSwitchContext(exstate,
+				DatumGetPointer(ExecEvalExprSwitchContext(exprstate,
 														  econtext,
 														  &isNull));
 			if (!isNull &&
@@ -121,7 +123,8 @@ TidListCreate(TidScanState *tidstate)
 		}
 		else if (expr && IsA(expr, ScalarArrayOpExpr))
 		{
-			ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
+			ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
+			ExprState  *exprstate;
 			Datum		arraydatum;
 			ArrayType  *itemarray;
 			Datum	   *ipdatums;
@@ -129,8 +132,8 @@ TidListCreate(TidScanState *tidstate)
 			int			ndatums;
 			int			i;
 
-			exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
-			arraydatum = ExecEvalExprSwitchContext(exstate,
+			exprstate = ExecInitExpr(lsecond(saex->args), &tidstate->ss.ps);
+			arraydatum = ExecEvalExprSwitchContext(exprstate,
 												   econtext,
 												   &isNull);
 			if (isNull)
@@ -470,16 +473,8 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	tidstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
-
-	tidstate->tss_tidquals = (List *)
-		ExecInitExpr((Expr *) node->tidquals,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9883a8b130..9ee776c4c3 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -120,7 +120,7 @@ ValuesNext(ValuesScanState *node)
 		 * is a SubPlan, and there shouldn't be any (any subselects in the
 		 * VALUES list should be InitPlans).
 		 */
-		exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
+		exprstatelist = ExecInitExprList(exprlist, NULL);
 
 		/* parser should have checked all sublists are the same length */
 		Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
@@ -242,12 +242,8 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 2a123e8452..628bc9f00b 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1826,16 +1826,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate);
 	winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate);
 
-	winstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->plan.targetlist,
-					 (PlanState *) winstate);
-
 	/*
 	 * WindowAgg nodes never have quals, since they can only occur at the
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
@@ -1894,7 +1890,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	foreach(l, winstate->funcs)
 	{
 		WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
-		WindowFunc *wfunc = (WindowFunc *) wfuncstate->xprstate.expr;
+		WindowFunc *wfunc = wfuncstate->wfunc;
 		WindowStatePerFunc perfuncstate;
 		AclResult	aclresult;
 		int			i;
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index 23b5b94985..d7616be065 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -156,12 +156,8 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.targetlist,
-					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index b19380e1b1..42bba543e9 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -3395,7 +3395,7 @@ eval_const_expressions_mutator(Node *node,
 						 * Else, make a scalar (argisrow == false) NullTest
 						 * for this field.  Scalar semantics are required
 						 * because IS [NOT] NULL doesn't recurse; see comments
-						 * in ExecEvalNullTest().
+						 * in ExecEvalRowNullInt().
 						 */
 						newntest = makeNode(NullTest);
 						newntest->arg = (Expr *) relem;
@@ -3539,8 +3539,8 @@ eval_const_expressions_mutator(Node *node,
  *		FALSE: drop (does not affect result)
  *		TRUE: force result to TRUE
  *		NULL: keep only one
- * We must keep one NULL input because ExecEvalOr returns NULL when no input
- * is TRUE and at least one is NULL.  We don't actually include the NULL
+ * We must keep one NULL input because OR expressions evaluate to NULL when no
+ * input is TRUE and at least one is NULL.  We don't actually include the NULL
  * here, that's supposed to be done by the caller.
  *
  * The output arguments *haveNull and *forceTrue must be initialized FALSE
@@ -3651,9 +3651,9 @@ simplify_or_arguments(List *args,
  *		TRUE: drop (does not affect result)
  *		FALSE: force result to FALSE
  *		NULL: keep only one
- * We must keep one NULL input because ExecEvalAnd returns NULL when no input
- * is FALSE and at least one is NULL.  We don't actually include the NULL
- * here, that's supposed to be done by the caller.
+ * We must keep one NULL input because AND expressions evaluate to NULL when
+ * no input is FALSE and at least one is NULL.  We don't actually include the
+ * NULL here, that's supposed to be done by the caller.
  *
  * The output arguments *haveNull and *forceFalse must be initialized FALSE
  * by the caller.  They will be set TRUE if a null constant or false constant,
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index c2ad440013..f85d3c8111 100644
--- a/src/backend/utils/adt/domains.c
+++ b/src/backend/utils/adt/domains.c
@@ -122,7 +122,8 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
 /*
  * domain_check_input - apply the cached checks.
  *
- * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
+ * This is similar to the handling of CoerceToDomain nodes in
+ * execExpr.c/execInterpExpr.c.
  */
 static void
 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
@@ -165,19 +166,20 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
 
 					/*
 					 * Set up value to be returned by CoerceToDomainValue
-					 * nodes.  Unlike ExecEvalCoerceToDomain, this econtext
-					 * couldn't be shared with anything else, so no need to
-					 * save and restore fields.  But we do need to protect the
-					 * passed-in value against being changed by called
-					 * functions.  (It couldn't be a R/W expanded object for
-					 * most uses, but that seems possible for domain_check().)
+					 * nodes.  Unlike in the generic expression case, this
+					 * econtext couldn't be shared with anything else, so no
+					 * need to save and restore fields.  But we do need to
+					 * protect the passed-in value against being changed by
+					 * called functions.  (It couldn't be a R/W expanded
+					 * object for most uses, but that seems possible for
+					 * domain_check().)
 					 */
 					econtext->domainValue_datum =
 						MakeExpandedObjectReadOnly(value, isnull,
 									my_extra->constraint_ref.tcache->typlen);
 					econtext->domainValue_isNull = isnull;
 
-					conResult = ExecEvalExprSwitchContext(con->check_expr,
+					conResult = ExecEvalExprSwitchContext(con->check_exprstate,
 														  econtext,
 														  &conIsNull);
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3c697f3c0d..509573df80 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6919,7 +6919,7 @@ find_param_referent(Param *param, deparse_context *context,
 			foreach(lc2, ps->subPlan)
 			{
 				SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
-				SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+				SubPlan    *subplan = sstate->subplan;
 				ListCell   *lc3;
 				ListCell   *lc4;
 
@@ -6960,7 +6960,7 @@ find_param_referent(Param *param, deparse_context *context,
 					continue;
 
 				/* No parameters to be had here. */
-				Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL);
+				Assert(sstate->subplan->parParam == NIL);
 
 				/* Keep looking, but we are emerging from an initplan. */
 				in_same_plan_level = false;
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index f81cf489d2..4ff9f5e945 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -72,7 +72,6 @@
 #include "catalog/pg_class.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
-#include "executor/executor.h"
 #include "executor/spi.h"
 #include "executor/tablefunc.h"
 #include "fmgr.h"
@@ -620,10 +619,9 @@ xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
 
 
 xmltype *
-xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
+xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue)
 {
 #ifdef USE_LIBXML
-	XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
 	xmltype    *result;
 	List	   *named_arg_strings;
 	List	   *arg_strings;
@@ -635,45 +633,40 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
 	volatile xmlTextWriterPtr writer = NULL;
 
 	/*
-	 * We first evaluate all the arguments, then start up libxml and create
-	 * the result.  This avoids issues if one of the arguments involves a call
-	 * to some other function or subsystem that wants to use libxml on its own
-	 * terms.
+	 * All arguments are already evaluated.  This avoids issues if one of the
+	 * arguments involves a call to some other function or subsystem that
+	 * wants to use libxml on its own terms.
 	 */
 	named_arg_strings = NIL;
 	i = 0;
-	foreach(arg, xmlExpr->named_args)
+	foreach(arg, xexpr->named_args)
 	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-		bool		isnull;
+		Expr	   *e = (Expr *) lfirst(arg);
 		char	   *str;
 
-		value = ExecEvalExpr(e, econtext, &isnull);
-		if (isnull)
+		if (named_argnull[i])
 			str = NULL;
 		else
-			str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr), false);
+			str = map_sql_value_to_xml_value(named_argvalue[i], exprType((Node *) e), false);
 		named_arg_strings = lappend(named_arg_strings, str);
 		i++;
 	}
 
 	arg_strings = NIL;
-	foreach(arg, xmlExpr->args)
+	i = 0;
+	foreach(arg, xexpr->args)
 	{
-		ExprState  *e = (ExprState *) lfirst(arg);
-		Datum		value;
-		bool		isnull;
+		Expr	   *e = (Expr *) lfirst(arg);
 		char	   *str;
 
-		value = ExecEvalExpr(e, econtext, &isnull);
 		/* here we can just forget NULL elements immediately */
-		if (!isnull)
+		if (!argnull[i])
 		{
-			str = map_sql_value_to_xml_value(value,
-										   exprType((Node *) e->expr), true);
+			str = map_sql_value_to_xml_value(argvalue[i],
+											 exprType((Node *) e), true);
 			arg_strings = lappend(arg_strings, str);
 		}
+		i++;
 	}
 
 	/* now safe to run libxml */
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 6992634c39..07cae44069 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -102,6 +102,7 @@ static TypeCacheEntry *firstDomainTypeEntry = NULL;
  * the DomainConstraintState nodes and applying ExecInitExpr to check_expr.
  * Such a state tree is not part of the DomainConstraintCache, but is
  * considered to belong to a DomainConstraintRef.
+ * FIXME: update.
  */
 struct DomainConstraintCache
 {
@@ -779,8 +780,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
 			r = makeNode(DomainConstraintState);
 			r->constrainttype = DOM_CONSTRAINT_CHECK;
 			r->name = pstrdup(NameStr(c->conname));
-			/* Must cast here because we're not storing an expr state node */
-			r->check_expr = (ExprState *) check_expr;
+			r->check_expr = check_expr;
+			r->check_exprstate = NULL;
 
 			MemoryContextSwitchTo(oldcxt);
 
@@ -859,6 +860,7 @@ load_domaintype_info(TypeCacheEntry *typentry)
 		r->constrainttype = DOM_CONSTRAINT_NOTNULL;
 		r->name = pstrdup("NOT NULL");
 		r->check_expr = NULL;
+		r->check_exprstate = NULL;
 
 		/* lcons to apply the nullness check FIRST */
 		dcc->constraints = lcons(r, dcc->constraints);
@@ -946,8 +948,8 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
 		newr = makeNode(DomainConstraintState);
 		newr->constrainttype = r->constrainttype;
 		newr->name = r->name;
-		/* Must cast here because cache items contain expr plan trees */
-		newr->check_expr = ExecInitExpr((Expr *) r->check_expr, NULL);
+		newr->check_expr = r->check_expr;
+		newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
 
 		result = lappend(result, newr);
 	}
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
new file mode 100644
index 0000000000..61c8b047ae
--- /dev/null
+++ b/src/include/executor/execExpr.h
@@ -0,0 +1,455 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.h
+ *	  Low level infrastructure related to expression evaluation
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/execExpr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXEC_EXPR_H
+#define EXEC_EXPR_H
+
+#include "nodes/execnodes.h"
+
+struct ArrayRefState;
+
+#define EEO_FLAG_JUMP_THREADED (1 << 0)
+
+typedef enum ExprEvalOp
+{
+	EEO_DONE,
+	EEO_INNER_FETCHSOME,
+	EEO_OUTER_FETCHSOME,
+	EEO_SCAN_FETCHSOME,
+	EEO_INNER_VAR,
+	EEO_OUTER_VAR,
+	EEO_SCAN_VAR,
+	EEO_ASSIGN_INNER_VAR,
+	EEO_ASSIGN_OUTER_VAR,
+	EEO_ASSIGN_SCAN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_ASSIGN_TMP_UNEXPAND,
+	EEO_INNER_SYSVAR,
+	EEO_OUTER_SYSVAR,
+	EEO_SCAN_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_AND_STEP_LAST,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_OR_STEP_LAST,
+	EEO_BOOL_NOT_STEP,
+	EEO_QUAL,
+	EEO_NULLTEST_ISNULL,
+	EEO_NULLTEST_ISNOTNULL,
+	EEO_NULLTEST_ROWISNULL,
+	EEO_NULLTEST_ROWISNOTNULL,
+	EEO_PARAM_EXEC,
+	EEO_PARAM_EXTERN,
+	EEO_CASE_WHEN_STEP,
+	EEO_CASE_THEN_STEP,
+	EEO_CASE_TESTVAL,
+	EEO_CASE_TESTVAL_UNEXPAND,
+	EEO_COALESCE,
+	EEO_BOOLTEST_IS_TRUE,
+	EEO_BOOLTEST_IS_NOT_TRUE,
+	EEO_BOOLTEST_IS_FALSE,
+	EEO_BOOLTEST_IS_NOT_FALSE,
+	EEO_BOOLTEST_IS_UNKNOWN,
+	EEO_BOOLTEST_IS_NOT_UNKNOWN,
+	EEO_WHOLEROW,
+	EEO_IOCOERCE,
+	EEO_DISTINCT,
+	EEO_NULLIF,
+	EEO_SQLVALUEFUNCTION,
+	EEO_CURRENTOFEXPR,
+	EEO_ARRAYEXPR,
+	EEO_ARRAYCOERCE,
+	EEO_ROW,
+	EEO_ROWCOMPARE_STEP,
+	EEO_ROWCOMPARE_FINAL,
+	EEO_MINMAX,
+	EEO_FIELDSELECT,
+	EEO_FIELDSTORE_DEFORM,
+	EEO_FIELDSTORE_FORM,
+	EEO_ARRAYREF_CHECKINPUT,
+	EEO_ARRAYREF_CHECKSUBSCRIPT,
+	EEO_ARRAYREF_OLD,
+	EEO_ARRAYREF_ASSIGN,
+	EEO_ARRAYREF_FETCH,
+	EEO_CONVERT_ROWTYPE,
+	EEO_SCALARARRAYOP,
+	EEO_DOMAIN_TESTVAL,
+	EEO_DOMAIN_TESTVAL_UNEXPAND,
+	EEO_DOMAIN_NOTNULL,
+	EEO_DOMAIN_CHECK,
+	EEO_XMLEXPR,
+	EEO_AGGREF,
+	EEO_GROUPING_FUNC,
+	EEO_WINDOW_FUNC,
+	EEO_SUBPLAN,
+	EEO_ALTERNATIVE_SUBPLAN,
+	EEO_LAST
+} ExprEvalOp;
+
+
+typedef struct ExprEvalStep
+{
+	/*
+	 * Instruction to be executed. During instruction preparation this is an
+	 * ExprEvalOp, but during execution it can be swapped out to some other
+	 * type, e.g. a pointer for computed goto (that's why it's a size_t).
+	 */
+	size_t		opcode;
+
+	/* target for the result of the current instruction */
+	bool	   *resnull;
+	Datum	   *resvalue;
+
+	/*
+	 * Data for an operation. Inline stored data is faster to access, but also
+	 * bloats the size of all instructions. The union should be kept below 48
+	 * bytes (so the entire struct is below 64bytes, a single cacheline on
+	 * common systems).
+	 */
+	union
+	{
+		struct
+		{
+			int attnum;
+		} var;
+
+		struct
+		{
+			Var		   *var;
+			bool		first;			/* first time through, initialize */
+			TupleDesc	tupdesc;	/* descriptor for resulting tuples */
+			JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */
+		} wholerow;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			/* faster to access without additional indirection */
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			int jumpdone;
+		} qualexpr;
+
+		struct
+		{
+			TupleDesc argdesc;
+		} nulltest_row;
+
+		struct
+		{
+			int paramid;
+			int paramtype;
+		} param;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			int jumpfalse;
+		} casewhen;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			int jumpdone;
+		} casethen;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+		} casetest;
+
+		struct
+		{
+			int jumpdone;
+		} coalesce;
+
+		struct
+		{
+			/* lookup info for source output function */
+			FmgrInfo	*finfo_out;
+			FunctionCallInfo fcinfo_data_out;
+			/* lookup info for result input function */
+			FmgrInfo	*finfo_in;
+			FunctionCallInfo fcinfo_data_in;
+			Oid			intypioparam;	/* argument needed for input function */
+		} iocoerce;
+
+		struct
+		{
+			SQLValueFunction *svf;
+		} sqlvaluefunction;
+
+		struct
+		{
+			ArrayExpr  *arrayexpr;
+			Datum	   *elemvalues;
+			bool	   *elemnulls;
+			int			nelems;
+			int16		elemlength;		/* typlen of the array element type */
+			bool		elembyval;		/* is the element type pass-by-value? */
+			char		elemalign;		/* typalign of the element type */
+		} arrayexpr;
+
+		struct
+		{
+			ArrayCoerceExpr *coerceexpr;
+			Oid			resultelemtype; /* element type of result array */
+			FmgrInfo   *elemfunc;		/* lookup info for element coercion function */
+			ArrayMapState *amstate;		/* workspace for array_map */
+		} arraycoerce;
+
+		struct
+		{
+			RowExpr	   *rowexpr;
+			/* the arguments */
+			Datum	   *elemvalues;
+			bool	   *elemnulls;
+			TupleDesc	tupdesc;		/* descriptor for result tuples */
+		} row;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int jumpnull;
+			int jumpdone;
+		} rowcompare_step;
+
+		struct
+		{
+			RowCompareType rctype;
+		} rowcompare_final;
+
+		struct
+		{
+			/* the arguments */
+			Datum	   *values;
+			bool	   *nulls;
+			int			nelems;
+
+			MinMaxOp	op;
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+		} minmax;
+
+		struct
+		{
+			/* tupdesc for most recent input */
+			TupleDesc	argdesc;
+			AttrNumber	fieldnum;
+			Oid			resulttype;
+		} fieldselect;
+
+		struct
+		{
+			/* tupdesc for most recent input */
+			TupleDesc  *argdesc;
+			FieldStore *fstore;
+
+			/* the arguments */
+			Datum	   *values;
+			bool	   *nulls;
+		} fieldstore;
+
+		struct
+		{
+			/* too big to have inline */
+			struct ArrayRefState *state;
+			int jumpdone;
+		} arrayref;
+
+		struct
+		{
+			/* too big to have inline */
+			struct ArrayRefState *state;
+			int off;
+			int jumpdone;
+			bool isupper;
+		} arrayref_checksubscript;
+
+		struct
+		{
+			ConvertRowtypeExpr *convert;
+			TupleDesc indesc;
+			TupleDesc outdesc;
+			TupleConversionMap *map;
+			bool initialized;
+		} convert_rowtype;
+
+		struct
+		{
+			ScalarArrayOpExpr *opexpr;
+			Oid			element_type;
+			int16		typlen;
+			bool		typbyval;
+			char		typalign;
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+		} scalararrayop;
+
+		struct
+		{
+			char *constraintname;
+			Datum *checkvalue;
+			bool *checknull;
+			Oid resulttype;
+		} domaincheck;
+
+		struct
+		{
+			XmlExpr *xexpr;
+			Datum *named_argvalue;
+			bool *named_argnull;
+			Datum *argvalue;
+			bool *argnull;
+		} xmlexpr;
+
+		struct
+		{
+			AggrefExprState *astate;
+		} aggref;
+
+		struct
+		{
+			AggState *parent;
+			List *clauses;
+		} grouping_func;
+
+		struct
+		{
+			WindowFuncExprState *wfstate;
+		} window_func;
+
+		struct
+		{
+			SubPlanState *sstate;
+		} subplan;
+
+		struct
+		{
+			AlternativeSubPlanState *asstate;
+		} alternative_subplan;
+
+		struct
+		{
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			int last_var;
+		} fetch;
+	} d;
+} ExprEvalStep;
+
+
+typedef struct ArrayRefState
+{
+	bool		isassignment;
+	int			numupper;
+	Datum		upper[MAXDIM];
+	int			upperindex[MAXDIM];
+	bool		uppernull[MAXDIM];
+	bool		upperprovided[MAXDIM];
+	int			numlower;
+	Datum		lower[MAXDIM];
+	int			lowerindex[MAXDIM];
+	bool		lowernull[MAXDIM];
+	bool		lowerprovided[MAXDIM];
+
+	Oid			refelemtype;
+	int16		refattrlength;	/* typlen of array type */
+	int16		refelemlength;	/* typlen of the array element type */
+	bool		refelembyval;	/* is the element type pass-by-value? */
+	char		refelemalign;	/* typalign of the element type */
+
+	Datum		replacevalue;
+	bool		replacenull;
+
+	Datum		prevvalue;
+	bool		prevnull;
+} ArrayRefState;
+
+void ExecInstantiateInterpretedExpr(ExprState *state);
+
+ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
+
+/*
+ * Non fast-path execution functions. These are externs instead of static in
+ * execInterpExpr.c, because that allows them to be used by other methods of
+ * expression evaluation.
+ */
+extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern bool ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+
+#endif /* EXEC_EXPR_H */
diff --git a/src/include/executor/execdebug.h b/src/include/executor/execdebug.h
index cf44c3edbb..d828a3f907 100644
--- a/src/include/executor/execdebug.h
+++ b/src/include/executor/execdebug.h
@@ -38,13 +38,6 @@
  */
 
 /* ----------------
- *		EXEC_EVALDEBUG is a flag which turns on debugging of
- *		ExecEval and ExecTargetList() stuff by EV_printf() in execQual.c
- * ----------------
-#undef EXEC_EVALDEBUG
- */
-
-/* ----------------
  *		EXEC_SORTDEBUG is a flag which turns on debugging of
  *		the ExecSort() stuff by SO_printf() in nodeSort.c
  * ----------------
@@ -86,20 +79,6 @@
 #endif   /* EXEC_NESTLOOPDEBUG */
 
 /* ----------------
- *		exec eval / target list debugging defines
- * ----------------
- */
-#ifdef EXEC_EVALDEBUG
-#define EV_nodeDisplay(l)				nodeDisplay(l)
-#define EV_printf(s)					printf(s)
-#define EV1_printf(s, a)				printf(s, a)
-#else
-#define EV_nodeDisplay(l)
-#define EV_printf(s)
-#define EV1_printf(s, a)
-#endif   /* EXEC_EVALDEBUG */
-
-/* ----------------
  *		sort node debugging defines
  * ----------------
  */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 32e1838e15..0c3212153c 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -70,8 +70,8 @@
  * now it's just a macro invoking the function pointed to by an ExprState
  * node.  Beware of double evaluation of the ExprState argument!
  */
-#define ExecEvalExpr(expr, econtext, isNull) \
-	((*(expr)->evalfunc) (expr, econtext, isNull))
+#define ExecEvalExpr(state, econtext, isNull) \
+	(state)->cb((state), econtext, isNull)
 
 
 /* Hook for plugins to get control in ExecutorStart() */
@@ -247,23 +247,101 @@ extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
 				  bool *isNull);
 extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
 				   bool *isNull);
-extern Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr,
+extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
 							ExprContext *econtext,
 							MemoryContext argContext,
 							TupleDesc expectedDesc,
 							bool randomAccess);
-extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache,
+extern Datum ExecMakeFunctionResultSet(SetExprState *setexpr,
 						  ExprContext *econtext,
 						  bool *isNull,
 						  ExprDoneCond *isDone);
-extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
-						  bool *isNull);
+extern SetExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent);
+extern SetExprState *ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent);
+
+/*
+ * prototypes from functions in execExpr.c
+ */
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern List *ExecInitExprList(List *nodes, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern List *ExecPrepareExprList(List *nodes, EState *estate);
+extern ExprState *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState *ExecInitQual(List *qual, PlanState *parent);
+extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
+
+extern bool ExecCheck(ExprState *state, ExprContext *context);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo);
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+#ifndef FRONTEND
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo)
+{
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
+#endif
+
+/*
+ * ExecEvalExprSwitchContext
+ *
+ * Same as ExecEvalExpr, but get into the right allocation context explicitly.
+ */
+#ifndef FRONTEND
+static inline Datum
+ExecEvalExprSwitchContext(ExprState *state,
+						   ExprContext *econtext,
+						   bool *isNull)
+{
+	Datum		retDatum;
+	MemoryContext oldContext;
+
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+	retDatum = ExecEvalExpr(state, econtext, isNull);
+	MemoryContextSwitchTo(oldContext);
+	return retDatum;
+}
+#endif
+
+/*
+ * ExecQual - evaluate a qual prepared with ExecInitQual (possibly via
+ * ExecPrepareQual).
+ */
+#ifndef FRONTEND
+static inline bool
+ExecQual(ExprState *state, ExprContext *econtext)
+{
+	bool isnull;
+	Datum ret;
+
+	/* short-circuit (here and in ExecInitQual) for empty restriction list */
+	if (state == NULL)
+		return true;
+
+	ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+	/* EEO_QUAL should never return NULL */
+	Assert(!isnull);
+
+	return DatumGetBool(ret);
+}
+#endif
 
 /*
  * prototypes from functions in execScan.c
@@ -361,6 +439,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h
index 0f821dc8f6..0bdaa548df 100644
--- a/src/include/executor/nodeSubplan.h
+++ b/src/include/executor/nodeSubplan.h
@@ -20,6 +20,9 @@ extern SubPlanState *ExecInitSubPlan(SubPlan *subplan, PlanState *parent);
 
 extern AlternativeSubPlanState *ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent);
 
+extern Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);
+extern Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, bool *isNull);
+
 extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
 
 extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index f856f6036f..bcf5ce503a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -31,60 +31,6 @@
 
 
 /* ----------------
- *	  IndexInfo information
- *
- *		this struct holds the information needed to construct new index
- *		entries for a particular index.  Used for both index_build and
- *		retail creation of index entries.
- *
- *		NumIndexAttrs		number of columns in this index
- *		KeyAttrNumbers		underlying-rel attribute numbers used as keys
- *							(zeroes indicate expressions)
- *		Expressions			expr trees for expression entries, or NIL if none
- *		ExpressionsState	exec state for expressions, or NIL if none
- *		Predicate			partial-index predicate, or NIL if none
- *		PredicateState		exec state for predicate, or NIL if none
- *		ExclusionOps		Per-column exclusion operators, or NULL if none
- *		ExclusionProcs		Underlying function OIDs for ExclusionOps
- *		ExclusionStrats		Opclass strategy numbers for ExclusionOps
- *		UniqueOps			Theses are like Exclusion*, but for unique indexes
- *		UniqueProcs
- *		UniqueStrats
- *		Unique				is it a unique index?
- *		ReadyForInserts		is it valid for inserts?
- *		Concurrent			are we doing a concurrent index build?
- *		BrokenHotChain		did we detect any broken HOT chains?
- *		AmCache				private cache area for index AM
- *		Context				memory context holding this IndexInfo
- *
- * ii_Concurrent and ii_BrokenHotChain are used only during index build;
- * they're conventionally set to false otherwise.
- * ----------------
- */
-typedef struct IndexInfo
-{
-	NodeTag		type;
-	int			ii_NumIndexAttrs;
-	AttrNumber	ii_KeyAttrNumbers[INDEX_MAX_KEYS];
-	List	   *ii_Expressions; /* list of Expr */
-	List	   *ii_ExpressionsState;	/* list of ExprState */
-	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
-	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
-	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
-	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
-	Oid		   *ii_UniqueOps;	/* array with one entry per column */
-	Oid		   *ii_UniqueProcs; /* array with one entry per column */
-	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
-	bool		ii_Unique;
-	bool		ii_ReadyForInserts;
-	bool		ii_Concurrent;
-	bool		ii_BrokenHotChain;
-	void	   *ii_AmCache;
-	MemoryContext ii_Context;
-} IndexInfo;
-
-/* ----------------
  *	  ExprContext_CB
  *
  *		List of callbacks to be called at ExprContext shutdown.
@@ -208,57 +154,60 @@ typedef struct ReturnSetInfo
 } ReturnSetInfo;
 
 /* ----------------
+ *		ExprState node
+ *
+ * ExprState is the top-level node for expression evaluation. It contains
+ * instructions (in ->steps) to evaluate the expression.
+ * ----------------
+ */
+struct ExprState;
+struct ExprContext;
+typedef Datum (*ExecEvalExprCB)(struct ExprState *, struct ExprContext *, bool *);
+
+typedef struct ExprState
+{
+	Node		tag;
+	bool		resnull;
+	int8		flags;
+	Datum		resvalue;
+	struct ExprEvalStep *steps;
+	TupleTableSlot *resultslot;
+
+	/* evaluate expression */
+	ExecEvalExprCB cb;
+
+	/* original expression */
+	Expr	   *expr;
+
+	/* FIXME: only needed during "compilation", should be thrown away */
+	size_t		steps_alloc;
+	size_t		steps_len;
+
+	Datum	   *innermost_caseval;
+	bool	   *innermost_casenull;
+
+	Datum	   *innermost_domainval;
+	bool	   *innermost_domainnull;
+} ExprState;
+
+/* ----------------
  *		ProjectionInfo node information
  *
  *		This is all the information needed to perform projections ---
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
+ *		pi_state		instructions to evaluate projection
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState	pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -300,6 +249,60 @@ typedef struct JunkFilter
 } JunkFilter;
 
 /* ----------------
+ *	  IndexInfo information
+ *
+ *		this struct holds the information needed to construct new index
+ *		entries for a particular index.  Used for both index_build and
+ *		retail creation of index entries.
+ *
+ *		NumIndexAttrs		number of columns in this index
+ *		KeyAttrNumbers		underlying-rel attribute numbers used as keys
+ *							(zeroes indicate expressions)
+ *		Expressions			expr trees for expression entries, or NIL if none
+ *		ExpressionsState	exec state for expressions, or NIL if none
+ *		Predicate			partial-index predicate, or NIL if none
+ *		PredicateState		exec state for predicate, or NIL if none
+ *		ExclusionOps		Per-column exclusion operators, or NULL if none
+ *		ExclusionProcs		Underlying function OIDs for ExclusionOps
+ *		ExclusionStrats		Opclass strategy numbers for ExclusionOps
+ *		UniqueOps			Theses are like Exclusion*, but for unique indexes
+ *		UniqueProcs
+ *		UniqueStrats
+ *		Unique				is it a unique index?
+ *		ReadyForInserts		is it valid for inserts?
+ *		Concurrent			are we doing a concurrent index build?
+ *		BrokenHotChain		did we detect any broken HOT chains?
+ *		AmCache				private cache area for index AM
+ *		Context				memory context holding this IndexInfo
+ *
+ * ii_Concurrent and ii_BrokenHotChain are used only during index build;
+ * they're conventionally set to false otherwise.
+ * ----------------
+ */
+typedef struct IndexInfo
+{
+	NodeTag		type;
+	int			ii_NumIndexAttrs;
+	AttrNumber	ii_KeyAttrNumbers[INDEX_MAX_KEYS];
+	List	   *ii_Expressions; /* list of Expr */
+	List	   *ii_ExpressionsState;	/* list of ExprState */
+	List	   *ii_Predicate;	/* list of Expr */
+	ExprState  *ii_PredicateState;
+	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
+	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
+	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
+	Oid		   *ii_UniqueOps;	/* array with one entry per column */
+	Oid		   *ii_UniqueProcs; /* array with one entry per column */
+	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	bool		ii_Unique;
+	bool		ii_ReadyForInserts;
+	bool		ii_Concurrent;
+	bool		ii_BrokenHotChain;
+	void	   *ii_AmCache;
+	MemoryContext ii_Context;
+} IndexInfo;
+
+/* ----------------
  *	  ResultRelInfo information
  *
  *		Whenever we update an existing relation, we have to
@@ -340,20 +343,20 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState *ri_onConflictSetWhere;
 	List	   *ri_PartitionCheck;
-	List	   *ri_PartitionCheckExpr;
+	ExprState  *ri_PartitionCheckExpr;
 	Relation	ri_PartitionRoot;
 } ResultRelInfo;
 
@@ -562,143 +565,54 @@ typedef tuplehash_iterator TupleHashIterator;
 #define ScanTupleHashTable(htable, iter) \
 	tuplehash_iterate(htable->hashtab, iter)
 
-
-/* ----------------------------------------------------------------
- *				 Expression State Trees
- *
- * Each executable expression tree has a parallel ExprState tree.
- *
- * Unlike PlanState, there is not an exact one-for-one correspondence between
- * ExprState node types and Expr node types.  Many Expr node types have no
- * need for node-type-specific run-time state, and so they can use plain
- * ExprState or GenericExprState as their associated ExprState node type.
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- *		ExprState node
- *
- * ExprState is the common superclass for all ExprState-type nodes.
- *
- * It can also be instantiated directly for leaf Expr nodes that need no
- * local run-time state (such as Var, Const, or Param).
- *
- * To save on dispatch overhead, each ExprState node contains a function
- * pointer to the routine to execute to evaluate the node.
- * ----------------
- */
-
-typedef struct ExprState ExprState;
-
-typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
-												ExprContext *econtext,
-												bool *isNull);
-
-struct ExprState
-{
-	NodeTag		type;
-	Expr	   *expr;			/* associated Expr node */
-	ExprStateEvalFunc evalfunc; /* routine to run to execute node */
-};
-
-/* ----------------
- *		GenericExprState node
- *
- * This is used for Expr node types that need no local run-time state,
- * but have one child Expr node.
- * ----------------
- */
-typedef struct GenericExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* state of my child node */
-} GenericExprState;
-
-/* ----------------
- *		WholeRowVarExprState node
- * ----------------
- */
-typedef struct WholeRowVarExprState
-{
-	ExprState	xprstate;
-	struct PlanState *parent;	/* parent PlanState, or NULL if none */
-	TupleDesc	wrv_tupdesc;	/* descriptor for resulting tuples */
-	JunkFilter *wrv_junkFilter; /* JunkFilter to remove resjunk cols */
-} WholeRowVarExprState;
-
 /* ----------------
  *		AggrefExprState node
  * ----------------
  */
 typedef struct AggrefExprState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	Aggref	   *aggref;
 	int			aggno;			/* ID number for agg within its plan node */
 } AggrefExprState;
 
 /* ----------------
- *		GroupingFuncExprState node
- *
- * The list of column numbers refers to the input tuples of the Agg node to
- * which the GroupingFunc belongs, and may contain 0 for references to columns
- * that are only present in grouping sets processed by different Agg nodes (and
- * which are therefore always considered "grouping" here).
- * ----------------
- */
-typedef struct GroupingFuncExprState
-{
-	ExprState	xprstate;
-	struct AggState *aggstate;
-	List	   *clauses;		/* integer list of column numbers */
-} GroupingFuncExprState;
-
-/* ----------------
  *		WindowFuncExprState node
  * ----------------
  */
 typedef struct WindowFuncExprState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	WindowFunc *wfunc;
 	List	   *args;			/* states of argument expressions */
-	ExprState  *aggfilter;		/* FILTER expression */
+	ExprState *aggfilter;		/* FILTER expression */
 	int			wfuncno;		/* ID number for wfunc within its plan node */
 } WindowFuncExprState;
 
-/* ----------------
- *		ArrayRefExprState node
- *
- * Note: array types can be fixed-length (typlen > 0), but only when the
- * element type is itself fixed-length.  Otherwise they are varlena structures
- * and have typlen = -1.  In any case, an array type is never pass-by-value.
- * ----------------
- */
-typedef struct ArrayRefExprState
-{
-	ExprState	xprstate;
-	List	   *refupperindexpr;	/* states for child nodes */
-	List	   *reflowerindexpr;
-	ExprState  *refexpr;
-	ExprState  *refassgnexpr;
-	int16		refattrlength;	/* typlen of array type */
-	int16		refelemlength;	/* typlen of the array element type */
-	bool		refelembyval;	/* is the element type pass-by-value? */
-	char		refelemalign;	/* typalign of the element type */
-} ArrayRefExprState;
 
 /* ----------------
- *		FuncExprState node
+ *		SetExprState node
  *
- * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
- * and NullIf nodes; be careful to check what xprstate.expr is actually
- * pointing at!
+ * State for evaluating a potentially set returning expression (like FuncExpr
+ * or OpExpr).  In some cases, like some of the expressions in ROWS FROM(...)
+ * the expression might not be a SRF, but uses the same machinery as SRFs
+ * (i.e. are treated like a SRF returning a single row).
  * ----------------
  */
-typedef struct FuncExprState
+typedef struct SetExprState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	Expr	   *expr;
 	List	   *args;			/* states of argument expressions */
 
 	/*
+	 * In ROWS FROM functions possibly can be inlined removing the FuncExpr
+	 * normally inside, this is the containing expression (which cannot return
+	 * a set) which'll be evaluated using the normal ExecEvalExpr().
+	 */
+	ExprState	*elidedFuncState;
+
+	/*
 	 * Function manager's lookup info for the target function.  If func.fn_oid
 	 * is InvalidOid, we haven't initialized it yet (nor any of the following
 	 * fields, except funcReturnsSet).
@@ -750,33 +664,7 @@ typedef struct FuncExprState
 	 * argument values between calls, when setArgsValid is true.
 	 */
 	FunctionCallInfoData fcinfo_data;
-} FuncExprState;
-
-/* ----------------
- *		ScalarArrayOpExprState node
- *
- * This is a FuncExprState plus some additional data.
- * ----------------
- */
-typedef struct ScalarArrayOpExprState
-{
-	FuncExprState fxprstate;
-	/* Cached info about array element type */
-	Oid			element_type;
-	int16		typlen;
-	bool		typbyval;
-	char		typalign;
-} ScalarArrayOpExprState;
-
-/* ----------------
- *		BoolExprState node
- * ----------------
- */
-typedef struct BoolExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* states of argument expression(s) */
-} BoolExprState;
+} SetExprState;
 
 /* ----------------
  *		SubPlanState node
@@ -784,10 +672,11 @@ typedef struct BoolExprState
  */
 typedef struct SubPlanState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	SubPlan    *subplan;
 	struct PlanState *planstate;	/* subselect plan's state tree */
 	struct PlanState *parent;	/* parent plan node's state tree */
-	ExprState  *testexpr;		/* state of combining expression */
+	ExprState *testexpr;		/* state of combining expression */
 	List	   *args;			/* states of argument expression(s) */
 	HeapTuple	curTuple;		/* copy of most recent tuple from subplan */
 	Datum		curArray;		/* most recent array from ARRAY() subplan */
@@ -814,197 +703,12 @@ typedef struct SubPlanState
  */
 typedef struct AlternativeSubPlanState
 {
-	ExprState	xprstate;
+	NodeTag		type;
+	AlternativeSubPlan *subplan;
 	List	   *subplans;		/* states of alternative subplans */
 	int			active;			/* list index of the one we're using */
 } AlternativeSubPlanState;
 
-/* ----------------
- *		FieldSelectState node
- * ----------------
- */
-typedef struct FieldSelectState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	TupleDesc	argdesc;		/* tupdesc for most recent input */
-} FieldSelectState;
-
-/* ----------------
- *		FieldStoreState node
- * ----------------
- */
-typedef struct FieldStoreState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input tuple value */
-	List	   *newvals;		/* new value(s) for field(s) */
-	TupleDesc	argdesc;		/* tupdesc for most recent input */
-} FieldStoreState;
-
-/* ----------------
- *		CoerceViaIOState node
- * ----------------
- */
-typedef struct CoerceViaIOState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	FmgrInfo	outfunc;		/* lookup info for source output function */
-	FmgrInfo	infunc;			/* lookup info for result input function */
-	Oid			intypioparam;	/* argument needed for input function */
-} CoerceViaIOState;
-
-/* ----------------
- *		ArrayCoerceExprState node
- * ----------------
- */
-typedef struct ArrayCoerceExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input array value */
-	Oid			resultelemtype; /* element type of result array */
-	FmgrInfo	elemfunc;		/* lookup info for element coercion function */
-	/* use struct pointer to avoid including array.h here */
-	struct ArrayMapState *amstate;		/* workspace for array_map */
-} ArrayCoerceExprState;
-
-/* ----------------
- *		ConvertRowtypeExprState node
- * ----------------
- */
-typedef struct ConvertRowtypeExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input tuple value */
-	TupleDesc	indesc;			/* tupdesc for source rowtype */
-	TupleDesc	outdesc;		/* tupdesc for result rowtype */
-	/* use "struct" so we needn't include tupconvert.h here */
-	struct TupleConversionMap *map;
-	bool		initialized;
-} ConvertRowtypeExprState;
-
-/* ----------------
- *		CaseExprState node
- * ----------------
- */
-typedef struct CaseExprState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* implicit equality comparison argument */
-	List	   *args;			/* the arguments (list of WHEN clauses) */
-	ExprState  *defresult;		/* the default result (ELSE clause) */
-	int16		argtyplen;		/* if arg is provided, its typlen */
-} CaseExprState;
-
-/* ----------------
- *		CaseWhenState node
- * ----------------
- */
-typedef struct CaseWhenState
-{
-	ExprState	xprstate;
-	ExprState  *expr;			/* condition expression */
-	ExprState  *result;			/* substitution result */
-} CaseWhenState;
-
-/* ----------------
- *		ArrayExprState node
- *
- * Note: ARRAY[] expressions always produce varlena arrays, never fixed-length
- * arrays.
- * ----------------
- */
-typedef struct ArrayExprState
-{
-	ExprState	xprstate;
-	List	   *elements;		/* states for child nodes */
-	int16		elemlength;		/* typlen of the array element type */
-	bool		elembyval;		/* is the element type pass-by-value? */
-	char		elemalign;		/* typalign of the element type */
-} ArrayExprState;
-
-/* ----------------
- *		RowExprState node
- * ----------------
- */
-typedef struct RowExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* the arguments */
-	TupleDesc	tupdesc;		/* descriptor for result tuples */
-} RowExprState;
-
-/* ----------------
- *		RowCompareExprState node
- * ----------------
- */
-typedef struct RowCompareExprState
-{
-	ExprState	xprstate;
-	List	   *largs;			/* the left-hand input arguments */
-	List	   *rargs;			/* the right-hand input arguments */
-	FmgrInfo   *funcs;			/* array of comparison function info */
-	Oid		   *collations;		/* array of collations to use */
-} RowCompareExprState;
-
-/* ----------------
- *		CoalesceExprState node
- * ----------------
- */
-typedef struct CoalesceExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* the arguments */
-} CoalesceExprState;
-
-/* ----------------
- *		MinMaxExprState node
- * ----------------
- */
-typedef struct MinMaxExprState
-{
-	ExprState	xprstate;
-	List	   *args;			/* the arguments */
-	FmgrInfo	cfunc;			/* lookup info for comparison func */
-} MinMaxExprState;
-
-/* ----------------
- *		XmlExprState node
- * ----------------
- */
-typedef struct XmlExprState
-{
-	ExprState	xprstate;
-	List	   *named_args;		/* ExprStates for named arguments */
-	List	   *args;			/* ExprStates for other arguments */
-} XmlExprState;
-
-/* ----------------
- *		NullTestState node
- * ----------------
- */
-typedef struct NullTestState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	/* used only if input is of composite type: */
-	TupleDesc	argdesc;		/* tupdesc for most recent input */
-} NullTestState;
-
-/* ----------------
- *		CoerceToDomainState node
- * ----------------
- */
-typedef struct CoerceToDomainState
-{
-	ExprState	xprstate;
-	ExprState  *arg;			/* input expression */
-	/* Cached set of constraints that need to be checked */
-	/* use struct pointer to avoid including typcache.h here */
-	struct DomainConstraintRef *constraint_ref;
-} CoerceToDomainState;
-
 /*
  * DomainConstraintState - one item to check during CoerceToDomain
  *
@@ -1023,7 +727,8 @@ typedef struct DomainConstraintState
 	NodeTag		type;
 	DomainConstraintType constrainttype;		/* constraint type */
 	char	   *name;			/* name of constraint (for error msgs) */
-	ExprState  *check_expr;		/* for CHECK, a boolean expression */
+	Expr	   *check_expr;		/* for CHECK, a boolean expression */
+	ExprState  *check_exprstate; /* for CHECK, a boolean expression */
 } DomainConstraintState;
 
 
@@ -1060,8 +765,7 @@ typedef struct PlanState
 	 * state trees parallel links in the associated plan tree (except for the
 	 * subPlan list, which does not exist in the plan tree).
 	 */
-	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState  *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1138,6 +842,7 @@ typedef struct ResultState
 typedef struct ProjectSetState
 {
 	PlanState	ps;				/* its first field is NodeTag */
+	Node	  **elems;				/* array of expression states */
 	ExprDoneCond *elemdone;		/* array of per-SRF is-done states */
 	int			nelems;			/* length of elemdone[] array */
 	bool		pending_srf_tuples;		/* still evaluating srfs in tlist? */
@@ -1372,7 +1077,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState  *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1418,7 +1123,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState  *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1534,7 +1239,7 @@ typedef struct ParallelBitmapHeapState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState  *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1563,7 +1268,6 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1712,7 +1416,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1759,7 +1463,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState  *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1857,7 +1561,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState  *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 2bc7a5df11..b39fcd197a 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -192,36 +192,19 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
 	 *
-	 * These correspond (not always one-for-one) to primitive nodes derived
-	 * from Expr.
+	 * ExprState represents the evaluation state for a whole expression tree.
+	 * Most Expr based nodes do not have a corresponding expression state
+	 * node, they're solely handled within execExpr* - but sometimes the state
+	 * needs to be shared with other parts of the executor, as e.g. the case
+	 * for AggrefExprState, which nodeAgg.c has to modify.
 	 */
 	T_ExprState,
-	T_GenericExprState,
-	T_WholeRowVarExprState,
 	T_AggrefExprState,
 	T_GroupingFuncExprState,
 	T_WindowFuncExprState,
-	T_ArrayRefExprState,
-	T_FuncExprState,
-	T_ScalarArrayOpExprState,
-	T_BoolExprState,
+	T_SetExprState,
 	T_SubPlanState,
 	T_AlternativeSubPlanState,
-	T_FieldSelectState,
-	T_FieldStoreState,
-	T_CoerceViaIOState,
-	T_ArrayCoerceExprState,
-	T_ConvertRowtypeExprState,
-	T_CaseExprState,
-	T_CaseWhenState,
-	T_ArrayExprState,
-	T_RowExprState,
-	T_RowCompareExprState,
-	T_CoalesceExprState,
-	T_MinMaxExprState,
-	T_XmlExprState,
-	T_NullTestState,
-	T_CoerceToDomainState,
 	T_DomainConstraintState,
 
 	/*
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index e570b71c04..c6857e536d 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -61,7 +61,7 @@ extern void xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode,
 			const char *msg);
 
 extern xmltype *xmlconcat(List *args);
-extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
+extern xmltype *xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue);
 extern xmltype *xmlparse(text *data, XmlOptionType xmloption, bool preserve_whitespace);
 extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null);
 extern xmltype *xmlroot(xmltype *data, text *version, int standalone);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 196e518e0d..be4983e8c1 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -145,7 +145,7 @@ typedef struct					/* cast_hash table entry */
 {
 	plpgsql_CastHashKey key;	/* hash key --- MUST BE FIRST */
 	Expr	   *cast_expr;		/* cast expression, or NULL if no-op cast */
-	/* The ExprState tree is valid only when cast_lxid matches current LXID */
+	/* ExprState is valid only when cast_lxid matches current LXID */
 	ExprState  *cast_exprstate; /* expression's eval tree */
 	bool		cast_in_use;	/* true while we're executing eval tree */
 	LocalTransactionId cast_lxid;
@@ -4711,7 +4711,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
 
 				/*
 				 * Evaluate the subscripts, switch into left-to-right order.
-				 * Like ExecEvalArrayRef(), complain if any subscript is null.
+				 * Like the expression built by ExecInitArrayRef(), complain
+				 * if any subscript is null.
 				 */
 				for (i = 0; i < nsubscripts; i++)
 				{
diff --git a/src/test/regress/expected/case.out b/src/test/regress/expected/case.out
index 4cc4851475..36bf15c4ac 100644
--- a/src/test/regress/expected/case.out
+++ b/src/test/regress/expected/case.out
@@ -308,7 +308,7 @@ SELECT * FROM CASE_TBL;
 -- Nested CASE expressions
 --
 -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation.  After
 -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
 -- the isNull flag for the case test value incorrectly became true, causing
 -- the third WHEN-clause not to match.  The volatile function calls are needed
diff --git a/src/test/regress/sql/case.sql b/src/test/regress/sql/case.sql
index 59268f8cdf..66b6e98fb1 100644
--- a/src/test/regress/sql/case.sql
+++ b/src/test/regress/sql/case.sql
@@ -166,7 +166,7 @@ SELECT * FROM CASE_TBL;
 --
 
 -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation.  After
 -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
 -- the isNull flag for the case test value incorrectly became true, causing
 -- the third WHEN-clause not to match.  The volatile function calls are needed
-- 
2.11.0.22.g8d7a455.dirty

#43Konstantin Knizhnik
k.knizhnik@postgrespro.ru
In reply to: Andres Freund (#42)
Re: WIP: Faster Expression Processing v4

On 13.03.2017 11:03, Andres Freund wrote:

Hi,

On 2017-03-12 05:40:51 +0100, Tomas Vondra wrote:

I wanted to do a bit of testing and benchmarking on this, but 0004 seems to
be a bit broken.

Well, "broken" in the sense that it's already outdated, because other
stuff that got merged.

The patch does not apply anymore - there are some conflicts
in execQual.c, but I think I fixed those. But then I ran into a bunch of
compile-time errors, because some of the executor nodes still reference bits
that were moved elsewhere.

Updated patch attached. Note that this patch has two changes I've not
yet evaluated performance-wise.

I got the following results at my system with Intel(R) Core(TM) i7-4770
CPU @ 3.40GHz, 16Gb RAM,
TPC-H Q1/Q6 scale 10, sharedBuffers=8Gb, pg_prewarm on lineitem table
projection:

Q1
Q6
Master
7503 ms 1171 ms
Your patch 6420 ms 1034 ms
VOPS
396 ms
249 ms
VOPS + patch 367 ms
233 ms

--
Konstantin Knizhnik
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#44Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Andres Freund (#42)
Re: WIP: Faster Expression Processing v4

On 03/13/2017 09:03 AM, Andres Freund wrote:

Hi,

On 2017-03-12 05:40:51 +0100, Tomas Vondra wrote:

I wanted to do a bit of testing and benchmarking on this, but 0004 seems to
be a bit broken.

Well, "broken" in the sense that it's already outdated, because other
stuff that got merged.

Yes, that's what I meant. Sorry for not being quite clear.

The patch does not apply anymore - there are some conflicts
in execQual.c, but I think I fixed those. But then I ran into a bunch of
compile-time errors, because some of the executor nodes still reference bits
that were moved elsewhere.

Updated patch attached. Note that this patch has two changes I've not
yet evaluated performance-wise.

And which are those?

I plan to do a bit of testing / benchmarking on this later this week,
depending on other testing already happening on my machine.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#45Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#42)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-13 01:03:51 -0700, Andres Freund wrote:

What's basically missing here is:
- pgindent run and minimizing resulting damage

Running into a bit of an issue here - pgindent mangles something like

EEO_SWITCH (op->opcode)
{
EEO_CASE(EEO_DONE):
goto out;

EEO_CASE(EEO_INNER_FETCHSOME):
/* XXX: worthwhile to check tts_nvalid inline first? */
slot_getsomeattrs(innerslot, op->d.fetch.last_var);
EEO_DISPATCH(op);

EEO_CASE(EEO_OUTER_FETCHSOME):
slot_getsomeattrs(outerslot, op->d.fetch.last_var);
EEO_DISPATCH(op);

EEO_CASE(EEO_SCAN_FETCHSOME):
slot_getsomeattrs(scanslot, op->d.fetch.last_var);
EEO_DISPATCH(op);

EEO_CASE(EEO_INNER_VAR):
{
int attnum = op->d.var.attnum;

/*
* Can't assert tts_nvalid, as wholerow var evaluation or such
* could have materialized the slot - but the contents are
* still valid :/
*/
Assert(op->d.var.attnum >= 0);
*op->resnull = innerslot->tts_isnull[attnum];
*op->resvalue = innerslot->tts_values[attnum];
EEO_DISPATCH(op);
}

into

EEO_SWITCH(op->opcode)
{
EEO_CASE(EEO_DONE):
goto out;

EEO_CASE(EEO_INNER_FETCHSOME):
/* XXX: worthwhile to check tts_nvalid inline first? */
slot_getsomeattrs(innerslot, op->d.fetch.last_var);
EEO_DISPATCH(op);

EEO_CASE(EEO_OUTER_FETCHSOME):
slot_getsomeattrs(outerslot, op->d.fetch.last_var);
EEO_DISPATCH(op);

EEO_CASE(EEO_SCAN_FETCHSOME):
slot_getsomeattrs(scanslot, op->d.fetch.last_var);
EEO_DISPATCH(op);

EEO_CASE(EEO_INNER_VAR):
{
int attnum = op->d.var.attnum;

/*
* Can't assert tts_nvalid, as wholerow var evaluation or such
* could have materialized the slot - but the contents are still
* valid :/
*/
Assert(op->d.var.attnum >= 0);
*op->resnull = innerslot->tts_isnull[attnum];
*op->resvalue = innerslot->tts_values[attnum];
EEO_DISPATCH(op);
}

which is a bit annoying. (the EEO_CASE is either a jump label or a case
statement, depending on computed goto availability).

It seems we could either:
1) live with the damage
2) disable pgindent
3) move the : inside EEO_CASE's definition, and only use {} blocks.

I'm inclined to go for 3).

Opinions?

- Andres

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

#46Andres Freund
andres@anarazel.de
In reply to: Tomas Vondra (#44)
4 attachment(s)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-13 22:37:51 +0100, Tomas Vondra wrote:

On 03/13/2017 09:03 AM, Andres Freund wrote:

Hi,

On 2017-03-12 05:40:51 +0100, Tomas Vondra wrote:

I wanted to do a bit of testing and benchmarking on this, but 0004 seems to
be a bit broken.

Well, "broken" in the sense that it's already outdated, because other
stuff that got merged.

Yes, that's what I meant. Sorry for not being quite clear.

And the patch already again has conflicts. Updated version, with a good
chunk of other changes, attached.

The patch does not apply anymore - there are some conflicts
in execQual.c, but I think I fixed those. But then I ran into a bunch of
compile-time errors, because some of the executor nodes still reference bits
that were moved elsewhere.

Updated patch attached. Note that this patch has two changes I've not
yet evaluated performance-wise.

And which are those?

Primarily around minor performance regressions for the evaluation of
very simple (i.e. single Var/Const) expressions. I did a fair amount of
work hunting these down.

Besides that, this version has:
- pgindented most of the affected pieces (i.e. all significant new code
has been reindent, not all touched one)
- Transported missing comments from execQual.c - afaics all relevant
ones have been moved over
- Replaced ExprState->cb with evalfunc - that's what it was named
previously, and I see no reason to diverge.
- significantly expanded header comments, especially in
execInterpExpr.c, explaining the basic approach of expression
evaluation. Feedback here would be greatly appreciated - I've spent
so much time neck deep into this, that I don't quite know in how much
detail to go.
- Initial polished commit messages and such.

My current plan is to not do very much on this tomorrow, do another full
pass on Wednesday, and push it, unless there's protest till then.

I plan to do a bit of testing / benchmarking on this later this week,
depending on other testing already happening on my machine.

Cool.

I've performed a bunch more benchmarks, and the results have been pretty
favorable. Due to some more work hunting down minor regressions I can't
make any tpc-h query perform worse than on master, with either
best/average of three executions. I also did a bunch of runs with
parallelism, and the results there are also pretty good - even better
than non-parallel sometimes.

These results include parallelism, but were done *before* rebasing ontop
of Noah's changes. There was also, during all runs, a browser and emacs
running, so it's certainly possible that there's some skew.

-par is with parallelism enabled to 8, with is without. Otherwise same
setup as two mails up.

master-par q01 min: 4629.643 master min: 15108.813 [diff +69.36] dev min: 12629.0 [diff +63.34] dev-par min: 3458.899 [diff -33.85]
master-par q02 min: 1048.511 master min: 1196.596 [diff +12.38] dev min: 1182.652 [diff +11.34] dev-par min: 1044.876 [diff -0.35]
master-par q03 min: 4651.492 master min: 6949.525 [diff +33.07] dev min: 6441.467 [diff +27.79] dev-par min: 3928.919 [diff -18.39]
master-par q04 min: 698.037 master min: 981.081 [diff +28.85] dev min: 947.506 [diff +26.33] dev-par min: 341.855 [diff -104.19]
master-par q05 min: 871.528 master min: 5119.728 [diff +82.98] dev min: 4979.498 [diff +82.50] dev-par min: 702.3 [diff -24.10]
master-par q06 min: 1107.307 master min: 1807.433 [diff +38.74] dev min: 1694.513 [diff +34.65] dev-par min: 738.037 [diff -50.03]
master-par q07 min: 4867.412 master min: 4745.141 [diff -2.58] dev min: 4682.068 [diff -3.96] dev-par min: 4207.644 [diff -15.68]
master-par q08 min: 432.159 master min: 1465.219 [diff +70.51] dev min: 1416.691 [diff +69.50] dev-par min: 418.331 [diff -3.31]
master-par q09 min: 3965.76 master min: 6441.099 [diff +38.43] dev min: 6220.095 [diff +36.24] dev-par min: 3875.673 [diff -2.32]
master-par q10 min: 1129.351 master min: 5818.318 [diff +80.59] dev min: 5406.125 [diff +79.11] dev-par min: 1107.759 [diff -1.95]
master-par q11 min: 247.598 master min: 291.061 [diff +14.93] dev min: 277.18 [diff +10.67] dev-par min: 241.983 [diff -2.32]
master-par q12 min: 1532.855 master min: 4552.034 [diff +66.33] dev min: 4431.584 [diff +65.41] dev-par min: 1395.703 [diff -9.83]
master-par q13 min: 8638.503 master min: 8009.64 [diff -7.85] dev min: 7891.661 [diff -9.46] dev-par min: 8314.437 [diff -3.90]
master-par q14 min: 723.878 master min: 764.35 [diff +5.29] dev min: 730.526 [diff +0.91] dev-par min: 717.66 [diff -0.87]
master-par q15 min: 1991.915 master min: 1809.162 [diff -10.10] dev min: 1716.983 [diff -16.01] dev-par min: 1879.927 [diff -5.96]
master-par q16 min: 1596.643 master min: 1843.553 [diff +13.39] dev min: 1728.467 [diff +7.63] dev-par min: 1429.103 [diff -11.72]
master-par q17 min: 354.357 master min: 418.761 [diff +15.38] dev min: 414.958 [diff +14.60] dev-par min: 322.471 [diff -9.89]
master-par q18 min: 11707.479 master min: 14216.567 [diff +17.65] dev min: 14206.893 [diff +17.59] dev-par min: 11642.863 [diff -0.55]
master-par q19 min: 116.819 master min: 325.531 [diff +64.11] dev min: 287.497 [diff +59.37] dev-par min: 102.909 [diff -13.52]
master-par q20 min: 430.85 master min: 500.92 [diff +13.99] dev min: 478.536 [diff +9.96] dev-par min: 410.548 [diff -4.95]
master-par q22 min: 460.712 master min: 626.207 [diff +26.43] dev min: 595.708 [diff +22.66] dev-par min: 434.851 [diff -5.95]

Thees seem like pretty neat results. Sorry for not cleaning up the
representation - too tired.

Greetings,

Andres Freund

Attachments:

0001-Add-expression-dependencies-on-composite-type-whole-.patch.gzapplication/x-patch-gzipDownload
�H��X0001-Add-expression-dependencies-on-composite-type-whole-.patch�\{s�6��������zX����T���jbM�J����T	J�P�B��x3����R$��d;����n����'�y����{����G�m�/��������������i7��~v+����f�����f�4��3v8����H$�������<�����xk\r)��N��-�#�`<P��g�u��z��l���a�3�����X�<5;�`�����%�=X�#�"pD`{0)|���2�=)�|X
v����/���Y 7c<�b�}?��k�g>,+�NO�����	��{l��S��$��Yn~C�q?�>��D�����1wq��=��'��bXF�i\z���36�r�����,j��tq
���f�=�t�y���"�������o����F_�^�,���)�?8�6��g�V
�e_���������S����i$f��S��#�z$�	��&�1�d���E<�	7	��F��^��g�@!��/�	���0���M�
���iV�a����9vu@:}�XD���R=��^D���~l����^�y���GQ�>�����g���~j��N��j����qz63��n��,�c6�����~�� a5�b	B
�
C������T��}�^�>q}>�+6�!�����^�D��R����dJG��$������[����l�o��^�]�9'C���B��!4c�<�ez��'���,F��(��o��
�2���l�V��E��I���]������"�n'�8���a�����dU����Q<"a��s�I�0���������N�P����
��^�f`��X8�
#&�D� �de�X4p�������f�����_�I�~�A���K>���5<�c��F��`<��n�_��b	�T
����>
�p��`�:`��L|�Az���`~W�*%�8y��=�gA(a��CX���y@�X���Pn���0�(�R�e.#��R��8��\�?�-�������5�,��q��o��"9~���o��Bt��5������1{7������21���All�[�_�����8�� ��v�S�Z(�.X�	b9�� �{�Q�&t;	�U��r5
G5�� ���Q�?;�j������E��oX$'�����z\�F�TR�����	�f�%�Z�	L�c��d��^Pwz�{M$g��|DF��Bk

�m��h"<�b�ECD��9KH�������<��A�=()*�&�x�
�y��`���	�����W2�x@X���1�:F}��}��@�����4���8OL��_��$Ha,@�@�$�j��	e@�rb0��7�|O�.<9'�Dx0��	e6
1,?/��&�
���i1�u�zN|6hIN��4�<�r(-�C��S�Au���)
(hq&�\J��o�SG�}.et�,�"�G��IL�`��W��/(���Av����f?���N"�&�r�Rq.B�8�V��#w������D`�)
d	z[��`����"��@���]��vC��g/�4KIh�
�=m�
���QBxtG�	����z�n���M���Ri

-���J����d
���^{�GH���P�@B3,@y"-�`��4<"���m���`f0���~tJ��� ����:�/A��o�h���k*����������W���������;�F����]���p��9S�S[�E!�@�u�g�X���f�����+���|�����Vr���qP�E�M�A4�3X��=�1��� +�����������o���[�T�Y#�F��F7��e2J����+�mH�}7��B�
29�r��@�r�0x�K.���B�`�����By�#%;��T���C|�*��N�f�^_��!`J��$5���q
�c�Q�l����`�=)
����B��{�������������acgO��x��:����?�G�,p�'h
0k����1�Ny���R�������������d��6���)i�	��B������|6X���g����p8��_�{������W��z?�\�+8vk���0�mS%�fN�����#�hH��?i!g;�==)���������;�w�|1MA!��;����@c�ks���'�9���l��L��S�f`)����OI����B ��*�)�����c�(��W��1Q��G��x��C�^-�_��%�r�,���(����y��o_�=����+x���@�`�)c�YB[��h�������*��*I�{� ��>N'':���N6N�����Yl���e,r\O�b��>��7��_������C}���`N�=T�l���,<��
��2�����>����?�LPO��A�EqQ���f@�<��A����)S9z[��K�� ����e(����5�:�A�u����^�����d���A�@�}z�����|5��4<lM1�+��c?8=�!L���p��V���6s���Q ���+g��0p�m1y���qH��lAG{<�Os�i���N�Ty�	�r���J��g���=�R�n5��/B��g�_�����]x�8�a���
4����t%���fJ[C�-�E�ZA��5;�M�o��F�$<]���B�4��}}d�lm�k6����FB;�_�3�p<����8U���Z�b���+K�*;���R��G����<�[kDU������V��^�`CE�@@���tD��@rM�9�>�D������"�Ie�C�=\@���u�����[OMY�qx.0{zb;+��j=:�ys�'b�T����L����<Y5����e���&��>t��V1����@��S���Ag�~0�G
���K�E��.������U�����p�.���[�)^>����X�l�:����'�]R/�N:�
]�k��)�Ka��+]F�N"�8�a�3� M�0@SY�F4������bK�|UQ2����������]/���u�v\H�n��n��2����#�_�����XV��z��Y�Z(��������
-���uaE�ud�Pn�P`���n{9���6�����|5u+�����!r5.F��|�*^q>&
+k�V�|�����P_~{���\I�W�����Fr�qA]k�%��w"�����J~.��P�&������	�$QL���H�+3��f���&��SU��B�����Q���R*��W���}u���H��@w��+�����^}&$R���Is7'&���n��JAV�_�f�� /�����s{N]�i��r@g,��_'!���	���CU�w4N�����d���oXJ���%�a�k=�.���A��������o.em�j�2���d9�����i�mgc�A�w��4q�����(\.�����$B��nA���"�R�E��G@�'_�j���X-w�-<�S�
��w��l�������2���C5�i�U�fs��u�:���}u��-�v���o4Z����5m�]�y�����u���4��� �>�&U������)�jHi������������I��`�c�g��`tr��qZ�H��_hze�R/^�3����'����a�Ukd	 OY;����JZT��S2��t��zCs[H$�8On<��&G�JU����F]�tmv�0����x�<�g����Y�Q�i��A&�Yc���������z�7vH{��D��[9f���!M��[IY�6_d>`hb�8 ���m����n��P�9IH�p����O��AF�9�1
dAL�`]Y�8�{ ��W����@�8I��=���D���p}3�1?����^��������+��iM��m��f�a���~�w^K�?`�����G������0
�����~T���
g���U>�8���)���w��������*���Y�
�,%��`OKV�i�.#R���X���6N��kM�]���}�X��cf��]G5�o�wu�z��qG�������
��8{��m"4@LP1�`�������p�7����C��9V��^��}K	;���Q�~b����k��lZ��8��J����Hy�����S_ruT�� ��T
,'Tb�T�B��O�,���Y8]�8]E�Mv���dZ;�P'�D�"�)0%���c}��FG-�4���Qrz�z�����/�������M�d@[���}JXB�*M��Y� 9�Y���>�7�(�4��(���������G��[f��=�&����5�Q�V�f�h] �5y�������iQk6}��Y�a@��7�|�V�O���5o�q
�ZV���k�=��>���j���(�,@�u�5]��f�Tzo�
�38�D�/r��W6]���.w�I������T�:hj��f~e��vMm�[L��K�w�*i}��uQ�=�U��i��M���g,7��O_��Z��?��F���sP��GVX����bu��K5�Y��]�?�K���}}��0<=� ��/��'5��D���b���@Fcv�x���z���|��Xe��z������a�I������vb��nR��f]�=�V�K(������5+���o���h�p3__\�T?E��3[��*�����fT�`=��E��*��O3�=O��v�V]OR>���=W��$&�o����p����l�����(��0��\E�����N���B*�
X��T��W;�o��6�7tK��V�5���0
���V���n�������7u��N�OE�U|��N)G���Hd#������}=�������r��3��9#��d�����%���]l�b/-���������A�jwzn�5�����\��������l|ou�sV9%_��0����1�\�������M��Z1G���z�t�Z���X��e�_��kG�����(�I��M�a��I�R��h�����D�/���aZ�f���3��L[.��C�����	O�L
0002-Make-get_last_attnums-more-generic.patch.gzapplication/x-patch-gzipDownload
0003-Add-autoconf-test-for-computed-goto-support.patch.gzapplication/x-patch-gzipDownload
0004-Faster-expression-evaluation-and-targetlist-projecti.patch.gzapplication/x-patch-gzipDownload
#47Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andres Freund (#45)
Re: WIP: Faster Expression Processing v4

Andres Freund wrote:

EEO_SWITCH(op->opcode)
{
EEO_CASE(EEO_DONE):
goto out;

Oh my.

which is a bit annoying. (the EEO_CASE is either a jump label or a case
statement, depending on computed goto availability).

It seems we could either:
1) live with the damage
2) disable pgindent
3) move the : inside EEO_CASE's definition, and only use {} blocks.

I think 3) is nasty because the result doesn't look like normal C. If
EEO_CASE() are potentially jump labels, then indentation becomes
correct. Why not accept it? It looks odd, but that gives a better hint
to the reader about what's going on.

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

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

#48Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andres Freund (#46)
Re: WIP: Faster Expression Processing v4
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 3d6a3801c0..d205101b89 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -47,7 +47,14 @@
 #include "utils/rel.h"
-static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
+typedef struct LastLastAttnumInfo

LastLast?

Patch 0003 is huge. It would be good to have someone at least read it
before pushing, but I don't think anyone other than you has done so.

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

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

#49Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Andres Freund (#46)
1 attachment(s)
Re: WIP: Faster Expression Processing v4

On 03/14/2017 08:53 AM, Andres Freund wrote:

Besides that, this version has:
- pgindented most of the affected pieces (i.e. all significant new code
has been reindent, not all touched one)

I think you'll need to add all the inner structs ExprEvalStep
typedefs.list to indent them right.

My current plan is to not do very much on this tomorrow, do another full
pass on Wednesday, and push it, unless there's protest till then.

I looked at patch 0004. Some comments:

* EEO_DISPATCH_DIRECT(op) takes 'op' as an argument, but it really
assumes that 'op' has already been set to point to the jump target. I
find that a bit weird. I guess the idea is that you always pass the
Program Counter variable as 'op' argument. For consistency, would be
good if EEO_SWITCH() also took just 'op' as the argument, rather than
op->opcode. But I think it would be more clear if they should both just
assumed that there's a variable called 'op' that points to the current
instruction.

* All the callers of EEO_DISPATCH_DIRECT(op) set 'op' just prior to
calling EEO_DISPATCH_DIRECT(op). How about having a macro EEO_JUMP(<step
number>), to encapsulate setting 'op' and jumping to it in one operation?

* ExecEvalStepOp() seems relatively expensive, with the linear scan over
all the opcodes, if called on an ExprState that already has
EEO_FLAG_JUMP_THREADED set. All the callers use it to check if the
opcode is a particular one, so you could check if the opcode matches
that particular one, instead of scanning the dispatch table to find what
it is.

* But is ExecEvalStepOp() ever actually get called on an expression with
EEO_FLAG_JUMP_THREADED already set? It's only used in
ExecInstantiateInterpretedExpr(), and it's called only once on each
ExprState. How about just adding an Assert that EEO_FLAG_JUMP_THREADED
is not yet set? Or drop the EEO_FLAG_JUMP_THREADED flag altogether, and
assert that evalfunc != ExecInterpExpr.

* How tight are we on space in the ExprEvalStep union? Currently, the
jump-threading preparation replaces the opcodes with the goto labels,
but it would be really nice to keep the original opcodes, for debugging
purposes if nothing else.

* execInterpExpr.c is quite a mouthful. How about exprInterpreter.c?

Attached is a patch, on top of your
0004-Faster-expression-evaluation-and-targetlist-projecti.patch, with
some minor tweaks and comments here and there (search for HEIKKI). It's
mostly the same stuff I listed above, implemented in a quick & dirty
way. I hope it's helpful.

- Heikki

Attachments:

faster-eval-comments.patchapplication/x-download; name=faster-eval-comments.patchDownload
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 4cf0883435..ba129948ef 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -3,26 +3,26 @@
  * execExpr.c
  *	  Expression evaluation infrastructure.
  *
- *	Expression evaluation now works by first converting expression trees
+ *	Expression evaluation works by first converting the expression tree
  *	(which went through planning first) into an ExprState using ExecInitExpr()
  *	et al. This converts the tree into a opcode based program (ExprEvalStep
- *	representing individual instructions); allocated as a flat array of
+ *	representing individual instructions), allocated as a flat array of
  *	steps.
  *
  *	This flat representation has the big advantage that it can be implemented
  *	non-recursively, within a single function.  This allows to interpret
  *	larger expressions from largely within a single function, keeping state
- *	between them.  In contrast to that, tree-walk based approaches in contrast
+ *	between them.  In contrast to that, tree-walk based approaches
  *	often run into performance issues due to function call / stack
  *	manipulation overhead.
  *
  *	The ExprEvalStep representation is designed to be usable for interpreting
- *	the expression, as well as compiling into native code. Thus, if possible,
- *	as much complexity as possible should be handed by ExecInitExpr() (and
- *	helpers), instead of handled at execution time where both interpreted and
+ *	the expression, as well as compiling into native code. As much complexity
+ *	as possible should be handled by ExecInitExpr() (and
+ *	helpers), instead of execution time where both interpreted and
  *	compiled versions would need to deal with the complexity. Additionally
  *	checks for initialization at run time have a small but noticeable cost at
- *	every execution.
+ *	every execution. (HEIKKI: What does this last sentence refer to?)
  *
  *	The next step is preparing the ExprState for execution, using
  *	ExecInstantiateExpr(). This is internally done by ExecInitExpr() and other
diff --git a/src/backend/executor/execInterpExpr.c b/src/backend/executor/execInterpExpr.c
index 21388f4086..2386fc898f 100644
--- a/src/backend/executor/execInterpExpr.c
+++ b/src/backend/executor/execInterpExpr.c
@@ -1,3 +1,7 @@
+/*
+ * HEIKKI: "Interp" is quite a mouthful. How about execExprInterpreter.c or
+ * just exprInterpreter.c?
+ */
 /*-------------------------------------------------------------------------
  *
  * execInterpExpr.c
@@ -5,19 +9,19 @@
  *
  * This file provides a "switch threaded" (all compilers) and "direct
  * threaded" (gcc, clang and compatible) implementation of expression
- * evaluation.  The former is among st the fastest known methods of
+ * evaluation.  The former is amongst the fastest known methods of
  * interpreting programs without resorting to assembly level work, or
  * just-in-time compilation, but requires support for computed gotos.  The
  * latter is amongst the fastest approaches doable in standard C.
  *
  * Both work by using ExprEvalStep->opcode to dispatch into code blocks
- * implementing the specific method.  Switch based uses a plain switch()
+ * implementing the specific method.  Switch-based uses a plain switch()
  * statement to perform the dispatch. This has the advantages of being plain C
  * and allowing to warn if implementation of a specific opcode has been
  * forgotten.  The disadvantage is that dispatches will, as commonly
  * implemented by compilers, happen from a single location, causing bad branch
  * prediction.  Direct dispatch uses the label-as-values gcc extension - also
- * adopted by some other compilers - to replace ExprEvalStep-> opcode with the
+ * adopted by some other compilers - to replace ExprEvalStep->opcode with the
  * address of the block implementing the instruction. This allows for better
  * branch prediction (the jumps are happening from different locations) and
  * fewer jumps (no jumps to common dispatch location needed).
@@ -84,26 +88,31 @@
  */
 #if defined(EEO_USE_COMPUTED_GOTO)
 static void **dispatch_table = NULL;
-#define EEO_SWITCH(d)
-#define EEO_DISPATCH_DIRECT(op) goto *((void *) op->opcode)
+#define EEO_DISPATCH() goto *((void *) op->opcode)
 #define EEO_CASE(name) CASE_##name:
 #else
-#define EEO_SWITCH(d) switch ((ExprEvalOp) d)
-#define EEO_DISPATCH_DIRECT(op) goto starteval
+#define EEO_DISPATCH() goto starteval
 #define EEO_CASE(name) case name:
 #endif   /* EEO_USE_COMPUTED_GOTO */
 
-#define EEO_DISPATCH(op) \
+#define EEO_JUMP(stepno) \
+	do \
+	{ \
+		op = &state->steps[stepno]; \
+		EEO_DISPATCH(); \
+	} while (0)
+
+#define EEO_NEXT() \
 	do \
 	{ \
 		op++; \
-		EEO_DISPATCH_DIRECT(op); \
+		EEO_DISPATCH(); \
 	} \
 	while (0)
 
 
 static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
-static void ExecPrepareInterp(void);
+static void ExecInitInterpreter(void);
 
 /* support functions */
 static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull);
@@ -127,10 +136,11 @@ static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool
 void
 ExecInstantiateInterpretedExpr(ExprState *state)
 {
-	ExecPrepareInterp();
+	ExecInitInterpreter();
 
 	Assert(state->steps_len >= 1);
-	Assert(ExecEvalStepOp(state, &state->steps[state->steps_len - 1]) == EEO_DONE);
+	Assert(state->steps[state->steps_len - 1].opcode ==  EEO_DONE);
+	Assert(state->flags & EEO_FLAG_JUMP_THREADED == 0);
 
 	/*
 	 * Fast-path for very simple expressions. "starting up" the full
@@ -141,8 +151,8 @@ ExecInstantiateInterpretedExpr(ExprState *state)
 	 */
 	if (state->steps_len == 3)
 	{
-		ExprEvalOp	step0 = ExecEvalStepOp(state, &state->steps[0]);
-		ExprEvalOp	step1 = ExecEvalStepOp(state, &state->steps[1]);
+		ExprEvalOp	step0 = state->steps[0].opcode;
+		ExprEvalOp	step1 = state->steps[1].opcode;
 
 		if (step0 == EEO_INNER_FETCHSOME && step1 == EEO_INNER_VAR)
 		{
@@ -176,7 +186,7 @@ ExecInstantiateInterpretedExpr(ExprState *state)
 		}
 	}
 	else if (state->steps_len == 2 &&
-			 ExecEvalStepOp(state, &state->steps[0]) == EEO_CONST)
+			 state->steps[0].opcode == EEO_CONST)
 	{
 		state->evalfunc = ExecJustConst;
 		return;
@@ -319,11 +329,11 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 	scanslot = econtext->ecxt_scantuple;
 
 #if defined(EEO_USE_COMPUTED_GOTO)
-	EEO_DISPATCH_DIRECT(op);
+	EEO_DISPATCH();
 #else
 starteval:
+	switch ((ExprEvalOp) op->opcode)
 #endif
-	EEO_SWITCH(op->opcode)
 	{
 		EEO_CASE(EEO_DONE)
 		{
@@ -335,21 +345,21 @@ starteval:
 			/* XXX: worthwhile to check tts_nvalid inline first? */
 			slot_getsomeattrs(innerslot, op->d.fetch.last_var);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_OUTER_FETCHSOME)
 		{
 			slot_getsomeattrs(outerslot, op->d.fetch.last_var);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_SCAN_FETCHSOME)
 		{
 			slot_getsomeattrs(scanslot, op->d.fetch.last_var);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_INNER_VAR)
@@ -365,7 +375,7 @@ starteval:
 			*op->resnull = innerslot->tts_isnull[attnum];
 			*op->resvalue = innerslot->tts_values[attnum];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_OUTER_VAR)
@@ -377,7 +387,7 @@ starteval:
 			*op->resnull = outerslot->tts_isnull[attnum];
 			*op->resvalue = outerslot->tts_values[attnum];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_SCAN_VAR)
@@ -389,7 +399,7 @@ starteval:
 			*op->resnull = scanslot->tts_isnull[attnum];
 			*op->resvalue = scanslot->tts_values[attnum];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ASSIGN_INNER_VAR)
@@ -400,7 +410,7 @@ starteval:
 			resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
 			resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ASSIGN_OUTER_VAR)
@@ -411,7 +421,7 @@ starteval:
 			resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
 			resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ASSIGN_SCAN_VAR)
@@ -422,7 +432,7 @@ starteval:
 			resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
 			resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ASSIGN_TMP)
@@ -432,7 +442,7 @@ starteval:
 			resultslot->tts_values[resultnum] = state->resvalue;
 			resultslot->tts_isnull[resultnum] = state->resnull;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ASSIGN_TMP_UNEXPAND)
@@ -447,7 +457,7 @@ starteval:
 			else
 				resultslot->tts_values[resultnum] = state->resvalue;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_INNER_SYSVAR)
@@ -459,7 +469,7 @@ starteval:
 											innerslot->tts_tupleDescriptor,
 											op->resnull);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_OUTER_SYSVAR)
@@ -471,7 +481,7 @@ starteval:
 											scanslot->tts_tupleDescriptor,
 											op->resnull);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_SCAN_SYSVAR)
@@ -483,7 +493,7 @@ starteval:
 											scanslot->tts_tupleDescriptor,
 											op->resnull);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_CONST)
@@ -491,7 +501,7 @@ starteval:
 			*op->resnull = op->d.constval.isnull;
 			*op->resvalue = op->d.constval.value;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -510,7 +520,7 @@ starteval:
 			*op->resvalue = (op->d.func.fn_addr) (fcinfo);
 			*op->resnull = fcinfo->isnull;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_FUNCEXPR_STRICT)
@@ -533,7 +543,7 @@ starteval:
 			*op->resnull = fcinfo->isnull;
 
 	strictfail:
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_FUNCEXPR_FUSAGE)
@@ -549,7 +559,7 @@ starteval:
 
 			pgstat_end_function_usage(&fcusage, true);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE)
@@ -577,7 +587,7 @@ starteval:
 	strictfail_fusage:
 			pgstat_end_function_usage(&fcusage, true);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -608,12 +618,10 @@ starteval:
 				*op->resnull = false;
 				*op->resvalue = BoolGetDatum(false);
 				/* bail out early */
-				op = &state->steps[op->d.boolexpr.jumpdone];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.boolexpr.jumpdone);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOL_AND_STEP_LAST)
@@ -645,7 +653,7 @@ starteval:
 				*op->resvalue = BoolGetDatum(true);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -683,12 +691,10 @@ starteval:
 				*op->resvalue = BoolGetDatum(true);
 
 				/* bail out early */
-				op = &state->steps[op->d.boolexpr.jumpdone];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.boolexpr.jumpdone);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOL_OR_STEP_LAST)
@@ -720,7 +726,7 @@ starteval:
 				*op->resvalue = BoolGetDatum(false);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOL_NOT_STEP)
@@ -737,7 +743,7 @@ starteval:
 			 */
 			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_QUAL)
@@ -750,12 +756,10 @@ starteval:
 				/* bail out early */
 				*op->resnull = false;
 				*op->resvalue = BoolGetDatum(false);
-				op = &state->steps[op->d.qualexpr.jumpdone];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.qualexpr.jumpdone);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_NULLTEST_ISNULL)
@@ -763,7 +767,7 @@ starteval:
 			*op->resvalue = BoolGetDatum(*op->resnull);
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_NULLTEST_ISNOTNULL)
@@ -771,7 +775,7 @@ starteval:
 			*op->resvalue = BoolGetDatum(!*op->resnull);
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_NULLTEST_ROWISNULL)
@@ -779,7 +783,7 @@ starteval:
 			/* out of line implementation: too large */
 			ExecEvalRowNull(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_NULLTEST_ROWISNOTNULL)
@@ -787,7 +791,7 @@ starteval:
 			/* out of line implementation: too large */
 			ExecEvalRowNotNull(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_PARAM_EXEC)
@@ -795,14 +799,14 @@ starteval:
 			/* out of line implementation: too large */
 			ExecEvalParamExec(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_PARAM_EXTERN)
 		{
 			/* out of line implementation: too large */
 			ExecEvalParamExtern(state, op, econtext);
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_CASE_WHEN_STEP)
@@ -810,22 +814,18 @@ starteval:
 			if (!*op->d.casewhen.isnull
 				&& DatumGetBool(*op->d.casewhen.value))
 			{
-				EEO_DISPATCH(op);
+				EEO_NEXT();
 			}
 			else
 			{
-				op = &state->steps[op->d.casewhen.jumpfalse];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.casewhen.jumpfalse);
 			}
 		}
 
 		EEO_CASE(EEO_CASE_THEN_STEP)
 		{
 			/* results already placed in correct place during preceding steps */
-			op = &state->steps[op->d.casethen.jumpdone];
-
-			EEO_DISPATCH_DIRECT(op);
+			EEO_JUMP(op->d.casethen.jumpdone);
 		}
 
 		EEO_CASE(EEO_CASE_TESTVAL)
@@ -850,7 +850,7 @@ starteval:
 				*op->resnull = econtext->caseValue_isNull;
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_CASE_TESTVAL_UNEXPAND)
@@ -876,7 +876,7 @@ starteval:
 					MakeExpandedObjectReadOnlyInternal(*op->resvalue);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_DOMAIN_TESTVAL)
@@ -895,7 +895,7 @@ starteval:
 				*op->resnull = econtext->domainValue_isNull;
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_DOMAIN_TESTVAL_UNEXPAND)
@@ -924,7 +924,7 @@ starteval:
 					MakeExpandedObjectReadOnlyInternal(*op->resvalue);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -935,12 +935,10 @@ starteval:
 		{
 			if (!*op->resnull)
 			{
-				op = &state->steps[op->d.coalesce.jumpdone];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.coalesce.jumpdone);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/* BooleanTest implementations for all booltesttypes */
@@ -952,7 +950,7 @@ starteval:
 				*op->resvalue = *op->resvalue;
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOLTEST_IS_NOT_FALSE)
@@ -963,7 +961,7 @@ starteval:
 				*op->resvalue = *op->resvalue;
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOLTEST_IS_FALSE)
@@ -974,7 +972,7 @@ starteval:
 				*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOLTEST_IS_NOT_TRUE)
@@ -985,7 +983,7 @@ starteval:
 				*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOLTEST_IS_UNKNOWN)
@@ -993,7 +991,7 @@ starteval:
 			*op->resvalue = BoolGetDatum(*op->resnull);
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_BOOLTEST_IS_NOT_UNKNOWN)
@@ -1001,7 +999,7 @@ starteval:
 			*op->resvalue = BoolGetDatum(!*op->resnull);
 			*op->resnull = false;
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_WHOLEROW)
@@ -1009,7 +1007,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalWholeRowVar(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -1068,7 +1066,7 @@ starteval:
 
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -1106,7 +1104,7 @@ starteval:
 				*op->resnull = fcinfo->isnull;
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_NULLIF)
@@ -1127,13 +1125,13 @@ starteval:
 					*op->resnull = true;
 					*op->resvalue = true;
 
-					EEO_DISPATCH(op);
+					EEO_NEXT();
 				}
 			}
 			*op->resnull = fcinfo->argnull[0];
 			*op->resvalue = fcinfo->arg[0];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_SQLVALUEFUNCTION)
@@ -1144,7 +1142,7 @@ starteval:
 			 */
 			ExecEvalSQLValueFunction(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_CURRENTOFEXPR)
@@ -1152,7 +1150,7 @@ starteval:
 			/* error invocation uses space, and shouldn't ever occur */
 			ExecEvalCurrentOfExpr(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ARRAYEXPR)
@@ -1160,7 +1158,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalArrayExpr(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ARRAYCOERCE)
@@ -1168,7 +1166,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalArrayCoerce(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ROW)
@@ -1176,7 +1174,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalRow(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ROWCOMPARE_STEP)
@@ -1188,9 +1186,7 @@ starteval:
 				(fcinfo->argnull[0] || fcinfo->argnull[1]))
 			{
 				*op->resnull = true;
-				op = &state->steps[op->d.rowcompare_step.jumpnull];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.rowcompare_step.jumpnull);
 			}
 
 			fcinfo->isnull = false;
@@ -1200,20 +1196,16 @@ starteval:
 			if (fcinfo->isnull)
 			{
 				*op->resnull = true;
-				op = &state->steps[op->d.rowcompare_step.jumpnull];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.rowcompare_step.jumpnull);
 			}
 
 			/* no need to compare remaining columns */
 			if (DatumGetInt32(*op->resvalue) != 0)
 			{
-				op = &state->steps[op->d.rowcompare_step.jumpdone];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.rowcompare_step.jumpdone);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ROWCOMPARE_FINAL)
@@ -1242,7 +1234,7 @@ starteval:
 					break;
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_MINMAX)
@@ -1250,7 +1242,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalMinMax(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_FIELDSELECT)
@@ -1258,7 +1250,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalFieldSelect(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_FIELDSTORE_DEFORM)
@@ -1266,7 +1258,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalFieldStoreDeForm(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_FIELDSTORE_FORM)
@@ -1274,7 +1266,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalFieldStoreForm(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -1291,12 +1283,10 @@ starteval:
 			if (*op->resnull &&
 				!op->d.arrayref.state->isassignment)
 			{
-				op = &state->steps[op->d.arrayref.jumpdone];
-
-				EEO_DISPATCH_DIRECT(op);
+				EEO_JUMP(op->d.arrayref.jumpdone);
 			}
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -1307,14 +1297,12 @@ starteval:
 			/* too complex for an inline implementation */
 			if (ExecEvalArrayRefCheckSubscript(state, op))
 			{
-				op++;
+				EEO_NEXT();
 			}
 			else
 			{
-				op = &state->steps[op->d.arrayref_checksubscript.jumpdone];
+				EEO_JUMP(op->d.arrayref_checksubscript.jumpdone);
 			}
-
-			EEO_DISPATCH_DIRECT(op);
 		}
 
 		/*
@@ -1326,7 +1314,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalArrayRefOld(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -1337,7 +1325,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalArrayRefAssign(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -1348,7 +1336,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalArrayRefFetch(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_CONVERT_ROWTYPE)
@@ -1356,7 +1344,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalConvertRowtype(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_SCALARARRAYOP)
@@ -1364,14 +1352,14 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalScalarArrayOp(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_DOMAIN_NOTNULL)
 		{
 			/* too complex for an inline implementation */
 			ExecEvalConstraintNotNull(state, op);
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_DOMAIN_CHECK)
@@ -1379,7 +1367,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalConstraintCheck(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_XMLEXPR)
@@ -1387,7 +1375,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalXmlExpr(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		/*
@@ -1403,7 +1391,7 @@ starteval:
 			*op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
 			*op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_GROUPING_FUNC)
@@ -1411,7 +1399,7 @@ starteval:
 			/* too complex/uncommon for an inline implementation */
 			ExecEvalGroupingFunc(state, op);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_WINDOW_FUNC)
@@ -1423,7 +1411,7 @@ starteval:
 			*op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
 			*op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_SUBPLAN)
@@ -1431,7 +1419,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalSubPlan(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_ALTERNATIVE_SUBPLAN)
@@ -1439,7 +1427,7 @@ starteval:
 			/* too complex for an inline implementation */
 			ExecEvalAlternativeSubPlan(state, op, econtext);
 
-			EEO_DISPATCH(op);
+			EEO_NEXT();
 		}
 
 		EEO_CASE(EEO_LAST)
@@ -1454,6 +1442,8 @@ out:
 	return state->resvalue;
 }
 
+/* Fast-path functions, for very simple expressions */
+
 static Datum
 ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
 {
@@ -1536,7 +1526,7 @@ ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
 }
 
 static void
-ExecPrepareInterp(void)
+ExecInitInterpreter(void)
 {
 	static bool prepared = false;
 
@@ -1551,27 +1541,6 @@ ExecPrepareInterp(void)
 	prepared = true;
 }
 
-ExprEvalOp
-ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
-{
-#if defined(EEO_USE_COMPUTED_GOTO)
-	if (state->flags & EEO_FLAG_JUMP_THREADED)
-	{
-		int			i;
-
-		for (i = 0; i < EEO_LAST; i++)
-		{
-			if ((void *) op->opcode == dispatch_table[i])
-			{
-				return (ExprEvalOp) i;
-			}
-		}
-		elog(ERROR, "unknown opcode");
-	}
-#endif
-	return (ExprEvalOp) op->opcode;
-}
-
 /*
  * Computes the value for a PARAM_EXEC parameter.
  */
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 003f4faede..e28bb2a85e 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -22,6 +22,11 @@ struct ArrayRefState;
 
 typedef enum ExprEvalOp
 {
+	/*
+	 * HEIKKI: How about renaming these to EEOP_* ? I think it'd be a
+	 * good mnemonic to remember that these are opcodes, and just one
+	 * extra letter.
+	 */
 	EEO_DONE,
 	EEO_INNER_FETCHSOME,
 	EEO_OUTER_FETCHSOME,
@@ -110,6 +115,11 @@ typedef struct ExprEvalStep
 	 * ExprEvalOp, but during execution it can be swapped out to some other
 	 * type, e.g. a pointer for computed goto (that's why it's a size_t).
 	 */
+	/* HEIKKI: Is it safe to assume that sizeof(size_t) >= sizeof(void *) ? */
+	/* HEIKKI: How tight are we with space in this union? It'd be nice to keep
+	 * the original opcode, and have a separate field for the computed goto ptr.
+	 * For debugging purposes if nothing else.
+	 */
 	size_t		opcode;
 
 	/* target for the result of the current instruction */
@@ -419,8 +429,6 @@ typedef struct ArrayRefState
 
 extern void ExecInstantiateInterpretedExpr(ExprState *state);
 
-extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
-
 /*
  * Non fast-path execution functions. These are externs instead of static in
  * execInterpExpr.c, because that allows them to be used by other methods of
#50Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#49)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-14 16:58:54 +0200, Heikki Linnakangas wrote:

On 03/14/2017 08:53 AM, Andres Freund wrote:

Besides that, this version has:
- pgindented most of the affected pieces (i.e. all significant new code
has been reindent, not all touched one)

I think you'll need to add all the inner structs ExprEvalStep typedefs.list
to indent them right.

Yea, I saw that. Since they're not named atm, That'd probably not work
well. I'd be inclined to just live with it :/

I looked at patch 0004.

Thanks!

* EEO_DISPATCH_DIRECT(op) takes 'op' as an argument, but it really assumes
that 'op' has already been set to point to the jump target. I find that a
bit weird. I guess the idea is that you always pass the Program Counter
variable as 'op' argument. For consistency, would be good if EEO_SWITCH()
also took just 'op' as the argument, rather than op->opcode. But I think it
would be more clear if they should both just assumed that there's a variable
called 'op' that points to the current instruction.

Hm. I dislike magic variable names. I think I prefer your idea of
passing the op entirely to EEO_SWITCH.

* All the callers of EEO_DISPATCH_DIRECT(op) set 'op' just prior to calling
EEO_DISPATCH_DIRECT(op). How about having a macro EEO_JUMP(<step number>),
to encapsulate setting 'op' and jumping to it in one operation?

Ok.

* ExecEvalStepOp() seems relatively expensive, with the linear scan over all
the opcodes, if called on an ExprState that already has
EEO_FLAG_JUMP_THREADED set. All the callers use it to check if the opcode is
a particular one, so you could check if the opcode matches that particular
one, instead of scanning the dispatch table to find what it is.

It should be fairly cheap at that place, unless the same expression is
instantiated twice for some reason. I have been wondering about adding
a second table ptr->op that can be binary searched for the operation to
make it cheaper.

* But is ExecEvalStepOp() ever actually get called on an expression with
EEO_FLAG_JUMP_THREADED already set? It's only used in
ExecInstantiateInterpretedExpr(), and it's called only once on each
ExprState. How about just adding an Assert that EEO_FLAG_JUMP_THREADED is
not yet set? Or drop the EEO_FLAG_JUMP_THREADED flag altogether, and assert
that evalfunc != ExecInterpExpr.

The primary use of ExecEvalStepOp() is really debugging, so I'd like to
avoid adding that restriction. I guess we can add a second function for
this if needed.

* How tight are we on space in the ExprEvalStep union? Currently, the
jump-threading preparation replaces the opcodes with the goto labels, but it
would be really nice to keep the original opcodes, for debugging purposes if
nothing else.

There's nothing left to spare :(. For debugging I've just used
ExecEvalStepOp() - it's inconvenient to directly print the struct
anyway, since gdb prints the whole union...

* execInterpExpr.c is quite a mouthful. How about exprInterpreter.c?

Attached is a patch, on top of your
0004-Faster-expression-evaluation-and-targetlist-projecti.patch, with some
minor tweaks and comments here and there (search for HEIKKI). It's mostly
the same stuff I listed above, implemented in a quick & dirty way. I hope
it's helpful.

Thanks!

typedef enum ExprEvalOp
{
+	/*
+	 * HEIKKI: How about renaming these to EEOP_* ? I think it'd be a
+	 * good mnemonic to remember that these are opcodes, and just one
+	 * extra letter.
+	 */

I don't mind the change.

+ /* HEIKKI: Is it safe to assume that sizeof(size_t) >= sizeof(void *) ? */

Yes, seems very likely - it's supposed to hold any potential sizeof()
return value. You could end up on platforms where that's not true, if
it used tagged pointers or such. I guess we could instead use a union,
but that doesn't seem that much of a benefit.

Greetings,

Andres Freund

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

#51Andres Freund
andres@anarazel.de
In reply to: Alvaro Herrera (#47)
Re: WIP: Faster Expression Processing v4

On 2017-03-14 08:28:02 -0300, Alvaro Herrera wrote:

Andres Freund wrote:

EEO_SWITCH(op->opcode)
{
EEO_CASE(EEO_DONE):
goto out;

Oh my.

which is a bit annoying. (the EEO_CASE is either a jump label or a case
statement, depending on computed goto availability).

It seems we could either:
1) live with the damage
2) disable pgindent
3) move the : inside EEO_CASE's definition, and only use {} blocks.

I think 3) is nasty because the result doesn't look like normal C.

We have a bunch of such constructs already however. foreach(),
PG_TRY(). That means 3) has the advantage that our editors and pgindent
can already deal with it.

EEO_CASE() are potentially jump labels, then indentation becomes
correct. Why not accept it? It looks odd, but that gives a better hint
to the reader about what's going on.

Seems likely that every editor would indent them differently :(

I'm leaning towards 3) for the reasons above and after trying out the
different indentations, but I don't have a strong opinion.

Greetings,

Andres Freund

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

#52Andres Freund
andres@anarazel.de
In reply to: Alvaro Herrera (#48)
Re: WIP: Faster Expression Processing v4

On 2017-03-14 08:44:24 -0300, Alvaro Herrera wrote:

Patch 0003 is huge.

I suspect you mean 0004? If so - yes :(. I unfortunately don't think
there's a useful way to split it in smaller chunks - I originally moved
ops over one-by-one, but that required a lot of duplicated structs and
such...

It would be good to have someone at least read it before pushing, but
I don't think anyone other than you has done so.

I'd love for somebody else
to look through it, I tried asking multiple times... Seems like
threatening a commit is the best way to get it :P

- Andres

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

#53Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#52)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-14 08:44:24 -0300, Alvaro Herrera wrote:

It would be good to have someone at least read it before pushing, but
I don't think anyone other than you has done so.

I'd love for somebody else
to look through it, I tried asking multiple times... Seems like
threatening a commit is the best way to get it :P

I'm willing to look, but the last few messages make it sound like you're
not all that close to a finished version.

regards, tom lane

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

#54Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Andres Freund (#50)
Re: WIP: Faster Expression Processing v4

On 03/14/2017 07:31 PM, Andres Freund wrote:

On 2017-03-14 16:58:54 +0200, Heikki Linnakangas wrote:

* How tight are we on space in the ExprEvalStep union? Currently, the
jump-threading preparation replaces the opcodes with the goto labels, but it
would be really nice to keep the original opcodes, for debugging purposes if
nothing else.

There's nothing left to spare :(. For debugging I've just used
ExecEvalStepOp() - it's inconvenient to directly print the struct
anyway, since gdb prints the whole union...

This comment above the union is not accurate:

/*
* Data for an operation. Inline stored data is faster to access, but also
* bloats the size of all instructions. The union should be kept below 48
* bytes (so the entire struct is below 64bytes, a single cacheline on
* common systems).
*/

On my x86-64 system, the whole struct is 64 bytes, but the union is only
40 bytes. The fields before the union occupy 24 bytes. IOW, the union
should be kept below 40 bytes, not 48.

Hmm. Could we make the instructions variable size? It would allow
packing the small instructions even more tight, and we wouldn't need to
obsess over a particular maximum size for more complicated instructions.

A compromise might be to have the fixed size, but have "continuation"
opcodes for the more complicated instructions. An XmlExpr instruction,
for example, could have an extra instruction after the primary one, just
to hold more fields. When evaluating it, we would just increment the
Program Counter by two instead of one, to skip over the extra instruction.

For reference, here are the sizes of all the structs in the union:

40: xmlexpr
40: scalararrayop
40: minmax
40: iocoerce
40: convert_rowtype
32: wholerow
32: rowcompare_step
32: row
32: func
32: fieldstore
32: domaincheck
32: boolexpr
32: arrayexpr
32: arraycoerce
24: casewhen
24: casethen
24: arrayref_checksubscript
16: grouping_func
16: fieldselect
16: constval
16: casetest
16: assign_var
16: arrayref
8: window_func
8: subplan
8: sqlvaluefunction
8: param
8: nulltest_row
8: assign_tmp
8: alternative_subplan
8: aggref
4: var
4: rowcompare_final
4: qualexpr
4: fetch
4: coalesce

- Heikki

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

#55Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#54)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-14 20:28:51 +0200, Heikki Linnakangas wrote:

On 03/14/2017 07:31 PM, Andres Freund wrote:

On 2017-03-14 16:58:54 +0200, Heikki Linnakangas wrote:

* How tight are we on space in the ExprEvalStep union? Currently, the
jump-threading preparation replaces the opcodes with the goto labels, but it
would be really nice to keep the original opcodes, for debugging purposes if
nothing else.

There's nothing left to spare :(. For debugging I've just used
ExecEvalStepOp() - it's inconvenient to directly print the struct
anyway, since gdb prints the whole union...

This comment above the union is not accurate:

/*
* Data for an operation. Inline stored data is faster to access, but also
* bloats the size of all instructions. The union should be kept below 48
* bytes (so the entire struct is below 64bytes, a single cacheline on
* common systems).
*/

On my x86-64 system, the whole struct is 64 bytes, but the union is only 40
bytes. The fields before the union occupy 24 bytes. IOW, the union should be
kept below 40 bytes, not 48.

Point. Will update.

Hmm. Could we make the instructions variable size? It would allow packing
the small instructions even more tight, and we wouldn't need to obsess over
a particular maximum size for more complicated instructions.

That makes jumps a lot more complicated. I'd experimented with it and
given it up as "not worth it". If we were to try to do so, we'd also
not like storing the pointer and enum variants both, since it'd again
would reduce the density.

A compromise might be to have the fixed size, but have "continuation"
opcodes for the more complicated instructions. An XmlExpr instruction, for
example, could have an extra instruction after the primary one, just to hold
more fields. When evaluating it, we would just increment the Program Counter
by two instead of one, to skip over the extra instruction.

I think for cases where 40 bytes becomes the limit, it's usually better
to have out-of-line state. We could do this, but we'd also waste a
bunch of space in the array (namely the 24 byte Op "header").

Greetings,

Andres Freund

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

#56Douglas Doole
dougdoole@gmail.com
In reply to: Andres Freund (#55)
Re: WIP: Faster Expression Processing v4

Andres, sorry I haven't had a chance to look at this great stuff you've
been doing. I've wanted to get to it, but work keeps getting in the way. ;-)

I do have one observation based on my experiments with your first version
of the code. In my tests, I found that expression init becomes a lot more
expensive in this new model. (That's neither a surprise, nor a concern.) In
particular, the function ExprEvalPushStep() is quite hot. In my code I made
the following changes:

* Declare ExprEvalPushStep() "inline".
* Remove the "if (es->steps_alloc == 0)" condition
from ExprEvalPushStep().
* In ExecInitExpr(), add:
state->steps_alloc = 16;
state->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);

I found that this cut the cost of initializing the expression by about 20%.
(Of course, that was on version 1 of your code, so the benefit may well be
different now.)

On Tue, Mar 14, 2017 at 11:51 AM Andres Freund <andres@anarazel.de> wrote:

Hmm. Could we make the instructions variable size? It would allow packing
the small instructions even more tight, and we wouldn't need to obsess

over

a particular maximum size for more complicated instructions.

That makes jumps a lot more complicated. I'd experimented with it and
given it up as "not worth it".

Back when I was at IBM, I spent a lot of time doing stuff like this. If you
want to commit with the fixed size arrays, I'm happy to volunteer to look
at packing it tighter as a follow-on piece of work. (It was already on my
list of things to try anyhow.)

If we were to try to do so, we'd also
not like storing the pointer and enum variants both, since it'd again
would reduce the density.

From my experience, it's worth the small loss in density to carry around
both the pointer and the enum - it makes debugging so much easier.

- Doug
Salesforce

#57Andres Freund
andres@anarazel.de
In reply to: Douglas Doole (#56)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-14 22:03:45 +0000, Douglas Doole wrote:

I do have one observation based on my experiments with your first version
of the code. In my tests, I found that expression init becomes a lot more
expensive in this new model. (That's neither a surprise, nor a
concern.)

I suspect that's to a good degree because back then it'd often end up
trying the "new" stuff, but then fall back to the old machinery due to
unsupported operations. Which isn't a concern anymore, because now
there's full coverage.

Otherwise the cost aren't, according to my measurements, higher than
before, excepting that some stuff that happened at execution time is now
happening during initialization.

In particular, the function ExprEvalPushStep() is quite hot. In my
code I made the following changes:

* Declare ExprEvalPushStep() "inline".
* Remove the "if (es->steps_alloc == 0)" condition
from ExprEvalPushStep().
* In ExecInitExpr(), add:
state->steps_alloc = 16;
state->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);

I found that this cut the cost of initializing the expression by about 20%.
(Of course, that was on version 1 of your code, so the benefit may well be
different now.)

Hm. Right now ExprState's are allocated in several places - but we
could easily move that to a central place. Have a bit of a hard time
seing that that branch during *initialization* time is that expensive,
especially given that previously we allocated a lot of memory separately
too.

On Tue, Mar 14, 2017 at 11:51 AM Andres Freund <andres@anarazel.de> wrote:

Hmm. Could we make the instructions variable size? It would allow packing
the small instructions even more tight, and we wouldn't need to obsess

over

a particular maximum size for more complicated instructions.

That makes jumps a lot more complicated. I'd experimented with it and
given it up as "not worth it".

Back when I was at IBM, I spent a lot of time doing stuff like this. If you
want to commit with the fixed size arrays, I'm happy to volunteer to look
at packing it tighter as a follow-on piece of work. (It was already on my
list of things to try anyhow.)

Yea, I think experimenting with that is a good idea. I just think this
is complicated enough, and I don't want to add a whole lot more
complexity for very debatable benefit.

If we were to try to do so, we'd also
not like storing the pointer and enum variants both, since it'd again
would reduce the density.

From my experience, it's worth the small loss in density to carry around
both the pointer and the enum - it makes debugging so much easier.

I found it annoying enough to print the instruction itself, due to the
union - so printing the opcode using a function isn't too bad...

Greetings,

Andres Freund

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

#58Douglas Doole
dougdoole@gmail.com
In reply to: Andres Freund (#57)
Re: WIP: Faster Expression Processing v4

On Tue, Mar 14, 2017 at 3:16 PM Andres Freund <andres@anarazel.de> wrote:

Hm. Right now ExprState's are allocated in several places - but we
could easily move that to a central place. Have a bit of a hard time
seing that that branch during *initialization* time is that expensive,
especially given that previously we allocated a lot of memory separately
too.

I didn't make any comparisons of the cost of the new init against the old
init with this change in particular - I just saw that it made the new init
faster. I also didn't play around to determine if the savings was found in
removing the branch misprediction or inlining or both.

I certainly wouldn't hold up your commit for this, but it's something that
might be worth a second look once the dust has settled.

#59Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#49)
4 attachment(s)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-14 16:58:54 +0200, Heikki Linnakangas wrote:

* execInterpExpr.c is quite a mouthful. How about exprInterpreter.c?

I applied most of your changes in the attached version.

Notes:
* I quite like how the EEO_JUMP/EEO_NEXT stuff looks now / after your
change, that's clearly better.
* I reverted the removal of EEO_SWITCH() - pgindent goes a bit bonkers
if it can see the switch(), but not the case: statements.
* I re-added ExecEvalStepOp() - for one it's hugely useful during
debugging, for another I'm actually using it in a follow patch (So JIT
compilation can run after the interpreter has been initialized).
Added code guaranteeing that we can't run
ExecInstantiateInterpretedExpr() twice.
* Renamed EEO_* opcode enum members to EEOP_* as suggested
* I've not yet renamed execInterpExpr.c - I don't like execInterpreter.c
because it doesn't reference expressions and execExprInterpreter.c
seems a bit long - but I can live with both (preferring the
latter). Not that surprisingly I can also live with execInterpExpr.c ;)
* Addressed your size_t sizing concern by using, as is proper anyway,
intptr_t.
* I tried to clarify the comment in execExpr.c's header that you marked
as hard to understand.

Greetings,

Andres Freund

Attachments:

0001-Add-expression-dependencies-on-composite-type-whole-.patch.gzapplication/x-patch-gzipDownload
0002-Make-get_last_attnums-more-generic.patch.gzapplication/x-patch-gzipDownload
0003-Add-configure-test-for-computed-goto-support.patch.gzapplication/x-patch-gzipDownload
0004-Faster-expression-evaluation-and-targetlist-projecti.patch.gzapplication/x-patch-gzipDownload
��r�X0004-Faster-expression-evaluation-and-targetlist-projecti.patch�<ks�F���_1��KH�/�M���"�������d��)C ��d&����=�	����U.*�A�===�=��u��=�F�t22'��p��O,cfp�Oy�7���4����>��!3&��;������@����N�{��w�����K�7#��u������������!{gF�����pt:�vo����b�'n�S������7l���^�B����!P.��M/6%~���4�%��+$�	��N�~�r�x��6p'W��q$�G�d�y����e
��S���\{r�����vF!����qEhJ{4�'�Z`x�t`��	w���6}��G� Z�����~���R�h*k����"�q��O��m�oKw���C�S�9~��pyDCs&B�,M���6#�� 6g\�z{k��G,(�wb�����ba.9�2�2Z�t[���3aF9R<��p���SY�-�����"�i��mz�@�+`�_2�����3��'�}��b�#��=&�)nf��@0���09��
���l��A�a�
�0Ak�<sc;��'��`�M���<�9�q]�q�(�^� ��J�c�:���(���@��L ������1j���t9fpQA�V&0�]�����2������k-5�b�HT���A��X��
��z��ej������ 5Q�C��2�A@.�,���8�����w���%:pV����1Z�>���)b�����	�mZ��9����^��+��&T��l'�yLO�L��\}@���L�N
�����'E��M��Z����\������I���d������ ��3}�)L�@EG&��P��
)�$����l���_�=�KLlV������@M]m���KSO-Ei&yS���6�P$�0���s)�q��F[��~z�����js����������o>�����+��������8�v�@�e������F06��qo���c���!{�-���_��:����n�����%�&��|�<�tl�������	 ���Z��@i�VH��0���T"���`�7����3���)Q�$K{�
+�d�X|q�*��5l-������/|��U#�A�)����@Y<�m>�=��vD�J	(%H�dN���iy�l���@��K�RwG�� %��}{��ti\��.���4�?�������%�3�#4����BB����l00����������]������p4D�����w^v���[�z�zav�7E����-KLq���+W���n�B1J�8��t.�(L�����z���5#�f�{�H:�K��{_��X0�c����ap���f�� ���������)V;jt�OA�*���l�{�D|�����:A����
H$�<d��{@���G���|x@��8�qA����nT�v�e���-�'����c��tgb���1"��?�B����� 	��hS��C#�
�%���o@r��9
��� ��pKz4�{������<fN��m��9� !$3�/<������gB�%�#�X.�Bx����1K�5���2�(�x��mT� ��uq��#r�d��]u�&��u��\e��$�XG�f,��K�n��<���}#�������w�������4�8*�>�!��xJ�BZJ:��J��H�(;�l���p)~������^^B�<l��:3
���q���`��|��G��a{2`.�l_�
q�/D��cE$��x�0f�,�c��[l<�!-<���q�l��pj0Ryu�����w	?��F�7����N+1�@��u#�n/]���1��uT�R6�����&��l22�����7LP��Vy��_~����1l���W��s�9�s�h�E���jh��ZuVc���}�O�*4��s�E����8����?xR*	Z�\k/�]�"eZ��78Z�B����z���8�c!A�/X�-���&���u%�Q����]V����|Q?���GDb�]�w��3�l��X7�����%~Xcu�D��<d�%>�6�<S}
�x&C	�~�������'X4Lx(���b�uF��xd����t�������q��v�����p�kFoJ���p�8��Gr� ��v�e�-%wUgE�?���<��S������`5$[�f
��=,���B%{�?��K����"���&��������
b�r�����2�/���[���`�}�4UK��y�Bf�
��K���b��k���}�j�4k���@�5����u*��D�>
!���������������3��������3�xof��I��mS���m���hJ���p������d��[�����L�}���R�r�.��iA���:%����H�)��l�m�L�c�%y��T����\05������"�m*)M&�0t��� s_�^�2^7Ry�����}��5'm�h�ibh_�"4�NEy�a� �^�@��dqX��SM��G��k��o�>V�*���8
K;��Qf{0�K��a)}%�s��P�
vl-h���{�D�|���&v�v����O�~���x�%���9f;Ap5�H�7Z�~�G���2Zi��*�b��5��N�5'_PHAj5���py�����wg�������q$�\Q��.{�1�Z�=bS����jL>O��'��^x�j$�Y�`��9N�_4RB�������-��
����GBpW�Ls���.Qz13EW�,=��6�����l�j���SI�<��y�9zXO���ZLsRA��������%6����b:���w�6=�h-s�q�l����������
E]�d�6�mj���aD*���K&W,����S�t�
����fR���t'ep�D�_I���o�X��-v�:-�0=��D�0x�hc�K��I��������������h���+�����|���.����O�e�s�z:��i�k�*�l{<�n��q��R�����i�@���@���O�:�����V�������x�l<�}�4���-�Ngxt�G��/qxZ����l��g��[
[����O��>���ve�A�,w��|���	�N\O}6VS�������@�U���-���������M?V"y�ul��=W�<��H��*}Wb`��}�J|z���%������n��}�;��=\����S!���>N���n�`q)�������E&�3�b:.z���'��db���"S�Ru�)��M��5�b]u�&�z�7����y�����f������G�%t�/�Fl�SX�����(U8O�(;��-�e5�2z'�R�������+��bO

�i��;�~�;W���V���x<u:��l2s���
�#T�_9 r=��R�j^�	jN���+��n1���S���FQ��g������u�?�g�p�/p�u�C`wx@9���������=��p���u:���c�����]B%����3�a�{�.�=�:��}MDd��i������BF�� /U���pUA8����:v��*��`�nS����8U������i��}�D�r0�|�$�� �
!;��Y�
�z8��p�X����8R�k���'��T�VB�&���Co����;������1��,m�b'%��`)�����%J��8-����~�BR`~'!}&�n�)R���)b_Q�7	���q��=aW$1l�������+ �����c\�>�]��������������93��;��������|Bm�������^����1k��kl��+uW�\��<����w��O	a��3y4'g0Wo%��3m�/�f�������.��2<�����5o��3rr9�����I��+�2��>���9�
a�z]��5A�5��h4rz3����#�5#�gi3 �Lg=���+��P��bO����0�b��]%�����s�^5<�����n���O��,��M�-0�Ftj�&Hx
Pr4�/qm���L���CpB�hK]������9���{��
'!AG� ��7����2aB2�6�!:b���QT�C$9�YOo�����p:6&��`j��yR{V>�����P�Au@U�}�>���sYvA��F$��ik��7���x�O���G#E�.p�_��:-N�MjD�G�tt"<���}����c�/���+�PH�5��'4�s:����*�m7{����Rm����9}k6�}x86��1�c��=V��n<#�R���
�o�C����mD������V�[�V��j�����
��e�6E^<��;G�+j0�+`(�V/�qt}�\���Ai�I=��y��Bp	n�]��i��PU�I��E�C��-���	�]�]��|���
������Fv#�9�s.���{�#'���k>����������9������Q�q����Uhg$9�6�g|��-:��m����o�=Hj�=��h��h���'��z�����t��n
��,����r���WX��y��H[��������+�i����C
����1P�h����$��?C����%��v~}uw;O�2�����>P�"rQ��sq���M��d��F��O_u��n����������8�@A�C_��7X2U=�]l��5]��yw�����>��q4�s���Nm��A�p�����5�M+#�>�r���	0[��A��9��
��m��A������Bm�
�T�j�8���`���f��I���TyT"#,k����
�k�B�E���*-������	K[���������i����^Fq���e$�U���bg6�&�|@u���m'���Qg���I�k�w4VZ������4�)����w��r��C�����������*.��h���',6�����:��?@�k5��bK�A���;xt��������Z&�zMQ+�E\���s,VZ���d�H6.no�o[��Z���c����{��.����_^_5����b�x�
]���_��[����(�~�~?"
����B��j!���������VP��>c��X�Q���I(�����\�GK<vj�s��n������`p;��{�Yn���c#z4�����:|Q�v-��\?9�A���]�JGnwK���=-�@�y��]�Q����X����S�Mu�E��-4��1�}���a������9���sr>j����a�IBw���M@�<�`Y��rPABQ�d5����HJ��d�fy&S��I������}k��^�����Q����t@�������:P���<����v������A`�J���N�\�	S��$U�K��%������v�3,��b4���~���*�@��=������z~����4.t��������%j�-q�[+
)��K�D�������PW�BW�������ok>�lk>�jkVtHKZ�������F; �@����>:0�D�aZ�Ef��;Ueq%�o81sX'�_C��z#R�@Z�)D����s��,�����*�( ��C����W�NJ�d�T�n�V-����Uo:Sb��>U,�sz��;3��DC��b7�w��������n��!x������XOg:�E���L|�/�����W��R�5�)���(?��j��)/�#�_	��4�@�2C�AE���;
�������,�}n���^K:������<J����^���:fp��^�n�d*Z�������s��d4��{�G������v�Sc����\�)7��h���]:�~{��[tJ��z[���ObR�pN/)�N�K���79��F�I�'�u�^`��������$��������L��s��g�N�+�I�3_6�"A	c���eM���Wg@��,;3�3�M,�>����.��{?��)|��O��������4>�[�?M�+��_������%��IH��Q�9'c��g�OB~R���'	Ov��f�[��$"^�7���<�"���|��Ag��K\{a~g��M9�s�������9���WI�f3@�qq��b�_��6����44i���6�~�����u��d�zAi0��8h���;L�9t>H�����4�/;d�(x���)kQ~����;��,Kx���`�w���3$����Z�\wR �y���E����[��WY����������!D���2Q��2\RwWh����S��zA7(��9�,�����~��f���+@�����������4)/�����f����/��dF�m�M��5�o�����mJO�bI%1��q42��<�F�|�j4/ja:����y�^D��-�
�R�<�{�?�I�I�y)H1=)������PW$�tg�t��X��f�����M�\P�����k�mEo�S����-���,E���B)oI�^���U���`0�QA]d�9t�/��a^2��@�3���%�p:b�Z���:�I�Rw�����vH2���IZ�(�UaYa�*u������^���:�B�o9��n�_D����������L�R��CT=�<�������
t��L�&DG�N��J�Y�)����O�&�h1\�����z&��b�f\���n��kH;#�,9���M�+S�e�=o�V��J�z5_1��% �w��L�y
D��`w�����(����Go`C�L%%4���8��~�+�����?�����(0�r

yK�����8�bfhtI����0q!ugnP>�3'�'���E�Pg���������%)AbI��M)��@�Br��k��l.D*�PJ��#�ZM�Q�t^���&GvO�I�L�H�!Cp~�����p����R�qJ��f���_��F��>G@�(3g7����e���C�5�~'�U�Ii\�[)c:�p�j���5;~�CL�xZ���A,��s_����Cx�v�S�	�����4��f�E"���l����{'nG�����tQ��-������2�mG������h�`e�2��d����y9��f���w_����������G��$fD�@:���\,��^���(�^`J~��W�������3,s��:-��{�9>�>��P�U�||�,��;��C��m���{�}������?���?L�1�����bz�Wz���d���[�4�]�/H�m a[�{�Mn���� �����t����IZn��QK���Wc�
>���(��M���T�K��?��|P���"��ra���_r0>G�{��-C/QM|1.0�(����$�}z�w3�r2��\���(���9]�P�N������Fv��G:��B�*:�[��~���I2e��_a���Z]�N��$
��f���(V�c6����DV�� �D�M�)����(��H6�]�8M��G�WDf
*K���|������`����h���__f�.����n�?c�< �)�y9��&l�<�}�N�Q���=�0�8��
�D=wUTP���X�!�y�
=+��qN
���6x�u�
44������9��l�4�V�z�U6�*��.��ai����wRN�����c����F#n(�N������*9��fPg�\�����_�s`OF�	��mL�����3.*��U�#Q��� 	`���`9l�3�MY\r��0�E�@���X�8���!<���sa���J�bCL'_D�2��1A���Q-�G��Eb�@,Y
6�z�<��ZdB�F`���F�;kM��2D\� %]�cE�k��^G�x�"N�d��>�~��(�Sb�"��D��2;��A9�?N�T��l���	J>��
J&o�-�i���`�+X�b�b����.w�g0Nz1h*�\i��4p�=
��R?�T���tw:lh�{������B�'���j��4�'
���9���-��*�C��u�yg@�ff��2��bF1p[,�c��O-u�s���Y��Z�9�L��D�!��^1X!�D�H����0u�Q�T���ltm������ECqqQ :J�O�������D��(td����=K��	OL��)��29�M�?��Rv&�{�EG�FL�y�Tos
�-H��h?����1w��[Q��g�"������&��1��+m����������@x�bH��DoW�/aW���z��'~3��o�O�F�N~u:�����q������L�3ou>�j�Rx/U����+�7���Q9|
vF�H�p8c�)%:�yF�YH�#���
�l*%�+�g"4�LQ������U+�6��x�^3
$����G?�l�������|�X��� ��	8b`�Y�s�AY�s����rP���(����Yp0�	I��d��cU�5y�S��I����I�:�b��Q�)o$�
��.��z�y�J�g�"��0�D�K�>����G��v��!�����x�O�!#�m�?��1:~��;4A����� ���
�:c�����d���FPP}7� �
"@��N�����v���g�l�T�?���h4��%�k�/k]�n4�Kj8���U�5��m:�X���;I��N�.����c�����LT���WhgB��b���
����(��xx�0ZTR������EE�r��=u.�����K�T�q<a�u(�8���%�~���vI843���F�����������/9�$
��l)������!����ec�����PL�/t�'\!1����v��&$�F�=���������qLf�>�p�/�D?������1L��Q���:����.��&�S*��b�����6�J�q��<��+o M6�7����h?
!%@�kRY�]�?�@n�.�P�<�7�����.���:��B����B2^�~�%�a	����3���xqb��0�b~I-���u&�,f����vM']�x���v�hfm6c�2x���6�y��U��0hQ=HO],RRy~��K�G�u�7$������#\vA5'�'IH�S&�D/X3�g��6���c��w~yy��HKi�{�\zrZB���=�Q�X��%y~iJW�k��lz`�_��L�r�F�q1l����:<R��yf��r���+�H�>�x(8�=��p���)�%���_��� Og0@��D3��E��l�H���wD�g��=9��a�(
�+��)�D�k���k z��J`�D��$1�-3O���qn���&j�t������/��>7�xKs�M�0�6����v���F�<�Z�<5�:�7�1��EJ�&�T��������%��]I/pec4�L��UK�o�]Z�$Oh����h,�������x_vw`�pah�S����W�5�������EmE�Z��r�a�w�iq�e�����3:�-^M�4xH�4�N�����"��
'f)<`��r���5��-'
�"�\����)U�6��y����l�R�&�y)��fl�����C��oz�e �|e^W�N#�9��}��al_��p������u0L�a��%@K(s��{��dR���x19O��x>�.&�T�:���@��S�8wY[�������������x
of��bn�
NIm��K����d�k��F�Lz*}�l?{�N9s�*����Ip���W������/�';��XO��(2��*��
Ek���')������Q2���=_�g@a���������E�����^����jp:N���50HO�I�;-R���HdC�e�>�3�42M�A:���D��{��{��N;g�4F�p�y�'�w����e#�Q��l9����F0�"@����OO�?�M��6Mb��;#�@�e#St�&#�^�#S*A��M�Vt�:�d#3E�������%���urB42������z�?=�?�}����9��b��}�5�N�d|R��I)@�u��h=�����Q,I��_'rr����c[>U�V��*��-Z;���o�;��U�Sa�B�7����i;����8���nt�!#�����`�t�a� g�n���1��${��J�w�B��Qr�M�JBI��<P��h4��fhvS�!�9K�������c�@BzbH�z���|2�E���3�jv!�uJ������h�7b���h�
% D�3����rd��K���@ '�$��0�%�����d's��IL���l��VI�6BwU@��3�(����I[v�6Ni�Y�U�X�f�9��
$9�O�D_x^%�����p��V��dI �'�D�6z��{E��e�1���o�0���!��^���y_�H�e�b:�F2������ "�3�P�{�b���k��"�RV���Y���C<-�s\�����(�Si8R�x�MEA�h���c�%!R2dbH-����w?�5`{3����b������
���MT�)������b���c_7��;'�)z�<O7�w`B~78J8Jl���!�?x�*6��C�1��2��Yw�h}���v�8����� �cE(�J���:M�Jn��qt4�c��
����5��~�?�����<��8�{�9P�ld����V9<�J�pyKv:��.CX������]��Bw���<�5+�l�B���C��!�� ��q�7���cH3N9GJ����X`��|��I�/X���R�z�J��$CX�����F`�=�cM��B���>�e�
���5/�2���t�K0�urf��a��"�>�]9���������O��������/�1$]����E�RwnI{����=-r�m����s���iV���(��[�{WN-������K�7���������~����Vd0_�%B��(�m����,���:�2�
v�UO�X�.��qo8�b�G���Pu�������bW���0��122����9m
��R`bc���.S��8��s:/=�()!&"y�_����
�%�Lm�$��Uxq�rp��qB�jQL!.q@��j���"��)�Tf?Lf@B9"��.rE��Q�4��O}���F�su�}w�jv�\K���Vy�edT(���>~�c�&�.�e���,��:��O�!�l��#��Y��8���C�#����57��s����8���$����b6��s@g����=���DU(�4�K7L`-c���2�����j���uNz�E����Ya�yX���>z�����?Xf���
"F�^7<������T�UC��������+�$,��}eJP��@�e���V�j}��Zoc�YS�2�a���i�'��
=����)=���p�Xe����U��LO�� Urk�H�{����s�'�D���L*!�I��m��^�����w~���}�O�TZwi4�/\�RjA&�Y��~!�B�{n{������x�U�Gk�7�h�j�'8g��5�^L�+�.���1m��������uvah:oF
3�.�Q�9����Iq��h�������]�����J�H�Is����������I�y���c�������1��?�X�C"3�aCoe`j<��A���a�s��d:�<���Y���L#���_�=���Z�,����NL�J�s��x1�&|e�Lt+&�g�7p�y���1|�C*�E�s���)��)�����ul����l�?�}�Yx���c�������� &����I������*J!���!�~���������>�<�2�r���c|�����������/�g��n��X@��{k�`B�n�l���B�q�s��#\����}�UT�����o?���_�a����Q�-LUBe����o��-e���G{h?���0����������6j��o�:i��t
�f�Ox�
fi��|���l`@|��l���c�x��e��~��|C�*~2YJ\����>���Xf�l����}�
y�ti�*D\�Pj���(���q~AyN�)�(��m[<��I�����I��J�"����R)�����`Z��q�h������sSa�Tj_������r��J��D_^_� #�8�!h�����EYJ��
P+�_w�{���R��i��%���]�2�i����v�:���U��]v���h��������R.���m1����g5qM��k�2�lJ�K%���P���z���S���O���&0���GI���X��
�[��/7��e��Y��LoVY7?��*T`�l��l�����.���=��c�ye��]����]=���,�������`�Q�i�s�}��k��a���{|p�����_G@�1��s�\%��rl���(z\G]�Eb���o�T�Q�}]0�
���y�D.��H�����s�\��w��p����`2�2����GY�ma�1lRwT�)�X1�}���O7�#����{x���>�-�9p�9]h*\y�8��B���\�|r�S�^]);�����Ew
z?�[~OmII��6
`��QC�3|hX�������>~�����LW��dN�X
/O���������4oE�<;�Yx�9��q��E�������������^V���v���g�]�s����������%��X�������L��p�����H�!yc.Im6eI�i�HA/
�l�*�m�������
� {�9�*��Y6�����
\������@�_���y��/�����L�f�4q����h@�I�-aB����yZd�������8'g�
�/�@����k���u�pk?�y�%��Q����H�(�^��y��jh�����?�F�"�����
D���%��.2k����4x�s��o��V�r�wu(ch*�Rr1�3���7�>���{����WeLi����d{(-%���W�d��Z����/�r�T8�����|
\g���B�[����8[�f;���Y�����5@�Js��y�o�u�E��_�h����Y�e�y��+����~��]N�����~������]���� ���`��Rx��6�:q�d8��YT|�M�|VJvF54V�T��FiU��`m��Y-
]�&�4	��T[�_u7MD��,������g��T[[+2Tm��y��J������4[U�e@��]/Kq�]_����x#H�S�����L�Su�tClyf��IO�d}-gQ�4U/��Z�T��H/7d�����z8��rb�� �U����[���8`ng-	9}����}u�pXG�a`�U�����������v��1W7;����V/��p 0�:�W���Q��S����#�_�?����������S)�����9&2;������>�vF�W����sGQV#W
��+]��zo�����9:<�u�nc#�0���g�\�-��B��������ayRM��`�����������q���7~Z/��9�6��H�p{
�u��a
����G����vp�3�Z>�����Q�����8�6��B�~v�o�D��` ��������&E<�e241��}����\��Z�b�V���2��~��:�5~�$������[��Q1�F����H�&IB�+T�H��N:��������i9�;^��`$�������Mu�]D\�|U�����Z������)����4^M��_������}��V(f��L����!`.�g�~�I-b�@(=�$�vH3�R������@kQEx��9:�����,)�7I0�IZL�*0=��Cg����u+�\���9j0�c�`���R���k�&
8��|pJ"��7���t�m�lm-�������������Z'���$|��8%T�P?���q��q}n������SC���s3����"�����^=�e��Ylj�HV�������q�z��:�����zCJ�m�6"`�	��B]Q�Y�E�	f��H�kH-�4Qy���CA����H��t��_�)rRc����-DW*�����PG�N�k�U���.�u�W��dK���6�x�
��U���w�\$����$��<c�$�WCe��T4����*��3�X��zNO�N�T��>�k�{^c�VD����*�.��J�����^)�<�v��+�q<���cH{���f�ji��-LBW���q2��w�J�C'�!���k���^���-C�r��Y��j�_Z��D�mqY)��KEO9}���m:��c�R����V����������x��:'��,{����" ��B�e������m�}V���;��,#����n����c@�H�x@z�(����.?���+MW��e�����d��������U�e}�^�
�������������!�fz���`�b4[���&x�N�E������"Uw�8e1�*
Zy���z����n�Y���vrc�J�k�U�����v|_�"�X�K�����]�w�p�����WR�v^I�Q������e��0���k�d�0����`�b���GQc��]���
{�W���?�u9Z���G�:�6�b��1%�'����5�
~|T��0bt�G�f����3�v�vl:bp��@�����b�T����M�V�-�_�����E��KrG������=�!`��R�Mw{�'���y�_�3'������-���l
�<?����P}M���L"�+�?�0H����P��"��0��rf��w�s�$��������M?������_+�H��|yk`36i>�g�6u1�O}�!(��x/�	P%�ee7�	u���Y�4���r���QL���')�^�F��������X������EQ.���2
F�b-���4��+0����N����$L���^��9Q-3GL�}Vj�M����U$����I�������/�s^
�-g�����$���-����P�h'Gh��XJ�a�I�f"����e]M�k�������[����z�g�_f�!f�D��J�G�97_'\p�:��O*���v2��7���AM�#ja��(��k&���h^q{�]�M7�x�,I7:�s�WQ��p���D/�pQT��1��@�i�I7=��bj
L�8�� ���u��(����[#���-rJ�n����pAV��E�O%�Y�aI3�(���
])�E�H!���Y�s�t�l��w��-U3M�<�;���
:��	��XB���5�p_���+��k�5�X@������o�nvz��C�p��*Z����<O�VU���+�}<!�����@?�h��F���Mkc���5�rq�G���lc����0h�Zk;�)���$�P�E����|M��AI��g��
v����P7�TIq':���
F�oc��m�~��!��-�� ���
Vt�9	�L5x�Xg��)-����;}�������LU���n�We��W�����*�F�5�����D����*E������#-�A��0wN�I,�S�qU7���Z����]�lHe�5<k>�Y"7��u����h�bf��Lq���I�&S�ff�V��/
j,c,��r]����|%.�95I�x��S��]i�
���_}f�=�9=�y��T�&����[�M�����!�n�}�+$�
5w��*�=pZ��5aUA������h���0��k�l(�*�2@�)B'q:1����]��c2�
K�rd�^��@��X���S���JOE��F�jz��t�����"�������`��qv��7�6I�M:`�=>����x�^L�A
~7��7[��`[�-��^S����Y@&��>�eG�)h��?�8
�[��Wo�D���h9r�*��m�o�qH?1�<��ib�5:u��G��,fGQ���0�U���3
	��+�k�xm�&��a��9wk���ZY�W�,�PEo�4DW�?��5�k�3~������R@�Z�c��11~y@O�s]SW�INglv�}��{N���m:�z�K(s�k��J����u\�Zv����z�y�9��`�����I��wr��V9����t�I�����;�C�4�I.\%���u^>�f��y6Ad�eA<]���JOxT:���U��W�^��<4�����A6�A��0���U��o{���tM�x<��g�y$G��$��	P�qJ��]��4�d�cX[���A��i���l3D�tJ��!�.f�����1�����6C���:�h��l0#�[#�x��f�?��9�X�@eWC���G
�*e	]D�"��Po �2E^�_���B��j�H�V&�X���w���q�&A,#����O�J��J� �T��&��tZ���z6�P���i�8C�zNJaqo����@t��*�|��ZW7A��zs%M4�xP�j��g��z���#@�/��/��~jz��z�m4���!�y���G����Kj�Y��'���#FQ���:�O��_�H6a��^L��>��[�Q����_ C�F��^���&��1�F�]����^X7����=����._����D�n�/���N{�z��By����/��#!s��\|�������9K-[�V����(�I�v��k�pX������Fk��*�M3Op������+��<��]_�x�G�x���K���A�����;G����\�ME�5����21���	�%����?B	fL�`>[�Qwd�w��5����!{h�����$�Fy�2�W��76�
x��$�O��'�QE���C>�l�b�%P��|�����r{D�`K�T�%�MMkz�&�'N�r��$���{�����:�L����� ��>�B��:�&�h����������*��*<���'��5V�tV��r��.�u�Y:�U�����Me�~��/\(�]��6O.���p�nq~J��>�b���.q���q���ffm}���f7��F�~�7�o�����R�ot9�7fI,�O��e���oA�p�����������_��|����H"gy:�2����1��1������n�p�q�F���m�fo�<{���z�u�;Bv<A
��E�OR�t��VD����;���������roh����U?:_�����\:vI�O�dp�N1�Ec��I��CY�����T�e� �4x����"��WMJ����[J�7����24K�53�p8�5G�������z/^��t�~n�����Z���3����?���8d�,o���Y�A(Q������}�v��?<WQP�+�!�8F�R�5c%V<��Wn0G��$s����]����:������g^�
��|�27Oe q�����y���!�����T8q��|��
,�Y<N�@T�����{��o*���;�
A:�I�=�����_��*�����Z"	"����?f[gY����=H���s�#��@`/K�X�p
����u[N.�j�xs�A�XDGMu]��^�?����(�1����"ss���O���!3|�8�Ve\�WU���F.N�O����OL���P]f]��h�o�����p��-���P�d���S�?�]���!w������H�/�w�fG;�t:������Y��i{(Y@��='������/5�n��<H4��|uZt���`��<q�f6�WM����������,�tpA�����p�������:��/��H�Es	����g�7t����dF�is�0�W��`�:�T����;E �(B�rC����`	
JJ�0D�_�7�n&�qO��
�:O�sH�/�Y:�i��W���I����
��������Sm�lr�}� ��O��Ky���  ���? ��
�.iF�@\�f�
�L����QX�4���4��S�V�E�>`h(��}�*7�M�Z�<\�u������z.K�������^��{����(�K�����2�)�R}����j�g��7E����	�Juwy<�����W�����_��C�aI�a}�)���������z��~5�(����ra���(�)�n�Y�uu��?��R2���r��`��2�E��������Z��j���W��J�,���W�P�����+��\9;��y1
��jo7���F�.7�ZN\���b�a�)
�{�i��W�V����G��[#�I��� 2�q���
�M�������k�P|q�1����;�q0��Rw
R�`
}��k��|c
�VXLt<��L���~ B��`�yve��wv���������� ��[�C��G+�;�(����|���������L�����#d�`�����X�m\k�S3�)�b�cC#�B��$�V���<�9���=�����X<�e�����F��S'+@rR�����g����0��h�0|5��� ��A
�vwA�?���3�^�"TN��>�y���m���wm�,-+�_�8}��}��yv���x����-rt��:���v�~�N���?�?:��Wj�z��vk�Y�����>��>��c�b��7���*�}�����;a�g��p�L�>��pb����������hg�T����j3�p:��s�p�@VzN��9�1�"���O��i���~htu�h�C�dHW�Ww�-���~J�U~��Q�MU:�,�6�Y�W'��^��v�T�>�����Nf1�T�*���<;�/�W��y��S��n_�����@u0L-�KqY����u|�Q�{Fe�*���e<+l�!�9�T�X�6�Ey��t�~���������
F7�Qs��6�y-�Z��XC���qu�:�����S���Tn/@M�KKo
i/P����/i1k���@P��>p<�������^,r��TF��'�L��W3�Gp8�� r�s�!�����1���L���J,8��u
P*R�j2'�/�$ub��#*�u��i&V
��WO7+�����%�������um���'y;G���>'d��a�2�Xb���!��"�����
j�E�@D�	Y��|)L ��8L`���a�g�8����E>����|��%Il/�`��\~������.���|�l�L(�$?[�Y��&\�u�����Fk��2��p'���7(�hq)
������f��%�p�A��/k�ZcvfMV���~����;�F�E5kjq��M4����\���vB)f���IL*��$�IV��$������4#���������*����W''����s��r�Ix�q�����*���Y|���7�04�&v�[���e�p,��S�+h��>�MBX�,f�s��g)S[�KE$��k����f0i�7��0���L�pLT�x�qRq�*`��������
'���f�������eE;X�w������mu�����������O��	C�w$����h�fYk`������<�U&����y����M�;I&��u��i��7����;��{�NJ��77���M�#�{���	
�d��Qz ��Y���o�)����3����#�*C2jT���������X�{"�}�Ym���������x��Dn�h���L�Y 
^�O���8E?&�WE�w��f+�?8�w~��:����l/�=��}�{�M$^��'y���-w����CrFkQT�G�Ug������������L���e�P��G�\i�J	� ����L�gl�1�]�4FF�����$��Fq:�������\��	������	����p6�#��g1�Xd�Y�Y�#�|��������iT����q�#o����a2f"������<��`Dz��K����8���z���_��W/��:�=q�����<7������p^������st��KH���X��������pBI�g�[�V�l�h[y�9B�����e��%mq`���G8<K����(�oW���u.�)S�%�{U(���%P�!�8�	Q�<��E��Iy��?{u�����^����u�����B�m�����"-�<`M3Ir(�� ���-�k-�L�����O��&{��1�S��%Nn�n���R=��/3���2)V�d���9�	�������f��X<���8�#4nA�^�M �C��d�� �����Fn���7U{h�MM�Y���vR����bC�R�&�I�[�O ��_4�@��}x��h����k���]��}���~5�D�}�l<�/,�,? j��yt�n�W�C��'�:�@}Q�����t����V|����dv��t���F�t�y��5%�q���F�w�����l����,_�a�Xr,�M����$���b@E�5VTy���j0���I�V���"#%>���W����?�c-��=!D�B���+Oi���8Y�M:�z��Qa�{|��]���t�{��i�������rE���(�\)��w���z^�Wg������h�WO��`�x�Ya�M��T��,�'���q^'��+,�������0�ZsH%������:������������0�x]+��1���s�%1�������l�|���lc-+_ ����mX+Ns�;�$l�b����~<��z��,�@�!�oi�SSr�*�Jd��&IfT�B3��a���b"K��<Gc�������]�V6�V�#;�����n�?����p��F����5���rQ|U8�
��C�����P�zp����Js����	f�P>-�"�E/)b��p$!�
SI��'���u���'�����]-3��2���\s
Dt,7=YX�a<�X;7"��C�$�0�P�m�6gH$E�s6,P�������K��kvJ��cd}$�[�Q��H���
*q��YbAh����sq����BjRE�|�p�� �z:��a{m4*m�e�zl?��Uu�*s������^��yBP���}��}��r�2��f.����?�������<����AH�\0������������P3[H��8R9_�J�IK{h?�����<�������T���{f����@{2��lJ�� (������-Ej(����{�~@��Z����~�_(�cG�	����V����2M��,�k����P8.�rE��h;���Q�'�~�H@I=�	�3�:W-�@{��������Ps_�{5�Q��a�����U�*����w1% dLvy����)��h}1R��dz�Z�1���^C��M�������qp��:��������_�/�W��<��x����}�=�w~9�t�`���0��I&�!��G��a��'�����	l��
�6�:���h�"T;U�z������GA`��k��geS@�G��g�N�k����=��P�������o?K��=?�qz���3$�$����kn�1������i���uMn�RK��[����?����j�&�w�L�_7���Y�{���@>���������N[��E�����;�lb��ew��Q���YX(E12��d6���X����{_�-�J�Hq�y��'������x�%_?,kW	�um�Rr���$u��gS�aR���$�+��.j��>SwD������N���
wy�2mR�p��R��<{b����7��4�w��I��Y	��+���u��%f�*OI?���lka���NN�/k����tS�G���9���<-�u����^���O������U"���fg���n�LP�|&�|���.+g���/=��kc�9���u�����{��l�q���(��X%l�vX�"�ky�Y/(	I)v����ox=G��t�i��<H��)��������9�I6D=�����e^ja��8�d��+O��1�Xi�-��"�@���3�R��x���>��]M��i�d"�c���l�.�QbW�''H%�'��2����
!�5��#���y2�J��
���IT5���<�+r9��X����Hd�O��a��(�k��B�'<����kwh��S������>��O�4f��8A 3�R}}_�VP*<���`�T�(���xH��b<M
 ��
 ��a��c0a[�!N%x\���C�
�)W�bP>m�)���,h\-�**�;g��������67+n|���u����u�G���k�|�x�i����������u�T��]�����~	�K^����W!���i���
�u0�ld_52~�y0��f���	w��05"�0�9*�Hx	W�.N~�u?	'[x�����k
V��)#��<�v����]��{3���F!B�n��'�+�#L0����pw����7�6I�B���.t��g���O���H<S%�D]�������3������H4}grd���_v���"����r
&QK#�%q)��[�������=��&�6��pV�3��@���������r3rXB���y�����Rf�����n��{kggv[����Z����i3�]�9V^�;S�����!j�/��yP�����
�8�P��]�O_va��iu�U�_=���0I>�zww����~��=x���w����w���������Q��G��}���"x��,�
jJ���A����mG f�[w�-�UN�>�1�C�����0\��KZi��'����}FF�&6��N{�II=�6�GR��*�/Q��X;j�2�b�6{�r�wVo�6���P�{�v4��F>�������ex����o����/d���o��G[
o��+4�
qB�H�h�Q{���%���x�O����g`�����.p�[����2g�������IgJ����������3K)jt��@�a'����i�3gz�qR��i��y����y"�.����������9�P�������a��a�?|X��$�I>��XuM�vw��U4�X�	2�r3��yh����������������|0L�Rz��W�3���������_�}���m�������!��1r��0KMy�HAp�3Z���j~�	���6pH����E�=���J�iy1���X�A��?��"�rf��e�yAv���
Ch���lzQp�k��)���)��&��2C���A��+���.�xR��
e@-�����|�C����d]e��V�	("f���T����E�yN��m�/L�mB��=�<+x�|9�_E<�Ybf���[����^>��^���/i����Q\��H�X�9-f(s�2F�q!�H�A%�������������l��V���(L�5.���~q�y��.c�jE���m����Y���P����(��U�S��*��v��l
���`"��D	�c���`�G���b��<��	�}@q�sZ��i�i9������B�c��+�W���>��c����e���qdj��
'
���y/?.8v�j@��(����,1�O�
�0�P��\��������	a����v1]-�6�6���1�z�e;K���e:�k�AH<E�����og�f��R�)���e����n�iO�,��I�oc8��.�����]�#���|��HZ��<����������8���q|!y����������?��t��g?�t�;�����]��%�E��v��a����sw����^j�d�B�Y44S�n�rE-f�t$�\�B���q:Hr|�8�^�������2�
"V�t�� �9z���Z���D���e��b��pd8������2N��*������zN��E�3@1�@�D�3N�,��z>e�m�8��`#SAOo�c�%.���e�;I���:+�����9�Jv1�Xg���F��	�BH/MWkSj'�1C�;���/)$X�J�|
���`�2j��\\�A6��Z��A3��������}����"ON�|�8��aG�N�f�?�<g+z���$<(��j��������4McK;�g����}�/��
/����V��w��G:�@T�g�b�rU��r��K8��a4c�m*-�T&�>��������������p;q��&���Rz��l��/.����); /�������x���NR C�V�����F����j8���D���(���K��/��uA���o���'����Y���&c��U%G�\z"��en�+�\���L}�����t��I�o�1e�e	��	�����
�Q�����~�:�������RD����N�5��u� ���Y��x�g������$�����M�}=�����b|"�y�����������{�r����4�gf�\D�4�70mE���������@F����F����l��i�����v&d�0���������z�����i�������aF��N���B1�u���������%EO���;��5�����K����A1���@D.>9���%����8���j�.��&lM~`<���R�!���$�����S���E3����`yf��<��F��&�3�����&��BY�c��/��I�1$�����OY"k�S����b�F8�	F��������6�K�	�n��=�����>��.��o�[��^����>�J�����$s�i�"����/����0�5E�fr��je���L����7�X�+.�����a���!��vKN
���N)���{�>���t��0�h�H�;������F��;B7���F�.Q����a�]���Q)ny:E�)$�H����F��)���q�+���Rz���s���������Yw����:�����m?%i���|���-)��'C/)
��FY�U���DN�
P�?��H�����$Lv��@2R;�i�Rd���l�&t�����+a�/��!������wE5�b�b�m4�:���l�
��ENe�R�@��KR=bt�U2���y�E&�	z
fS'������"�R���<[Q2�PtI�-)]���=D�^�+#���
�y}F0�WF�(����b�=��]_T��z�-l������C=�O��������`!��Sl�����5�y��u��1�8��.V4��}�8c�NMQu�t�9p�u����Fs�sgT�@����\[M`�F����������+2��6��Hnu�d>���fg��:���#�,���j�G��m�]����=�.-v�\GPv�8	�~��F����e4�u�s�����	h�@]0aJ�T���MK�h��"�H�Z�,�?��:&FT������TN�D���e�l�!������:�_m��<���{������������m�w�����beB?P�
d���
�\!*;�p�)��`��[r~�{I���^b�!y��T�(P��(��
i�rL�	���g�m���% ��uI�b�������1lu���D�+�K����'g5���8�FI_�E���kP�u����?{�r�+���f��9�_p�K�Q�;�������������y�V}_��3���uOs�����h������1�����G�������j����TQp�{�-Q��4\�������t����;�������qx��������~����r��?�����-����^[������fu+)@����v��u`7�d�w���`q�==��0�B�y����TW������8&�S3����^[�'�09��w�H�������9:<�uBt������������e-���!V�17��zGU�/P����5t!��9!�p����;�?v�����;��u������&(U^J�����V]yL���?��T=V������;���G�!U�?����m���������:���d�0m��Io��%5|^LSm�{�g�C��l���oXd�!e�a�\d&4s�X2I!����7���!�n��l$5�y��*~L����'�I�V��3q�&��DWr��,�m_���a����� D�I�S���

�]mH�LCe�����6[��.Y��e��1[�l��Y�z�q�j���GQ��+���Xq/�5W�������$��|^��t
��������Y�<��d��|��h�mh�)��T������9��W[Q���-���K�U��QCZ+�lA��
y�x�i�6O�E��DI��#�8M��YZ];�XH�BJ��*uJ#0�s���eL<m����SI�E5f�����7�+F��*���P�v�����@
���3\����g��_hy��'���|�!��p?�����fW��3q
4n
���C�����Pn���

=z������X�f&U���W.`8QR�]����M	��>@���-����� ��@pY7 o
��"��d���� [uH7YHW�O��iM��8�������M@5��<P��x��:56�Z������
��t�K]�7��sIk�DE��:� �����#���$���k��O�q�z�lM��m����S7��.��+��^[h?'���@��Q"�$��"��/�j��i�c#�����z&����O>�#���a�(�����XreV�V�%�v��CU�o��q59��6W�*��v�b7�7������a�$<6/��j������4*$��|�_D���&���cG�<���$�&#=]�hBv���Y�^G���!5��F�E"]d�k�Z|�lE�����)���1o��[�y���U���5���J��w
BS]��DY�����4kF
�!,��o��%#�����O���0��X�l��#<I�'����f�K�pT��)a��a�DZ�~���+hERq��
��e�]�B��!��#�|�*~���0���v:�7�{�L:{����}^�1
�8?��� �����2�S�
�%xWj���G���BK����/6�,�V�O������B�T=�_�*+y|��i�����%>s0�)�=-"6��S������~_��p��^���&�3���na�5)�T������������J'�d�>��kN;�Lv��{����n�s�d�W�vG�a��l��m�5
��Tp���(�w�-�;� ~ms�&�{���h����-(	��������c��J�y�6��DN"e�s�����wa'K�)G��!���Xs5LYT�Gm�f��W�S�3�l������9o��x��<Y��f�w�z�Aoar����p?&����,;W�p�=��c�];����@MKNi�%�:TW��&`��y��9�n>7�l�n�����
h�s���J>�x��sN=A�-�6�2�?�,jzh�����Y=��[���:���3;TYf&!���8��k�?%����m��m�[���8��� ?"z�;q�|�\���uAC����.�u>��x�(���c�Uy�I��
@�
���PN0��
�� N5�p��3T�E%\�2@|�	����B9�K'���g,������q��s}'��J�U�w�\����Z��������mT
\���G��8�r�qL�5���{C]�\����R�wD!`���j*~��P �]i�A6QMH$6�b,�KG���r���]���[��e���"�T�����L����?
�������m����������e����n�����yJ)��j���B	e�D�&���r7rfF�U��)�2��UZ�:���{�l~��;I���i��������������m:HP�\��������:�}57o��&_��r����`$	L���eQk(��HF%��7�����R�@��������{�n:v�]w2�}�����=�\}��cL�����~)Q��r�fau0���f��?`b6��f�2��	���q2��dZ�L}{C����91?����|&�)��B�����h�i I�	���%�b:D�R��-�,�({��[({q�)?d9%�8��"�d�	�������'���u��.�(�y�6TL��u�0�x�1������X`t�,����FW�]	��L
,�H���f��8���<g�h����(�f�8������Zi���}Dv���B����s�f�wM3����<����Q��~����+T�|�(t��y%d-�,�;(��D���uK�@�@YJ
��IQ�O��/b*��rId�� �����X�9<3$�\NA���c'�E�:��(���4X�m�`����b��-<`�9o���{0�-������D�l�
�2��>[��h_�����(]��\(�ay��sN�*���%+������L����Z�_B����4�7=�5qm�����|\+�u`���skl� ����FC��V�|%x���YD	R�K?�,�����tX�v�^�Ll0e�-�$���vHL�)V�*���|�
iT! Zb���Vk��s�t��**����8x�|G��O���O�j�����p�k~���jq>�������?re�B���$5�#F�?��4�6A��Y�jl��GI��C-O�+L���$t%d	\h?�q�anp���w��)5���>��M���L+#,R�~�2��1Ov����#�.c.@_�� !�����>�-�'����Xn��s����E\{������$�A198����:�EN��F��W��A���>2.�i6��p��x�~A���>�-L��
���{�+kQ� ��
�o�7����)��x����R���/m ����.�������i��h��������=WmE:-�D:]���I���
��0�f����Ut�<��o������4C��I�p{��wb��G���W���*�8�B��[�V���
��o��[������K��v�"�=�� �/�jXcC��w�5i>���[�4tgT�H��Mx��i�iu��'�@r��!�����JT��b���x?��mR�L;AB~u���{1dg-��W�/�W���H�&��]r�O�#6JG�<$\f�,���'X��p���F��%O�J';���b
�<}�i��(A3Xa�TD�J�UBu����|�L�h_-X���;�
}
�^�#��{3L4�� �s^��U��zA/Hm�E\	���z�O��!�%�s$Nw��������N*���	�1O��y�������j�,yf
���Y�${�b���mN�w�p���o���K{�� �B6z���nW�sSAa���������{C��f��R����"��q�Py\B&���p���X���)�7�liZ�AH�y���w~V�B�,��h��Q#����	����Ag_�2��m�m�F�r����0K��+�5��Kh���'�Q:H���6���JU��&��Q������z�K�����V�#o�b$�����t�Q6,��4���QR���������J��t*���N�%�N��$�o>7��$7���K�hj����'j+T�be�t���Jq��{k��}M��^�<T/$���U�XG�WA���.�2����T�%O��l� /&S�c����u�J�
��i�C���Z9����G[[���\��f�c��:���]�4��n<���������"��l��?�>�pq�2��r�b.xrQt�}t�����X;����<����i�O����M{~�v���YM��d�{�M�����~�����s�H�/�w7��|��3��X��4'��{�U����Y�&��|r����������$OG��z�)7s�`Uf*awP�vN���jr��
���(�����^����U��uV��d)v@��2��4(�qWr)��3C{�*O���;�5�OUMHY�PPQ�L��^��V����%��l��g����-�p@z���Q�8��:F���^���$[wL&�C_��2���Ou���p/T�f�3���2� &p��x����O#�����9!���6��Xw��-@!�����!+--8��klC���E�{o<\�Z0Py)%����Y��w����@�/��Q"!
���;Ayyow4����~�������>���zz)�og��T�����t~��[��� ��1���h�m���
!&��ogJ�L��p��=!��k�Gzu��/4�����g0@�u+�rq�A�T_t1�w�E������ ��@9�]{9��h�����nL���
[SJ�M��^��5rd�;rH���j?�Y��n���k#�W��U,��r���
p��<[`d*��n��NEgr���0��n�����_�uW��2��^�>����g���;X�z}��U#n�T/�_������'(kqK����g1����Z
 ����z������\G�?Q��c���t�Z���S�&����~L�e�c%��Z��*��+�E�r�{��+r�bylV�^����6���9L���$-��3��zx|`y�?^]�����U����WU�-h|H��5a�PK"�;���VUz����2n�L���m%_�n��9���2�)�(���*�*��2���c�����^����>\g�pr���_��_�h�l�K���v��ju�t���cI��7����	��
q�{���T�oiJO�Y;(Uis�`������mV�K��Y�n�K�`N�5g=b����}'I:'+3�)��9G&'�0~H�FU='F��h�N)�~Q�y8e��9
=��t|M}�A:�Lr\�.G5��\t
�����l�c��(A��$��$����|���\,f��`��y��"������=w�����:sC���HL.������)�B��!�;T��TH���������
���/���i��@��K
��i$CZ��c-��8�S#��0����^�������9	ZL�R��(�qJ���4�>2���J��P��M�V�*|��t���	�6,L�Q�[�J�]4:''�Qv{1��v<B�V��^�G;���9`����"�7�U"��Gb��g���.e���\.B�LO@,�hg�Ka	�o��r���O�=���W�$�)�72Q��?Bg�=|�R�Q���7*B*(�������x�~�Xs .W�����AN���TH%�
]�������4���v ��DZ�2��(��=�:#�����Z����F�^�#�A%��XC�lno�R�G�e�� ���e=�,o��
��!��;��b���H{x=DgX���S;��3��4���������W�g��v���T8�
��2�������HN+f���z
k�h��WER�`�|��h�L�����n�3�Dd$����]��)��(��N�Q3��l)�TI��;<v��_-%�k	LP`�@�1�
���S�8�+L�h
)9	l���?�&g�z����
�����'�6U�6?P$y�`����N�p�l�n���XZ���B��Iq�������;��0j|^4�a�p@�$fk3�hE��&MW>Cr�l;�q����}�����oT�r�o��T��>��r8T����@[��������1�����$)��T���)��m���f���������������O����{p"t*S�)�Z�Q��1-�D9�KQ���MH����c�IA��L&q�I���JxKm�)�&�	��,w�LQ"���Izq9���	�L�gNV8�O4I���k�>���L�w�� oc*�����[�v<b�X�Fb�xf�������������p�}������?K'���a/�3���@8T�l��dN��_�\6#j�lZ��������E
���Q�`�(&�b�d�[t����x�@A��oW�n6�E7��wT�> �����g[�
S��%�����U�{XH���D�;�wW6����z�9=���?��&��8u�����z?~�c��9L�vWpz�S���'Y�\7��{�q���$Z�����L8�)��"Z
�M)E��*���CT�F��RjEY�}qN~����^��$�������Y5��i�f�?O�'R�axt����
�8�Vl���AW��`����(��x�s��@{s����1���O
�>O�a���U��_���y�x5+��]���k�?q����Vn{4���� �Q�6v��ux�����W'Nd������Y��9���{�S���� ���P�U!�@R3O9��%l:]QZ�
O^��C�]�pu�Uwm�,}��z������$q�g�[��q9��K�#�����7��@���Vm*(�5fK������xF�~�c�)T��P~�qx����������c����X��d�<3�j�Z"��+�A����n�@�a)�z,�9*�!R(���[j��9n�+�L��&�9�'^o%�|�V��8���@�p�7��1tisRv5��B������>�3�M�{����W���������V��c�����������bf�+�Rh��)��&���s��	���<[�<q���G^�81��	'���=��-�7��*�a�D"�9�L��i���L���g�T����t�Y;$�R:N�T*��I��XH�����(�����	N�m<Xp��hG1���� ?���qf|:�Nt]�T7L|�=���R+�_t�
��0�����/2�9���,{����!P��(���R�c���Q�#�i���W�7�n��}��!�oEfjfd�h<�[T��Jtd5�����s�������Tkw�'���	�G��b~��K?Cmq���+C�����Qu�����)<(�1�MD�O�;�y6�%�R("��T�_�_T������X����t*�%���T�.���%�!A���5y��i��^�D%jkL9'	V�I���s/��X���UzyV �U��VB�yi����X:�Z�Se�( ���V��l����}B�$�1�7�LGT\U�6
��k��_��H���/�����3�
�����IIi~�O~}���a���[1><�b�E`n����SL����e�H�z.����J_��s�w�W�7�q����4Q����|C�z����/	�L��!_g�����{��rj��K��� �<���1A�����(���,ZZ������"r9�������O�������D�u�2yN���w�}(S��F���4	}(1�A�z�01��vn��LL��1��	�>�'��rL���C���\����_�����1{
�T�|!B/���j3�����0=��'���t�L����o���
�����/o���2	��S�%<�r`�)'9�A�����c�u�IJ���bqn�J����[}
?&���'��:��}�B���F����pj����+�
�r[��D���"��2�!����=�F�^��]
~JK\��4p>lh`��������`��	�{83�$���
�4��s��%6L�~+��{=���s`a�H\e�H�t@�t���H��1ojY�e%��K���2)	QIJ~�z�~�@z�� R����%u�@=x	F�;�'���Z�Xj&V*1�y���v���z(�d�6��n({�\�<E�e��)�;��Q��y��`mf+]4I�Q:�t$s��������&�87�i��=���y!v?$����m�y��#�q��	������x���!�,���amy7����*���D#>�Z2(�������r������%PN?����Ay�
&,����I��]P��r�P�����4�r0��z1?*�Y��U}y������/�g��/����|y�Ak@�N^������TE���4�F�h�K'@�4�
��6��L�b��{I�������Hf����{D)��TP����a>`p��h��z����{qX
'�P8�M!!�EBR�&t������!-d2[E��+��Q�Nk��v���9��I�����2X�R��*N2�p"A��m��[�.7K�����4e��q�Q�"L��.'���@�7�/�N<:h���7����={�����Q�oN��O�w��#��??��y�g~�i����Wu4��q_;t�.h+J���3[�����f�����}��������}_�����<�U��
p{�,Z5�d�'���vN�G��I]Q"G�r0tcMt���E
���\=�|b�l����k��ev����|������H�I�e@L�ns�7\Zg-h�Ds�R�P���6�K#q����-WL�I����Q:Ru{�_��
�\`����bC���a3��2��F�P�*��\�-8�{�P�|F����KS������i_�/���u5g���H�Bqz?wN~���_w�~��y�>�u���d��j����$����8"��]���|voV�=gN���<�V"s�K��?���y��h+�s����35C�����J��6���t��>�R��J�Z��N�e�%]�K��e���oY��{k�1�0�K�)s��[����o-{��	����)������}��� �E;7�Y8��l�k'���R.'3/^�wq���v���������5
'�%5����NK/Ip7��;���\���H)�G���������-'�g{�4����C�v0��������X�3Zq[�����][�M��w)w�0�N}�:0�z�C����nE�l���(��+KK����d�kqg��6LB4N�U�4%g]48'T� �[{b9F@���z�C7�&:L�YL������8`���s��A��i���/�.e
TPs����)}X����R�FGc��p���D�N]�+��q��xp��X�
L�sS�RNcd����.z����	�b+����FD�a5�$�q�39��=�/����g��
mq5��$���x��#
&{u�����j����U�{��jK�j�����������.�9%��O�I�A�K��a����OY\���EK�Y&�uM.r1�GH#P�d�o)�(����y���p���j5��=/������w
��������8A��X�S�?�5��We����a@��� ~��.5�$�>sI��ubt��-����"q���)���~1�xX�+T���`o�R���!������p�%n���]S��^)�K*�D��-�P��o�������J9'��y��j]������������T��R�����n�m<�{������LJ����Kn��M�X�B�JX"��AT[T�
�~�����1N< V���r}^���l9�377�?�;�GDh�$�
K�:�9� ����3}W������Q���0���[��*
���'�}���h>8������8a��Q81��w��N��{_PW/�i4�)�Q]���y�>O��g}���8��O��$~��I$���x"&e�}Z���W�7<�4��3��S/�����_�a����mi��r�lf�%��;a{�;�����{j������������!��XA���C�;c
U�T:�BE�����G�;V���I�$K�X��X=8�4[�8�a6�K0�(��Gl��>Z[ ��`d2	�{$_��*h:�e�)��9!�oCwLe!~�:�vC���Bm�?k� �77?�J[�����O���
ne��=�+u�U�����$�q����H� �q3�h@��|��ym�.7	{&�8�������O}>��=&���(����9���_�`�W>�z��PFQ�V�1������h�B+�L�����*g�I���]Yg�/����I�C�Zx+����U�J�3[���*�<Ge=}���a�Q������K4^�U��8���.P�e��$�!�������,����a�H��s�H�O�`��F�����W���1�{�>�u��fS*�%>�LaK�]l��-��������N]� ��m����<+yb77��=kbr�|h����0��y:���8=�P�A�����m��������YI�%�b��Y���`�U���]�N��Q@�c�A�d��t��n�R�Q<���h6:��zq��I�I@/��5�����-�GZ8.$�B"\��B��2�#y	��b�^v����w2'�
�cZ�����P����|��A�i�>w�����t8#G�;�T=vJ�Z�r����	���m�R|�0�2��N�D08��2��*��{[�D���
20m<���<�>������R���q�N�fj����W��|(���-�G���������� �����q�40���1�FQ����\��i��#[�@6����NF2������Sm�iB��D8��}=���9>� C6k|�����pr�X�fAH�k�# 1��l���&���\l���n
��>���0�_Ux�-n�&���1��E
��NQ�!>�q�a:�5j������u���R^,����
����ZK\+MJZ�@�	*�5�;��)��\]f�1�4>��}vh�_������lV�[!=uJ�S��9,k����i�V����m�6�}]�p+%�63��GZ�X^��7��X+������m��+��er��&�x��p���hn"{BJ�mV���"���������m����NE�����\B9���&kjS��	5�8����.������f;�8X��8$u^
��u^������1�2&����������r�j6�����,��>��6?�p��Q"�Q�.�%��m��}Q�8��������qg(����6�{E~��M�4��M��?Hb�o2��9���|��n3E��������?�C5�rj��A��
�,Roc.6���j����l�O=:Ha	�y:_�o�F�p����b~D�P��-	����:�\���An?�S`��d�I�4I����^����Q��r	��SI�^�����pV���Y�ro���WFiV+j����!��]B�s]3�kd9@C�rp��]��Z�W��7
*��<����y�rn%�'��,�-��TL������rm[=H�	a�'�����\L����2)��]>HZ���T}�8`���D�; �����&t#N-�Z��h��w��+����s�Z��Cu,S�{)��KT�ZPz�������+N��t�{m4mx&szN�h��'�[b�q�&�d�=��u=�1���E����	
��S(!PO�1�k2����~TlG��k����-�F9.{z��4)Z�GVPn���R��UG���K��)���U���=;����m�.;�5����U�����_�R_�2��,�+��A����#�K -@��u�������$���]6������J���%h��d��Y�YI���d=2��-H�G1�=2������P����^��>�VJP��@8�Gv{�(�!����.���[I����$�R��L
S^Q����������s�KbvvaoI�
.�(����s�;9����e�j�)&r�
Cp���$~#�WI��N�c#��+�
D$^V��|��$�v}��y����=��3�����2�2��=�yY�X�_$�,�V������;'&�����c��q6�h�)�fB_j)iQ�J�u^����,��I�>����� ��h`r!���X�\Ee���/{`�@�0����9$C�NT>��w@p��i���\�J�N���F I�������[a 	��Y;�IPg����	#hf�$�����N�����Z,�J�{-�,�Y���?M�G��Y��5-��zm�����g��	:XJ,*1o��a�q��/�S�$Lk�x��zm��+����$F�X���tr�u���re�(�YQ����)��#	��������S]�`&����������	S���"g
tq	�x{���EJ�������bK�����qA����}�,$���t���eg�]~id�-��z�6������7W�����<���(�`��U/}����i��>�s�2h�[������R��E<�������k@��m�I��'�����o�P�4�Br�E;�kG����tGN0��~s��Ca��y���;�2!O�%G���<D�k�.E)��I���2�
�u��\C���
�}�#�G���	��
E�aJ4�]
��ky���H��o9JR�-<�?:� �~v������!*w��k*�!��r,�I�l��s��^��%��#�h]!n������K������8�kn��&�\)��������i'Q���������4Y�g�����!����� `:�����'!�g���`���T��y%�j�����%"7�5<���bh$1���g�y5��X�Q�
��4�t����t8(�QW�}�n���/����I<��
�>Q\�I��	fL�����dWsS�
��=������kg��[+�X��?�Y��a��N���!�:���J�]�������w�9!Q���8�spU����lV75��!�3����/�D&l99�����pF���/N�/�Y=��t��[R�5f0���QYf'���G2�f9�n%��������Ws��S	m�Sq�@gJ���QE�ZE�v���C�
�gR������p�M��=@���y^�p���0txI^4���%�^!J���%H�j�lze^`��������s/g-:�]v$���~x7k:(0pS�z�,�8����K�g���o�u4K�C�����
�|�u��8C��x/�
�������g�G�3���f\��1�,��R�S�r�p��2�p��
Q��{MDe�!�:�n�����;�tl[%��U�q_b|���6�Xcu0u�O%N)C�v �7�����'�v6u{G�g��q%Pd�MPC�ya|(��SD�����
��./z�
.�������6�<�(��P&k�f0�l�:;��T�������w��'��R�$�r�[S�����4c��=����g���������l��*�83*	�
�V���1��3+G��
v��lA� {��d�{�y�>^����<������\�+��2q
X��K��&���h*��|�!������V��U�����<������X�S.��=������w|������#&�}�K�Jx��@��P{�����'K8�=1�lK�,�g�M���f����k�f���4�����I<l����\��aC��}��W����
w�D���<j����~�38�x��;�0��*���|�;��n��x��v�f{j�rv�t���MZ����l+�����]xg��v��G�a��V$n��������Ei���5��"��3��c2#5��-<�\��kS��4��f�u(���j�@b0IG�b�+A�m�y��������Ah�L��	�������aK�GC<(�&h��\�0���R�}� 0�w4*����l�'I������6A�i� �?�[��  ���$��V<�f�]�Z�/4�5���r�S�Z����_��
�U�����N�	�K)����R�eg��X9
�r���PSY���r����i��V�������$���.S`�f� 1��B�T7d?�r�+�����.]l�u�J2��G._��l<�I����R1��%v�y����Wa��fD����j<q��W`����74g2�&������y��v�E��S.����������Q�?�/�6dn�����b��G��
�c�s�dm�@���=�u��)���E�����Q��,�r	��-aV�0"�������p!���Q�`�fi��0n��J�x���=�:��z��-���/Q(�����D�)���8��I�`��-�����b#�U�L L�l��D��/����AO������ch+�*����y���������t���[�j�y�����PWYyn��"�vN��G���	/�
"JR����� ��������kHNkfyOs5<������kk�N�^Y���������w�]��l�t�)�QjJ��0��B� o������ 
S`]L��
N�uF�%[P���y���@��7�MJ21�3�����$� �ah,�&�B�������M9:�R�,��a���<B�t�'8�n�l�4Z����S��E1��C�I���M�wfr@�A�RW�</�����r����\ m���H�N��x>��y��N���vh. 
2'G�N��W�r���3�iY�7�{��S��6��Vp��8^���j����>*\�=Of����{S/����1�K�5��V������lG�`�J����+�J�.����#��i�869����8�r��ar�?waW���Kx�����v�]��v/�=bw�YF���R�-P��Lf�7],rRN���"J�g��0y�d���?Lf@k-q���B��k�Y�5��Y����T�~#���o��.5������/��1��m�c�2�u��������S�W�����LH'�&�������H(p��(O9�R�'�=��p�{��:���?��%d~��
�]x����)1��8��
��yN�J�b�d_�}�]��_�w,���6�:��-5��K�����U,f8����	?0�=����	]i����7s�B�d�6��p8�f�u��b���Zw���Y��s|�9��B�Q�s!]:��y�)�>"E13q4����@�T\j������p��Q���(���������h�h^vS��������Z�pu  �NT��t��bZ��S�F�oI�J-ij9�u1}3J�e��h��|�-?s�d�q����]��-��J�jH
���m����}���_c��!R�X�����K�1�h����)��e����I)���2.����)��>N��p�{n�i&��#`b1�g���@�x%'���:�"�M�x{�Rb�I'N�X�H.8}+	?9@kK<�����1B�����$����������~�6�	���g�4t���
a���Kr���3�mL�������ADx�hdQ�.�-
�$_V�f\d&|Lr���W�Z���2�I�W�$�5J�)cC��(�
#+{M��ry]��2�0=Z|�h4�m�����HF^sc�[v y�^�B��L���s:r�E� (����o:�l����a�d7����tl�B�'����5	Sr�M�W:O����d�@�z/�������	%V�����W��2�	������w��~�����/5��E��,
��n	�M��p@�)�XnI���h��$�"V��]��*����������x:���|�x@��r��&��9@q�B~5�I_T����{^�@?��������HaX|�9���'�+�����P����fd� ;�
lS�O�]C�r�i���e�x6^������e����`���L���i���%Kw�����S#��XRm�������������������_�����%8�Yu�[?�2��E ]��S�BBx���������������r�*v.�F�^�Y>��h���he���P��2DK�S,�����6���K�������L�k�����a.-�����M7��G/,����1�5{��*�B�V3����F���^�B9�����X����j�
�L��_K"�61FY�����X�.�@�4L���X�A6k��"���3v�����^�����>$�zk�$VBg��&�����
��jc����[['	�_m��n�v��31�H�.�n!;k��{EiK����n����qvV�����+|����>�L���q}�*��� ��W���Xz��K��d�*�R�U\1������%C�F�X��[p��afR��F�[H!��`�K�:�Bo��B�&�,�Dy�[����
�V�����9���|��l�-��J���Ry���L"�������!4A���W�r��V���a��K��y�q��/�KQ��uh+��n�r���@]j�'�,��#*�1w��S���"a�3�I@&3,�
��>�u�s���=?��@F?�7�����-��N��U��tT�Z�K�����n�V�}��s��������q'��g� �zA��#����E�����q*�����Qc_�
���)F��"E�,��$4��C;�Sa����jvS��H@��Gb-1��S#�#�KO�Z1W>E
)�K,I�D������C�V��W�bH��\E*��T�?�8I*b U���y��@	�@7}�����A�F��"��A��_Z���L����+G�s���mC��&�k������\n�S�Q�I��i��������;S<�;9)��y�"��JK��Zo,W(��=�UX%/���4�d�c�e��?Z����`�>�>��~�##�B-X��E�
D�t���"&C���btQ���-�OP��dE����=�U��p��[�g�j�]%��c�l]����F�=����G�/W���II�JP�2���<��7�h������z9���*�������.H|��	��(Z�,G�������$0-U����BI�~N����oM	em��U���~Y�z��
����H�2�z�� CU�#?�,���X���;%��B�&$�U��=\��z��OG���?��Mg6�-���h���^��lT�C)=d�U���I�5��
������yk�"����zY����e3�J<����-�����r�F�@e�w=n��H0?�b>���bO�K!DlV=�w���$#�@B�����Q�C�>~
�y��E1����O��VdO���M�0]�Ts>�����kbRw�f��7�S�b7��c�7�F�)��;�z,,#j��)��P1������e4>bw@�x�Zd�MM���
�waI��xF���� �S����Y�1t=�U4W��l"�`$��'�.0��T�����\�$%�������|�D����y�jz��z��M��D>r��<�����w�RCb�#��T-T�����uvr������~m���$%�h�;�����C��N�>3��\��(���Z���P��UZ qRv�N��XQ:	�����"9��P�2�N(�Es O��������P�q��]����J-������Jn���i�C��kP)���YF���m�Z|������wP���������p��t���W��/�t�;��W�����G_
�_5�6~������|5�����>���/�`f��c�������F��G_~��&�/��#tP��W�������;������%P��}���@~�dq�/���"������f>lLm�:m������5H���)�SF;;�qes�5�~�'�!��R�Qv"vv.����]�����>��"��t��w��o��]���]@�
z�t+6`�U�,0����3�0�!�(�b�_���D_T���]��f�m���"+�!��Ok���^k�X�7����x5�#p����a+��F����&�\@��2�148�@����S���Hp����w�e��f��&�������\E�Nf�t�������n�B���x=��/�X�
�/~
�B���G���
��=�0>�A�vw08O�r6W���'�I�l9�|&����xG��
`U:�/�X��)|�D�`4��d4~�ek�KD�G_���1�s�D���),
���A�!��<rA���(9����������(R�Z�t�J�����4#�/W����?F��03$����1�l��I~�4�x�Q�g������Q����{����)&{�i6mt�A�G&���P�2~�4�TF�3�i�aS/Z�s�P��8	�}�]�[D���uL��&P������S�!��"�?"E�&��u8�w�78�kv�����/�o���o���'�����x���)��9'�+�d���wT,�$#z�$�b-�@4�1x��*x��r�Q7�	�L@V�����=2����u:���{��4^���lo�?�h��#�6���
5k	�cx�\5#���{����H�����C�U�N�=��Z����J���<�c�fq�D��N�v*����w�e
H�������:�)�l��E0��R5qP�3��U
�����:�������J9���/�x����D�S6S#�(�B8^���l��Q9�V�Tz����%�J@��AVFa�R��Z����)\�\�����V	|��s�4M
���ZO��[b�L�-D����y2�z�po��;|����}sx��L'=����J�zD����o�wX�����xO2�m�91$�$m]��"����
�#e��'I��]�_f9��h����1hF{�}�u����oZ���_�	�7?��s8K���d��Hr�`�=}������x5M),���8�,���H��Gx�?"Ogz��'
�2mI���s�|����^�u�;��|K}k)�Q� \=�`&���w��EM��m����Vy��p�L�B��{���
�i&��tz@'5s��c�)�U�������&�9�A2N���k����������-v�0C�W�D�5��~��oS,m\��5�����2~�~����������2�3��n�"~#����bR����I;�&Bk�t����kx?�5st�� 'r-B��Yh��V,Xg3�R�S�b�_K(�{�m��`�n#�m�v@�G�M��N�0�A��b����
k���Y<80��8�%T�$1F%�E��c	;����XA{�N��|����=���v�F5r�A9l�=J���$�E�J*fJN�������Q����IA��R�s�\Z��v>&�Ii!NXp�"D0�>HvV���B��v�����&x�-#|��~'�a5����b�r�3�"&���%�F�M��I���Z{�������o�*1o�N��j��D���acT��aC�p� 1� �����))-�9,R���i�o��u��4g/�mDtPy�f�&�)�����T�aX2Qx[3S��p�gW�n�tF�B����������aRcK1cl6�s����8nH�������[a����hLZ�z��X�F�b��'�t@�����bd<�e��	"���&x�t�Gd"��(C�[���#� ���)�j���������������'��'�|hZF�'�F.�q�-�	u3����L�53�qZx��80���U��k�
)��
}�����{?4u��u�6T��]��/�FM=$�bHY�����5��������0������~�e$f�����mg���z�S|Rju��/����=(���x���w�a�8a�� �:��UbP�������ZK���t�n��HK��Z��fI��:?L1�vP�W7��B���/��%���X�6���l���N�0�l�N�KW���������P�=����O�����pU������U|P�5,J;�jK��8�����#��[@g���S�Pp��a1<5��
!b�Q`g�gw%�
����I����nKD9�b�F�{�������_�������6I�~p�R����~F�2���|8oG��V�?�
��d��z2�;����_vG��aJOon�oC���>X�����~�D����[�(��|r�R!������Zo(B��4q�j�\�sN�1�'�����e����6����n�v�$c�����U��������4����2`���pt��8t��~�4z��f�er����������IC����
7$��Dm?��>��S1���U�����������ey��bU���u0����F���u��0,RM'���������e��#�^�����d�N.�N��T��~g*�)��X��������������N1yLA��[�6�i���4FI|@hd!�������U�f����H�&AI���x�_3���p��uS�)����MAG�"O<S�H�\��6� ���Xo�2���M�� %�9D6�%o����	�9�Ne<y1O��`Qg�6��@g{��*�����0���i�E���f{%�)'�@����JP���t8��|jQ�������u��P�������Y/�����V��L!I����
��
�hm���3@Yk���O���*�������h�BqC�]\>����S��1�f�����bq�H��B/M�H��pC9��	�a���jS��!���~1���n�dM6>����O�G�a�
	���������mR�(n����w�����,�c*���B�EaH-A�)I-!9x��#b�W&\4'7hg��,�����'����PL�@3��t���!��X�����&2�i4c�)c�{9�N%_�t��}�����p2���	�m�ch�l��X����
����V��1��mJ� ��@�3�cJ�vh<0s���c�q����4`�
qLR' �m+X��i7h�����DF�t7�m�q�J��`��3�����j����p��+�#�=?�J<�F�/Ng�/.���L�r���7���Uv�����?���N]���Rz��id�r�4B��2�� tI�:{�]�3y�c-�b��������#���14���ti�����D?���,���.����r��-�I����Jr�5�z)$����v_��6-�3c���Y���f��U��Px!V��{o8�,����H�}���5"���r�(o��D����$#�����Y��QYc8��1!�W&9�P����	R��Av�''@w_��@D�rpj{�=-�$ToE\h#�Qo���-R���|�!D��B����gO#�1�Q)���]	|y���d�E����{���r��v�F���w��In�t����M9e~��a��S=0Q���4d!?�
l���<f:����b�)�v�TN���g��"�&�\�i��C��;����{{j�c���|:�#�0��Gz��S�4��z��)}a�����]��v��o����9��S�/�
���tp0��]@����7�<C$�:�j��\����a������G�����Q���1[V�1��A�{Up�1�!�N����� 9�����6�%U�X�z(��������N0����#�.�W��r���	��s,���$���\���Z9����Z��tB�5���������!��SZ9�
���Z�H�U�nzX���������&r�Sp1%����� l��(^����x��*W�W��	�&�x'Pu��}��Qq��m�?�?/�8#�<���MdBk��B;&�#�P�T_�y
�e�|��)�o��A(�K�$����8I���Xmg[L�g����%�����F:/�1��X%7"�a�I�1e���r�9&���2(�p��%�R]l#�S2���"Q�{��
��E��K�,b��{�x���M0
IQ������{�J�X�#��"%n�jj�O;�h�K�����MjAr/5od����l$K���qe��9��r���G���p#�y�$�Y�*�4�4`�zEUt�1�3�A�gM?N�7���,���~� ��o��U|���O���5�����=�n2" ��<��)�p�XQ���%�Po8��h$���g��]��N�="^��:��8���w��G����0��4K,�#;��%
���R*	W�UYa����v��Wj��m�����A
%_��{��rA�H�^�Z����wcY���w>���6���hr~
�miJ���[�f|�m�l��@�a>�������F,��O����PNH��N��;�����}s�u�=�(���h(	kl�`�02�C)���5W���4���N�z��}��6U��]���L�]�2m�G���D�bh��(��F�I
�l�%�d���p,���W��F�;�[��Nr�SL]����������M�*��3����jM`U(o�Q���t+{P"�Q��E������)��G��G������U�y�{��1�.(7V9�]��Z����9�3�%������<k!17��'V�,�����oz�Qp��1SR���e�M]e7�[�K	��	�1!����y=����.�����|L�B�O��Ss_G�CR�����R���a���F�y3�\���{���g����7����������U�X���kL�����eh��
�h5���<�M���������2y�\��
���u�\��5���"��Rd�,o���*kc���_��+w~���g;%Y�~J���@��l�z,�r#��H��'�(F�OYJ����XI4���6^'R8����P'��f�� ����O�W��*8�)wUPQ�x�R3���h~�*b-������8��zt����)b
�E��'[�O���}����_��qp� ����������~&�C�5��E������V���7�KE�JU?E�IW��v��>���_���2���u��m�9������'���LE�5���N��d#VhO�X��Ax�[un�1�/�5��m�9K��
�%5E�h�AYJ��=`V����I7��j�B�|(m������x{�����������Fu�~�q�_�"�]Q��9����:���#�^	z������3�gM�~�qP���c,1`�������j5��*9�����2�������r��=�T�e�����0w���m���`�U�N�%�pf9`�cv�����Q<�o'��j�]	��Op�n1���P���C����~��������4i���k�F�}��n��SJ�]_JZx����F8�.%���Tr��v�R��������mRJ�>�Q)i+�y�������vg,"+o:�.)U7��Nu�����?W$/���G���N�P��kt0o�g�B�E<@�����A�@q)� riL'v�DgXIX�&H[��,zL@�#�8����N����sLT�	�S.t�j�����q_��Mp�$��5r�dm����OZ1%#�E��~�����5�x$�$)
�$�L<z��L'����)��6:rJ����Q:HE��@������X�S�t+�k	�aP�QV�V���F�Fp�,�
 ?HJ����ScQCu�Tw��j�l��c�0"��LP$�I2����Z��t��G�e��8��x\=���D�CHs��`��9����Q����H�F�����9{����"H<,��,�F�p�P5RSx�s#������' ���#�RN$����}//��1��\�R�
U1fv�=����K�A�l��}�k����s��PZ�����8	yhg 3]R@W���~3z{�J�t$���������c���n��P��kvp/W�e;�z�e��H���0|�B�U�e�q���������n}�\W�Z*}[����m@MuO������&��x��m�������0���e��#��X�<F���T$�����M�������TG�e���S?�(�S�'�*4b���Sr��������Y�O��K'I�M5
F������5%�3������j�U��Fp��E�m����i��O�a� ���VST�5eUGY-��W*�w��22;
��W��
����F�d��"���������C�}����FWS�"?$���(�/�
������m�Dih��Oz�^F?��F���s�K���	�D�(`*��2�Er��K�2����nyJ	�Yx�`��f�=�����8�/���t�����f�
��u�����5����-Te���W8I$|DK���;�;�������) �!K�\�[k�^���v�	�u~M5�S��3��T��p%�cx�R���I�30?x���@���x��!��%�lx�$2�9�marK4�Py|e(������(0������
6f|���\�� Wn+�[���1����5�����C���������N�Y�T�v��1�F��Qi���w��|#N��P�����a��5#�	�
>���������8�����?�C�g������������d�������8����G���d��9
h�OZ����k����}F��v��on��	k�ou�\�rL>/D�D� ��A���o���e�a!����[�;�M�k��fu��-�P_����k�����l�����Mu�V�R�F���~���c�iH)S����d�L��������(��*��{��j���]�xO��?�	�n���P	<�9�;r?��-���S��U�8���4�/XkX�8M������za�+���zu��g8b�.3��;�����4�N1�'�W�H�Le�JdX�Zo�0q
�Y�E�@J,��=��Ct,'��8�F�c	��[�bJ�_8�r�R��-T��%�	��)�d�+���1p={M6kT*�� �h�������[�>��V��L�|D�";��g������<B�I6~�w�����hC���t
�5�RN8x��a=%�#G�N������X@�{|�99c��XneD�I�ex@�� +���m�����/%9���]�9��G�W�y����P(F����>�%������}���0�eXxay�Np���G��0X�y���A���������{z�l`����T��x2C��r�%X�����a��4!���hk��})�x�$l{]�:�?/��h��;�1����F:�����������~������6lP�m^.dYm+E-
�U�<�+��-q����|T,1��j���,�(���N|
z+ey�7���|f{��?�j��p�\n�A�i�)��Z�>���S����s�,���=�R&�z�L�x����6#�n�!�xsCG�D,.m5�f�����q�<�V{Uh�S�{N�9��d���V(�V����f|Y�Y�d��8�:It�����2@��,9QW��Vn,`x�v�]�����s��\lL�Q�Y����Xl_	}���a����K���u�ZlW�K_������_��H�<�r�1�V���2�]�h��+���?jj������|;�h.d��!�2��A���f9@o@)=������*({#V����C��o��)������L��`��f�'/Uw�B"�3���rn[���g��B���eK+h�R���o������$-���Z��8q��M�%����q.���A���<�����RYZ6%V$���L�����L@�6Pa���R��� ���@����
���".K�����X>3!U��Fk��(3A��N�}�$-���>�u�U���=?��@�<�7�����-�|t���;�L��d�����mZ���v�G�������g6��G��@�Z-��LJ���~7��o�/j��L��<��O^�H�V�Z3"v���T��>� ����sq��B��$'-������|��O���h�������$��H�td;"��,W��;L)�C��V7��9�%���������N�B,�Q� �k�i�z/����S
��4�-)kZK�S*���+"�k���J�h�d�~}����g��='���|,���rP��>%�([�|~{���)��J[�P��� #������'�gg��G���`�.t������Q�hG��g�aJ ��B�B���5�O�����'�,�R�x�D�<�G��i=������i�f7L��{\�~��4v���~V3k��B~�9(���d�a��Oi�R����(����xF���!����@����w�m���i�������`�>����g1G�8�Bn��C��g�v���0�2�}���7���y�&v�5:�g?�T������L�2�h���*MnR�P��O�o64����8����l�N@����MPN���||J�������|�6���|����0J�m)b`P�Mf�|2O�	/�u����\[�^Xc����%�U������}���z�����U���V�W�0��#sPE��,F�����]x���,J(;�Z'���g�����������i����+��*E1h:\�����Rom1���Z�G�
��M�zw��Z\�OLS��
�9����M���fG��M|�t��9a]k�?�����b�w�*XVcr�>j��(��������~���UF�]@)���������#�B?���u��.�'���?�~�8�p�\�K
�`�����f��^�<�	��QC�-�\��TIE��0�Q�	XO���R�H)��8Mh�L&=�M d�;h�:@hlK��V�W��
�^�y<�+���>�����9�dQ���l���m�|�i����4z^���9�
�U�4���1O����K�Iwf����e0�R���R�h���IM�C(#����6Bb�>~�P�=��~K�t0������u3�Y�
�����7��u��QT���[�Q2@gi�p�R�A���PF���L���f.�����.��f���(�F'�/�3Y;��F���2�����:���{G��UN)=�$1d�#
2��qHq�7�?��N)��w���WE��s@�(�U�g��wa������B}���vls��,z����G�?���c1��
_�;b�s�#&?��.E�cL`t��������8���������K���g�6#�K\Dyl�q���N�Q�R��k���R��e��h��t�T�ZL�g���f��YW����s�^iR����s�J���f���=r��yAk�[�(	���(s`�<-9�9`�o��OVKI����u����W�������a���u�d���O����3n�������'�JuY��K1e(��X�o�4�����[�����P���h��j����-Q�;��g���J�?\�1;�.���������o[{{���o���Y��FY*�q�Hd�h�H�i���E����d�M���?"�t Z�+�3�wL��'�#����dt���]����p�:�T�\�\��R!�1������d����)���c��]~s7%����<��hkG���,���;���VM�[�N�Y�������Z{��w�����Cq@ts�%V�f$����7�#�}����ZD��$�T8�$���9d���W������O~<��	��R)U����������0��#�'z�w������l�@���c��T> ����*�F�4�z=���/�������&7�g|�%~���'��M��]�Dp�\�O�}�����������[�)�h��nrwr;�:���3�`���"�(���U�����>0X
��]�'������������Yi������Q��#ts�fd�\X�N���������ba��4���4��8��:�JB/~�\�@sd��-����P+T��E�">���At9����T�p��A��*�q-��Z#!�W\��)l����yR���Z�\�s��aw4�C?X(�����	wVS�r������������z��wrFu�"�%�����}T`�����E���d�.�����:����k�*-HNs2=��4��Ku�wM9w�XS3���7"��q��������p_���=�/�	�Bf��V��^e*��r����[x�nQ�<h|0F�N���
��|
��sx=!NO=;5���2���A��r���B�V�/t��+�5l�:�����!���u�Qzc�#V_��M����8��W�"����w�����������s5 �1�x@{�PH7�A����i��C4������b�U[Q�{����lO������q��eWG� �
������Iw
Ei�w�qO������8���U������|�=B�2rj��B��`�h�D@NL�_~��C���z���v�"
�
�;<o�y?!�@���^oq/Yp��r�JO����>>s�!�P��r��
9F-�)cO
%�Y��I[Q��p�����v��D����[%����V��gU�����V�^���^1t����C�v���8�W�9��"�����RnP\.�X����+�G5p0���{J
���c�h�
Ve��R�A���
���%�1�A�i�;6�#}6bn���+�v�	�@��e�������&��8���Xo�b&�:�R�w�E��
�F�#��h���7����B���}n@���#�.���aUn����sZ_3�%o�pd-5��u0N��]�jZ>�r�n?�+����I'M<���������m�~2�6�9��S^�u�2VM�5��,�����p"���0o�2�U
��A�$KB�|�_�`hN|5O�r�
A���V����4����+��|4������������>�T]w:��M�H��#���-������+(tF����c�1��Sjk�1s
R/oea���nPW�Jfu�C�W�@d����q 	&�P��H@>Sc�@p�<v�SG
[K%������%3	s���I�H3������g�X�(6�QOT����'���D����(b}S����vu���4�	����l�Mfa�g������_=�Q�r�k�Lyh�
c�2�#Fire���-�Z����74���gVeQhc����6�|�����vP;��D*������D��X�M9d��5�E^��i����+���H��U-�{�(
A��5�����jF���}?����5���<O����RT�Y��wu��,���Zh��K�*������0OF������cE�#D���yw�.o���(*����:��-X^A��HPm3��/��������_����5#�
�(kl#V�����TTn����;��d�yf.s���L�J���}4X�P\�]��mgr���S(�:dtR	F��z~��i)7�������^��6� 29����$]�!V�bG��3���%\[�f}��j����U��x��_�!�*����sc*
4�L�i"��T E"��pEr_x�	�7��&�����B�.w`�_��2{�(p��B\�d������;	�z�)a<tv���Ub�m#��0]{�2�YDK�z�-�_|��e(��<��Nm�K�(,���_�xvJ�������k'6�|}���G�oP��?7���{���ct�S��T�����u���m�i���IM�$Nc����&�9�"����9XY��~�Uk���;����}oeb��pO�����!������������m���LXY���+�_<)��j�J���������*wO�jz*�9�������l|����2�[wX8I2��"�����
�^zH���nQ��D�DB,�=���(�C��2.�7+Re��T$���>�R��X���~����i��5��8����K��{���������5�����;Lf���Q�#���ul�(��z�Z��S?9�6��:F��$)��qT��<�z3��;��Y��sJ�^j�9-�\���c��f|�l�b��9}�<x�}���Q^�_d�A#=r��+2�����KB_�b�Cw�������l��l�~��t}�����z���-��e�bJ: S���[��$��&[��x��
iT<�	�Ye�a�G�-�~�]$�W�z��i�(��Fx�����H�o�����:�/���a�����(�UG�f�wR�Y^3%��!��Lf��]&�|�%������I�C����[��w!3�	B����ks����%�7[z�Q����o��������_|��{����c�a�y�lg0��(;��8�!�v����{aLA��Q�����/���.<kq����c@�O ��D2���D#�08%������<�+��0�����������V��\w��:`�]|�!liXG%-�}�V���u��'/�v�Ale�hGk7������<��T�4g_T�7�r�����
��
�����bw������<>�X'3����8`���s �h��XS�m��0[�pA�&��,ud�{�U� z��EK�N� S�E���l��B���J!�K�s7�%HP�P�;mSR��2�
y���mAD{�`�ShI�7�)P_���9�����S��O�:]a�t��Rz�>���[N���9<�n$�EO����p.%F���#�:�����	��.hM M������|lx�mI���DMA��a^>[7I�1�[�u[��qg�-a�xI*!S��D��t��v;��=����W�jtr1��c���r��]�w���JqjnT�>��{V��f�HhR�T������^lJ��X���'-�q�_�]
��g���-����<���J���_�H�E��-����t?�L�-	;X�C�_eP�������Q���S��b?Z���'�aY�!�U�VM�(���|p�{}J_�[����>����&��y�����������pk��
�mdBx�!��xL�e��Z�-m�����EO&q��g�I�pYM�@��=��8N�&c�%�N���<��82������U/:E$�y4��\]
h#J�P6�a ��mj�N�G�%��J������4u&�!��c�`�cXP�.ji�J�*��88:���L@3B��'6���CHN*Z�}��O�	h���qD�^�w�@�18f�f����yjsG����`��'dv�������Tb���)����a�5��$�i���(",O��x��
�u����5#V���lv��}�!Z�B�aN���PV�����O#_�x������k��y]q�2!���|tV�b��*��/�EN��;T|��������z���N��Vl�V���%�Ox������(����}3&`�A�HL�����p�o����|����n���&!����d6_���q�����Q���Z<�.�K~K��O�[�6A��;��/r<�h,���#�)��Tc�F���Z�`�����/pBy���s
���t�4���="��7�>�qy�])�1��.�,j���?���k��\�w��n���n���n��������ag��k�}u����+?Z~�{����\�����X��$n
��x�'�a��6#I�t�{Z�K�5��q���W��(�����\����"5+��������_E���_���|�Fr���$���P^aS��w����FeL��1!��M��������l���H�r(��2����~_7acn�@as�e���)0��f/�o�����"���
�$��<p�F�q��>i�� ��m�Up��������+��gk:���#���G7���$�E��xj�x0�xCQ*��F����$&��� 4[�
5
�\����2�3�AT����2f�N�#O�o�����0QX�I�vFY�MSz]����I,��6��t(�[�_����#���P�r��q�z���C���?W�T9�#� &���Dv1�6�aI��E��*,���H�w"�B��/"�|e��6�H)��s,�H��N,wbus2�]�E%�V?g���Q��}"|��f'���|�N4e����G{{�G�Qz��_}�.���g����e�Mw$��M��j�&���X�]��\U���d������Bh �d�wOmn7��G����>��,�T/M��=���i��A$a95��I�p��\���F������l1�%����I�@w������0�V������
�E�Kry��K6�i���lKW�
}��Kz�?.��(0�����&x��G�����`�a���&y�=���g���3.I!����2��qu����Q��%i�P ^R���PT�:\G���fW�]������KJ�^��s��������>����u0!\N��!/����:P0������U�~I2�U.�*����`�
9�`� F�zg��<�w�5?Af_����`��p���X|JQ�`�q��)�B��^��\�o���`b:�T��\�tc7���^��D�rN
V�U
+�N�0�xM�
�4z��J��v���������L�?d�K:�B�&r���4�=�y��$���q:}�t���XG53��?V����'�4B��������+.�{��q]��2����N�&���O��n/��g��O%b��WR�Nz!��E�p���>�����^���������>�{(�}S+Q�\)?s��N��$��
�+�Br���2����J��d���� ��;���������L�!��A�!\��&L�7B���������y{���EJ���Y�������R������rM��sdm�����J�H��u�:,���Y��2��$\o=���k?��D �C4�����(d2)����%�Vd��Q�bk�����I`�LmW������j��<��C�	r�4��t^|K?��I<�#����|�U�d�u�_G�C���O�k�ne�OzEV��eU��������U�p�W[e�������j{M�H�-����jc��kNF����x�� ���d�emt9����
���_�3�b�2���!�������r�LC��N)s�_����,�����D���]��0��Xy�>#�����S�H��z���Dv������O��qe��'yF�
P$���T��Qd|L�PiF���{�n5	�u��������a�^q�V�?��)�4�W����y���3\��������;�����b���Q ��t�\"�l92GWO����1�:�|��D�='��T��c"�����4sn��Tp���I
�@$��8Z^�}����&ZLAj�&hR_>4t��y_kI�.w�:/0I"����E��D�Js'���C���
uV��K����
LW����aoO�
��4w�������N���4H�0��4:��(����"_�E�|�����/�N�������a��{�b����x
��K�T3R��N��3	33�����+�z�t�q#��bmX2[8j��;�)6��z�8L���X/�z���1��(�����H6
���81JH"S�;����!������o���il6��B��O�	�Q�/��.L^�������#��<-0�\)�k<���&�+��w6$��Be[p_"%�=��������G�H:B9�{.h�����A�gdm>�v��+���^O�&����0Q�\�w���&	h�M���
&�4B@�;�?`��G�����g<|K�pBL�>���C?�=Q����L���vH�����{��[,w�QvN�������<W�B�e��q6/ur���t0����
�lIl�l�g�<�V�C#lO�� ��8P���u��X�x0�L�N�� ~���t�b����_�p���H�pJ���s�$t�D�H)�9�s���[��>����7�J���m�'�d~�h�M���d��W�
Fn����LB����,��
�/���+4I�d)��gR��:[pS����t��h�!&�`w���aF
<��
���YZ/�y�}D;���X�D6L���(EE�ZE��e��X�t���"09�2�$d����2.��� XYl"���tu������[0��$������{�{��
���K��Q/���{x;�C���z�5�����&(My��N���������7�9f�k���#��.a�j�c��;~x>��z��������51�������n�R�69�U%]A�\�9��E!r_����q1/��r���J��G�_5��;����$"�_3���AM������9_�F7����m(jU!�QHb��i�p�?�x]�pf��+�9n�C<�����'	�ij��
�S����J���S�p���$JS-���n� �����b[^`���/	��g���%4�}���q�P?o�����`5��)���f/�����JC�F��������{C]���j���k��vDa`W/�pvVi!z���� ��%e&g�xn�����Q��-
�S�.(�� ��%c�&MW0�G.E�������]66@��H�,Gr-K�	yJ
O����@B�1�9���s@Q����{0��G���5U��2�>,�
*��1'��5�.ILI���������l�1x!���\>��~��)_?@ W�?!�)p�HO���r�O��.S��G�Co ����\A���������p�$���C�+�%�UB<���9��X�qY���Pg��n����w�[)���%y���������#{@q����@(����� K�� d "O2=���>�EJ,%{W����T���O ��kj�?�s0(y
�rI���_~��.��
DUZq0a���_u����5s+{/��k�Y��r�m2�%�1�	�*���#sx����Z����/�o�E��(�YciY
mm�q���#��P����K���H�N�N��T�9Q
���%�5[T�L�J� w�J��-���qT��K�qc�y�Y1�p}���I��O��gq\H�Y��4�1��m:��zH��:����-���x0l%3h����i��KE�����8�3�s����������i>*�i�u��'I<�Nqt�����D�`J��(\%�:��4"~%$�F��42E�������Z��}�{������[����������9���*I��5�iFk�1�������Z�;��]�C�$>�Z�80�+S��,�&Wh-�b�p�#?��](�6���G���},�^�~��|��[T3����,�5�9���A+Tn7�9'��6 �?xW��*���)Oc����u��B����/)'��j��IR��I�N`��=�j�?���U?��+���o�]
��T�
}���|&���Q���^��X��3l���Z��`?����eb��x\Y\�y��}T�s�������e�S h���_��9�2y���+��aBL�������' ��}���������>'ph�\�M~.���T�j_�df��0Bq��0����<��2m�����WV�8_��,l��#��h�r~��8�����l#p�
���q�(E���S������_�b�]�W�����0��$��^�LME�6�9����d<����1�rBS���E� �P�gS��^���I��2��}V�dF��A�v�)/a��D.B	P��[�,Ov��K�@3�r�4��;���Hc�r<<~�)q?�"N����e:���i�a:lb��p�&��E�]�W'� +}��1_���,�B3��`��\<j:��U��s�/�Y�|�$��0C5�������-�'�P�7��9?5M��E�{h��E����rD����mT������=�i7�k���������L���9�9��<�{P+��r���=US��+zJ�c��6/�(3�	��j��Y��8�S-��V�z*-90O#�%���="TV�Iu��s��n9�HB��+s��@� ��3�7�O������/K��dK�>�����M��R9MS3M��5 Y~
C�%ue-����[�����TpWj��vwC���z�/W�18�0�@5�:���PI�!N��E�J
��<�@��������H�8�Iz�TY��*	�<�z�z���}�������?Y���8��o���X�}l�KH�����`�:
�A8ZE��r����+rTvwR���gs(\�eyL���EE>t�
1����+�J����'���*�����]f<>���!X�B����������k���f������t��8�{��C�de��i��F�������i���X�s�+L8$n4>�v9���b^Rc����HD��x��h�ym���-��2�������+�=�������I.��,���t`���#��Qq2	������^�_n��t�y��c�����
�M�,s�������|1���
f-������7���'&�d{K��jE���1������K*
��N�{���}�/��V|?��o�]�X��I�!�3�p����4���x���
>�������!�x?(���K`q�8L)��9
�����v_�f�����wnCHh�L�xm�
Z���pLr0,}�%J��'�/�W8��gH�Na97z���gz)��'

��N�������R�fG�yj��;:;�Pa��=���&�����`5��q5$BO`��1&+��y���S?
w�M*���G���^XG�0�{�I�aQ2��+F�����[�
�������E:�Ja�U���BFR���]�y��\�����U[��`�\S�E��8��p+,ft[���r(!|��i����N�C��O<H�l�L��~N���2�V�SkE�����]y�����s�<����,��
c)��P�s�&Squ�zW*����z����T�f@��9��+|����P���W�A��`��t�Ss8�oP���`# �;>��;	���)��k���)#���9B������1�����`�l4���&*���`�!�A���d�6�������2����p��k>�w�w�X�$
~�N���A�����:�����E��|�FS~|
��h�8�/����#����B�hwQ��'B	��|9�KL2D�
�	��<�S�v�����.y:�i�y��J��*�w&�rFZ[��w���+0�}��6�P���|��)�0�p7�������Z8����x�����F���]����c������g?����N���6�ahJ�$�E���0���fn���y�����p�-�s2 `�@g������x1�t��5��$�����#�('f����J$/�f�>�aN����*1���RD���6|�LNkD:�ws~#��A��^/�
��Ay��&����M���I�q�rxm��}
<c��,3���gs���5�YE��'�O�_���/�g��/��a���8��l~�|��L}5>6�{M����8h��<�$~�N��P��_C�m����-Fl��r�f;@	��-��`��m��w�J��U]8���L�}�C����Z�s/w�QR��-Ek����E0$d��!%�zJ�d
���'n+���l-~��{��:k��)���������������U�4TE�I&X���o[��vX"�T&�Z��l7M��������&4�
�7t�rL�o��~�+�����v��RoV������O;g^7����;4�1�U�Ob��T�w��j:>3q<~hnf��
�?E�xkq�+�n�_q�G�`�*Q���V/��{B���x�SQ
8���9@M�a����	�|7�d���1�����sb��5�c����E�[L�4���1�����	H��
c.^���]z�k�l:L����n��4*sc�b�����I4i��~��0�Mp��*,��������f?��1����p������:�$Qq��C���KS���a���}�����}o���d�}qR�I����O�������Ox�Nr������@HQ�������4'N3	����ZvoV�=gN���TVvr�X������	QN�����Y���*���oA��k�-D��=�.��I������?q��[�|I���fY�������z��>���r����V��a�[��1F��Sp/�T����"(�#��L���f�4sOlq.Y����K�z�����]K�9;�i�L�>���	�
�����������w4�1v��8Ia�#���j����jGD_�����X�
�;7Nv����U��)f�@�mme��c0���V�������&CNE��������
�1�,�'�:�_��OD��#o�C���b�JY8�X
����P�5��:$�e��(T�]aU[�}��kj�y!���n@�% _&q1.�e+R��U����Q�$
~E���M�G�q��h4��rB%�4�Q�sN�>	��y4��5cUDW8��M���`���=���(M�*[��g������@�{
x�����BtJ�4"���,>R
@ABG��sFcl�������I^{���gi��l���1�������5 KnN��BL�!W��c��h[�#����Z��N�rr��;fIe���c�0���]�K��s��g�A�{J���)/��]���[���P���[�,�2vXu���y������z�.��F 3�]k�����
fHNof"Vd��Q����H����$���������s`'����C���It?���YhRUk���D'��
�������I�t�X��b����0�'����w�|�t�~\����:X��g���w���qv!Bz����p2.�%�S�������]�B��c���^�I�ml:I�o�4Q���(�+:���!�=(���3$�"l�Gj�n���h��Rm���C��@<�)_b�Sr2htd�lL���s��c�����@cu�}�k��c���@p��B��Yl������I��1�E:}���x��4�g;&�t�V8��,��R���tl�>mFv���������^bx;Ro�EuQ�QF��W�:�sr^o-��]���_�lQ�2cd$*b(���tx�y���U������e|�S~����u�!��X�y�����b�UR$v�m��lJ\}���a6�����W���C�VH9�a�3$���~C�������3����r�avP"��sr���gh�oI5�fz�%��G2
x����z&���M>�+�������Y�KE���]�O�|Da�������Gtr�v�q��n�y�������Q�E3��p(U-�ve���L�'A�bp	r*N��{��F�����g�|��4�����/g����H��)�;+�.L~�+����Q2wc3ER����d�-���??����:9�����,���3�����.�l4�B���Y��\��\�,�$g��_�����$`�'�p�Y����x�l*� 8@�������k���w��y�Eu7��A<� f� 4�������f`P����jQ��i����<�������y�M_)2���Y�w�9=���?�t����t��g�G�?.���w�9nk��?u^��Ia���?����d���z��w.�w�k����I����D"��!\��m@V+,X�z����^z�����+����8:��ab�dv��&1���������Kf�*+�^66ym�%�x����pu���2�=l��K�{�a7l
q�L]���w��FB�!g��%���s��yk���WY�vz^K��w7�����<�^V�|12��$O�u�.����I��`LE��:h����Dw��T}���&�ud�b,`���m�6��t�WJ0iE��^<�������A"����5���9��?��A������Y�$]B�
���(f
uH�>�����,�-���]���O��\9�1�����r�����P�Ld�]�����"�h��#
�m��r=xt�K�	���]��|/ZP��^�bj�[��,,q��1_���v���������R��=�o���e:O�=��'��w9�9���D� �]��q��"�{��j5�;������?�5�(�YJ�����/��/�n�Co���d�{�N�R���������"�,��u�S����-���$�D�����9N�K���>�����A���B��:x�c�$:��e|��~�{���`.'&���������j�!���'�`P��#]s���:%7�:�q�����
�|YJ���d��cH�'�jO��8��Ia����t�	BI��_�$�D�GD=���CB����I��2�ok�y*z���y����X_yng���:��t����o9WQ9Y"�0�����?�Q��6�3�����8MF�1�V��a����4W���+�_����r�snAI*-��l@
mTI��wG������������O���@��L+��pR��T����W�u�
���Y�|u	��+L��u��S\�|�$�
Y��d9�*��5��9e"��ZY�KL�`�@S�lFv�,7OP��h~�J�F)]���Z��rO�5<6p���Y��f���d��`M�����������Q�$1�
-�F�k4v��u�C�
�I3r{++!�{<������P�c�J9~#��N7�`0	zK+��B�~N�2�"�f�$��z�z���}�]<�:et��7���������?*\����Ku{����<U��\���>\Y��x�7��K��[��)�3�<9�U���1��4.��4�:�L�U�������Q�x�1���CO*�s(�[4�^|<r{F��y��%e��Dd
�k�J����M��*��@9M�j�C�u����������	��e�Z�����,-�������I����R����>�8bS:M
`��_�{g��Ew�rS�U�$NX�/.�#y�}��`���w�n�H�?������,�&�{4�>�$;���G�c��������$8hY�n��[���E�33���cI���[uuu]��&aF�{���3�C���f1u�L��@P�"������p���w9���c�NW��~����Y*�pe1Z1u'1�9�\<�)q��r��n�'`(����H��=�!4���U�wY���VdmZe*�E��!N`=z�(�)�@�%��G/�Y��EN�1��s��%�
�(��}��
�`�c=("��������VN�b��E��l(�8�Q��)k�<p�;���L�M�x�2l�}�=����0���o��\ n����E��h���(/Y��$������9���[��������fw��j���c^����������*[~�X����A�L��[���Q@0]L�+�Fv�;���<�������D"P�����E�S7�3x|f�����H+B��
��gdN��:K�����s����{%cZa<��E����&NXY? y
D���o|�S�vI���R9(OA��K&a���!���73/W������Z��t�\2=*�������C�&����y����n:_�u�vE�T�����#����^_q��J���G��V��7�%���='��\��N�����be�����hn��7��o�1k�_�|uN0O���>;GDNH�Y��3&�������]�'�)ge��q���g��L�jR���o�3%��'���m�c����������V>O<�9���I,��jS,�
����UH����W$�����ur����v
��!�}1F� �;��<S�e@-$�g���u��M_�:A��Z�v`���sE��}��p��~����<�8��h�oc�;
�F��x��s����~������}����� ��#>T��Kf���PA6�<��My����������y�G��e4���&�4J?:��������6�|~?��xB�:�_��6'��~�8��������������k$s���B*F����������yY4H99�'3r���S�c�����3I0�|��^��q��bM�>���%9��������� ),�����S%���T�'�UeF��X��F"��4��S#�\�*H�t�"��-��Y����2��iie:�������~�C
m�d��t�aU|S�6`GYR�l[(P�P8N�����wi�SfN��f��J�q������������J������2��4������	���������4'�xxO�,1� E9'�R�JHg&���*2Z[��PU^@���\s�J������T\�%����]N9rl��j�
����>s�O���9��A�s}<-^���>�P0��7�;W|d%A,RDB�Rrh�!�p<�2~�����X�����d���k���@:�i����
woq4@�������U�]�����PZ�[��eE.�RVI]�n�����%�-�{�A.�=��|� f�a���L��0���i�*d��oN�U�E�bn���R	JY�R�U�WF��T�5^zW@���R-��%����Y���%������@���`X�3+��s�g�F�'O@�UY)�/V��jEYAOV�&+��������J�qo��.2u:s�g�zH��3\R�l�����)�T�u]}y���
�������h����k�M����aQy���@���tV�l6�[:��3'�4\���>�_�|����g�����IQ�1���Ww#p���"����VP�s!g�O��bVK T����&S���b|s���t7���q��-P]N8c�����2,s�$�@%�0�-���{V��w���
���{�� ������8'G�Y����Pw1��	/�3��WbtTfs�{q,t*�qFa��:�=Ci���O.�����o�L���<"�{KAduhR�����\vM�
�yC�`��"��m�h~z�P�v|�\���v4�j��Y��4������u�{V3�m����cI��!<���L0��M�S����kN���1)s^���a�*~!}���Q�k��=2�H7������J�8l�G=�!�@�0O$��Cy'��f~-����BK8��]:�u����pD�<��rq�K�Qe�(W���6�/F��3D+�L*G��{1o�K��U�F{E��2����/d�to����W��~�!���UfI�����W�L
>���	am�=�*-��~C9�H��������k\kI�wu�)��
1-���\�31u��5
���^'9�Jh�n��U������^�'E�K"������+gy8���
IP�cL Q�QI~!��E�k(�%��Xm��1U,�i�+E�%i��Y���uM.'�,�\�P!tu��GD�&UQFm������[;�+�}�8S$;��/2��Y}��,����E��G�|^�y�O��
�%*��<:[��"��658�N9x�9����"�R�w�������R���h�"s��� ��(�DnZ�����p�#��m�9�fzxub���C��FV:E5�����H�7S�w���i�1�T��yY,c|38q��:	��],0)�U�F\L��W3�r�8V����i8I����Q���������Ty6T�������>B�c��I�V�`#O��Jr�����z�����������U�"���g��	"�Q��F��P�[��(n����>� �]U�(+�zl*�s���&E�����E��[�������Y������V�J�J�t�E:��vS*�DC�����+�{�x%���C
SfQE��L$�QOek�!]Ps�+��\�
`s�f5}��}:�nO��&����:EVu�7$x����yN\�������Xh�]r^B�#�Y6W��g�.~�@����1	�J�!���y�a	]u�|mT�i�'�"���J\g[���[1-�����)|�vA�_G�|� Qq����x�]z�$L�&�m������(�������30d��}�(B��J�xQN��x����o��u��B�3Y|N��N��K^p�IH�Q<X=�"t"��99LP�z��0sc��AY��#�n�:B�d���j����/H,��7��������)�D�vY�;^�g�^K�j��;l�G��[�#'B�^O�t���wAy��\7��Oo�����2D�m\�x��'=�#�����{������	��hF<��?:c������Bu�%�GS�N�~>=?���G�E8
X�Y,' �fu\�$����$)����&6Zl)�C�������\x�Qm�v����%/_����5YU��s��;��t����:��~�ei7%���h�������D
5���(
����@>���8����xR�(�(���i8
o2|����w��������.D�
���{�x�8��
��t?s�"���P������Ri�'��X�9�q�&
(I@��Ec��qb��b�6u��uK���v*j���*J�w�\*��B!+_A��z��pL�|�g�(���qm��i���un}��I���������xp�jx�-�����<�"/�N����J&i��$����8�*�m��V��$�\[!�;��H��%���$�����������H�^������i��~���6iLkhC��#�)�al�����T�yc��~���Qw�G����^�r��E6�%d�"��G}}r��H_��������);\TJ��������@���I���>^h�����6J�5��NS���V���e��D\a�[`����g�6t����z�u(���*FBtd�a������
u��}>�??�N�5jo��Tp��@~e�e���T��^'����DI�}�:,�2�*��U�������*��"�cLJ���#�,;O#6@e� iO�bf1u��f1�s Q)���f����-O���X���E��Th�Z�5RBh5A�s�F��^��(g���.s7L$�0c�
x<�fEj���{"�CdGhu_2��@��mDF8�C� J0�W��R���R��R�4�5V�V� t;0�c���1��#|�DFhh��������1��X[M������O�>]
��D�P=���lB�ao�y���BveY�R(�h�9�;���&����C<$w�IT�x5��������;����u ��<�0��/���MoI3����%&�j�qZg1}x/����k�c�Ez��a��N3�b�2L��M`�i��zT"�Cw���8/�CA���������`���%���p������F����4�{�J
������J��I�&q����D4h�;q���M|`&:/�P����<&|�
&�Tr5��U�H���.�v�T-)p�a�f����rw
�]���������K���Fp2J����'!��������������,�Y��k��r~�"�`���sH�����pO"n�H��j�����Qd,s�4�1<�z*����a���.g,�
��lhh��)���Q��bL�w��
��f=��1�0i#��p���q��X"
D�w|31k�,�6������`��=(�]c�.T�Z��"��b�#H���E��4MB������)/P������������oI7���^F��d���i~[�<�q$��
�4�/Z���/a�0��p��9�@#�1
m8+�,��E
4v��Z�
�	��?����@�����6�������"�9����;y�	��A�����.��UCN_��o����p�����F�Z����U����"s*�8��3�f�����V��~y��O�/��u��BvV�A9(���7��A��V�VR���\�Q�/�� Pw����J�e99��O.���3)��:��(��s|��3���(��r�>9O���h`��<�[XE�(:���������U1D���Y��3@�$��B��?N�C���l-w�ER��`�GE�t9Y
��6�^�G�P0CO&"�z�~��c�'�1������D��T"���h��vGu����0���)@�����TNqC��V`�`����68�o^���.��A���g����	�.�Ni]5-�,���������7�����7��_�@`����(u\����������7J���W����0N,�d<���A�;����ode��\%����������v]'G���x���D^�h��=����u.��w��|����]�`��`�E�"_T�g�EEK�%y
u!���U���
3�'z�o�.�&!'��"3�MA������\�U�V��m��ol�*@��55O�`�	`���v�h��e��}7Z�����EM���
��5>�_ ���m`��c��U����{�R�m%�F;a���3���a"��2�s��8�M[S7;�Gx��=,�4gY4'9��Ls��h�a�5����=����3Ga:����Ve0�0���T�+�OcD�fI�w#�7?E\X�=��������KUm�!%���D�e��k����qW�E]�kW��U/_����OY�|�2&��h�mb���|�x>��Wz6M@�``���4Ja��nnY��#R�
�q�;�V��xv�9}l����m�e7��t
���0}I�L��,�a���Cf<I����H���D<x8&�5�G;������}G��U]���Q��U�
���*{n
��y�?��UW�s�T!/Du�E��=b[Q�**����#QqQ��s5#��E(��]�]n����G�Yvk|��5�V��[
lk����%H����s\D��d��������P�i<�?t�T���i��8F��~^�4�C�b ��s��]�P
��h���=�d�HSc��>x[M�gS��3h����OI���N�#6����V���8��Z�j�����������&��4���e�����;=TA}*�\��0t(�S�J	f(9
��v9*~a�8����-F'�v,`�*�+���roT���+��S���[��`�����Gzx~�=���R)��A��|�L����n..����tN'h��$��'yy7�fA�d��_ta_J;���r}�#i:(=��8�[�D�Y�K�T���X�N$Q�=B���+�[���H?��$QV5���>������&C��Xja0��
O�:i���Y	u��Fd3�y_�B2�>���Xnb����(~F���������PA�MV���������sE@5�Oq��K��M�*�1�\:������r^�'x�E�����C��U��ID�e�����_���PSX�B�����*O��p�u�8s�U�F,�+X:���M���O�/����@�N���,�o����c)�Y�x\H%�%�(����Y��������7��81���g	+=��5�+�	p@�8iGVB��s�b������nu��*Yq5��%��W\��D�}��E�.����{#�%�+�EY�1���28��J����R�j5i�>�����8<f���4{����Q���#�Y('$��L����eF.���Q\j/V����nfy�e_US�G]��x��Hac�z:�\��r�����g���>��;����%�k�������w�0������?)�����"�e-Y�q�R."��E��J�pR�ZO�At��~��E��^�R��������QQ�[�������#d���4��
���p��VM�V9�����%2f��#�NW�3�� ?@�K���+�� ���\,�U�I+&NY�����J��o�k���:����J!�d����ro1��z	�
j�����f����68R�3�Y�0[f}e��l��>���y�$��zH���n)"���2���I�����a�T��`����Y�"�|�Y������_��W�LS�����d��E�k��xM
N(1����8�3����R���A4,��Q]�^B��������������P}����s|�9���^����X�n<�)~gR<�zzZ���I��l�S4�j<{���x�"_Cx���Q��75����;�������4�_&b��A�u�"���Og��c���$�ir�p[��-��gZ�k��m1�
u��'�AF������G�j���<�X��~�p����u�h"a�Z@w�;�d�/O�..�/N������lB���):%sGh�.�Q�S4�A8	$���d�s��2���=�Vz�-1��y�2�����g��F��G�'V�K�>E*�P]��YC��@�RR9��d��M�e*�?��0�vA��U�%�syV�FX� N.���`O��P�i����4���<�*�������S��ju!�s��j=�>��){'�K��BZr���#�e���/Fb���� A�c����_Z���������s���<����X�E���XH0�a:T��,��#�)n�"kw! UJ��� ����2���8����C��I��k������;�����_������Q�4�SD�/r3�.��zUL����w�l<����P[W���O��F�@�����f�*���ci>o�Vn���`�*���f��}��x�~�_L�'�c$�1Tpi��S�O��!����������&V�pt��	��Pt��/�i���=9�'b���z�	
�n�^
K����Z��MY�J�lS9��.�n���8;�\��	X���x8_|���8�
���;��$�FD�*���t�dU<���)��n>=#�T/��KCf5�K���`�e�N����Z�Q)F�Y��q���s.S��2%�a8�GqT�V
�rg��������)!��@����DG�#H������sX�k�[��m��r��_��o�r���8�����CX�����k[U
}���x�������x��|�yts��E���J)��*cPK�b��M�]5Py	���b�pz�@}��M��w4��!��a�������D�c��_���@*`k�^��k��O.)�f�����&�Vk`�$���|Y����.e{��E}�w�
����|���-v�B�X0�I�6�`"Yn��k���M��dZS�i
������A�"F������s
������>��X�Y�K<�]8JBxT��������� ���i&��9:	���C�.c(�[��Z���u��$?XH�6��
���/��d~Qbq��|������<5��FG��d���~��	��0~ ���xc���
k���F�j�X�=;G<p�Y�p%z$e�A���Zy#��)�������\!��>:<���{����H�z�U_@H�S���WU��??>y}z~r�}����K����}Nc�������e)y�{o��u0I������a�%C�V�.�-�X����weq��T8�'���r��b�v��3�J�ZV���g|���~�	������B�~J���Ka�����p�5�}y Y���x��nZ�����UI%����R���C2W[���WL��8~Y|QD���
�wX��1�#fM�CK����p*�:y �+p.��y�Q��hY�����DW�(�V�*^��r:�����x\H%K��\���[��W.+1�U��]����Pb��>�y�=���
���D(�T��K���M)�
�L�4������$mn�?���T�)F����$�J���.��B�D�����jF�P�]M�2#�9��a9U$��/��fkv���G���U��dJO!��rx"Qj��S��I�Uf�5������|��+r�"	�M��C(>�{�W���n%���V	t\������oC����q%�>�f��d�-q�A/���&3���+I=8����m�R,�J��t��8���GU�{7u�#�O�b`K4_�,A��:^�~�?�1�������Bcv	�Y��k���������r��cD�3!�������g�0M�nd�X�,S�q�a["�C��0T�=��H��4M��]V�����G�l�&:B:�����f�1��LyW�R����R2`:�$u�S�gX������zH���f6
S�r�c�w!Z�Pto�F���@R�MQ��b�i�N2�-�����'����=��Q�!�� ��%:$5GNW����G[����>�Z�^�G��=�}/�DQ	���dr������d���)�/37�y��C�8�\n����O�y		�/��%�k�MF4�������?e2�'���/�I�H�4�����d��0Mi��Q���U&
L��Ho�N%1��8�'��LMn[��L$��AM���#����	��F���U=�S~i2��3Gc����gW'uA	:,�1�g������b���|�$w�����7��������������NDs�b
�RA���M���q�s?|��z�����4Gy���GDs3m���;*4��=Q�2��2�)Up��3=�1 f��}�i=8�d�k��{������&�&C���������T��}}������a����i�L�\�j��h��r�_\�|�W����b*�$����l2�yX�b�'�����/J@��y���#t "l�t�b���X1�G�L�q0#l��v�+;5V�b
�T�PY�Kw���6��ltP+���Z���p8Yc.�u>���dc���b�c�)����d���<�L2��T�=
�<���dd:m�7���#udS�i��y"z�}���Rl�}U��8_���r��'����l*c�U"�A�����Rf�l8|�R����d�k���~O��p�8�&T���8�@�D�U�j��:����r��}<�i�Q�S��8����s�XO3�sm�a���i�PYC�y}q�i��GL�|-�sI0)1�1�9��K��U>�<���d��F��r��2��Y����\�����&�OL��	��ZaRN�7N�01��lv]�S�@2����(MR=y��P&1�r]-��3���5�������m�a�)|�����+��-L3��c�er8�)_$��B�N��s��O��f��I���v��s����SdM��7�g���������*V2����~���bp5y���H��i�������d���V�e���;m������s7q��B�������������������53��E}�����D�;�}���h��z|`�(�G�`��h�qf3���H
9P6����p�o����g�������n���l�[���v;�R��#�C��[��u���k?��r@;���#P���k��i�J�������r���/�3�`@����s�z`5A���8&)���&hd�VjF�Q��B��Y
&��T�6'y"y���O���v��Ww�r\�V�+�b \
oL1�1��$�Qay&I���&�3��W���������J��RT�0��(�1�1��M�I�93f�X~�s�������t
/����6NU(C$q�$$`���ex�rB�^�����^�A
�(�Y.�	��?��V��w(��p;��g�Q��G����i*�8�n��Y�R�5�24�e�%L3�I�y��Td��}5jbr���I�6P��p�~���?4�Y8�,x^N�)4�_��I���n��=��*3������xxg\��e?��?
?h�ar�������S����@����s���O}z\��(�*�n��,�{�H�K!��$hm��7~�J��K����	g�8\!���^������b\��t_����Ss���]��W�Q�������n���\K��
bD�an/D�dA���M��~g��:1*n}��Y�����Sg������3NZ<�����8�4�����R"^��1��m&��$w���i�(�,�oL�%����4IQ���8,#��e&S�@�|�th3�wqv�e�|+Q�o�q�d�*��"�kkh���cR�zp+y�,m�[@��UW#�7�����
G��5.6���p���;�����[K>L,���@��Z���h6��Q����K����{1���E�#�;�Z�l���X�U.���}N��Q��S�?N�������N�Sd;S�Pc����\^u�{�|�0�a���5����c����tq�tx|-`j�l�#s(&8�]�a{o�^�B�}�&���g)~�uS����������v5���$�f�}Ddk�����DS����S<x9�"��w��UP��������[�����)�O�����?-z�ztEP�MW�5��������.����/`B���`��=+��=_�Z�c�p��.���o���s�|"q����{I��!�p��U	����@���lb�kd�g7�EC�>t��/z���S��p� ��,���h��?�P?�y�������C�Q5�n-E�x1fQ^��������V�����t��N-I<��A;.;Gv�����I(s-��I��4�����X
����
7��m����Z���dkXH��J�l����Ul!��E�������r?�'�^8hf�u��93���nl4�U�Tc��@>�
��8/_�O�)�(pT�Bii��9d�Q2�~��N�k!��^�3������Mtj�9sek��7������m�~wH*3�����Q��zu�D���9�`�3�{dWT����������S`��:���if���O��>(,��0��sN��H�:�
�������`8]� �Ws������N�h����e��_b:w�b^��K��/x�5�AoQ�g�����������6��p�5����v�������z����D(
mw6�(
�"����B���J	�������9���������Z���6�C9=�:�;kU�������w[4f��h����o�$u��#�����F�u%��{o1�^����U'y�<��i�&T<�`�"��M����i���[civ��q��%��j�q����Eb
�x���J�v�!�1A�����65V�=�+�qI�i`oJ����u1#]�q6�/��yC7�<o��x��?pREN\�?��nG���f�=������f���S�9U
7v���l�/;jk�D9��)�����b;��RJWI`��(�#���5-��(]a����2����Z�/��RR~?�G5S�%�g��<qklP�b	m�M0
����a��uBQ�
��87"k�7�A��#j��.�.=���xk�$�{�D!�>��(
��K"e��@���x��m�m]>B�,��k&���	�0���FB�a?K=G����I9����2�"oL�i1��A��VF����$���hyi��
��EF�4�1�
'7�l����	�1Q��
]F�g slC�����X{#�=�,�Q�;�������ma=G�_�i"���9��,�H��n-�J���ndY����D�h�o�S�V%�qJ����G�_�p���F�Q�{���D3���U:�*!��Z�A�~�'��u�2%B�S.�,:Y�H�$�
�Z���
]cz����
M�\����p+[�9on���gW*^����n����~���b{�D��fI��j�SU%����nBh����,�N����7�#���tu�3�&������-_���2F~�"�=�AvHq��

HS#:d�e@�q�iZ�`�9�K�U��P�����v/[t��H2	���rOv�w�MStR������*�5���%0$;�)�5��:��1��h���i�eJ�lgO�e��5]�I��~�w���b���F�^����&2���s��h����q���U���3�e��hZ�/6���=
Z1����e_��S�-.~V���{�V������iKG��b�M����1�Jzjm-ZP�����@��P%%�U�X�%+�H�Y��E~���V���fc&~���2Y�,�<���z�,�P�hu4�X�^@@��mmY+�����9c�$��%������A����a��&��+��������L�Wsu�e�a1��(�
�/kn�������c���
�xn�e8onU����#N
/�����x��t��FO�,�O�Z3�=�x��_�������*�Sch�P���x��!��N	,-C2Ec����'UQ]�L�%��c��'�>�?�40R@e��,����q��d����'O�3�~H��L�U=r�g��TS'iA5K��e�kXq�R|��+�HZ��M�l^�I�������r8�.#O��G���{������Z\��1��������;�vK�KX�����&u�aT��	-ue�t�}��oJ�]���������Z��n�@gw,����#r#���X7
���f�",Z,VCa���M�PMW��f��E[;�~����:[����"5h�@�T"9���>	J�'��F7��I��M�Au�(��4�=�
��>DvZ������7���~���n�d6��F�`@���xt�t)Z��:l��e�i������l�m�%?e�����`Y���l�)��^m���0\��v�f�����F�<onp�[���������&��%��h�S��~�����F&�h����r�;T0�7�WQ�!��{5������+����/��U$+��=���*�\�I3��u��;����{��H|��������Qp;��Y�u���.94���[��V��A�Df���@#�AI���+�H��������.a}�� ����yx��J�J������
?�F�2*��W�T�7}��<��-���V��;�pz�rS�J���^;���&������.Jq������r����<�����x��8H�����	�� 6���c�<0^��3���(�5TH;[��]`!�����g58�=���t}a=E9
D~h,��kV��"P�U�(����.V#�7P\h�P�U�f�e���i��&xu��X�x��p��Jt�������j%�-�sO����e�����O(����
]��'|uk�8�C�q����F���*�������k�}X�y������b/�{�X}�f��n-������3*���~.����c�+a�4���'�������"��z�5����rl��8����p����(�+�g'�w��~����z�����2��R���B}B�6q��
L�s8�5����g_�*K��J��G��8����uZ���I��{�3^����B��RKV��.���a<��:�z(}�Gi@&�.$G��pp��0�W������d4�H�LhDO�m�����b�R����L���|�V���Xa5����R=���1���������t�vw��e[��g��<�����2���Ye:d����C����W��M�s���.��^�[Qw�Q5�
~������	U@����}��<��a�K�������$���p+j
�;�Q�����[���p�k�[p_��������x�l	+��������:��>�_�`<-���</�\�#�;�������������p�mv�{�"���oU=�`��#�p���0��_1�)�X��F��\�R�y?�d����Es�q�jU��1�����)M�K�t�7���"��V4
��(�W���v�!��$�`��Z����v���V�k6�������j-c�\�w��$3n�Id����P<���,����pp'�?
�0���X�BQWs��+�t76[����������6�����3�e�~�����fa�W���(�0|f��*:(��?��V � �NM�����V����5h6;��h��mm�/K]���[�D�$��&:�*W2sD��]��
���(�?iQQu��2���$�&I��-�xQ��Q�����+~�n�k�Apc��&�6	�������%wM B�~2"[n���Lt�!ig��~9HO!�?�m�N�e]g�7b��A���$I^do�/-"%�

�HRk|+�d`�1l����kSU�/|�����n�CV,���������e�]I�P
"&��o]�s���t�*��U|�o���C`�*����xY��
���������e��j�Z~:l��D�z��N_t�?��,g�����>�)@�^���9CMZ�
��."���6_{>g�8<��8�_9wf�VB�Y�m�_��W]�����u�x�����"t[�R�P�Zu���e������
�A��YX�B�(`K�B��$_K�,oB��:�[Qu�MOw��f�m�������%�
k������M�1����S�_���`���k�auE������:��6*�[�������5����q\�������f+�mE;��N����nE�+!�������;��h�X@]�X�~�H<��1,�0���>5���6�:Z��B���v�'GX4\��\E����[����n�����pk�����Z�Z��R���{{�������_��}�H�$����%���
a����c-2�\�M���k6�3�$�/	w%N���Te��wXV�XW+�c/'�����#��?]����� ������%)�t��������k�����2���d��$U�)�n�U�!45�����mn���������i�/{��$UjF��#��w2���?_o�r����n���6J�Zm�K #k�v8"Nk[�9����Wp?_�#r��?':���o���H�
(��RS�i����C ?����
��L�`M
��������u��p��&�brf79�q|l1�����>�h�^]��������6p��6K.��x�a��h<
8�p����`/��A���'	HxC�|�1[�nN_qz����������-&��Zo��[�0�@"�u[L&�����vk��9����>������eu,hK�K�IV|�hL�
�m��_x&\>s�)7c��;��]}<�w.������B�<,���k��V��(W�g�@�,�1MB�����6{<X���Yc�
C>����Q���!%`=�)@�I�'��5nCAw(�'	�n��^LZI�<�_����9�����ST����!��RGK��p���4��_j�u������?Q���(�����|����I���9�R���)U�����.�Xe�8�(�-k�d����/N��W'oOUM3����?w�ON���]��V��Ak�4������:��P��� �z���
����Q�Z��v�DA[��w��a���j��-�}�j�/I@i�1V��Z���jo�h���V$F���Q�����(a`�J�R����[����Z�����n�����Vk=/����_�!�8������}���o�c��z����t��Bw�Q�]���^	5p��n�Q��S6��js�7�
+l=V��j������[`s�/��BT{��{��Cp��K���Ou,Gf(nS��JnUfH]�d8+[$qV��B��T���Ll�w�NK�T�Wvq���7~���j�-�+t�\Y�Z
���v�Y��"��&;�;���	|2v���P������3��C�kl������/���;05�\:I�.<�k�`��C2�����M��^�;1���� i�r�S|_b���fq^Ue���v�v�������N�.k��\�>����&��T_Yd'��*3�'�������h}�1<K6��}��Pt�
���oI�,���KG���|:6�E�Q�$:Z�������:GG��+��("����s�rr5	�|�SQ�(���d�
��}��6K�G���kX"��T��
}�\����9>��xlk*��s5��*���,IV�������~{sHj8�:Qk�
I]����[����H�!�O��tE�?��v�YL�/bBL.�����|��\����
%��OV>�����v��ZA��#���z{-�����m�M��-{���O�>i\�1��P�&�]���)Z�z�`W�R��xe�$Wo/��_��R"�Ir�l6��u�/���]|yX��4=�g��?��>���;m�Aw���@�i�X�?�� ���C��J����:�~4�5�;��7�����-`&��D��;���n�	E�.q�@��o�����5S�Y,U�!^�Uw�&�k2W�XZw�],!T����/��W�=%�q���%��B<���}����(P�H���i1U�L�\4S���63��b�������Ru�r��O���h-��?��mgo��xZ��1���Y	i$	�D��k�����/vU�2��8��id�m�	�^,`Vxwo���3oj�Vsb.�,R����V35d:�J��,ek 9��@e�����v]�����{|�����_��YM?���_DV9�d�;��*��U���*8�xr�}���=�w��h��U�Gd��B��h��XU���t�[�`��K�~1�l�WP���Gic-wP:����u:����A�Yv��^��-i�T��W�q��9+g������H8��UPD���N�eI�3��([Y�M�@�{�/�*��*�v����������n���6���+��^Ne"�7\;���_L`�������`)kVC�-�|����R�,�}��;����R�A�_8"wU�cr��Y	=��(�@I����X��
���q�����b^<��R�|;��Y��D�`a�`����3�J�����s�-��yD_(7���v�h���k�������kexaX�(��W���*=���^Q���=�P�;�;[�"~�87�Y6{	������Js�o!�1W�L����(��+�e��s)��I��=J���p 6`�~����;]��7���}A�Y��b��������u4�@���	'��"�q~K�bS�Up	�&*��%w�5�:��;�?�������]�(�k�W���w@h�#"[8��+�3�&@m+UDku��%V����^���}�9{4����V)��#6������m�FK����K�o��>����=��q� �����K���*��!���;8O1Z���#
V�HVS�V}ivKq��W[��Q'��;���I[��p�'*����\f$���R�:�/O�e�4Vj�����"t��I�'��m��(�?\M��`��=�[lI���%�Tc�����7��Y+�����6� �8��9D(`
��ya�.�nbVj"�;d�������Rq�'��Lf�.ed��%�}c���c@���a;��C��BME��nP�>�L�q<Ai�H����35�����Y����PL\�����'T����
��<f>�<X����@��}�%�[�'d������:�qq~tq�������������~	T��5��_��{��R:�XJ|3���5���l��y�9m2��������.s�rY��3��:�m�0�;~���Q�B�q��<)%	]
O�\r.-$+)�]d}��.,%%	x���k�P
P�;��$�:�T���7FXv��
�������h��5�1MJTx9s���\�r��)\:Y{c��Ko�3.��ky|����O'�'m��X��[�������	�Cm�����|�XZl�����Z���.�_����_����������6�om�������a�)��\Y����!��n/l��{������n��mv�4Z�H����L����+�A����S��Yu����X�W������Q4a#��D�B�-�XnmsO������Q���#�p����	��i��Y.��F�*O���ZO�c�|�%VN���.��~��1\�Y���r!X!u�8�Ea���8��6/�^5G����{A];\����
7n�a��_;�+�pye����;�'��� ���}<[����s�C�O��h>�����������M�u�5���������4P�b�_�+�7���8��5�y���1��2�u���Sa���`���i��t�������o�����Au<�[��Y�o*�������y�m�w0��0�hu����1��Q�r������N�u����:�|]3�Y��N��(u`<��e����n�,K���7��W'������������������k� ����`��F	��)g�FQ�Q��"_��9�My���GM��E��}������,����)n�H|f;��4�����L�����eE�h�I)K��;�M�al;�hIS���������?���_Q��W���J~q�L"`	��f7�<7���nq��1���<7���1��.��r�!l�Fq6��Fe���n�����w{�e�K<h���L�&����U�;�!`�1�c�4;�����M=p�������������X��9L6�2-qN/������P��DR+0��L��{�{k����9Nf��R��l�t�72�G�m�'/�6�e���}�7��z@�uD�k�!�}���{Z��+�0��(���d������d�<]T����y2i��3d��Rzr��Z:F�w�|�q��w����`+���kYA�^�4����,Tz�7�$���w����U����
{+�9j��f����dJN�i�]����5�Y������Y�=��J}���� pu�������9�d��4�3B���u�� ��M�;��K	�2_����=��9����:<��������G�����=WD��	�[P���FG���p42�
������i�WYV�jl��KI+���=0X�����YQFV3wI�)�����b�t���A��������:w�hwMn��Z�*p�$n���f~A��d����e=�;�'Z/����{}�r��J��`�7�f�B�%�t�L������V��6��bTX��!M�����.�o�=��f��SQ80=b�{4�������9*���N~�/�����t:�W8�R�m�����2���C��_�W�Tt2�����aNQ��"�_�<�*�_�$�W7-2;A���H�<�A�c[�9V���?���TZ��d�����5����ta��*�G��Tio�~��+s��}��d���@%�f��(X�z�z��o��"�Q����yi�?YUy���{��^��s5����7���&��<Uu=�*�E*�k� %��l�+���[ZB�f]�9�J�7���"�J��������$/����-(6~�s�*��q�F�X���de[P�	���'6���j'g�����+Y�C�7��Q��ny��@�6��t�Q6�U�WW�+L�w�Q�����jW��#���T������v��n5�-��z�����������X9u�H��0�O'`W��	��%;��N��L��-5�������
��������h
���^(�35��|T����oK�UI'rh��o�V�ln����v����Bvl%��&�jL��?���{��y��<�O���u:WU%���!0�a{3�7�Q���v;[���3����H�Y�?vH�HK�������9�����O[T�H
&��$�/���%�E����4������XT��:u����;(�M9����F�{�o^b�����'��tJ5�(�<��Y�4N��V�o��C#a4�xD����~�M�i�Q�������(LU�&yt�v������
��'������?(*d2�^$,�X��/v�X��
������/�d� �K���!���U)�1�/
��y�����O�2���0������J�P0��o�t�	uJX��o�s� ���O�h+�Y��<����[�7�f�����b����{k{g�%�#�Ik����T��Q%M���Z]��O���\����i8>����)��8������1�Q+���4�� ��A�A2C&b�FQ��u�b�}���(C'I0J&7QL(6M�Aav�>��ogin����d�������6�_��\K���d���h|d������uR3H���V��&;{{�����3����cfB��.u�pC;��y��W��G�6��<B�TE����J��J�������vP�����������#�_3H��vf��rFi�������Q� ����,�	��!���
2����"�����v�tN��j��g�h:�"]�iU�.;�no��_��������jY!�����Wug���6�~�!��d-O�d:2�`d�
s��F�.x��B�8-����G����k�'��!"�tNR�'��Ha:f��T-[Wz��V��,��o��G,X@��,�7O�~N���q/&W4���Y4��������X�d����+�!���D��9e~>T�q�x��4����T�����m���5D�������,�l��SiU�'��0�U�����Q�Ov�5*mX����.�xZ:��0i��0�����[�����*�AAZ�W��W�d.��E)����
��>����l�d����D�=���������e��din&tah:�����g��:�5���{�o*�S���k���u�����h��5�G���mG���5����9���v��)��&j����Q�s��b�jL�*��������]<�t������4f5���)��!s�AE{@���Qc""���om���_�l=���su��g����0�"=2����2�K����H0[F�� ��QR�����g21�����*|�/�=��)�(��(���vz8qj#��r���x8�Y��t\�(�Yv�j��wi��,��`X.����)�u�bf+�2�HZj�X����i(�(yE��b���g�	cu����8J��`������^�9�F����1����z0�>���l:��f��v����U�������!�jQ�9he| ������t�&����^uN�"J,�F��$�4�����~��n�w�dY��u���z�����65�b�x�P��Y�/�_�����n���z����V��y���v��B�B�o��Ap
32�n?T�bx��"m�7@�+��(���������_��(�{�&��3��Z6As�{�M����V���H��
L`��A�����V��%�m�+U���.��Y!l
���)���������2n��?n�I�6g��W������J��-���Fj}����������<F��iY�w(��b��H��Z��nsz�vK����]�����+[��ZeV_����K�>;�S�no����x)�p�"����	tp0GI�c��,o�vo
��X"b.�����}��p(aQ��>�KWFK��*�#hp�_�]xO��G�(a�-�aPw�w)4I$��,����%��,!����~�HKvk��������B�Z�\��1ywD�e�g�Z����z�M��FgA(�iy_t���
��F�H-�QV��`�^��1���J�:�����(9	'�q/J���������~�#�s$�4��3���.54�Q����Ac�k��������������pfA���1*��)���c�,bl26Y"�nA(�c�f�`A��]��fQ�DFS��Xu6[���p�i6������j�;�[��	�����������.�Y|
75.9�����)�����U�]��7�u�0�W��'5�#MX��^UA������[�[��^���?�o��Dy�����?[��O'���LX���k�����[A�s������������O�����$�N�1�r�����:|������C]���F)|���-�����l����Uo���J��M���e�J�y`���'@n�V
V�a
W�D�Yz'�L*�r���0����]f��	Hr���'@�<X���:,q��J*�f4������m6��o����������-�VtE29#+?�woNz9hB��������3��*�&%�p�A=�z{���yz�W����1��.�������`��?�e���!�g�*-��i�^�������&�8�O0���4����:J)1����WH���w0?V���K|�E��>TMH{���#��'���r,U��5�H�5� &����Y�7�����"���T�/lC�2��5�B8G��}�je7P�����k��514[`G����RM�g�X�.���i�X����|$����l��O�	S�j�^_g�c�~K��(�6�������j/M}s�e�V�k������Y��5�����-H+��5I���+��E����!�c$q�� 9��8%�e����RZ����=��y�E$�����e���w��sy�[��8<./���c����,��<��	�n��i��Fb�b��JZ�SU�;��j������q�y�����	������nA����"S)�87�fg�Vv�
�)���"������PKi�g����� m���;UG��n��q9��H�7(�_����������J�d'���c�a���b�M%��W����������j������=H�d�}R�/�o1�o�Si��6��^{��l�G���N���$���[�p�*I�.RIF����_}�_��i*��:�(SX3���G��ho{����T���������'Wd����A3�OE���A�DUpX����V�(�(���y�4L3`pe��_�"��.[�6�L��?��q�1����X�9�!3�Lu�U�V��;4�b'm��q��:.�3[f���d�o�����E�� ��z�����X�}j�����'@V5��	�A����;��Ng����Z�%��
n���r|���5���J��������X������m/eJS���LK� ����;P]�=l��,��	&�g YD������{t�c{���� ��*x@��X�z�9��6if�������G���G�4������7���wZ��*,.��e�����*�K��a,���4��-W�`�d1f�dI�31DOpqGuS_Z���J��mI���X���[��f�4�wQJ-�Q��R����Z�#��<]�$UI�����K^U���m����������N{��v�
Q��\D���by��q�����2�O�v���M�Or?�-�L�x����0%/%�x����������V������N�nomFsbV�+�teU����A$�'���K����u�����u�6T����,��e;��g(~{���R}�p
��~
�o5�}�!�
�X�d�8��q���4��,N�����)���U�����_9��
���9���\����
2e�E�L(��j�M%kt+\&wX�tBU�^�_'��=�l�~T�'M-e�o�bO��7�9���\q;�kk���N�)��5\�*=��W*a����{����T"�<	����r~��t�1L�g
�uco������,�{�V�"�4-����&m�j4�x������`������:$��:7�
�O.��lc�q]\:��&Xo����q���qt_���]Pm��d��w�~�"J��L��D�^��d���H��{A�x��d	z��>'�v���$.?Q����xx�M��/d���Y��V	��h�Cx2�:���!u���k<`���������H���?+G��:>�F�?!��y��k���t�b|�P\$�t���z�����A�r�`����\RC%������V���l�����v�]}1�A*��e���3"��� �2����x ����d�x����������%���"�U���A���`�2oe��
+�F6����n/h�u���OrL]���|4�c�>�j�Q&���?fc���e�O�)���\ ��IBq�5��F�Y4�7�;<�`����KQT��m	����afKZ��9��0��l�`�2��]�c�.���������(�U�K��,�b1����6�vk���ND�:�`:D&�ybZg(i��l�$3r8�W�bwHLT#������0
3�<
�s�{x�'G�}�57/Q��i��b�7�i��#._~0�~����d������gu�tD��[g*0g7�7��ue�WXuq7��i�dE�r^}�X�����V���+��ilm���oZ�6�������%W�4-l�:8�
W��|��tE��p��s"���q��pp|HM��]K���-Z����;������l�����q�9����~�����c���,m��3�Z�?R���<��C,���V�.s���Q��;�/[��\�����fg����xgww�;��[���a��p�z;�������7��t)��2y����Q�q=D���#��Y8�(���L;�8i/+b�yNvV���(����+�������k�`��i�YZ����v�wZ��;��YG�n�1)k�+P��rO��bgu)%�-\�u�������
��4e���~Fn�$��v2]
��`�R�I��=��&��&`�����X�e�����y�2�9i�U����P�<�m7���p���n������;]\���'��a}�����������������M�c ���AO��5��M��O$���������}�Z��mq����	bo��.����dR�h[����|�.��j��&��Y�<k��(���,u����}*�c&���
�����{V�.JSj��/�Z�;{��[<D����I�������j^S}"���,I�@��Hkj�&e�r�eC`5@x�p�2��SN���M�v67�[-���-i��9�#cD�v
e�S��ipG������#�&jB�K?��7�&�}���=[�'�2��mxE��������U�*cx&��]|2��<���1�\H���1Cm4�0!6�3C���,��������m���b���D�E�8�dQ�!
t�H�$��	p���V��>��?5|��,�����a��$���P#�n��[�\'*�(3��2�F�@��0���eq�\�c���������=���r!��K�����d�
e�G�p�M�5�+�<Yn�1n��q8�fq�n� ��?K�+����J%���P����
�W
{�	�Q�����
S|��wl��,��*R�#�V�E�����Ud;v�Y����9	(�J���2�h0uo������o<���Up�v�G3�9��o�E8���(x'������q���^B�Ird��dI�l��,�n�^�/��_*�J��r�����ln�7��������Vkg��X�Y�H2�2��c��h��=�jL���XKN�T�%�^���~��1bY$��Q^�|����6�kNpp5���.���|;f���x����h��n�3��(�<���`������E��O?�=���rUZ:���yy���q�;�=���s�(	]a C����U�/�S��V����`wjSrbs|��{tq~u}yxz~�=����g]I-n�&���v^�i�Op��
���)	_d���-;�%e����/�����-/)�T�I��y��~WO}�)��	u��dr���){�}���,������..���������L��n,�1���/)S���Cr�����W��>�����&��5���ob.���&:�[�i�F��#�����J|=
���Z�It�,�c��/�Y�e��7B�j|&H�w���\,�$�A����H��*��qj�&�%��K+����wAH��w�8�TP�D+�]�8���I5t+W���2/F��\S|����������^���;�� �����������������(
�m�|��8&P}WKv�s,K��6�!_32��;�5�����I@�wI������=�7��z����itZ��:����4���Y�f���Qp��&S
��&Mf�����etC�����'��(�0�#*�Q��$��-��{�e~�l�������������OP%1�]�B��$G�A��Xit�-~�[�������7�J�d��^�+D3qT|�(����@
���Y����\p�u��'��.O�_���������;9j��O
Z����6O�->��,&?�=���^�]w��<9<����8#$t�l���[MS2���Y�]�������8=?���>�>�����)�x]Z~utx^R�`~9���%��.8��:}s^�V>A��*H�o��u����{��������B��+�K\�����"b��7��Q�"�ru����������Y�O�2����*Z��
..+a����EH��������L��7����u��JE-�3T�ty�����;�<|�E�P(�]=7{wxu������;n*��(�N��eQ���������?�mmlJ?�BV~$#��f�_���|~��������Og'����\]�\Zs9>��>=�p����9-9#�3D���k��_^��__�vN�����K�N���GoagO����_��[�������Gs�NO���N�N�sIe����������/N9����5��N������v����������g��B&Z�r"�6����N���sh� =6�0k�xp��-."�|����t���g�6�y�����x����
�:�n����2X�wg�����=�>����
������;�nts����_����/_���t�U�%c�&�7(7?���5����&LC�.fV��A�A�Ta��^Lp�����2����RN�fQ�������IJ�6pl�M�jbOxw{��Cq=T��i7_g��K�mJ�Z2�;D��B�9n�= a�R G{����-RBL���"������(A�DR��&,�9���������'�c�z^KL���z��9!&�� ����h�$���m<^?E���:w�VK`��$j
v�z�9����Z�s��m��LZCelWG�e�a`�B��R2c���
�3	`���7�S�{�<�`�(�}
�)/g��/a������/�����uay<FsTx���m��$�B?X�
s���c,��1�X=S��<�|zMi���������rQx�&D�z';y��yw���4����+I7s�D���C�C�
(:�
�A��m���}��#�3�\��CL2�1��1�qY[{�FA_N�PW�J"S��D8zv�3�lw	(\�YSN�M)w�O47�=be)T#d����ie�	fG���x�-�[���%Y�����E8�J�^f�b��P)�"�	��ED�����"J���6������V(6��0�D Kf�j �Z,�^8]]�����Z%=���&��;�'K��*qj��	#A'F���4���q
��Ov�k��0n�k�2�<���mD���V!�x���/F�m�x����
~��z��RS��AMq4��	L����8@�JK���K����=R^j,)���h����7h�����j����t3�}�_���~_�8s-�e�����,�$g"�>��0�C��!�B	�a}���H��Q�|����Z�����T4!������$��M�>[M����p���\��L����������i�q��l+p.[�3��#|�����@.$��|�A���L����k��Y�w..�#�W��P������������)���8�q<�_������w��
�������<���kdg��b��:��������fnfP���+2���x�S���w���[6���1i�)�D�������h7�c�U�5��4~�~���r���M)��72vG@�3�=J&��4�����>��4	UOj_M)p#^1�D��[$�@g��������-e���8����rwt5�����g{$P�W�u����qG��pC��MJ����_2�X����6�����4WV8S���Le�1�fk�a�M���m��8F4�������[�9Bt�{uc�MSi<x,�bW�hh7�-�7}w���"��;�;�4L���!�T�tZU)<BLh*v��D�Q7W8�o�R��[-�)f5��U�;�&w����(���_�����J1!�CzY��*�c,!\�l���HW��_�~<>}�WS��IOoc���,�?M��s��"T�\��?�]�I�������SI�PA��f��3��P
�����b���BL�������)�������)�4���2�'��ig�i��D�B�L�i}��b���}��Z����$?a1L!�q4/=�����X�E����h]���9�������*z�v���BA^c���h������2�#��3�-G��S�-Oi�=����H��Y��8���	�k��~����0Z�qHS,!����g�	}Xb�\-���O��,��e��XOq1,����h�ca���P�q}Dk~�=�o�	��oO���$��>9e9{$�Y�+�y,���x�?�C�����h�8,F��GBH�Gq�_���/���S�EO2gepN���$o�G@x#�����r�=����|�>>�b0<<
����&�x9��,��R7�Rk�A�������**"�pk��
z�fs���7�������\P�vU5���Woo��=�� ������U��������71��+�n����#9	��~#-e{�������z���'pj'�t�����P�����=�A���2XU���/1�{�<=�����U�A-����_%�3��g����������������Wn�>���\]�]\��A.�\*bd�����"f�)�IkV���d���/������;�1��}m�����,+�Q�5��]��+��p���CM���Ay�= D�Tk|^�5l({��t���I{��;�\C�p��nn�;;�f;��i��;%q�AZ@'�yO��y�}�h���G6P�����&���I�5�j6��b'�{4��dY@
"���#}�d�r��L��
9WR�o$C��m�;��0�������Yc�V{�i�?�l��AU�u���@���6����&%%
84��OI��4%����\��$c�coq�N��+t�b�������&������^�oGk�&�Q��fy���|6���S���/�Z��������L	X���=x6~8��: �4_J�(��������A`HO�~7/p0���
�x����[��6U�
+A��r���r��"�����h({��0j/O�h����)��������%�eW�w����
�^�������� ��,^��d�:�~��K'U����T�p����Uj
�9*O��Z����CyW%	�uS���H��0�Y�o����h9�b�Xp�B.u�K��!*������a!��KPy������oT�a�B-1���-G��XC>V�����C
3k �	F�1w6�������������mgI�b�h����'����R�z��p��ex&�{I��QN�i��-VvP^�����'� ���������Jr������5���lh��Q$b({
���|��a��|t��<�^_^�_��������J�����()�	������Rj��/����9���w�%���2�*�w�CP�!nB
F�/yw��A�;�����:�sTqf�����G�����IkB�+y����"�����%������ g�L��@�D�UA"lb��[I�$��Y<hD@��(%K}o;�svt��>�)J��pF+���]���@���MeZ3�Y52������Bl�B��|]4�&i��K����R��������
����m��� ��]���@.���raP���N=����G��8�l/����%�\�e��O+��-��vX�'��3H �?N���f����P.���ZE���0����Q�
��$���d�t�WMB]a��a
���M�LG�R�
�C��%/+j3������n(b��Dj����Z5U+�{�����������<pTdfir{�����@%E�``���93�0�����������	Y�-��!�Iv��2��4��l�3I|�T$^�Ej���ZN�0���u>�{,���~e��.j��(��8���j;��<������_|i���$clR0jr�8��\���Ez��F�]�x�I����E�SYIG��#�9�q�K-�{��O
����t4����+��gs���W��w$E�'R��������8ssrH]�����|px�3��,��.�c�����$�q[����M�7�a�Sd�7����r��%}$fK�ja�SI��u����p��l�a���7.!�s����I��B���-�p-��YS��V��*~z*M���Q�-���V�yx��&����h,��������{I������F*g2�5��p�Td��^��!]���j�m�wZ�;�f��mu���Z%����X��P�V��?H�=O��*���2�����48���0�[��C��X��rm�Xj�-��?M[�7���b��	ER��3L'Au0&�Q��m*�������0�N��U�9/�o�1���4+�fd�_h�OF��D��RP=���{#�F�{K)d\#�F$.&�o��2����St�1�����4����xfdy��au�.Y�$�����/�r�'V�����ggu"Iw ���#����S8�y?��y�t��,h���/h}�P�u1�����o
X>�_w���)�N)������*���8=������_��
���R<k�r��������'��f
��8X�$���/X�fT�q-r`����]�(��#t�;W�$����t��U��2�����A��o|4(�����}<��S�R��������S4�)��n����6��6�t���Q����kT@���pLQ�D������A��{;|�����,D�����
&���:�Aw���_����i�f,��w�@�d��= H�p#������1ND�v�^DV������T��U�5hr���uxC�3Nrr�]����	>�������������?����_������� Dz1d�����l{��5���o��B�������.��Z�E��E[hz�aj���P����@'�;^
����}0|^W�cF�����"!����Mt���`U����G�7s:�b���'d���|`�+te�,:j�n���<+G��=z�r��[qp�1t��P!9s��a��Y>H�&MyK���1���V}Y��;����V��~��[pP��m�{���xZ.j���?O�
HF>+a����\	z2��h��5����5[�(�J��d��#����ei�����v9�WF���J��l�P�U��/l��������$�@���L(-������F�����|f��l�XzT*�Z��F�����\���D�S0z�0s����R�6@�����X�VF��'�6��,X"Q9I��[S��L�s����u�Dn��Q�,@���4�AGd�m��q{�����cg�/��M�1?~���(��A����\��Td�h���s+<���.����V�c�QW��BRN�^*D�&�(E��.��H;���{��5���W0�������Rt�����k*�J]Q�p���P�`���Xf��cz����w��1�\����)�C)7� �$h����sfX�$�H'4d+K��X�(*��2`��r��*�����&J��:&��X2q�{	�oE��-�e8�����ha�F2�� ry���U����@�c/W�W��}L���SJM>���!7i����y������KNE��,���1�Da�\���)��>B���E0����U��U�])�l��F���q�Z���&�1`y�����`\~3ao�0_�
kn1
��� ����9i2����S�-g,����6p�����@�$�Z�K�����d
�WUA�d%	�P���Y83�����6��\)�_`�QW(0����A}S����^�K�S�}�����c^�����0����	s��L��D����S��D]�D#>�������xfC��.�F�C�HX���D�l�F~b��-F��W\*-*{T[�1��~��qo��6I&�����ZgJiqZ�c�7S+K5�������(sj�G���ZX�5�AT=!�q��D<[���6��d1%t<�����"0�x(�{�`�M'�����%��pm�J$}C�PK��$(�O�f�IM,�hO���T	�`+������a&]��r/���I����on1�;�c����^1d���ZJ��n@�P�A$�|	(��{e ��AB�����p��U�/�7�C������.g������V���@���o@�������E����`%_�8���P�����Av�l��V��H��b���6J���A����>����|��~�	�������W�o<^L����^��#����,��x���m���~��bz��bz���b��'�[0XLo���*��o�&��x��~��b����j�������i������XY�VR!����o�(��XIL_Y{��~c1�k�W���p'��W@XM ��D���	����$������FQ �1G �Q.����o��}+h[ ��������C.�Q���2���)��h��	��!)�]�b�<aNl�EN��[���L.o�
����^�H�.�y)���[����M��}V���)�[��D�O�	&����#N\�Z��}�����1�+U��c-�����%�M(��3P6\�N�r��}�w�]����wO���I�U>J��D�|�Y�K`�T-B�F�������/�
��f��KE�S�
Gq�����^�+|@��o�\Q�]�w��Q|<,�����e�~'e��5��kv���c�p�0]����D�����m6���6��jEd����o�.B�6"rJPq��/�����H����$�%����"d,�%K]a���j�O���##��$�2�Av�
��@v���(Gt����BzY���N���ll����[�j��`R�]��/[H�E�R��-���N���-�%�c���g�5�����W���YK�������7�R����F'(���O�T'A.nnh��`&~�������lmlWg!�J�I�;�,j�f7�n�'pe��.zl�����G��@���X���22m&�+�KaB�"�z@��d����Q?�}����b�L�����(�A�����@{WT[N�!��cxHgY��(e��y���<d{+������F�����U���S~n�0`d�5-�V5;~��H0�jMb�m����Q����6E6�F�V����f(��t-���l;��0�� ����q6E�H��plP�%��&h�~�_�I�JQ�kr�R�=J�R�����<k��=P�f�������R3������5
��rc%Wu��,���Vf�9�c�F������*k�WZv����J
:S,�D�hX��u����[F0@�[�|iH�X����56R�/����.����|.��6�����*���tVT���-9�2�&����7�z�W���L�S&��]���b����I"���M8k]6s\�RY.�g����������(�/_����X��7P�jon&� ���������4��S�u�/���R������UyV#-��g�����9S4��Q*�7F�5�&���/v���;�+~l�KAKF��I^N�h�
.�����$.P��Q��Y���}��a�]���]��[\�&���
���Jx}%ZS��K�MQE,��p��k��	U*�\{����+�+�CU�������KEf�����GS:l�����W�O19�gq�'uL��p:����Y�xF�����|�K�	_��Lt��K4hp���&a�Z�,d��Y����M�D�Y4:p���P2\f���|1�r���|8�0����!h��{B�o���oG^�	���������b�;Y��
M$ld��Pn>}�g��444!���� �*/���L����S7�����������S����S	�!}��FQ B���n��
(�?m�R7y�]��O��P�I��'�u�����y����lk("��^5I3�}���y���(���t�����:i��|�5'������F�y5+3�f����N�	�;�jsJeA�A R�����m��l�+]���������,��._3	���1��8D�%%����9s-nFM�P��p��!(��T�Et�_o��O���$����������WI��I>R���������^�fqU�;��k�.�����f��6��q���������h���|�F��m��I�n��u;��!o������u9�B���ti�p��]V��EF}��Tjs��9�t�x��H�Ij���!
������yp�j���M�1����m���Q�t��K�3@�uv-��n�77����V���ckhyfp� %H#�<2HD��(?�������)^z��~
��j�q��G4���f�=tc���i����]>������m
"]�@B��(Cg�W�a����i��4
�u`m��;�1n��_�N���e�R-[�����T�6������8n���p�m��bgw�bw�g��T������V���:;LP&�P�����8�1�qH���-#��}I{�*bO�����~2�����ko����u����,��b�������)J� X���P��M�@���S�*�'�_z��u�j�0�E��
.��9�'6�-e�@x�����.(d)�����<y�%�C��
i>]2Y1����g�$����j�A�`�CR%$[�tG+��E����f����8<�x�����
i�}����PZ������J�z�����/������>h3�`<tC������Ig��X-���/_�VT�j)�|_F��y(F#f|XjZ���Vv���:>�|��y2�TH+�V�3r�$)��L��,43a�L3��oyr����5x��]C�f�8��mbenK��mg��sX���/��iv�l�����C�N�g����n��d7�kM��?�tr�$}���>���E!m!�d����D �;�:�M�<~b�������IZ,�;���-��O{�^������gL��`��r���t����|f;^�I��,q��^^���u�2��/K-�f����m�7��k�*7�j�8��.]*�~DfwE��������zX�%����n�������Q��|�D�0a{ �+)�3k�R�y�E��q��-��qV�(>�vOzNC'=1�,�-�(����,
q=n��~2b�J���U��A�-�]�fS�>��,x<�^:�,��S��?�1��m�C����7����f���v�)������:m�@U� k��v�B��{t��5V�Q�0	����^����Q<�Zq�~��9�~}����3�e���1��x������&�n��J)�{�@N#�&���Wx�d����
�32&��0i+�{�����Y��"-��g� �l���Q����Gz]���[�M/�nn���K�)z]k~�kdQ�����j�L�1�IwOI�y��Ww�����4�){K���F �
�~:9����5
}v��J>�u�{#7e�ev(�U���t���~�����v��
2K���1#/���(�|�T����/�)+�H�9��;�S�
O�NR05�u�d������$��s!G
�,�(d�x5f�
>J�����qx~�(2�~2e��Y�q���8�!	����Y����qq�����+���4@���M�40��B4b�hS������R���G��a�"��(����t��:I�)�"A�.^-r�yy)��9��D����uR����7&�6����=h��!��������y���N<O�Py}M1������C������2�
|VA�])����$����f���|�(��Yg��Z��:�a% "+F7�������,IQ��Wq?G����u����b��ll>�l��h���R��'\������&���N7)�o�O�os�����Z��MR��;�e�����zSL�W�,��'\��7�B�k�v}z�Ar�S�������b�	f�E�"g�
p����l���n���E����s�rAr��<���
Or-
R���;_X��9�1s��]��*���`lu:��!I?ql��Ex��Q|3y�����d�DV��iHet�	���4k��]���3��_u��C�W\K?�OB-�P�^��9�4��?^�C�R�@�������SN������+�x�3��k�%��s��X(<	:b\����atReD�x���,��@`<�$6j��?���]����n�������L�S/���2�����?�~[���T���?����
�����.�J�����l
�-�_z`[lv����mU�$�bNFb��0/g����������a��l�6���A{7\��aQ�;OC{�S�F��m,�0:��+t������U���2�\�'WW�������Ip~q|r���	b��F�GMf;��e���h��R��i�mV�W;�1`H���
1��K#q�������Rw�,�{��(_A��UN�O�LJ�K3���:Q��l���a2�pt��x$y��t�p���-��O��-`Q4P���a��q��2Y��8�L����MSR�e
W���R�F2|j���zS�c�y�5mPZ��=�����N������!�?�8�p{z..�Yn����<���-k@����Y��`�<�$�T�p�obM��'P�K����V��"�R��{�l��.��j�8��=Q5��[.qD��8R4^�R�K]�W�W`F��/�Q��Z_��F����n���j6�;p�F��;�j�jC(V�+���6Y���9��N��i���w7�L(03�i��|���������F}�	�FA{u��qv�$�,��_0LW������J�j��e�Q#���:;��bPP����0]�rSJ
WU����[	���]���cD��:�8��:�J2�=�.��s���Gd�S9��R�P�:u(�
!]-�5���p�$yM���X����pm�;��xP:���7��g� �����l��[�N�36��^'�w��aI��J0�$TVa��6s��/�5�+��������"M�����@G������F�r����W�������%��eZ,B�.��tb�;>��d�5�~���	!�,��A�xR�/�@�V���>�3���{�:��Z���d}��%�m�D�_��(�`�%�d�8:,�-�%�
r9n�3Np���,�F3b~�m�����=&�Q�R*�.���;��\���E���l�)=!�D.�s��4d������Dr�gR��I�=�H���!�8�"��(��^'�6FT!�����N��p����A���0`�>�:�^	l
����K�9�Z���8�S01s����u��_E��6�/�����ap���6�y��D%E��������v�������[a�H3DbAE]K2L�W'g'G��C�dG�W'��Wg@Q��s�.P�m����;9�`��|i?����_7��3q������I����1��!Ih��q��?�s;��9�3�;��y0�J����pwcU`��s�0��%����Q�nGE	���p�2�k�c�N��qa��������f����K�����>����iq��L��k'u����0��I�6�H�6�!g1r�d����7a�R�n�w�������p�?��og����
{�����@m�]�;����j�}������Wp�rz��8�_
G�5I��[XE0xskow�3�5��;�`go���`�<P��F1�vI���������������M�W{	�Ws�����sa�<��ptN^���'���o��6d"�j�(��m�X�bTz��~k�b
K�rr�l�����Ve���4h(������6#x�i�;�V������z��N{g�9�������,|���
#60Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#53)
Re: WIP: Faster Expression Processing v4

On 2017-03-14 14:19:18 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-14 08:44:24 -0300, Alvaro Herrera wrote:

It would be good to have someone at least read it before pushing, but
I don't think anyone other than you has done so.

I'd love for somebody else
to look through it, I tried asking multiple times... Seems like
threatening a commit is the best way to get it :P

I'm willing to look

Cool.

but the last few messages make it sound like you're not all that close
to a finished version.

Hm, doesn't seem that far off to me - the latest few rounds of changes
have been largely polishing (naming, fighting w/ pgindent, language
polishing in comments, etc), otherwise I've basically just added a few
lines of optimization after doing more profiling.

Greetings,

Andres Freund

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

#61Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#59)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

[ new patch versions ]

About to leave, but I had time to read 0003:

It seems bizarre that you chose to spell the new configure symbol as
HAVE__COMPUTED_GOTO rather than HAVE_COMPUTED_GOTO, especially so
when the comment for PGAC_C_COMPUTED_GOTO alleges it's the latter.
Also, being a neatnik, I would insert both the definition and the
call of that macro between PGAC_C_BUILTIN_UNREACHABLE and
PGAC_C_VA_ARGS, keeping it more or less in alphabetical order.

regards, tom lane

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

#62Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#61)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-14 19:34:12 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

[ new patch versions ]

About to leave, but I had time to read 0003:

It seems bizarre that you chose to spell the new configure symbol as
HAVE__COMPUTED_GOTO rather than HAVE_COMPUTED_GOTO

I went back-and-forth about this a number of times. We have a bunch of
symbols defined with HAVE__ as a prefix (and some with HAVE_GCC__) - and
more of the nearby code seems to use __ rather than _. I don't really
know why we started doing that, but it's far from new..

Any idea why we introduce __ stuff?

Also, being a neatnik, I would insert both the definition and the
call of that macro between PGAC_C_BUILTIN_UNREACHABLE and
PGAC_C_VA_ARGS, keeping it more or less in alphabetical order.

That works for me.

Thanks,

Andres

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

#63Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Andres Freund (#62)
Re: WIP: Faster Expression Processing v4

On 3/14/17 19:40, Andres Freund wrote:

Any idea why we introduce __ stuff?

Because the symbols start with an underscore:

/* Define to 1 if your compiler understands _Static_assert. */
#undef HAVE__STATIC_ASSERT

There is apparently some inconsistency when symbols start with more than
one underscore.

But we don't need to introduce underscores that are not there.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#64Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#63)
Re: WIP: Faster Expression Processing v4

On 2017-03-14 23:10:25 -0400, Peter Eisentraut wrote:

On 3/14/17 19:40, Andres Freund wrote:

Any idea why we introduce __ stuff?

Because the symbols start with an underscore:

/* Define to 1 if your compiler understands _Static_assert. */
#undef HAVE__STATIC_ASSERT

Oh, I guess that makes some sense. But we do so very inconsistently,
given we only keep the leading underscores not the trailing ones (see
HAVE__VA_ARGS, HAVE_FUNCNAME__FUNC).

But we don't need to introduce underscores that are not there.

Indeed. Don't want to repost for just this, but consider it adapted
(and moved as requested by Tom).

- Andres

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

#65Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#62)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-14 19:34:12 -0400, Tom Lane wrote:

It seems bizarre that you chose to spell the new configure symbol as
HAVE__COMPUTED_GOTO rather than HAVE_COMPUTED_GOTO

I went back-and-forth about this a number of times. We have a bunch of
symbols defined with HAVE__ as a prefix (and some with HAVE_GCC__) - and
more of the nearby code seems to use __ rather than _. I don't really
know why we started doing that, but it's far from new..

Any idea why we introduce __ stuff?

The nearby stuff is describing features that have a specific name that
includes a leading underscore or two, like __builtin_unreachable().
That doesn't seem to apply here, so I wouldn't add extra underscores.

regards, tom lane

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

#66Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#59)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

[ latest patches ]

I looked through 0002-Make-get_last_attnums-more-generic.patch.
Although it seems relatively unobjectionable on its own, I'm not
convinced that it's really useful to try to split it out like this.
I see that 0004 removes the only call of ExecGetLastAttnums (the
one in ExecBuildProjectionInfo) and then adds a single call in
ExecInitExprSlots which is in execExpr.c. To me, the only reason
ExecGetLastAttnums nee get_last_attnums is in execUtils.c in the first
place is that it is a private subroutine of ExecBuildProjectionInfo.
After these changes, it might as well be a private subroutine of
ExecInitExprSlots. I'm suspicious of turning it into a globally
accessible function as you've done here, because I doubt that it is of
global use --- in particular, the fact that it doesn't deal specially
with INDEX_VAR Vars seems rather specific to this one use-case.

So for my money, you should drop 0002 altogether and just have 0004
remove get_last_attnums() from execUtils.c and stick it into
execExpr.c. I suppose you still need the LastAttnumInfo API change
so as to decouple it from ProjectionInfo, but that's minor.

regards, tom lane

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

#67Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#66)
Re: WIP: Faster Expression Processing v4

On March 15, 2017 12:33:28 PM PDT, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@anarazel.de> writes:

[ latest patches ]

I looked through 0002-Make-get_last_attnums-more-generic.patch.

So for my money, you should drop 0002 altogether and just have 0004
remove get_last_attnums() from execUtils.c and stick it into
execExpr.c. I suppose you still need the LastAttnumInfo API change
so as to decouple it from ProjectionInfo, but that's minor.

I think it's quite useful in other places too, we do repeated slot-getattrs in a bunch of places in the executor, and it's noticeable performance wise.

Andres
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

#68Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#67)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On March 15, 2017 12:33:28 PM PDT, Tom Lane <tgl@sss.pgh.pa.us> wrote:

So for my money, you should drop 0002 altogether and just have 0004
remove get_last_attnums() from execUtils.c and stick it into
execExpr.c. I suppose you still need the LastAttnumInfo API change
so as to decouple it from ProjectionInfo, but that's minor.

I think it's quite useful in other places too, we do repeated slot-getattrs in a bunch of places in the executor, and it's noticeable performance wise.

Color me dubious. Which specific other places have you got in mind, and
do they have expression trees at hand that would tell them which columns
they really need to pull out?

regards, tom lane

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

#69Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#68)
Re: WIP: Faster Expression Processing v4

On 2017-03-15 15:41:22 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On March 15, 2017 12:33:28 PM PDT, Tom Lane <tgl@sss.pgh.pa.us> wrote:

So for my money, you should drop 0002 altogether and just have 0004
remove get_last_attnums() from execUtils.c and stick it into
execExpr.c. I suppose you still need the LastAttnumInfo API change
so as to decouple it from ProjectionInfo, but that's minor.

I think it's quite useful in other places too, we do repeated slot-getattrs in a bunch of places in the executor, and it's noticeable performance wise.

Color me dubious. Which specific other places have you got in mind, and
do they have expression trees at hand that would tell them which columns
they really need to pull out?

I was thinking of execGrouping.c's execTuplesMatch(),
TupleHashTableHash() (and unequal, but doubt that matters
performancewise). There's also nodeHash.c's ExecHashGetValue(), but I
think that'd possibly better fixed differently.

Greetings,

Andres Freund

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

#70Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#69)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 15:41:22 -0400, Tom Lane wrote:

Color me dubious. Which specific other places have you got in mind, and
do they have expression trees at hand that would tell them which columns
they really need to pull out?

I was thinking of execGrouping.c's execTuplesMatch(),
TupleHashTableHash() (and unequal, but doubt that matters
performancewise). There's also nodeHash.c's ExecHashGetValue(), but I
think that'd possibly better fixed differently.

The execGrouping.c functions don't have access to an expression tree
instructing them which columns to pull out of the tuple, so I fail to see
how get_last_attnums() would be of any use to them. As for
ExecHashGetHashValue, it's most likely going to be working from virtual
tuples passed up to the join, which won't benefit from predetermination
of the last column to be accessed. The tuple-deconstruction would have
happened while projecting in the scan node below.

regards, tom lane

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

#71Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#70)
Re: WIP: Faster Expression Processing v4

On 2017-03-15 16:07:14 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 15:41:22 -0400, Tom Lane wrote:

Color me dubious. Which specific other places have you got in mind, and
do they have expression trees at hand that would tell them which columns
they really need to pull out?

I was thinking of execGrouping.c's execTuplesMatch(),
TupleHashTableHash() (and unequal, but doubt that matters
performancewise). There's also nodeHash.c's ExecHashGetValue(), but I
think that'd possibly better fixed differently.

The execGrouping.c functions don't have access to an expression tree
instructing them which columns to pull out of the tuple, so I fail to see
how get_last_attnums() would be of any use to them.

I presume most of the callers do. We'd have to change the API somewhat,
unless we just have a small loop in execTuplesMatch() determining the
biggest column index (which might be worthwhile / acceptable).
TupleHashTableHash() should be able to have that pre-computed in
BuildTupleHashTable(). Might be more viable to go that way.

As for ExecHashGetHashValue, it's most likely going to be working from
virtual tuples passed up to the join, which won't benefit from
predetermination of the last column to be accessed. The
tuple-deconstruction would have happened while projecting in the scan
node below.

I think the physical tuple stuff commonly thwarts that argument? On
master for tpch's Q5 you can e.g. see the following profile (master):

+   29.38%  postgres  postgres          [.] ExecScanHashBucket
+   16.72%  postgres  postgres          [.] slot_getattr
+    5.51%  postgres  postgres          [.] heap_getnext
-    5.50%  postgres  postgres          [.] slot_deform_tuple
   - 98.07% slot_deform_tuple
      - 85.98% slot_getattr
         - 96.59% ExecHashGetHashValue
            - ExecHashJoin
               - ExecProcNode
                  + 85.12% ExecHashJoin
                  + 14.88% MultiExecHash
         + 3.41% ExecMakeFunctionResultNoSets
      + 14.02% slot_getsomeattrs
   + 1.58% ExecEvalScalarVarFast

I.e. nearly all calls for slot_deform_tuple are from slot_getattrs in
ExecHashGetHashValue(). And nearly all the time in slot_getattr is
spent on code only executed for actual tuples:

│ if (tuple == NULL) /* internal error */
0.18 │ test %rax,%rax
│ ↓ je 223
│ *
│ * (We have to check this separately because of various inheritance and
│ * table-alteration scenarios: the tuple could be either longer or shorter
│ * than the tupdesc.)
│ */
│ tup = tuple->t_data;
0.47 │ mov 0x10(%rax),%rsi
│ if (attnum > HeapTupleHeaderGetNatts(tup))
75.42 │ movzwl 0x12(%rsi),%eax
0.70 │ and $0x7ff,%eax
0.47 │ cmp %eax,%ebx
│ ↓ jg e8

- Andres

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

#72Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#71)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 16:07:14 -0400, Tom Lane wrote:

As for ExecHashGetHashValue, it's most likely going to be working from
virtual tuples passed up to the join, which won't benefit from
predetermination of the last column to be accessed. The
tuple-deconstruction would have happened while projecting in the scan
node below.

I think the physical tuple stuff commonly thwarts that argument? On
master for tpch's Q5 you can e.g. see the following profile (master):

Hmmm ... I think you're mistaken in fingering the physical-tuple
optimization per se, but maybe skipping ExecProject at the scan level
would cause this result?

I've thought for some time that it was dumb to have the executor
reverse-engineering this info at plan startup anyway. We could make the
planner mark each table scan node with the highest column number that the
plan will access, and use that to drive a slot_getsomeattrs call in
advance of any access to tuple contents.

regards, tom lane

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

#73Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#72)
Re: WIP: Faster Expression Processing v4

On 2017-03-15 17:33:46 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 16:07:14 -0400, Tom Lane wrote:

As for ExecHashGetHashValue, it's most likely going to be working from
virtual tuples passed up to the join, which won't benefit from
predetermination of the last column to be accessed. The
tuple-deconstruction would have happened while projecting in the scan
node below.

I think the physical tuple stuff commonly thwarts that argument? On
master for tpch's Q5 you can e.g. see the following profile (master):

Hmmm ... I think you're mistaken in fingering the physical-tuple
optimization per se, but maybe skipping ExecProject at the scan level
would cause this result?

I think those are often related (i.e. we replace a smaller targetlist
with a "physical" one, which then allows to skip ExecProject()).

I've thought for some time that it was dumb to have the executor
reverse-engineering this info at plan startup anyway.

Yea, it'd be good if this (and some similar tasks like building interim
tuple descriptors) could be moved to the planner. But:

We could make the planner mark each table scan node with the highest
column number that the plan will access, and use that to drive a
slot_getsomeattrs call in advance of any access to tuple contents.

probably isn't sufficient - we build non-virtual tuples in a good number
of places (sorts, tuplestore using stuff like nodeMaterial, nodeHash.c
output, ...). I suspect it'd have measurable negative consequences if
we removed the deforming logic for all expressions/projections above
such nodes. I guess we could just do such a logic for every Plan node?

- Andres

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

#74Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#73)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 17:33:46 -0400, Tom Lane wrote:

We could make the planner mark each table scan node with the highest
column number that the plan will access, and use that to drive a
slot_getsomeattrs call in advance of any access to tuple contents.

probably isn't sufficient - we build non-virtual tuples in a good number
of places (sorts, tuplestore using stuff like nodeMaterial, nodeHash.c
output, ...). I suspect it'd have measurable negative consequences if
we removed the deforming logic for all expressions/projections above
such nodes. I guess we could just do such a logic for every Plan node?

[ scratches head... ] What deforming logic do you think I'm proposing
removing?

regards, tom lane

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

#75Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#74)
Re: WIP: Faster Expression Processing v4

On 2017-03-15 18:16:57 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 17:33:46 -0400, Tom Lane wrote:

We could make the planner mark each table scan node with the highest
column number that the plan will access, and use that to drive a
slot_getsomeattrs call in advance of any access to tuple contents.

probably isn't sufficient - we build non-virtual tuples in a good number
of places (sorts, tuplestore using stuff like nodeMaterial, nodeHash.c
output, ...). I suspect it'd have measurable negative consequences if
we removed the deforming logic for all expressions/projections above
such nodes. I guess we could just do such a logic for every Plan node?

[ scratches head... ] What deforming logic do you think I'm proposing
removing?

I thought you were suggesting that we don't do the get_last_attnums (and
inlined version in the isSimpleVar case) at execution time anymore,
instead relying on logic in the planner to know how much to deform ahead
of time. Then we'd do slot_getsomeattrs in the appropriate places. But
I understood you suggesting to do so only in scan nodes - which doesn't
seem sufficient, due to the use of materialized / minimal tuples in
other types of nodes. Did I misunderstand?

- Andres

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

#76Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#75)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 18:16:57 -0400, Tom Lane wrote:

[ scratches head... ] What deforming logic do you think I'm proposing
removing?

I thought you were suggesting that we don't do the get_last_attnums (and
inlined version in the isSimpleVar case) at execution time anymore,
instead relying on logic in the planner to know how much to deform ahead
of time. Then we'd do slot_getsomeattrs in the appropriate places. But
I understood you suggesting to do so only in scan nodes - which doesn't
seem sufficient, due to the use of materialized / minimal tuples in
other types of nodes. Did I misunderstand?

We would need to do it anywhere that we might be projecting from a
materialized tuple, I suppose. From the planner's standpoint it would be
about as easy to do this for all plan nodes as only selected ones.

Anyway the core point here seems to be that skipping ExecProject misses a
bet because the underlying tuple doesn't get disassembled. A quick-hack
way of seeing if this helps might be to do slot_getallattrs in the places
where we skip ExecProject. I'm not sure we'd want that as a permanent
solution, because it's possible that it extracts columns not actually
needed, but it should at least move the hotspot in your example case.

regards, tom lane

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

#77Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#59)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

[ new patches ]

I've started to look at 0004, and the first conclusion I've come to
is that it's *direly* short of documentation. To the point that I'd
vote against committing it if something isn't done about that. As
an example, it's quite unclear how ExprEvalSteps acquire correct
resnull/resvalue pointers, and the algorithm for that seems nontrivial.
It doesn't help any that the arguments of ExecInitExprRec are entirely
undocumented.

I think it would be worth creating a README file giving an overview
of how all of this patch is supposed to work. You also need to do a
whole lot more work on the function-level comments.

A specific thing I noticed in the particular area of
what-gets-returned-where is this bit in EEOP_SCALARARRAYOP setup:

+                /*
+                 * Evaluate array argument into our return value, overwrite
+                 * with comparison results afterwards.
+                 */
+                ExecInitExprRec((Expr *) lsecond(opexpr->args), parent, state,
+                                resv, resnull);

That scares me quite a bit, because it smells exactly like the sort of
premature optimization that bit us on the rear in CVE-2016-5423 (cf commit
f0c7b789a). What's it really buying us to overwrite the return value
early rather than storing into the fcinfo's second argument slot?
(The memory of that CVE is part of what's prompting me to demand a clear
explanation of the algorithm for deciding where steps return their
results. Even if this particular code is safe, somebody is going to do
something unsafe in future if there's not a documented rule to follow.)

Another thing that ties into the do-I-understand-this-at-all question
is this bit:

+        EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
+        {
+            *op->d.boolexpr.anynull = false;
+
+            /*
+             * Fallthrough (can't be last - ANDs have two arguments at least).
+             */
+        }
+
+        EEO_CASE(EEOP_BOOL_AND_STEP)

It seems like this is missing an "op++;" before falling through. If it
isn't, because really BOOL_AND_STEP_FIRST is defined as clearing anynull
and then also doing a regular BOOL_AND_STEP, then the comment seems rather
off point. It should be more like "Fall through to do regular AND step
processing as well". The place where the comment would be on point
is probably over here:

+                        case AND_EXPR:
+                            /*
+                             * ANDs have at least two arguments, so that
+                             * no step needs to be both FIRST and LAST.
+                             */
+                            Assert(list_length(boolexpr->args) >= 2);
+
+                            if (off == 0)
+                                scratch.opcode = EEOP_BOOL_AND_STEP_FIRST;
+                            else if (off + 1 == nargs)
+                                scratch.opcode = EEOP_BOOL_AND_STEP_LAST;
+                            else
+                                scratch.opcode = EEOP_BOOL_AND_STEP;
+                            break;

although I think the Assert ought to be examining nargs not
list_length(boolexpr->args) so that it has some visible connection to the
code after it. (This all applies to OR processing as well, of course.)

BTW, it sure seems like ExecInitExprRec and related code ought to set
off all sorts of valgrind complaints? It's not being careful at all
to ensure that all fields of the "scratch" record get initialized before
we memcpy it to someplace.

regards, tom lane

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

#78Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#76)
Re: WIP: Faster Expression Processing v4

On 2017-03-15 18:48:28 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 18:16:57 -0400, Tom Lane wrote:

[ scratches head... ] What deforming logic do you think I'm proposing
removing?

I thought you were suggesting that we don't do the get_last_attnums (and
inlined version in the isSimpleVar case) at execution time anymore,
instead relying on logic in the planner to know how much to deform ahead
of time. Then we'd do slot_getsomeattrs in the appropriate places. But
I understood you suggesting to do so only in scan nodes - which doesn't
seem sufficient, due to the use of materialized / minimal tuples in
other types of nodes. Did I misunderstand?

We would need to do it anywhere that we might be projecting from a
materialized tuple, I suppose. From the planner's standpoint it would be
about as easy to do this for all plan nodes as only selected ones.

Yea.

Anyway the core point here seems to be that skipping ExecProject misses a
bet because the underlying tuple doesn't get disassembled. A quick-hack
way of seeing if this helps might be to do slot_getallattrs in the places
where we skip ExecProject.
I'm not sure we'd want that as a permanent
solution, because it's possible that it extracts columns not actually
needed, but it should at least move the hotspot in your example case.

Hm, that could hurt pretty badly if we actually only access the first
few columns.

I wonder if we shouldn't essentially get rid of all slot_getattr()
calls, and replace them with one slot_getsomeattrs() and then direct
tts_values/nulls access. Where we compute the column number is then
essentially a separate discussion.

Looking around most of them seem to access multiple columns, and several
of them probably can't conveniently done via the planner:

src/backend/catalog/partition.c
1635: datum = slot_getattr(slot, keycol, &isNull);

acesses all the partitioned columns and has convenient location to
compute column to deform to (RelationGetPartitionDispatchInfo).

src/backend/catalog/index.c
1797: iDatum = slot_getattr(slot, keycol, &isNull);

acesses all the partitioned columns and has convenient location to
compute column to deform to (BuildIndexInfo).

src/backend/utils/adt/orderedsetaggs.c
1196: Datum d = slot_getattr(slot, nargs + 1, &isnull);
1359: Datum d = slot_getattr(slot, nargs + 1, &isnull);

no benefit.

src/backend/executor/nodeMergeAppend.c
246: datum1 = slot_getattr(s1, attno, &isNull1);
247: datum2 = slot_getattr(s2, attno, &isNull2);

accesses all columns in the sort key, convenient place to prepare
(ExecInitMergeAppend).

src/backend/executor/execQual.c
594: * caught inside slot_getattr). What we have to check for here is the
600: * Note: we allow a reference to a dropped attribute. slot_getattr will
637: return slot_getattr(slot, attnum, isNull);
676: return slot_getattr(slot, attnum, isNull);
1681: return slot_getattr(fcache->funcResultSlot, 1, isNull);

Already converted in proposed expression evaluation patch.

src/backend/executor/nodeSubplan.c
367: dvalue = slot_getattr(slot, 1, &disnull);
395: prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
565: prmdata->value = slot_getattr(slot, col,
1015: dvalue = slot_getattr(slot, 1, &disnull);

Accesses all params, convenient location to compute column number
(ExecInitSubPlan).

src/backend/executor/execCurrent.c
186: /* Use slot_getattr to catch any possible mistakes */
188: DatumGetObjectId(slot_getattr(scanstate->ss_ScanTupleSlot,
193: DatumGetPointer(slot_getattr(scanstate->ss_ScanTupleSlot,

Accesses only system columns.

src/backend/executor/nodeSetOp.c
107: flag = DatumGetInt32(slot_getattr(inputslot,

Not an actual column.

src/backend/executor/nodeGatherMerge.c
678: datum1 = slot_getattr(s1, attno, &isNull1);
679: datum2 = slot_getattr(s2, attno, &isNull2);

Same as mergeAppend (huh, why did we copy this code), i.e. beneficial.

src/backend/executor/functions.c
970: value = slot_getattr(slot, 1, &(fcinfo->isnull));

no benefit.

src/backend/executor/execJunk.c
253: return slot_getattr(slot, attno, isNull);

no benefit.

src/backend/executor/nodeNestloop.c
136: prm->value = slot_getattr(outerTupleSlot,

accesses all future param values, convenient place to compute column
number.

src/backend/executor/execGrouping.c
100: attr1 = slot_getattr(slot1, att, &isNull1);
102: attr2 = slot_getattr(slot2, att, &isNull2);
170: attr1 = slot_getattr(slot1, att, &isNull1);
175: attr2 = slot_getattr(slot2, att, &isNull2);
501: attr = slot_getattr(slot, att, &isNull);

accesses all grouped-on values, but only some have a convenient place to
compute column number.

src/backend/access/common/printtup.c
545: attr = slot_getattr(slot, i + 1, &isnull);

Debug routine.

contrib/postgres_fdw/postgres_fdw.c
3213: value = slot_getattr(slot, attnum, &isnull);

Computes all parameter values, convenient-ish place to compute colun
number.

- Andres

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

#79Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#77)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-15 20:09:03 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

[ new patches ]

I've started to look at 0004, and the first conclusion I've come to
is that it's *direly* short of documentation. To the point that I'd
vote against committing it if something isn't done about that.

Yea, I asked for input about what's hard to understand and what's not -
I stared at this for a *lot* of time, and it all kinda looks easy-ish
now. I'm more than willing to expand on the below, and other pieces.

As an example, it's quite unclear how ExprEvalSteps acquire correct
resnull/resvalue pointers, and the algorithm for that seems nontrivial.
It doesn't help any that the arguments of ExecInitExprRec are entirely
undocumented.

Generally whatever wants the result of a (sub-)expression passes in the
desired resvalue/resnull. E.g. when doing a function call the
individual arguments are each prepared for evaluation using
ExecInitExprRec() and resvalue/resnull are pointing into fcinfo's
arg/nulls[i].

I think it would be worth creating a README file giving an overview
of how all of this patch is supposed to work. You also need to do a
whole lot more work on the function-level comments.

Ok.

A specific thing I noticed in the particular area of
what-gets-returned-where is this bit in EEOP_SCALARARRAYOP setup:

+                /*
+                 * Evaluate array argument into our return value, overwrite
+                 * with comparison results afterwards.
+                 */
+                ExecInitExprRec((Expr *) lsecond(opexpr->args), parent, state,
+                                resv, resnull);

That scares me quite a bit, because it smells exactly like the sort of
premature optimization that bit us on the rear in CVE-2016-5423 (cf commit
f0c7b789a).

I don't think there's a danger similar to f0c7b789a here, because the
"caller" (i.e. the node that needs the expression's result) expects
resvalue/null to be overwritten. It'll e.g. be the value "slot" of one
arm (is there a better name for one part of a boolean expression?) of a
boolean expression.

What's it really buying us to overwrite the return value
early rather than storing into the fcinfo's second argument slot?

That'd work just as well.

(The memory of that CVE is part of what's prompting me to demand a clear
explanation of the algorithm for deciding where steps return their
results. Even if this particular code is safe, somebody is going to do
something unsafe in future if there's not a documented rule to follow.)

I don't think there's a danger here, but I think you more generally have
a point.

Another thing that ties into the do-I-understand-this-at-all question
is this bit:

+        EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
+        {
+            *op->d.boolexpr.anynull = false;
+
+            /*
+             * Fallthrough (can't be last - ANDs have two arguments at least).
+             */
+        }
+
+        EEO_CASE(EEOP_BOOL_AND_STEP)

It seems like this is missing an "op++;" before falling through. If it
isn't, because really BOOL_AND_STEP_FIRST is defined as clearing anynull
and then also doing a regular BOOL_AND_STEP, then the comment seems rather
off point.

It's intended to fall through this way, i.e. the difference between
_FIRST and not is just that only the former clears anynull. What the
comment is about, admittedly too cryptically, is that the _LAST step
that then evaluates anynull cannot be the same step as
EEOP_BOOL_AND_STEP_FIRST, because bool AND/OR always has at least two
"arms". Will expand / move.

BTW, it sure seems like ExecInitExprRec and related code ought to set
off all sorts of valgrind complaints? It's not being careful at all
to ensure that all fields of the "scratch" record get initialized before
we memcpy it to someplace.

It worked not long ago - valgrind's replacment memcpy() doesn't trigger
undefined memory warnings, it just copies the "definedness" of each byte
(or bit?). But your point gives me an idea: It seems like a good idea
to VALGRIND_MAKE_MEM_UNDEFINED() the "scratch" step at some convenient
places, so the definedness of individual operations is more useful.

Thanks for the look!

- Andres

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

#80Andreas Karlsson
andreas@proxel.se
In reply to: Andres Freund (#59)
1 attachment(s)
Re: WIP: Faster Expression Processing v4

Hi,

I got a test failure with this version of the patch in the postges_fdw.
It looks to me like it was caused by a typo in the source code which is
fixed in the attached patch.

After applying this patch check-world passes.

Andreas

Attachments:

expression-processing-typo.patchtext/x-diff; name=expression-processing-typo.patchDownload
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 1763be5cf0..2ba5a2ea69 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -42,7 +42,6 @@ static void TidListCreate(TidScanState *tidstate);
 static int	itemptr_comparator(const void *a, const void *b);
 static TupleTableSlot *TidNext(TidScanState *node);
 
-
 /*
  * Compute the list of TIDs to be visited, by evaluating the expressions
  * for them.
@@ -101,6 +100,7 @@ TidListCreate(TidScanState *tidstate)
 			else if (IsCTIDVar(arg2))
 				exprstate = ExecInitExpr((Expr *) linitial(fex->args),
 										  &tidstate->ss.ps);
+			else
 				elog(ERROR, "could not identify CTID variable");
 
 			itemptr = (ItemPointer)
#81Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#79)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 20:09:03 -0400, Tom Lane wrote:

That scares me quite a bit, because it smells exactly like the sort of
premature optimization that bit us on the rear in CVE-2016-5423 (cf commit
f0c7b789a).

I don't think there's a danger similar to f0c7b789a here, because the
"caller" (i.e. the node that needs the expression's result) expects
resvalue/null to be overwritten.

Yeah, that's what I thought when I wrote the broken code in ExecEvalCase,
too. It was wrong. Basically you've got to be sure that no aliasing
can occur, and I think the only way to be safe about that is to have a
very clear rule about where results are allowed to get returned to,
preferably one that doesn't ever re-use the same target. (I think the
repeated use of the same subexpression result address for the arms of
an AND or OR is okay, but it would be a good idea to have a clear
statement of why.)

The thing that actually made the ExecEvalCase code into a bug was that
we were using ExprContext-level fields to store the current caseValue,
allowing aliasing to occur across nested CASEs. I think that in
this implementation, it ought to be possible to get rid of
ExprContext.caseValue_datum et al altogether, in favor of some storage
location that's private to each CASE expression. I'm a bit disappointed
that that doesn't seem to have happened.

Eventually, I would also like to find a way to remove the restriction
imposed by the other part of f0c7b789a, ie that we can't inline a SQL
function when that would result in intermixing two levels of CASE
expression. An implementation along the lines of what I've sketched
above could handle that easily enough, as long as we could identify
which nested level of CASE a particular CaseTestExpr belongs to.
I don't know how to do that offhand, but it seems like it ought to be
soluble if we put a bit of thought into it.

regards, tom lane

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

#82Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#81)
Re: WIP: Faster Expression Processing v4

I wrote:

Andres Freund <andres@anarazel.de> writes:

I don't think there's a danger similar to f0c7b789a here, because the
"caller" (i.e. the node that needs the expression's result) expects
resvalue/null to be overwritten.

Yeah, that's what I thought when I wrote the broken code in ExecEvalCase,
too. It was wrong.

Along the same line, I notice that you've got some expr step types
overwriting their own input, the various flavors of EEOP_BOOLTEST for
example. Maybe that's all right but it doesn't really give me a warm
feeling, especially when other single-argument operations like
EEOP_BOOL_NOT_STEP are done differently. Again, I think a clear
explanation of the design is essential to allow people to reason about
whether this sort of trickery is safe.

regards, tom lane

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

#83Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#59)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

[ latest patches ]

I looked through 0001 (the composite-type-dependencies one). Although
I agree that it'd be good to tighten things up in that area, I do not
think we want this specific patch: it tightens things too much. Consider
this variant of the existing test case in create_view.sql:

create table tt (f1 int, f2 int, f3 int);
create function ttf() returns setof tt as 'select * from tt' language sql;
create view vv as select f3 from ttf();
alter table tt drop column f3;

Current code allows the above (and causes the view to return nulls
afterwards). The 0001 patch would forbid the DROP, which is fine,
but it would also forbid dropping either of the other two table
columns, which I think is insupportable.

Also, given the above table and function, consider

create view vvv as select ttf();

This view returns a 3-column composite type. Now do

alter table tt add column f4 int;

Now the view returns a 4-column composite type. But at this point the
patch will let you drop the f4 column, but not any of the earlier three.
That's just weird.

So I'm unhappy with the specific decisions made in 0001. I think what we
really want there, probably, is for find_expr_references_walker to do more
than nothing with Vars referencing non-RELATION RTEs.

Having said all that, I think that 0001 is contributing very little to the
goals of this patch set. Andres stated that he wanted it so as to drop
some of the one-time checks that execQual.c currently does for Vars, but
I'm not really convinced that we could do that safely even with these
additional dependencies in place. Moreover, I really doubt that there's
a horrible performance cost from including something like

if (unlikely(op->first_execution))
out_of_line_checking_subroutine(...);

in the execution code for Vars. And that certainly isn't adding any
complexity for JIT compilation that we don't face anyway for other
execution step types.

So my recommendation is to drop 0001 and include the same one-time
checks that execQual.c currently has as out-of-line one-time checks
in the new code. We can revisit that later, but time grows short for
v10. I would much rather have a solid version of 0004 and not 0001,
than not have anything for v10 because we spent too much time dealing
with adding new dependencies.

regards, tom lane

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

#84Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#83)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-17 11:36:30 -0400, Tom Lane wrote:

Having said all that, I think that 0001 is contributing very little to the
goals of this patch set. Andres stated that he wanted it so as to drop
some of the one-time checks that execQual.c currently does for Vars, but
I'm not really convinced that we could do that safely even with these
additional dependencies in place. Moreover, I really doubt that there's
a horrible performance cost from including something like

if (unlikely(op->first_execution))
out_of_line_checking_subroutine(...);

in the execution code for Vars.

But it actually does (well, for some relatively small value of horrible)
The issue is that op->first_execution is actually badly predictable,
because it will be set back/forth between executions of different
expressions (in the same plantree). Obviously you'll not notice if you
have a Var and then some expensive stuff, but it's noticeable for
cheap-ish expressions (say e.g. a single Var). So the branch prediction
often doesn't handle this gracefully - it also just expands the set of
to-be-tracked jumps.

If we had a decent way to actually check this during ExecInitExpr() (et
al), the whole discussion would be different - I'd be all expanding the
set of such checks even. But I couldn't find a decent way to get there
- when expressions are initialized we don't even get an ExprContext (not
to speak of valid slots), nor is parent->plan very helpful.

That said, it seems this is something that has to wait for a later
release, I'm putting back in similar logic as there was before (not a
branch, but change the opcode to a non-checking variant).

And that certainly isn't adding any
complexity for JIT compilation that we don't face anyway for other
execution step types.

Obviously a if (op->first_execution) isn't an issue, it's actually only
doing the first time through that's not easily possible.

So my recommendation is to drop 0001 and include the same one-time
checks that execQual.c currently has as out-of-line one-time checks
in the new code. We can revisit that later, but time grows short for
v10. I would much rather have a solid version of 0004 and not 0001,
than not have anything for v10 because we spent too much time dealing
with adding new dependencies.

Doing that (+README).

Greetings,

Andres Freund

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

#85Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#84)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

That said, it seems this is something that has to wait for a later
release, I'm putting back in similar logic as there was before (not a
branch, but change the opcode to a non-checking variant).

Yeah, I was wondering if changing the opcode would be preferable to
a first-time flag.

So my recommendation is to drop 0001 and include the same one-time
checks that execQual.c currently has as out-of-line one-time checks
in the new code. We can revisit that later, but time grows short for
v10. I would much rather have a solid version of 0004 and not 0001,
than not have anything for v10 because we spent too much time dealing
with adding new dependencies.

Doing that (+README).

OK. I believe that we can get this committed after the documentation
problems are sorted. I noticed a lot of small things that bugged me,
mostly sloppy comments, but I think that the most efficient way to
handle those is for me to make an editorial pass on your next version.

regards, tom lane

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

#86Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#81)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-16 11:15:16 -0400, Tom Lane wrote:

The thing that actually made the ExecEvalCase code into a bug was that
we were using ExprContext-level fields to store the current caseValue,
allowing aliasing to occur across nested CASEs. I think that in
this implementation, it ought to be possible to get rid of
ExprContext.caseValue_datum et al altogether, in favor of some storage
location that's private to each CASE expression. I'm a bit disappointed
that that doesn't seem to have happened.

The patch actually does so - during ExecInitExprRec the "relevant"
case/domain testval is stored in ExprState->innermost_*, those pointers
are then stored directly in the relevant steps for
CaseTest/CoerceToDomainValue evaluation. Unfortunately
CaseTest/CoerceToDomainValue are reused outside of domain / case
expressions in a bunch of places (plpgsql uses CaseTest for casts
evaluation, validateDomainConstraint/domain_check_input evaluate domain
constraints without a CoerceToDomain node). I.e
ExprContext.caseValue_datum etc. aren't used for normal expressions
anymore, just for the ad-hoc hackery in a bunch of places.

I'd like to get rid of those usages, but that'd recurse into rewriting
plpgsql casts and other random pieces of code into a different approach
- something I'd like to avoid doing at the same as this already large
patch.

I've been pondering if we can't entirely get rid of CaseTest etc, the
amount of hackery required seems not like a good thing. One way I'd
prototyped was to replace them with PARAM_EXEC nodes - then the whole
issue of them potentially having different values at different parts of
an expression vanishes because the aliasing is removed.

Eventually, I would also like to find a way to remove the restriction
imposed by the other part of f0c7b789a, ie that we can't inline a SQL
function when that would result in intermixing two levels of CASE
expression. An implementation along the lines of what I've sketched
above could handle that easily enough, as long as we could identify
which nested level of CASE a particular CaseTestExpr belongs to.
I don't know how to do that offhand, but it seems like it ought to be
soluble if we put a bit of thought into it.

I haven't thought overly much about this, but I agree, it looks like it
should be doable.

That seems to suggest my PARAM_EXEC idea isn't necessarily perfect -
inlining would cause aliasing again, but it'd also not hard to fix that
up.

- Andres

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

#87Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#86)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-16 11:15:16 -0400, Tom Lane wrote:

The thing that actually made the ExecEvalCase code into a bug was that
we were using ExprContext-level fields to store the current caseValue,
allowing aliasing to occur across nested CASEs. I think that in
this implementation, it ought to be possible to get rid of
ExprContext.caseValue_datum et al altogether, in favor of some storage
location that's private to each CASE expression. I'm a bit disappointed
that that doesn't seem to have happened.

... Unfortunately
CaseTest/CoerceToDomainValue are reused outside of domain / case
expressions in a bunch of places (plpgsql uses CaseTest for casts
evaluation, validateDomainConstraint/domain_check_input evaluate domain
constraints without a CoerceToDomain node).

Yeah, there's various stuff that did that for expediency.

I'd like to get rid of those usages, but that'd recurse into rewriting
plpgsql casts and other random pieces of code into a different approach
- something I'd like to avoid doing at the same as this already large
patch.

Agreed, maybe we should just plan to clean that up later.

I've been pondering if we can't entirely get rid of CaseTest etc, the
amount of hackery required seems not like a good thing. One way I'd
prototyped was to replace them with PARAM_EXEC nodes - then the whole
issue of them potentially having different values at different parts of
an expression vanishes because the aliasing is removed.

Yes, replacing all of that with Param slots had occurred to me too.
We might want to keep the special parse node types for convenience in
reverse-listing, but having them act just like PARAM_EXEC for execution
purposes seems promising.

Eventually, I would also like to find a way to remove the restriction
imposed by the other part of f0c7b789a, ie that we can't inline a SQL
function when that would result in intermixing two levels of CASE
expression.

That seems to suggest my PARAM_EXEC idea isn't necessarily perfect -
inlining would cause aliasing again, but it'd also not hard to fix that
up.

Right, inlining would probably require some parameter-renumbering to avoid
aliasing. But at least we'd have a clear framework for how to handle it,
whereas the way things are now, it's just impossible.

regards, tom lane

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

#88Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#87)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-19 23:55:50 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

I've been pondering if we can't entirely get rid of CaseTest etc, the
amount of hackery required seems not like a good thing. One way I'd
prototyped was to replace them with PARAM_EXEC nodes - then the whole
issue of them potentially having different values at different parts of
an expression vanishes because the aliasing is removed.

Yes, replacing all of that with Param slots had occurred to me too.
We might want to keep the special parse node types for convenience in
reverse-listing, but having them act just like PARAM_EXEC for execution
purposes seems promising.

As long as that special parse-time node is part of the same value
numbering, that makes sense (could just name make it a subtype of param
ala PARAM_CASE). I don't think we actually do anything useful in
ruleutils etc with either CaseTest or CoerceToDomainValue.

- Andres

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

#89Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#77)
4 attachment(s)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-15 20:09:03 -0400, Tom Lane wrote:

I think it would be worth creating a README file giving an overview
of how all of this patch is supposed to work. You also need to do a
whole lot more work on the function-level comments.

I tried to improve upon both fronts. I've added the higher level
explanation to executor/README, but I don't feel very strong about that.

I'm not quite sure it's exactly what you wanted however, the above ask
could also be understood to have more of an motivational angle,
describing why and what exactly is changed? I'm also still not sure how
understandable it's for anybody that hasn't had their head in this for a
while...

I've also, as discussed nearby, re-added code akin to the checks in
ExecEvalScalarVar, with the discussed adjustment of the opcodes for
later executions. Now that I've done that, I'm not sure I like that
approach that much - another alternative would be to change an
ExprState's evalfunc to ExecCheckAndExecute() after initialization.
That'd have the advantage to work nicely for JIT. Either way, that can
trivially be changed later.

Additionally I added a regression test for the nearly entirely untested
nodeTidscan.c, after I'd broken it previously without noticing (thanks
Andreas).

I started a run through valgrind, without complaints up to
create_function_2. Can't run all of it (and contrib) right now without
prematurely running out of power on a plane.

Did I understand correctly that you'd rather just merge
ExecGetLastAttnums into execExpr.c, instead of making it globally
available?

Greetings,

Andres Freund

Attachments:

0001-Add-some-basic-nodeTidscan.c-tests.patch.gzapplication/x-patch-gzipDownload
0002-Make-get_last_attnums-more-generic.patch.gzapplication/x-patch-gzipDownload
0003-Add-configure-test-for-computed-goto-support.patch.gzapplication/x-patch-gzipDownload
0004-Faster-expression-evaluation-and-targetlist-projecti.patch.gzapplication/x-patch-gzipDownload
#90Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#89)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-15 20:09:03 -0400, Tom Lane wrote:

I think it would be worth creating a README file giving an overview
of how all of this patch is supposed to work. You also need to do a
whole lot more work on the function-level comments.

I tried to improve upon both fronts. I've added the higher level
explanation to executor/README, but I don't feel very strong about that.

I'm not quite sure it's exactly what you wanted however, the above ask
could also be understood to have more of an motivational angle,
describing why and what exactly is changed? I'm also still not sure how
understandable it's for anybody that hasn't had their head in this for a
while...

Well, I wasn't totally sure what was needed either. I'm coming to this
relatively fresh, having paid little attention to the thread up to now,
so maybe I'll try to add material to what you wrote as I figure things
out.

Did I understand correctly that you'd rather just merge
ExecGetLastAttnums into execExpr.c, instead of making it globally
available?

Yeah, just moving it over seems like the thing to do for now. We can
expose it later if there proves to be an actual reason to do that,
but as I mentioned, I'm doubtful that there will be one.

I'll start reading these...

regards, tom lane

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

#91Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#89)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

Additionally I added a regression test for the nearly entirely untested
nodeTidscan.c, after I'd broken it previously without noticing (thanks
Andreas).

I went ahead and pushed this part, since it seemed pretty uncontroversial.
I added a bit more stuff to get the LOC measurement up on the planner
side too.

regards, tom lane

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

#92Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#89)
Re: WIP: Faster Expression Processing v4

... is there a reason why resultnum for EEOP_ASSIGN_* steps is declared
size_t and not just int? Since it's an array index, and one that
certainly can't be bigger than AttrNumber, that seems rather confusing.

regards, tom lane

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

#93Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#92)
Re: WIP: Faster Expression Processing v4

On 2017-03-20 16:06:27 -0400, Tom Lane wrote:

... is there a reason why resultnum for EEOP_ASSIGN_* steps is declared
size_t and not just int? Since it's an array index, and one that
certainly can't be bigger than AttrNumber, that seems rather confusing.

Not that I can see, no. I guess I might have "overcompensated" when
changing it from AttrNumber - AttrNumber isn't a good idea because that
needs an extra move-zero-extend, because 16bit indexing isn't that well
supported on x86. But that doesn't mean it should be a 64bit number -
to the contrary actually.

- Andres

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

#94Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#93)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-20 16:06:27 -0400, Tom Lane wrote:

... is there a reason why resultnum for EEOP_ASSIGN_* steps is declared
size_t and not just int? Since it's an array index, and one that
certainly can't be bigger than AttrNumber, that seems rather confusing.

Not that I can see, no. I guess I might have "overcompensated" when
changing it from AttrNumber - AttrNumber isn't a good idea because that
needs an extra move-zero-extend, because 16bit indexing isn't that well
supported on x86. But that doesn't mean it should be a 64bit number -
to the contrary actually.

OK, will fix in the edits I'm working on.

regards, tom lane

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

#95Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#89)
Re: WIP: Faster Expression Processing v4

I've been busily hacking away on this, trying to make things cleaner and
fix a couple of bugs I stumbled across. (Confusion between ExecQual and
ExecCheck, for instance - we apparently lack regression tests exercising
constraints-returning-null in corner cases such as table rewrite.) It
will probably be a day or two more before I'm done.

A couple of naming questions:

* I concur with Heikki's dislike for the file name execInterpExpr.c.
Would it make it any better to switch to execExprInterp.c? I think
that having all this stuff under a common pattern execExpr*.c would
be a good thing (and I notice you've already got one comment referring
to them that way ...)

* execQual.c doesn't seem to have a unifying reason to exist anymore.
It certainly has little to do with evaluating typical qual expressions;
what's left in there seems to be mostly concerned with SRFs. I feel
like it might be a good idea to rename it, but to what? execExprUtils.c
perhaps? Or maybe we should destroy it altogether, shoving the SRF
stuff into nodeFunctionscan.c and moving what little remains into
execUtils.c.

* I do not like the function name ExecInstantiateExpr(). Webster's
defines "instantiate" as "to represent (an abstraction) by a concrete
instance", which does not seem to me to have a lot to do with what this
function actually does. There's nothing very abstract about its input.
More, the implication of "instantiate" is that you can instantiate any
number of representatives of the same abstraction, but this scribbles
on the input in a one-way fashion. I think perhaps something like
ExecPrepareExpr or ExecFinishExpr or something along that line would
be better, but nothing is really standing out as le mot juste.

Thoughts?

regards, tom lane

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

#96Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#95)
Re: WIP: Faster Expression Processing v4

On 2017-03-22 10:41:06 -0400, Tom Lane wrote:

I've been busily hacking away on this, trying to make things cleaner and
fix a couple of bugs I stumbled across. (Confusion between ExecQual and
ExecCheck, for instance - we apparently lack regression tests exercising
constraints-returning-null in corner cases such as table rewrite.) It
will probably be a day or two more before I'm done.

Thanks!

A couple of naming questions:

* I concur with Heikki's dislike for the file name execInterpExpr.c.
Would it make it any better to switch to execExprInterp.c? I think
that having all this stuff under a common pattern execExpr*.c would
be a good thing (and I notice you've already got one comment referring
to them that way ...)

That works for me.

* execQual.c doesn't seem to have a unifying reason to exist anymore.
It certainly has little to do with evaluating typical qual expressions;
what's left in there seems to be mostly concerned with SRFs. I feel
like it might be a good idea to rename it, but to what? execExprUtils.c
perhaps? Or maybe we should destroy it altogether, shoving the SRF
stuff into nodeFunctionscan.c and moving what little remains into
execUtils.c.

Yea, I was wondering about that too. What would we do with
GetAttributeByName/Num?

* I do not like the function name ExecInstantiateExpr(). Webster's
defines "instantiate" as "to represent (an abstraction) by a concrete
instance", which does not seem to me to have a lot to do with what this
function actually does.

It perhaps makes a *bit* more sense if you view it from the POV that,
with the future JIT support (WIP versions of which I posted previously),
it'd actually create a compiled function which'd largely be independent
of the of ->steps (except for the non-hotpath functions, which'd still
end up using it). So one of the above "conrete instances" would be the
interpeted version, another the compiled one. No, not an entirely
convincing argument.

There's nothing very abstract about its input.
More, the implication of "instantiate" is that you can instantiate any
number of representatives of the same abstraction, but this scribbles
on the input in a one-way fashion. I think perhaps something like
ExecPrepareExpr or ExecFinishExpr or something along that line would
be better, but nothing is really standing out as le mot juste.

Either of those work, but they don't strike me as perfect either, but I
can't come up with something better (ExecReadyExprForExec()?).

Greetings,

Andres Freund

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

#97Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#96)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-22 10:41:06 -0400, Tom Lane wrote:

* execQual.c doesn't seem to have a unifying reason to exist anymore.
It certainly has little to do with evaluating typical qual expressions;
what's left in there seems to be mostly concerned with SRFs. I feel
like it might be a good idea to rename it, but to what? execExprUtils.c
perhaps? Or maybe we should destroy it altogether, shoving the SRF
stuff into nodeFunctionscan.c and moving what little remains into
execUtils.c.

Yea, I was wondering about that too. What would we do with
GetAttributeByName/Num?

I was thinking execUtils.c for those.

* I do not like the function name ExecInstantiateExpr(). Webster's
defines "instantiate" as "to represent (an abstraction) by a concrete
instance", which does not seem to me to have a lot to do with what this
function actually does.
...

Either of those work, but they don't strike me as perfect either, but I
can't come up with something better (ExecReadyExprForExec()?).

Actually, ExecReadyExpr seems kind of nice for this (using "ready" in its
verb sense, "to prepare (someone or something) for an activity or
purpose").

regards, tom lane

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

#98Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#96)
Re: WIP: Faster Expression Processing v4

BTW, I'm fairly concerned by what you did in nodeTidscan.c, ie delaying
compile of the TID expressions until TidListCreate. I think that probably
fails for cases involving, eg, subplans in the expressions; we need
subplans to get linked to the parent node, and this way won't do it till
(maybe) too late. Barring objection I'm going to rearrange that so that
we still do the compile part at executor start.

regards, tom lane

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

#99Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#98)
Re: WIP: Faster Expression Processing v4

On 2017-03-22 13:15:41 -0400, Tom Lane wrote:

BTW, I'm fairly concerned by what you did in nodeTidscan.c, ie delaying
compile of the TID expressions until TidListCreate. I think that probably
fails for cases involving, eg, subplans in the expressions; we need
subplans to get linked to the parent node, and this way won't do it till
(maybe) too late. Barring objection I'm going to rearrange that so that
we still do the compile part at executor start.

No objection here.

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

#100Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#97)
Re: WIP: Faster Expression Processing v4

Looking at some of the coding choices you made here, I see that you're
making a hard assumption that called functions never scribble on their
fcinfo->arg/argnull arrays. I do not believe that we've ever had such
an assumption before. Are we comfortable with that? If so, we'd
better document it.

regards, tom lane

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

#101Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#100)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-23 13:14:59 -0400, Tom Lane wrote:

Looking at some of the coding choices you made here, I see that you're
making a hard assumption that called functions never scribble on their
fcinfo->arg/argnull arrays. I do not believe that we've ever had such
an assumption before.

I think we did that before, e.g. ExecEvalScalarArrayOp(). Think there's
others too.

Are we comfortable with that? If so, we'd better document it.

I think it's ok, but we indeed should document it. I recall a note
somewhere... Can't find it anywhere however, might have misremembered a
note about pass-by-ref arguments. fmgr/README? A note in
FunctionCallInfoData's definition?

Greetings,

Andres Freund

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

#102Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#101)
Re: WIP: Faster Expression Processing v4

Stylistic thought ... I am wondering if it wouldn't be a good idea
to replace EEOP_CASE_WHEN_STEP, EEOP_CASE_THEN_STEP, EEOP_COALESCE,
and EEOP_ARRAYREF_CHECKINPUT with instructions defined in a less
usage-dependent way as

EEOP_JUMP unconditional jump
EEOP_JUMP_IF_NULL jump if step result is null
EEOP_JUMP_IF_NOT_NULL jump if step result isn't null
EEOP_JUMP_IF_NOT_TRUE jump if step result isn't TRUE

One could imagine later filling out this set with the other BoolTest
condition types, but that seems to be all we need right now.

These are basically just renamings of the step types that exist now,
although EEOP_ARRAYREF_CHECKINPUT would have to drop its not-very-
necessary Assert(!op->d.arrayref.state->isassignment). Well, I guess
I should say that they're renamings of the semantics that I have
for these steps in my working copy; for instance, I got rid of
casewhen.value/casewhen.isnull in favor of letting CASE WHEN expressions
evaluate into the CASE's final output variable.

At least to me, I think the compiling code would be more readable
this way. I find WHEN_STEP and THEN_STEP a bit odd because they are
emitted after, not before, the expressions you'd think they control.
ARRAYREF_CHECKINPUT is pretty vaguely named, too.

regards, tom lane

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

#103Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#102)
Re: WIP: Faster Expression Processing v4

I found a rather nasty bug :-( ... the comment in EEOP_INNER_VAR_FIRST about

+            /*
+             * Can't assert tts_nvalid, as wholerow var evaluation or such
+             * could have materialized the slot - but the contents are still
+             * valid :/
+             */
+            Assert(op->d.var.attnum >= 0);

is actually averting its eyes from a potentially serious problem. If we
go through ExecEvalWholeRowVar, which calls ExecFetchSlotTupleDatum, and
ExecFetchSlotTuple decides it has to materialize the tuple, then
ExecMaterializeSlot does this:

/*
* Drop the pin on the referenced buffer, if there is one.
*/
if (BufferIsValid(slot->tts_buffer))
ReleaseBuffer(slot->tts_buffer);

slot->tts_buffer = InvalidBuffer;

/*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
* mustn't leave any dangling pass-by-reference datums in tts_values.
* However, we have not actually invalidated any such datums, if there
* happen to be any previously fetched from the slot. (Note in particular
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;

The problem here is that once we drop the buffer pin, any pointers we may
have into on-disk data are dangling pointers --- we're at risk of some
other backend taking away that shared buffer. (So I'm afraid that the
commentary there is overly optimistic.) So even though those pointers
may still be there beyond tts_nvalid, subsequent references to them are
very dangerous.

I think that it's pretty hard to hit this in practice, maybe impossible,
because the normal case for an "on-disk" tuple is that
TTS_HAS_PHYSICAL_TUPLE is true, so that ExecFetchSlotTuple won't change
the state of the slot. If we have a virtual tuple that has to be
materialized, then by that very token it won't have a buffer pin to drop.
But I find this fragile as heck, and the aforesaid patch comment surely
isn't adequately documenting the safety considerations. Also, if there
ever were a live bug here, reproducing it would be damn hard because of
the low probability that a just-unpinned buffer would get replaced any
time soon. (Hm, I wonder whether the buffer cache needs something
analogous to the syscaches' CLOBBER_CACHE_ALWAYS behavior...)

Besides which, I really really don't like the lack of an "attnum <
tts_nvalid" assertion there; that's just obviously failing to check for
very simple bugs, such as getting the FETCHSOME steps wrong.

So I think that we have got to fix ExecEvalWholeRowVar so that it doesn't
clobber the state of the slot. Right at the moment, the only way to do
that seems to be to do this instead of ExecFetchSlotTupleDatum:

tuple = ExecCopySlotTuple(slot);
dtuple = (HeapTupleHeader)
DatumGetPointer(heap_copy_tuple_as_datum(tuple,
slot->tts_tupleDescriptor));
heap_freetuple(tuple);

That's kind of annoying because of the double copying involved, but
tuptoaster.c doesn't expose any functionality for this except
heap_copy_tuple_as_datum(). I figure we can improve it later --- it looks
like we can refactor heap_copy_tuple_as_datum to expose a function that
reads from Datum/isnull arrays, and then call it on the slot's
tts_values/tts_isnull arrays. Seems like it might be a good idea to
think a bit harder about the TupleTableSlot APIs, too, and reduce the
number of cases where execTuples exposes destructive changes to the
state of a slot.

Also, while trying to test the above scenario, I realized that the patch
as submitted was being way too cavalier about where it was applying
CheckVarSlotCompatibility and so on. The ASSIGN_FOO_VAR steps, for
instance, had no protection at all. Think I have that all fixed up
though.

I hope to have a fully reviewed patch to pass back to you tomorrow.
Or Saturday at the latest.

regards, tom lane

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

#104Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#102)
Re: WIP: Faster Expression Processing v4

Hi,m

On 2017-03-23 17:40:55 -0400, Tom Lane wrote:

Stylistic thought ... I am wondering if it wouldn't be a good idea
to replace EEOP_CASE_WHEN_STEP, EEOP_CASE_THEN_STEP, EEOP_COALESCE,
and EEOP_ARRAYREF_CHECKINPUT with instructions defined in a less
usage-dependent way as

EEOP_JUMP unconditional jump
EEOP_JUMP_IF_NULL jump if step result is null
EEOP_JUMP_IF_NOT_NULL jump if step result isn't null
EEOP_JUMP_IF_NOT_TRUE jump if step result isn't TRUE

One could imagine later filling out this set with the other BoolTest
condition types, but that seems to be all we need right now.

Hm, no arguments against, but I'm also not particularly excited about
the change.

These are basically just renamings of the step types that exist now,
although EEOP_ARRAYREF_CHECKINPUT would have to drop its not-very-
necessary Assert(!op->d.arrayref.state->isassignment).

I won't shed a tear about that assert's removal.

Well, I guess I should say that they're renamings of the semantics
that I have for these steps in my working copy; for instance, I got
rid of casewhen.value/casewhen.isnull in favor of letting CASE WHEN
expressions evaluate into the CASE's final output variable.

That sounds like a sensible change (in the abstract, I obviously haven't
seen your working copy).

Greetings,

Andres Freund

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

#105Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#103)
Re: WIP: Faster Expression Processing v4

On 2017-03-23 20:36:32 -0400, Tom Lane wrote:

I found a rather nasty bug :-( ... the comment in EEOP_INNER_VAR_FIRST about

+            /*
+             * Can't assert tts_nvalid, as wholerow var evaluation or such
+             * could have materialized the slot - but the contents are still
+             * valid :/
+             */
+            Assert(op->d.var.attnum >= 0);

is actually averting its eyes from a potentially serious problem. If we
go through ExecEvalWholeRowVar, which calls ExecFetchSlotTupleDatum, and
ExecFetchSlotTuple decides it has to materialize the tuple, then
ExecMaterializeSlot does this:

/*
* Drop the pin on the referenced buffer, if there is one.
*/
if (BufferIsValid(slot->tts_buffer))
ReleaseBuffer(slot->tts_buffer);

slot->tts_buffer = InvalidBuffer;

/*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
* mustn't leave any dangling pass-by-reference datums in tts_values.
* However, we have not actually invalidated any such datums, if there
* happen to be any previously fetched from the slot. (Note in particular
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;

The problem here is that once we drop the buffer pin, any pointers we may
have into on-disk data are dangling pointers --- we're at risk of some
other backend taking away that shared buffer. (So I'm afraid that the
commentary there is overly optimistic.) So even though those pointers
may still be there beyond tts_nvalid, subsequent references to them are
very dangerous.

This applies to the code in master as well, no? An ExecEvalScalarVar()
followed by an ExecEvalWholeRowVar() would have precisely the same
effect? Do we need to do anything about this in the back-branches,
given how unlikely this is going to be in practice?

So I think that we have got to fix ExecEvalWholeRowVar so that it doesn't
clobber the state of the slot.

That seems like a good plan.

Also, while trying to test the above scenario, I realized that the patch
as submitted was being way too cavalier about where it was applying
CheckVarSlotCompatibility and so on. The ASSIGN_FOO_VAR steps, for
instance, had no protection at all.

I don't think that's true - the assign checks had copied the code from
the old ExecBuildProjectionInfo, setting isSimpleVar iff
(!attr->attisdropped && variable->vartype == attr->atttypid) - we can
check that for projections in contrast to normal expressions because we
already know the slot. The relevant comment for that, from before the
patch, is:
* inputDesc. (Note: if there is a type mismatch then ExecEvalScalarVar
* will probably throw an error at runtime, but we leave that to it.)
*/

I hope to have a fully reviewed patch to pass back to you tomorrow.
Or Saturday at the latest.

Cool.

Greetings,

Andres Freund

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

#106Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#103)
Re: WIP: Faster Expression Processing v4

On 2017-03-23 20:36:32 -0400, Tom Lane wrote:

So I think that we have got to fix ExecEvalWholeRowVar so that it doesn't
clobber the state of the slot. Right at the moment, the only way to do
that seems to be to do this instead of ExecFetchSlotTupleDatum:

tuple = ExecCopySlotTuple(slot);
dtuple = (HeapTupleHeader)
DatumGetPointer(heap_copy_tuple_as_datum(tuple,
slot->tts_tupleDescriptor));
heap_freetuple(tuple);

Hm. One disadvantage would be that repeated whole-row references to the
same table would be a bit slower, because we'd repeatedly form a tuple
from a virtual one - but I have a hard time coming up with a scenario
where that'd matter. I'd suspect that in the end it'd probably even
have a *positive* performance impact, because right now the next scalar
access will have to deform the whole tuple again, and that seems like a
lot more likely scenario.

Greetings,

Andres Freund

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

#107Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#105)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-23 20:36:32 -0400, Tom Lane wrote:

The problem here is that once we drop the buffer pin, any pointers we may
have into on-disk data are dangling pointers --- we're at risk of some
other backend taking away that shared buffer. (So I'm afraid that the
commentary there is overly optimistic.) So even though those pointers
may still be there beyond tts_nvalid, subsequent references to them are
very dangerous.

This applies to the code in master as well, no? An ExecEvalScalarVar()
followed by an ExecEvalWholeRowVar() would have precisely the same
effect?

Yeah. The other order would be safe, because ExecEvalScalarVar would do
slot_getattr which would re-extract the value from the newly materialized
tuple. But there definitely seems to be a hazard for the order you
mentioned.

Do we need to do anything about this in the back-branches,
given how unlikely this is going to be in practice?

Probably not. As I mentioned, I think this may be only theoretical rather
than real, if you believe that buffer pins would only be associated with
slots holding references to regular tuples. And even if it's not
theoretical, the odds of seeing a failure in the field seem pretty tiny
given that a just-released buffer shouldn't be subject to recycling for
a fair while. But I don't want to leave it like this going forward.

So I think that we have got to fix ExecEvalWholeRowVar so that it doesn't
clobber the state of the slot.

That seems like a good plan.

Yeah. I have the stopgap code in my working copy, and will look at
refactoring the tuptoaster code for better performance later.

Also, while trying to test the above scenario, I realized that the patch
as submitted was being way too cavalier about where it was applying
CheckVarSlotCompatibility and so on. The ASSIGN_FOO_VAR steps, for
instance, had no protection at all.

I don't think that's true - the assign checks had copied the code from
the old ExecBuildProjectionInfo, setting isSimpleVar iff
(!attr->attisdropped && variable->vartype == attr->atttypid) - we can
check that for projections in contrast to normal expressions because we
already know the slot.

Hmm, I see ... but that only works in the cases where the caller of
ExecBuildProjectionInfo supplied a source slot, and a lot of 'em don't.
As the code stands, we are unable to use ASSIGN_FOO_VAR in quite a lot
of places, including everywhere above the relation scan level.

I'd already put in the infrastructure to add ASSIGN_FOO_VAR_FIRST
step types. I could take it back out, but I wonder if it wouldn't be
smarter to keep it and remove the restriction in ExecBuildProjectionInfo.
Or maybe we could have ExecBuildProjectionInfo emit either
ASSIGN_FOO_VAR_FIRST or ASSIGN_FOO_VAR depending on whether it can prove
the reference safe.

regards, tom lane

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

#108Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#107)
Re: WIP: Faster Expression Processing v4

On 2017-03-23 21:26:19 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-23 20:36:32 -0400, Tom Lane wrote:

The problem here is that once we drop the buffer pin, any pointers we may
have into on-disk data are dangling pointers --- we're at risk of some
other backend taking away that shared buffer. (So I'm afraid that the
commentary there is overly optimistic.) So even though those pointers
may still be there beyond tts_nvalid, subsequent references to them are
very dangerous.

This applies to the code in master as well, no? An ExecEvalScalarVar()
followed by an ExecEvalWholeRowVar() would have precisely the same
effect?

Yeah. The other order would be safe, because ExecEvalScalarVar would do
slot_getattr which would re-extract the value from the newly materialized
tuple. But there definitely seems to be a hazard for the order you
mentioned.

Do we need to do anything about this in the back-branches,
given how unlikely this is going to be in practice?

Probably not. As I mentioned, I think this may be only theoretical rather
than real, if you believe that buffer pins would only be associated with
slots holding references to regular tuples. And even if it's not
theoretical, the odds of seeing a failure in the field seem pretty tiny
given that a just-released buffer shouldn't be subject to recycling for
a fair while. But I don't want to leave it like this going forward.

Ok.

Also, while trying to test the above scenario, I realized that the patch
as submitted was being way too cavalier about where it was applying
CheckVarSlotCompatibility and so on. The ASSIGN_FOO_VAR steps, for
instance, had no protection at all.

I don't think that's true - the assign checks had copied the code from
the old ExecBuildProjectionInfo, setting isSimpleVar iff
(!attr->attisdropped && variable->vartype == attr->atttypid) - we can
check that for projections in contrast to normal expressions because we
already know the slot.

Hmm, I see ... but that only works in the cases where the caller of
ExecBuildProjectionInfo supplied a source slot, and a lot of 'em
don't.

Right, the old and new code comment on that:

* inputDesc can be NULL, but if it is not, we check to see whether simple
* Vars in the tlist match the descriptor. It is important to provide
* inputDesc for relation-scan plan nodes, as a cross check that the relation
* hasn't been changed since the plan was made. At higher levels of a plan,
* there is no need to recheck.

and that seems like reasonable to me? That said, I think we can remove
that assumption, by checking once.

As the code stands, we are unable to use ASSIGN_FOO_VAR in quite a lot
of places, including everywhere above the relation scan level.

Hm? If inputDesc isn't given we just, before and after, do:
if (!inputDesc)
isSimpleVar = true; /* can't check type, assume OK */

I'd already put in the infrastructure to add ASSIGN_FOO_VAR_FIRST
step types. I could take it back out, but I wonder if it wouldn't be
smarter to keep it and remove the restriction in ExecBuildProjectionInfo.
Or maybe we could have ExecBuildProjectionInfo emit either
ASSIGN_FOO_VAR_FIRST or ASSIGN_FOO_VAR depending on whether it can prove
the reference safe.

I think it's probably ok to just leave the check in, and remove those
comments, and simplify the isSimpleVar stuff to only check if
IsA(tle->expr, Var) && ((Var *) tle->expr)->varattno > 0)

- Andres

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

#109Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#108)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-23 21:26:19 -0400, Tom Lane wrote:

Hmm, I see ... but that only works in the cases where the caller of
ExecBuildProjectionInfo supplied a source slot, and a lot of 'em
don't.

Right, the old and new code comment on that:

* inputDesc can be NULL, but if it is not, we check to see whether simple
* Vars in the tlist match the descriptor. It is important to provide
* inputDesc for relation-scan plan nodes, as a cross check that the relation
* hasn't been changed since the plan was made. At higher levels of a plan,
* there is no need to recheck.

Ah, I'd forgotten the assumption that we only need to check this at scan
level.

I'd already put in the infrastructure to add ASSIGN_FOO_VAR_FIRST
step types. I could take it back out, but I wonder if it wouldn't be
smarter to keep it and remove the restriction in ExecBuildProjectionInfo.
Or maybe we could have ExecBuildProjectionInfo emit either
ASSIGN_FOO_VAR_FIRST or ASSIGN_FOO_VAR depending on whether it can prove
the reference safe.

I think it's probably ok to just leave the check in, and remove those
comments, and simplify the isSimpleVar stuff to only check if
IsA(tle->expr, Var) && ((Var *) tle->expr)->varattno > 0)

Not sure. It's a pretty fair amount of duplicative code, once you finish
dealing with all the ExecJustFoo functions in addition to the main code
paths. At this point I'm inclined to take it back out and improve the
comments around ExecBuildProjectionInfo.

regards, tom lane

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

#110Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#109)
Re: WIP: Faster Expression Processing v4

On 2017-03-23 21:58:03 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-23 21:26:19 -0400, Tom Lane wrote:

Hmm, I see ... but that only works in the cases where the caller of
ExecBuildProjectionInfo supplied a source slot, and a lot of 'em
don't.

Right, the old and new code comment on that:

* inputDesc can be NULL, but if it is not, we check to see whether simple
* Vars in the tlist match the descriptor. It is important to provide
* inputDesc for relation-scan plan nodes, as a cross check that the relation
* hasn't been changed since the plan was made. At higher levels of a plan,
* there is no need to recheck.

Ah, I'd forgotten the assumption that we only need to check this at scan
level.

I'd already put in the infrastructure to add ASSIGN_FOO_VAR_FIRST
step types. I could take it back out, but I wonder if it wouldn't be
smarter to keep it and remove the restriction in ExecBuildProjectionInfo.
Or maybe we could have ExecBuildProjectionInfo emit either
ASSIGN_FOO_VAR_FIRST or ASSIGN_FOO_VAR depending on whether it can prove
the reference safe.

I think it's probably ok to just leave the check in, and remove those
comments, and simplify the isSimpleVar stuff to only check if
IsA(tle->expr, Var) && ((Var *) tle->expr)->varattno > 0)

Not sure. It's a pretty fair amount of duplicative code, once you finish
dealing with all the ExecJustFoo functions in addition to the main code
paths. At this point I'm inclined to take it back out and improve the
comments around ExecBuildProjectionInfo.

I'm ok with both.

- Andres

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

#111Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#110)
Re: WIP: Faster Expression Processing v4

Another modest proposal:

I'm not really sold on the approach of using EEOP_FETCHSOME opcodes to
trigger initial tupleslot de-forming. Certainly we want to have a single
slot_getsomeattrs call per source slot, but as-is, we need a separate
traversal over the expression tree just to precompute the max attribute
number needed. That can't be good for expression compile speed, and
it introduces nontrivial bug risks because the code that does that
is completely decoupled from the code that emits the EEOP_VAR opcodes
(which are what's really relying on the de-forming to have happened).

What do you think about a design like this:

* Drop the FETCHSOME opcodes.

* Add fields to struct ExprState that will hold the maximum inner,
outer, and scan attribute numbers needed.

* ExecInitExpr initializes those fields to zero, and then during
ExecInitExprRec, whenever we generate an EEOP_VAR opcode, we do e.g.

state->last_inner_attno = Max(state->last_inner_attno,
variable->varattno);

* ExecInitExprSlots, get_last_attnums_walker, etc all go away.

* In the startup segment of ExecInterpExpr, add

if (state->last_inner_attno > 0)
slot_getsomeattrs(innerslot, state->last_inner_attno);
if (state->last_outer_attno > 0)
slot_getsomeattrs(outerslot, state->last_outer_attno);
if (state->last_scan_attno > 0)
slot_getsomeattrs(scanslot, state->last_scan_attno);

This would be a little different from the existing code as far as runtime
branch-prediction behavior goes, but it's not apparent to me that it'd be
any worse. Also, for JIT purposes it'd still be entirely possible to
compile the slot_getsomeattrs calls in-line; you'd just be looking to a
different part of the ExprState struct to find out what to do.

regards, tom lane

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

#112Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#111)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-24 11:26:27 -0400, Tom Lane wrote:

Another modest proposal:

I'm not really sold on the approach of using EEOP_FETCHSOME opcodes to
trigger initial tupleslot de-forming. Certainly we want to have a single
slot_getsomeattrs call per source slot, but as-is, we need a separate
traversal over the expression tree just to precompute the max attribute
number needed. That can't be good for expression compile speed, and
it introduces nontrivial bug risks because the code that does that
is completely decoupled from the code that emits the EEOP_VAR opcodes
(which are what's really relying on the de-forming to have happened).

Hm. We had the separate traversal for projections for a long while, and
I don't think there've been a a lot of changes to the extraction of the
last attribute number. I'm very doubtful that the cost of traversing
the expression twice is meaningful in comparison to the other costs.

What do you think about a design like this:

* Drop the FETCHSOME opcodes.

* Add fields to struct ExprState that will hold the maximum inner,
outer, and scan attribute numbers needed.

* ExecInitExpr initializes those fields to zero, and then during
ExecInitExprRec, whenever we generate an EEOP_VAR opcode, we do e.g.

state->last_inner_attno = Max(state->last_inner_attno,
variable->varattno);

* ExecInitExprSlots, get_last_attnums_walker, etc all go away.

* In the startup segment of ExecInterpExpr, add

if (state->last_inner_attno > 0)
slot_getsomeattrs(innerslot, state->last_inner_attno);
if (state->last_outer_attno > 0)
slot_getsomeattrs(outerslot, state->last_outer_attno);
if (state->last_scan_attno > 0)
slot_getsomeattrs(scanslot, state->last_scan_attno);

This would be a little different from the existing code as far as runtime
branch-prediction behavior goes, but it's not apparent to me that it'd be
any worse.

I'd be suprised if it weren't.

I'm not super strongly against this setup, but I fail to see the benefit
of whacking this around. I've benchmarked the previous/current setup
fairly extensively, and I'd rather not redo that. In contrast to the
other changes you've talked about, this definitely is in the hot-path...

Also, for JIT purposes it'd still be entirely possible to compile the
slot_getsomeattrs calls in-line; you'd just be looking to a different
part of the ExprState struct to find out what to do.

Yea, we could do that.

Greetings,

Andres Freund

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

#113Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#112)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-24 11:26:27 -0400, Tom Lane wrote:

Another modest proposal:

I'm not really sold on the approach of using EEOP_FETCHSOME opcodes to
trigger initial tupleslot de-forming. Certainly we want to have a single
slot_getsomeattrs call per source slot, but as-is, we need a separate
traversal over the expression tree just to precompute the max attribute
number needed. That can't be good for expression compile speed, and
it introduces nontrivial bug risks because the code that does that
is completely decoupled from the code that emits the EEOP_VAR opcodes
(which are what's really relying on the de-forming to have happened).

Hm. We had the separate traversal for projections for a long while, and
I don't think there've been a a lot of changes to the extraction of the
last attribute number.

That's easily disproven just by looking at the code:

/*
* Don't examine the arguments or filters of Aggrefs or WindowFuncs,
* because those do not represent expressions to be evaluated within the
* calling expression's econtext. GroupingFunc arguments are never
* evaluated at all.
*/
if (IsA(node, Aggref))
return false;
if (IsA(node, WindowFunc))
return false;
if (IsA(node, GroupingFunc))
return false;
return expression_tree_walker(node, get_last_attnums_walker,
(void *) info);

The WindowFunc exception hasn't been there so long, and the GroupingFunc
one is very new. And who's to say whether e.g. the recent XMLTABLE patch
got this right at all? We could easily be extracting columns we don't
need to.

I'm willing to leave this as-is for the moment, but I really think we
should look into changing it (after the main patch is in).

regards, tom lane

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

#114Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#112)
1 attachment(s)
Re: WIP: Faster Expression Processing v4

Attached is an updated patch. I believe this is committable, but
since I whacked it around quite a bit, I'm sure you'll want to look
it over first.

Please be sure the commit message notes that function EXECUTE permissions
are now checked at executor startup not first call. We need to document
that in the v10 release notes as an incompatibility, and I'm sure we'll
forget if the commit log doesn't point it out.

Some loose ends that remain to be looked at, though I do not think any
of these are reasons to postpone commit:

* I'm concerned that we now do not have enough check_stack_depth() calls.
In our off-list discussion I wanted to add one to ExecProcNode, and you
were unhappy about that ... but it'd still be a lot less stack checking
than we do now.

* I still think we should look into removing the EEOP_FETCHSOME op types,
or at least finding some other way to perform the calculation of the last
attnums in the mainline expression compilation path.

* As we discussed, ExecEvalWholeRowVar is now using a pretty inefficient
method for flattening tuples into datums. I will take a to-do item to
fix this.

* ExecInitCheck is really just ExecInitExpr with a make_ands_explicit
call in front of it. It turned out that most of the places that were
(or should have been) calling it were doing a make_ands_implicit call
for no other reason than to satisfy its API. I changed most of those
to just call ExecInitExpr directly. There are just a couple of call
sites left, and I think probably those are likewise not really that
happy with this API --- but I didn't take the time to chase down where
the expressions were coming from in those cases. It seems possible
that we could remove ExecInitCheck/ExecPrepareCheck entirely. I'll
look into this later, too.

* As we discussed, CaseTestValue/DomainValue are pretty messy and need
to be rethought. That might not get done for v10 though.

* I'm not very happy that execSRF.c has two somewhat different
implementations of largely similar functionality, and
SetExprState.elidedFuncState seems like a wart. That's mostly a
pre-existing problem of course. I'm satisfied with leaving it as it is
for now, but eventually some refactoring there would be good.

The attached patch is against HEAD as of last night (commit 457a44487).

regards, tom lane

Attachments:

faster-expressions-v5.patch.gzapplication/x-gzip; name=faster-expressions-v5.patch.gzDownload
#115Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#114)
Re: WIP: Faster Expression Processing v4

btw ... I just got around to looking at a code coverage report for this
patched version, and that reminded me of something I'd already suspected:
EEOP_INNER_SYSVAR and EEOP_OUTER_SYSVAR seem to be dead code. That's
unsurprising, because we never try to access a tuple's system columns
above the scan level. If a query asks for system columns, those get
passed up to upper query levels as ordinary user-side columns.

We could keep the execution support for those opcodes, or we could rip it
out and throw an error in execExpr.c if one would need to be generated.
I'm a little inclined to the latter, because it seems like the plan is
to grow more support for every opcode in the future. We don't need to
be adding support for unreachable opcodes.

regards, tom lane

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

#116Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#115)
Re: WIP: Faster Expression Processing v4

More random musing ... have you considered making the jump-target fields
in expressions be relative rather than absolute indexes? That is,
EEO_JUMP would look like

op += (stepno); \
EEO_DISPATCH(); \

instead of

op = &state->steps[stepno]; \
EEO_DISPATCH(); \

I have not carried out a full patch to make this work, but just making
that one change and examining the generated assembly code looks promising.
Instead of this

movslq 40(%r14), %r8
salq $6, %r8
addq 24(%rbx), %r8
movq %r8, %r14
jmp *(%r8)

we get this

movslq 40(%r14), %rax
salq $6, %rax
addq %rax, %r14
jmp *(%r14)

which certainly looks like it ought to be faster. Also, the real reason
I got interested in this at all is that with relative jumps, groups of
steps would be position-independent within the steps array, which would
enable some compile-time tricks that seem impractical with the current
definition.

BTW, now that I've spent a bit of time looking at the generated assembly
code, I'm kind of disinclined to believe any arguments about how we have
better control over branch prediction with the jump-threading
implementation. At least with current gcc (6.3.1 on Fedora 25) at -O2,
what I see is multiple places jumping to the same indirect jump
instruction :-(. It's not a total disaster: as best I can tell, all the
uses of EEO_JUMP remain distinct. But gcc has chosen to implement about
40 of the 71 uses of EEO_NEXT by jumping to the same couple of
instructions that increment the "op" register and then do an indirect
jump :-(.

So it seems that we're at the mercy of gcc's whims as to which instruction
dispatches will be distinguishable to the hardware; which casts a very
dark shadow over any benchmarking-based arguments that X is better than Y
for branch prediction purposes. Compiler version differences are likely
to matter a lot more than anything we do.

regards, tom lane

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

#117Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#114)
Re: WIP: Faster Expression Processing v4

Hi,

On 2017-03-24 17:16:15 -0400, Tom Lane wrote:

Attached is an updated patch. I believe this is committable, but
since I whacked it around quite a bit, I'm sure you'll want to look
it over first.

Indeed.

Points:

- It's going to take a while for me to get used to execExprInterp.c - I
constantly try to switch to a nonexistant buffer ;)

- Like your approach with
/* Check that current tupdesc doesn't have more fields than we allocated */
in ExecEvalFieldStoreDeForm().

- I like EEO_FLAG_IS_QUAL.

- I think at some point (not sure whether in your or in my revision)
ScalarArrayOpExpr lost its permission check. Added that.

- Added note that we knowingly don't perform permission checks for
input/output funcs.

- Both pre/post patch, CoerceViaIO doesn't invoke
InvokeFunctionExecuteHook - I'm still unclear what that hook is
useful for, so ...

- The !caseExpr->defresult result branch is currently unreachable (and
its equivalent was before the patch) because transformCaseExpr()
generates a default expression. I'm inclined to replace the dead code
with an assertion. Any reason not to do that?

- I see you kept determination of the set of constraints to be checked
for domain at initialization time. Made note that that a) might change
b) could change behaviour.

Please be sure the commit message notes that function EXECUTE permissions
are now checked at executor startup not first call. We need to document
that in the v10 release notes as an incompatibility, and I'm sure we'll
forget if the commit log doesn't point it out.

Done.

* As we discussed, ExecEvalWholeRowVar is now using a pretty inefficient
method for flattening tuples into datums. I will take a to-do item to
fix this.

Thanks.

* ExecInitCheck is really just ExecInitExpr with a make_ands_explicit
call in front of it. It turned out that most of the places that were
(or should have been) calling it were doing a make_ands_implicit call
for no other reason than to satisfy its API. I changed most of those
to just call ExecInitExpr directly. There are just a couple of call
sites left, and I think probably those are likewise not really that
happy with this API --- but I didn't take the time to chase down where
the expressions were coming from in those cases. It seems possible
that we could remove ExecInitCheck/ExecPrepareCheck entirely. I'll
look into this later, too.

Thanks^2.

* As we discussed, CaseTestValue/DomainValue are pretty messy and need
to be rethought. That might not get done for v10 though.

Yea, I'm disinclined to tackle this just now, there's enough other stuff
pending - but I'm willing to tackle it for v11 unless you beat me to it.

* I'm not very happy that execSRF.c has two somewhat different
implementations of largely similar functionality, and
SetExprState.elidedFuncState seems like a wart. That's mostly a
pre-existing problem of course. I'm satisfied with leaving it as it is
for now, but eventually some refactoring there would be good.

Yea, that's not pretty. Given the historical difference in behaviour
between SRF and ROWS FROM (the latter always materializes, the former
only when the set function does so itself), I'm not sure they can easily
be simplified.

I'm not really sure how to get rid of the issue underlying
elidedFuncState? I mean we could move that case into nodeFunctionscan,
above ExecMakeTableFunctionResult's level, but that doesn't really seem
like an improvement.

I think there's some argument to be made that we should move all of
execSRF.c's logic to their respective callsites. There's not really
much shared code: ExecEvalFuncArgs(), tupledesc_match(), init_sexpr() -
and at least the latter would even become a bit simpler if we didn't
support both callers.

Thanks a lot for your work on the patch!

And pushed.

- Andres

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

#118Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#117)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

- The !caseExpr->defresult result branch is currently unreachable (and
its equivalent was before the patch) because transformCaseExpr()
generates a default expression. I'm inclined to replace the dead code
with an assertion. Any reason not to do that?

Ah-hah. I'd noted that that code wasn't being reached when I did some
code coverage checks this morning, but I hadn't found the cause yet.
Yeah, replacing the if-test with an assert seems fine.

I think there's some argument to be made that we should move all of
execSRF.c's logic to their respective callsites. There's not really
much shared code: ExecEvalFuncArgs(), tupledesc_match(), init_sexpr() -
and at least the latter would even become a bit simpler if we didn't
support both callers.

Possibly. All that code could stand to be rethought, probably, now that
its mission is just to handle the SRF case. But at least all the cruft is
in one place for the moment. And I don't think it's anything we have to
fix before v10, it's just cosmetic.

Thanks a lot for your work on the patch!

You're welcome!

regards, tom lane

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

#119Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#116)
Re: WIP: Faster Expression Processing v4

On 2017-03-25 12:22:15 -0400, Tom Lane wrote:

More random musing ... have you considered making the jump-target fields
in expressions be relative rather than absolute indexes? That is,
EEO_JUMP would look like

op += (stepno); \
EEO_DISPATCH(); \

instead of

op = &state->steps[stepno]; \
EEO_DISPATCH(); \

I have not carried out a full patch to make this work, but just making
that one change and examining the generated assembly code looks promising.
Instead of this

movslq 40(%r14), %r8
salq $6, %r8
addq 24(%rbx), %r8
movq %r8, %r14
jmp *(%r8)

we get this

movslq 40(%r14), %rax
salq $6, %rax
addq %rax, %r14
jmp *(%r14)

That seems like a good idea. I've not done this in the committed
version (and I don't think we necessarily need to this before the
release), but fo rthe future it seems like a good plan. It makes sense
that it's faster - there's no need to reference state->steps.

which certainly looks like it ought to be faster. Also, the real reason
I got interested in this at all is that with relative jumps, groups of
steps would be position-independent within the steps array, which would
enable some compile-time tricks that seem impractical with the current
definition.

Indeed.

BTW, now that I've spent a bit of time looking at the generated assembly
code, I'm kind of disinclined to believe any arguments about how we have
better control over branch prediction with the jump-threading
implementation.

I measured the performance difference between using it and not using it,
and it came out a pretty clear plus. On gcc 6.3, gcc master snapshot,
and clang-3.9. It's not just that more jumps are duplicated, it's also
that the switch() always adds a boundary check.

At least with current gcc (6.3.1 on Fedora 25) at -O2,
what I see is multiple places jumping to the same indirect jump
instruction :-(. It's not a total disaster: as best I can tell, all the
uses of EEO_JUMP remain distinct. But gcc has chosen to implement about
40 of the 71 uses of EEO_NEXT by jumping to the same couple of
instructions that increment the "op" register and then do an indirect
jump :-(.

Yea, I see some of that too - "usually" when there's more than just the
jump in common. I think there's some gcc variables that influence this
(min-crossjump-insns (5), max-goto-duplication-insns (8)). Might be
worthwhile experimenting with setting them locally via a pragma or such.
I think Aants wanted to experiment with that, too.

Then there's also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71785
which causes some forms of computed goto (not ours I think) to be
deoptimized in gcc.

Greetings,

Andres Freund

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

#120Ants Aasma
ants.aasma@eesti.ee
In reply to: Andres Freund (#119)
Re: WIP: Faster Expression Processing v4

On Sun, Mar 26, 2017 at 12:22 AM, Andres Freund <andres@anarazel.de> wrote:

At least with current gcc (6.3.1 on Fedora 25) at -O2,
what I see is multiple places jumping to the same indirect jump
instruction :-(. It's not a total disaster: as best I can tell, all the
uses of EEO_JUMP remain distinct. But gcc has chosen to implement about
40 of the 71 uses of EEO_NEXT by jumping to the same couple of
instructions that increment the "op" register and then do an indirect
jump :-(.

Yea, I see some of that too - "usually" when there's more than just the
jump in common. I think there's some gcc variables that influence this
(min-crossjump-insns (5), max-goto-duplication-insns (8)). Might be
worthwhile experimenting with setting them locally via a pragma or such.
I think Aants wanted to experiment with that, too.

I haven't had the time to research this properly, but initial tests
show that with GCC 6.2 adding

#pragma GCC optimize ("no-crossjumping")

fixes merging of the op tail jumps.

Some quick and dirty benchmarking suggests that the benefit for the
interpreter is about 15% (5% speedup on a workload that spends 1/3 in
ExecInterpExpr). My idea of prefetching op->resnull/resvalue to local
vars before the indirect jump is somewhere between a tiny benefit and
no effect, certainly not worth introducing extra complexity. Clang 3.8
does the correct thing out of the box and is a couple of percent
faster than GCC with the pragma.

Regards,
Ants Aasma

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

#121Andres Freund
andres@anarazel.de
In reply to: Ants Aasma (#120)
Re: WIP: Faster Expression Processing v4

On March 25, 2017 4:56:11 PM PDT, Ants Aasma <ants.aasma@eesti.ee> wrote:

On Sun, Mar 26, 2017 at 12:22 AM, Andres Freund <andres@anarazel.de>
wrote:

At least with current gcc (6.3.1 on Fedora 25) at -O2,
what I see is multiple places jumping to the same indirect jump
instruction :-(. It's not a total disaster: as best I can tell, all

the

uses of EEO_JUMP remain distinct. But gcc has chosen to implement

about

40 of the 71 uses of EEO_NEXT by jumping to the same couple of
instructions that increment the "op" register and then do an

indirect

jump :-(.

Yea, I see some of that too - "usually" when there's more than just

the

jump in common. I think there's some gcc variables that influence

this

(min-crossjump-insns (5), max-goto-duplication-insns (8)). Might be
worthwhile experimenting with setting them locally via a pragma or

such.

I think Aants wanted to experiment with that, too.

I haven't had the time to research this properly, but initial tests
show that with GCC 6.2 adding

#pragma GCC optimize ("no-crossjumping")

fixes merging of the op tail jumps.

Some quick and dirty benchmarking suggests that the benefit for the
interpreter is about 15% (5% speedup on a workload that spends 1/3 in
ExecInterpExpr). My idea of prefetching op->resnull/resvalue to local
vars before the indirect jump is somewhere between a tiny benefit and
no effect, certainly not worth introducing extra complexity. Clang 3.8
does the correct thing out of the box and is a couple of percent
faster than GCC with the pragma.

That's large enough to be worth doing (although I recall you seeing all jumps commonalized). We should probably do this on a per function basis however (either using pragma push option, or function attributes).

Andres

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

#122Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#121)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On March 25, 2017 4:56:11 PM PDT, Ants Aasma <ants.aasma@eesti.ee> wrote:

I haven't had the time to research this properly, but initial tests
show that with GCC 6.2 adding

#pragma GCC optimize ("no-crossjumping")

fixes merging of the op tail jumps.

Some quick and dirty benchmarking suggests that the benefit for the
interpreter is about 15% (5% speedup on a workload that spends 1/3 in
ExecInterpExpr). My idea of prefetching op->resnull/resvalue to local
vars before the indirect jump is somewhere between a tiny benefit and
no effect, certainly not worth introducing extra complexity. Clang 3.8
does the correct thing out of the box and is a couple of percent
faster than GCC with the pragma.

That's large enough to be worth doing (although I recall you seeing all jumps commonalized). We should probably do this on a per function basis however (either using pragma push option, or function attributes).

Seems like it would be fine to do it on a per-file basis. If you're
worried about pessimizing the out-of-line subroutines, we could move
those to a different file --- it's pretty questionable that they're
in execExprInterp.c in the first place, considering they're meant to be
used by more than just that execution method.

regards, tom lane

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

#123Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#122)
Re: WIP: Faster Expression Processing v4

On 2017-03-25 23:51:45 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On March 25, 2017 4:56:11 PM PDT, Ants Aasma <ants.aasma@eesti.ee> wrote:

I haven't had the time to research this properly, but initial tests
show that with GCC 6.2 adding

#pragma GCC optimize ("no-crossjumping")

fixes merging of the op tail jumps.

Some quick and dirty benchmarking suggests that the benefit for the
interpreter is about 15% (5% speedup on a workload that spends 1/3 in
ExecInterpExpr). My idea of prefetching op->resnull/resvalue to local
vars before the indirect jump is somewhere between a tiny benefit and
no effect, certainly not worth introducing extra complexity. Clang 3.8
does the correct thing out of the box and is a couple of percent
faster than GCC with the pragma.

That's large enough to be worth doing (although I recall you seeing all jumps commonalized). We should probably do this on a per function basis however (either using pragma push option, or function attributes).

Seems like it would be fine to do it on a per-file basis.

I personally find per-function annotation ala
__attribute__((optimize("no-crossjumping")))
cleaner anyway. I tested that, and it seems to work.

Obviously we'd have to hide that behind a configure test. Could also do
tests based on __GNUC__ / __GNUC_MINOR__, but that seems uglier.

If you're
worried about pessimizing the out-of-line subroutines, we could move
those to a different file --- it's pretty questionable that they're
in execExprInterp.c in the first place, considering they're meant to be
used by more than just that execution method.

I indeed am, but having the code in the same file has a minor advantage:
It allows the compiler to partially inline them, if it feels like it
(e.g. moving null checks inline).

Greetings,

Andres Freund

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

#124Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#123)
1 attachment(s)
Re: WIP: Faster Expression Processing v4

On 2017-03-25 20:59:27 -0700, Andres Freund wrote:

On 2017-03-25 23:51:45 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On March 25, 2017 4:56:11 PM PDT, Ants Aasma <ants.aasma@eesti.ee> wrote:

I haven't had the time to research this properly, but initial tests
show that with GCC 6.2 adding

#pragma GCC optimize ("no-crossjumping")

fixes merging of the op tail jumps.

Some quick and dirty benchmarking suggests that the benefit for the
interpreter is about 15% (5% speedup on a workload that spends 1/3 in
ExecInterpExpr). My idea of prefetching op->resnull/resvalue to local
vars before the indirect jump is somewhere between a tiny benefit and
no effect, certainly not worth introducing extra complexity. Clang 3.8
does the correct thing out of the box and is a couple of percent
faster than GCC with the pragma.

That's large enough to be worth doing (although I recall you seeing all jumps commonalized). We should probably do this on a per function basis however (either using pragma push option, or function attributes).

Seems like it would be fine to do it on a per-file basis.

I personally find per-function annotation ala
__attribute__((optimize("no-crossjumping")))
cleaner anyway. I tested that, and it seems to work.

Obviously we'd have to hide that behind a configure test. Could also do
tests based on __GNUC__ / __GNUC_MINOR__, but that seems uglier.

Checking for this isn't entirely pretty - see my attached attempt at
doing so. I considered hiding
__attribute__((optimize("no-crossjumping"))) in execInterpExpr.c behind
a macro (like PG_DISABLE_CROSSJUMPING), but I don't really think that
makes things better.

Comments?

Greetings,

Andres Freund

Attachments:

0001-Disable-gcc-s-crossjumping-optimization-for-ExecInte.patchtext/x-patch; charset=us-asciiDownload
From ce42086871ebfcbb7ae52266e9c4549b5958e80b Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 26 Mar 2017 22:38:21 -0700
Subject: [PATCH] Disable gcc's crossjumping optimization for ExecInterpExpr().

GCC merges code in ExecInterpExpr() too aggressively, reducing branch
prediction accuracy. As this is performance critical code, go through
the trouble of disabling the relevant optimization for the function.

To do so, have to detect whether the compiler support
__attribute__((optimize("no-crossjumping"))) as a function
annotation. Do so in configure.

Discussion: https://postgr.es/m/20170326035927.5mubkfdtaqlrgm2d@alap3.anarazel.de
---
 config/c-compiler.m4                  | 35 ++++++++++++++++++++++++++++++
 configure                             | 41 +++++++++++++++++++++++++++++++++++
 configure.in                          |  1 +
 src/backend/executor/execExprInterp.c |  9 ++++++++
 src/include/pg_config.h.in            |  4 ++++
 src/include/pg_config.h.win32         |  4 ++++
 6 files changed, 94 insertions(+)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 3321f226f3..ac72539dec 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -297,6 +297,41 @@ fi])# PGAC_C_COMPUTED_GOTO
 
 
 
+# PGAC_C_FUNCATTR_NO_CROSSJUMPING
+# -----------------------
+# Check if the C compiler understands the
+# __attribute__((optimize("no-crossjumping"))) function attribute.
+# Define HAVE_FUNCATTR_NO_CROSSJUMPING if so.
+#
+# At some later point it might make sense to generalize this so we can
+# check for other optimization flags, but so far there's no need for
+# that.
+#
+# Have to enable Werror, as some compilers (e.g. clang) would
+# otherwise just warn about an unknown type of attribute.
+AC_DEFUN([PGAC_C_FUNCATTR_NO_CROSSJUMPING],
+[AC_CACHE_CHECK([for function attribute disabling crossjumping optimizations], pgac_cv_funcattr_no_crossjumping,
+[ac_save_c_werror_flag=$ac_c_werror_flag
+ac_c_werror_flag=yes
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+extern int testme(void);
+int
+__attribute__((optimize("no-crossjumping")))
+testme(void)
+{
+    return 0;
+}
+])],
+[pgac_cv_funcattr_no_crossjumping=yes],
+[pgac_cv_funcattr_no_crossjumping=no])
+ac_c_werror_flag=$ac_save_c_werror_flag])
+if test x"$pgac_cv_funcattr_no_crossjumping" = xyes ; then
+AC_DEFINE(HAVE_FUNCATTR_NO_CROSSJUMPING, 1,
+          [Define to 1 if your compiler understands __attribute__((optimize("no-crossjumping"))).])
+fi])# PGAC_C_FUNCATTR_NO_CROSSJUMPING
+
+
+
 # PGAC_C_VA_ARGS
 # --------------
 # Check if the C compiler understands C99-style variadic macros,
diff --git a/configure b/configure
index 4b8229e959..87c4799be3 100755
--- a/configure
+++ b/configure
@@ -11835,6 +11835,47 @@ if test x"$pgac_cv_computed_goto" = xyes ; then
 $as_echo "#define HAVE_COMPUTED_GOTO 1" >>confdefs.h
 
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for function attribute disabling crossjumping optimizations" >&5
+$as_echo_n "checking for function attribute disabling crossjumping optimizations... " >&6; }
+if ${pgac_cv_funcattr_no_crossjumping+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+ac_c_werror_flag=yes
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+extern int testme(void);
+int
+__attribute__((optimize("no-crossjumping")))
+testme(void)
+{
+    return 0;
+}
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv_funcattr_no_crossjumping=yes
+else
+  pgac_cv_funcattr_no_crossjumping=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_funcattr_no_crossjumping" >&5
+$as_echo "$pgac_cv_funcattr_no_crossjumping" >&6; }
+if test x"$pgac_cv_funcattr_no_crossjumping" = xyes ; then
+
+$as_echo "#define HAVE_FUNCATTR_NO_CROSSJUMPING 1" >>confdefs.h
+
+fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __VA_ARGS__" >&5
 $as_echo_n "checking for __VA_ARGS__... " >&6; }
 if ${pgac_cv__va_args+:} false; then :
diff --git a/configure.in b/configure.in
index 6c74214171..dc677a5dae 100644
--- a/configure.in
+++ b/configure.in
@@ -1336,6 +1336,7 @@ PGAC_C_BUILTIN_BSWAP64
 PGAC_C_BUILTIN_CONSTANT_P
 PGAC_C_BUILTIN_UNREACHABLE
 PGAC_C_COMPUTED_GOTO
+PGAC_C_FUNCATTR_NO_CROSSJUMPING
 PGAC_C_VA_ARGS
 PGAC_STRUCT_TIMEZONE
 PGAC_UNION_SEMUN
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 982d16c6c8..3a4e8f759b 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -269,8 +269,17 @@ ExecReadyInterpretedExpr(ExprState *state)
  * As a special case, return the dispatch table's address if state is NULL.
  * This is used by ExecInitInterpreter to set up the dispatch_table global.
  * (Only applies when EEO_USE_COMPUTED_GOTO is defined.)
+ *
+ *
+ * In various versions GCC merges parts of the various branches below - that's
+ * harmful because it reduces branch prediction accuracy. As this is
+ * performance critical, go to the trouble of disabling the relevant
+ * optimization if necessary (only gcc has that option).
  */
 static Datum
+#ifdef HAVE_FUNCATTR_NO_CROSSJUMPING
+__attribute__((optimize("no-crossjumping")))
+#endif
 ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 {
 	ExprEvalStep *op;
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index e1c1c9e9b4..93883780f4 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -188,6 +188,10 @@
 /* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
 #undef HAVE_FSEEKO
 
+/* Define to 1 if your compiler understands
+   __attribute__((optimize("no-crossjumping"))). */
+#undef HAVE_FUNCATTR_NO_CROSSJUMPING
+
 /* Define to 1 if your compiler understands __func__. */
 #undef HAVE_FUNCNAME__FUNC
 
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 5af8369202..8c560883dc 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -139,6 +139,10 @@
 /* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
 #define HAVE_FSEEKO 1
 
+/* Define to 1 if your compiler understands
+   __attribute__((optimize("no-crossjumping"))). */
+#undef HAVE_FUNCATTR_NO_CROSSJUMPING
+
 /* Define to 1 if your compiler understands __func__. */
 //#define HAVE_FUNCNAME__FUNC 1
 
-- 
2.12.0.264.gd6db3f2165.dirty

#125Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#124)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

I personally find per-function annotation ala
__attribute__((optimize("no-crossjumping")))
cleaner anyway. I tested that, and it seems to work.

Obviously we'd have to hide that behind a configure test. Could also do
tests based on __GNUC__ / __GNUC_MINOR__, but that seems uglier.

Agreed.

Checking for this isn't entirely pretty - see my attached attempt at
doing so. I considered hiding
__attribute__((optimize("no-crossjumping"))) in execInterpExpr.c behind
a macro (like PG_DISABLE_CROSSJUMPING), but I don't really think that
makes things better.

I think it would, primarily because if we find out that some other compiler
spells this differently, we could handle it totally within configure.

Isn't our practice to put __attribute__ at the end of a function
declaration or definition, not in the middle someplace?

regards, tom lane

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

#126Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#125)
Re: WIP: Faster Expression Processing v4

On 2017-03-27 09:33:43 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

Checking for this isn't entirely pretty - see my attached attempt at
doing so. I considered hiding
__attribute__((optimize("no-crossjumping"))) in execInterpExpr.c behind
a macro (like PG_DISABLE_CROSSJUMPING), but I don't really think that
makes things better.

I think it would, primarily because if we find out that some other compiler
spells this differently, we could handle it totally within configure.

I'm unconvinced that we could sensibly map different compiler's options
on the same option name - I'd be surprised if they would have a similar
enough effect.

Isn't our practice to put __attribute__ at the end of a function
declaration or definition, not in the middle someplace?

We could move it to the declaration - which doesn't seem like an
improvement here, given that it's about optimization not API changes -
but in definitions you can't move it after the function name:
static Datum
ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
#ifdef HAVE_FUNCATTR_NO_CROSSJUMPING
__attribute__((optimize("no-crossjumping")))
#endif
:
/home/andres/src/postgresql/src/backend/executor/execExprInterp.c:279:1: error: attributes should be specified before the declarator in a function definition

Greetings,

Andres Freund

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

#127Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#126)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-27 09:33:43 -0400, Tom Lane wrote:

I think it would, primarily because if we find out that some other compiler
spells this differently, we could handle it totally within configure.

I'm unconvinced that we could sensibly map different compiler's options
on the same option name - I'd be surprised if they would have a similar
enough effect.

[ shrug... ] OK, next question is whether pgindent treats this
layout sanely.

regards, tom lane

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

#128Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#127)
Re: WIP: Faster Expression Processing v4

On 2017-03-27 11:22:40 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-27 09:33:43 -0400, Tom Lane wrote:

I think it would, primarily because if we find out that some other compiler
spells this differently, we could handle it totally within configure.

I'm unconvinced that we could sensibly map different compiler's options
on the same option name - I'd be surprised if they would have a similar
enough effect.

[ shrug... ] OK, next question is whether pgindent treats this
layout sanely.

The patch was run through pgindent, i.e. it seems to be ok with it.

- Andres

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

#129Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#128)
Re: WIP: Faster Expression Processing v4

As to the point of whether it actually helps or not ...

on gcc 6.3.1 (Fedora 25), yes it does. Seems to be one "jmp *something"
per EEO_NEXT or EEO_JUMP.

on gcc 4.4.7 (RHEL 6), it makes things *WORSE*. We go from about half of
the dispatches getting routed through a common location, to *all* of them
(except one; for some odd reason the first EEO_NEXT in EEOP_NULLIF
survives as a separate jump). This seems like a bug, but there it is.

So this means we'd need some serious research to decide whether to apply
it. And I'm suspecting we'd end up with a compiler version test.

regards, tom lane

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

#130Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#129)
Re: WIP: Faster Expression Processing v4

I wrote:

As to the point of whether it actually helps or not ...
on gcc 4.4.7 (RHEL 6), it makes things *WORSE*. We go from about half of
the dispatches getting routed through a common location, to *all* of them
(except one; for some odd reason the first EEO_NEXT in EEOP_NULLIF
survives as a separate jump). This seems like a bug, but there it is.

So after a bit of googling, this is a very longstanding complaint:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39284
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71785

(hm, think I know the second submitter)

I'm not sure we should be relying on a gcc bug fix that's barely four
months old. Even assuming it fixes the issue without new regressions,
most people are not going to have it anytime soon.

My feeling at this point is that we might be better off disabling
the computed-goto case by default. At the very least, we're going
to need a version check that restricts it to latest gcc.

regards, tom lane

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

#131Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#129)
Re: WIP: Faster Expression Processing v4

On 2017-03-27 11:52:05 -0400, Tom Lane wrote:

As to the point of whether it actually helps or not ...

on gcc 6.3.1 (Fedora 25), yes it does. Seems to be one "jmp *something"
per EEO_NEXT or EEO_JUMP.

on gcc 4.4.7 (RHEL 6), it makes things *WORSE*. We go from about half of
the dispatches getting routed through a common location, to *all* of them
(except one; for some odd reason the first EEO_NEXT in EEOP_NULLIF
survives as a separate jump). This seems like a bug, but there it is.

Gah :(. I have gcc 5,6,7 here, but nothing earlier. I'm now inclined
to go the version check routine, for the individual versions we can (and
want) confirm this on... I'm not too concerned about not doing so on
gcc 4.4 or older...

So this means we'd need some serious research to decide whether to apply
it. And I'm suspecting we'd end up with a compiler version test.

Indeed.

- Andres

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

#132Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#130)
Re: WIP: Faster Expression Processing v4

On 2017-03-27 12:18:37 -0400, Tom Lane wrote:

I wrote:

As to the point of whether it actually helps or not ...
on gcc 4.4.7 (RHEL 6), it makes things *WORSE*. We go from about half of
the dispatches getting routed through a common location, to *all* of them
(except one; for some odd reason the first EEO_NEXT in EEOP_NULLIF
survives as a separate jump). This seems like a bug, but there it is.

So after a bit of googling, this is a very longstanding complaint:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39284
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71785

(hm, think I know the second submitter)

I don't think that's precisely the same issue - as long as some of the
goto branches survive, we're not hitting the full brunt of the compgoto
thing. I think we're essentially avoiding some of that because we're
"precomputing" the dispatch_table lookup.

To count the number of jumps I used:
gdb -batch -ex 'disassemble/s ExecInterpExpr' execExprInterp.o|grep jmpq|grep -v ExecInterpExpr|wc -l
which'll only include indirect jumps (since otherwise jumps will look
like "jmpq 0x35f2 <ExecInterpExpr+2066>")

standard flags -fno-crossjumping
gcc-5 (5.4.1-8): 34 82
gcc-6 (6.3.0-8): 34 82
gcc-7 (7.0.1): 71 108
gcc-snapshot: 72 108

So that doesn't look too bad.

My feeling at this point is that we might be better off disabling
the computed-goto case by default. At the very least, we're going
to need a version check that restricts it to latest gcc.

In my measurements it's still faster in at least gcc-5/6, even without
the option (largely because it avoids array bounds checks on the jump
table built for the switch).

Greetings,

Andres Freund

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

#133Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#132)
Re: WIP: Faster Expression Processing v4

Andres Freund <andres@anarazel.de> writes:

On 2017-03-27 12:18:37 -0400, Tom Lane wrote:

My feeling at this point is that we might be better off disabling
the computed-goto case by default. At the very least, we're going
to need a version check that restricts it to latest gcc.

In my measurements it's still faster in at least gcc-5/6, even without
the option (largely because it avoids array bounds checks on the jump
table built for the switch).

Hm. What test cases are you using?

regards, tom lane

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

#134Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#133)
Re: WIP: Faster Expression Processing v4

On 2017-03-27 13:30:11 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2017-03-27 12:18:37 -0400, Tom Lane wrote:

My feeling at this point is that we might be better off disabling
the computed-goto case by default. At the very least, we're going
to need a version check that restricts it to latest gcc.

In my measurements it's still faster in at least gcc-5/6, even without
the option (largely because it avoids array bounds checks on the jump
table built for the switch).

Hm. What test cases are you using?

I used tpc-h - seems like a realistic enough testcase.

Andres

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