Thread: Over-optimization in ExecEvalWholeRowVar

Over-optimization in ExecEvalWholeRowVar

From
Tom Lane
Date:
This example in the regression database is a simplified version of a bug
I was shown off-list:

regression=# select (
select q from
( select 1,2,3 where f1>0 union all select 4,5,6.0 where f1<=0
) q
)
from int4_tbl;
ERROR:  record type has not been registered

The reason for the problem is that ExecEvalWholeRowVar is designed
to bless the Var's record type (via assign_record_type_typmod) just
once per query.  That works fine, as long as the source tuple slot
is the exact same TupleTableSlot throughout the query.  But in the
above example, we have an Append plan node for the UNION ALL, which
will pass back its children's result slots directly --- so at the
level where "q" is being evaluated, we see first the first leaf SELECT's
output slot (which gets blessed) and then the second leaf SELECT's
output slot (which doesn't).  That leads to generating a composite
Datum with typmod -1 ... oops.

I can reproduce this bug in all active branches.  Probably the reason
it hasn't been identified long since is that in most scenarios the
planner won't use a whole-row Var at all in cases like this, but will
prefer to build RowExprs.  (In particular, the inconsistent data types
of the last sub-select output columns are necessary to trigger the bug
in this example.)

The easy fix is to move the blessing code from ExecEvalWholeRowVar
into ExecEvalWholeRowFast.  (ExecEvalWholeRowSlow doesn't need it
since it doesn't deal with RECORD cases.)  That'll add a usually-useless
comparison or two per row cycle, which is unlikely to be measurable.

A bigger-picture problem is that we have other once-per-query tests in
ExecEvalWholeRowVar, and ExecEvalScalarVar too, whose validity should be
questioned given this bug.  I think they are all right, though, because
those tests are concerned with validating the source rowtype, which should
not change across Append children.  The bug here is because we're assuming
not just that the input rowtype is abstractly the same across all rows,
but that the exact same TupleTableSlot is used to return every source row.
        regards, tom lane



Re: Over-optimization in ExecEvalWholeRowVar

From
Merlin Moncure
Date:
On Fri, Jul 11, 2014 at 2:43 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> This example in the regression database is a simplified version of a bug
> I was shown off-list:
>
> regression=# select (
> select q from
> ( select 1,2,3 where f1>0
>   union all
>   select 4,5,6.0 where f1<=0
> ) q
> )
> from int4_tbl;
> ERROR:  record type has not been registered

Thanks Tom!

If anybody gets hit by this, the workaround I use is to put the inner
subquery as a CTE:
select (select q from(    with data as (    select 1,2,3 where f1>0    union all    select 4,5,6.0 where f1<=0  )
select* from data) q)from int4_tbl;
 

merlin