Thread

Commits

  1. Fix interaction of CASE and ArrayCoerceExpr.

  2. Teach eval_const_expressions() to handle some more cases.

  3. Support arrays over domains.

  1. BUG #15471: psql 11 array concatenation in CASE takes on values from the CASE expression when using enum_range

    The Post Office <noreply@postgresql.org> — 2018-10-30T07:39:52Z

    The following bug has been logged on the website:
    
    Bug reference:      15471
    Logged by:          Matt Williams
    Email address:      pg@mattyw.net
    PostgreSQL version: 11.0
    Operating system:   Confirmed on MacOs and Alpine Linux
    Description:        
    
    Below is an example .sql file that replicates the problem. Put simply, when
    we array concat with enum_range in the result of a CASE statement the
    concatenation takes the expression from the CASE statement, not the enum
    range.
    
    if you run the below .sql file against postgres 10 and 11 you'll see ex1 and
    ex2 return the same array. However ex3,4,5 all show arrays concatenated with
    the value in the CASE condition (TRUE, 'true' and 1). They all return the
    expected array under postgres 9.6 and 10. The change only appears in
    postgres 11.
    	
    The last example (ex6) shows the CASE rewritten to remove the expression,
    ex6 works the same under postgres 10 and 11.
    
    These results for ex 3, 4 and 5 were surprising, I couldn't find anything in
    the docs that alludes to this change, and I've been unable to find any
    online discussion regarding this.
    
    --- Start of sql file ---
    SELECT version();
    
    DROP TYPE IF EXISTS myenum;
    
    CREATE TYPE myenum AS ENUM ('e', 'f', 'g');
    
    SELECT enum_range(NULL::myenum);
    
    SELECT ARRAY['a', 'b', 'c', 'd'] || enum_range(NULL::myenum)::text[] as
    ex1;
    
    SELECT
    	CASE TRUE
    		WHEN TRUE THEN ARRAY['a', 'b', 'c', 'd'] || ARRAY['e', 'f', 'g']
    		WHEN FALSE THEN ARRAY['a', 'b', 'c', 'd']
    END as ex2;
    
    -- All of the above works as expected
    
    SELECT
    	CASE TRUE
    		WHEN TRUE THEN ARRAY['a', 'b', 'c', 'd'] ||
    enum_range(NULL::myenum)::text[]
    		WHEN FALSE THEN ARRAY['a', 'b', 'c', 'd']
    END as ex3;
    
    -- In the above case statement we'd expected the output: {a,b,c,d,e,f,g}
    -- However under postres 11 we get the following output: {a,b,c,d,t,t,t}
    
    
    SELECT
    	CASE 'true'
    		WHEN 'true' THEN ARRAY['a', 'b', 'c', 'd'] ||
    enum_range(NULL::myenum)::text[]
    		WHEN 'false' THEN ARRAY['a', 'b', 'c', 'd']
    END as ex4;
    
    -- Postgres 10: {a,b,c,d,e,f,g}
    -- Postgres 11: {a,b,c,d,true,true,true}
    
    
    SELECT
    	CASE 1
    		WHEN 1 THEN ARRAY['a', 'b', 'c', 'd'] ||
    enum_range(NULL::myenum)::text[]
    		WHEN 2 THEN ARRAY['a', 'b', 'c', 'd']
    END as ex5;
    
    -- Postgres 10: {a,b,c,d,e,f,g}
    -- Postgres 11: {a,b,c,d,1,1,1}
    
    SELECT
    	CASE
    		WHEN TRUE THEN ARRAY['a', 'b', 'c', 'd'] ||
    enum_range(NULL::myenum)::text[]
    		ELSE ARRAY['a', 'b', 'c', 'd']
    END as ex6;
    
    -- In this form we get the same answer for both postgres 10 and 11:
    {a,b,c,d,e,f,g}
    
    
  2. Re: BUG #15471: psql 11 array concatenation in CASE takes on values from the CASE expression when using enum_range

    Tom Lane <tgl@sss.pgh.pa.us> — 2018-10-30T11:59:35Z

    =?utf-8?q?PG_Bug_reporting_form?= <noreply@postgresql.org> writes:
    > Below is an example .sql file that replicates the problem. Put simply, when
    > we array concat with enum_range in the result of a CASE statement the
    > concatenation takes the expression from the CASE statement, not the enum
    > range.
    
    Wow, that's ... bizarre.  I'm thinking that we probably did something
    silly in the big expression-execution rewrite, but it's not clear exactly
    where.  Anyway, will look into it if Andres doesn't beat me to it.
    
    Some poking at the examples finds that it seems to be necessary to have
    the coercion to text[] to show the problem, eg this seems fine:
    
    SELECT
      CASE 1
        WHEN 1 THEN ARRAY['e', 'f']::myenum[] || enum_range(NULL::myenum)
        WHEN 2 THEN ARRAY['f', 'g']::myenum[]
    END;
    
    What's really strange is that EXPLAIN VERBOSE shows that the planner has
    correctly elided the CASE altogether in each case, eg for ex5:
    
    explain verbose SELECT
      CASE 1
        WHEN 1 THEN ARRAY['a', 'b', 'c', 'd'] || enum_range(NULL::myenum)::text[]
        WHEN 2 THEN ARRAY['a', 'b', 'c', 'd']
    END as ex5;
                                  QUERY PLAN                               
    -----------------------------------------------------------------------
     Result  (cost=0.00..0.07 rows=1 width=32)
       Output: ('{a,b,c,d}'::text[] || (enum_range(NULL::myenum))::text[])
    (2 rows)
    
    So how come it's still affecting the result of the array coercion?
    
    Looking at the generated plan tree with debug_print_plan narrows
    things down quite a bit: the array coercion looks like, eg,
    
                   {ARRAYCOERCEEXPR 
                   :arg 
                      {FUNCEXPR 
                      :funcid 3531 
                      :funcresulttype 197116 
                      :funcretset false 
                      :funcvariadic false 
                      :funcformat 0 
                      :funccollid 0 
                      :inputcollid 0 
                      :args (
                         {CONST 
                         :consttype 197117 
                         :consttypmod -1 
                         :constcollid 0 
                         :constlen 4 
                         :constbyval true 
                         :constisnull true 
                         :location 72 
                         :constvalue <>
                         }
                      )
                      :location 61
                      }
                   :elemexpr 
                      {COERCEVIAIO 
                      :arg 
                         {CONST 
                         :consttype 23 
                         :consttypmod -1 
                         :constcollid 0 
                         :constlen 4 
                         :constbyval true 
                         :constisnull false 
                         :location 14 
                         :constvalue 4 [ 1 0 0 0 0 0 0 0 ]
                         }
                      :resulttype 25 
                      :resultcollid 100 
                      :coerceformat 1 
                      :location 85
                      }
                   :resulttype 1009 
                   :resulttypmod -1 
                   :resultcollid 100 
                   :coerceformat 1 
                   :location 85
                   }
    
    So somehow the planner is messing up and inserting an outer CaseTestExpr
    value as the source of the elemexpr's coercion expression.  IIRC,
    ruleutils doesn't print the elemexpr at all, which is how come we're not
    seeing the mistake in the EXPLAIN output.
    
    However, ArrayCoerceExpr has abused CaseTestExpr that way for awhile, so
    it's still not very clear why it broke in v11 and not before ...
    
    			regards, tom lane
    
    
    
  3. Re: BUG #15471: psql 11 array concatenation in CASE takes on values from the CASE expression when using enum_range

    Andrew Gierth <andrew@tao11.riddles.org.uk> — 2018-10-30T12:49:25Z

    >>>>> "Tom" == Tom Lane <tgl@sss.pgh.pa.us> writes:
    
     >> Below is an example .sql file that replicates the problem. Put
     >> simply, when we array concat with enum_range in the result of a CASE
     >> statement the concatenation takes the expression from the CASE
     >> statement, not the enum range.
    
     Tom> Wow, that's ... bizarre. I'm thinking that we probably did
     Tom> something silly in the big expression-execution rewrite, but it's
     Tom> not clear exactly where. Anyway, will look into it if Andres
     Tom> doesn't beat me to it.
    
    I took a look, and what I'm seeing suggests that commit 3decd150a2d
    might possibly be relevant here (at least to explain why it breaks in 11
    but not 10).
    
    What's going on in eval_const_expressions_mutator is that
    context->case_val is set when recursing into the elemexpr in the
    ArrayCoerceExpr case, so when that sees a CaseTestExpr inside that, it
    replaces it (incorrectly).
    
    -- 
    Andrew (irc:RhodiumToad)
    
    
    
  4. Re: BUG #15471: psql 11 array concatenation in CASE takes on values from the CASE expression when using enum_range

    Tom Lane <tgl@sss.pgh.pa.us> — 2018-10-30T13:30:54Z

    Andrew Gierth <andrew@tao11.riddles.org.uk> writes:
    > "Tom" == Tom Lane <tgl@sss.pgh.pa.us> writes:
    >  Tom> Wow, that's ... bizarre. I'm thinking that we probably did
    >  Tom> something silly in the big expression-execution rewrite, but it's
    >  Tom> not clear exactly where. Anyway, will look into it if Andres
    >  Tom> doesn't beat me to it.
    
    > I took a look, and what I'm seeing suggests that commit 3decd150a2d
    > might possibly be relevant here (at least to explain why it breaks in 11
    > but not 10).
    
    Good guess but I don't think that changed anything.  It looks to me
    like the culprit is commit c12d570fa, so it's my bug not Andres' :-(.
    Before that, we weren't abusing CaseTestExpr as part of ArrayCoerceExpr.
    
    Probably, the fix is to have eval_const_expressions be sure to
    save/clear/restore context->case_val when dealing with ArrayCoerceExpr's
    elemexpr.  That possibly does mean that we have to undo the generic
    processing of ArrayCoerceExpr, but it was already broken before that
    transformation.
    
    This is all some more fuel for the idea that we need a less messy
    substitute for CaseTestExpr.  As it happens, I was just fooling with
    that yesterday, and hope to have something to post soon.  But it'll
    be too invasive to back-patch.  I'll try to fix this particular
    problem more locally.
    
    			regards, tom lane
    
    
    
  5. Re: BUG #15471: psql 11 array concatenation in CASE takes on values from the CASE expression when using enum_range

    Andres Freund <andres@anarazel.de> — 2018-10-30T17:42:00Z

    Hi,
    
    On 2018-10-30 07:59:35 -0400, Tom Lane wrote:
    > =?utf-8?q?PG_Bug_reporting_form?= <noreply@postgresql.org> writes:
    > > Below is an example .sql file that replicates the problem. Put simply, when
    > > we array concat with enum_range in the result of a CASE statement the
    > > concatenation takes the expression from the CASE statement, not the enum
    > > range.
    > 
    > Wow, that's ... bizarre.  I'm thinking that we probably did something
    > silly in the big expression-execution rewrite, but it's not clear exactly
    > where.
    
    It can't be "the big one", because then it'd be in 10 too, right?
    
    > So somehow the planner is messing up and inserting an outer CaseTestExpr
    > value as the source of the elemexpr's coercion expression.  IIRC,
    > ruleutils doesn't print the elemexpr at all, which is how come we're not
    > seeing the mistake in the EXPLAIN output.
    > 
    > However, ArrayCoerceExpr has abused CaseTestExpr that way for awhile, so
    > it's still not very clear why it broke in v11 and not before ...
    
    Without further analysis, the only commit that looks midly relevant is
    
    commit c12d570fa147d0ec273df53de3a2802925d551ba
    Author: Tom Lane <tgl@sss.pgh.pa.us>
    Date:   2017-09-30 13:40:56 -0400
    
        Support arrays over domains.
    
    Greetings,
    
    Andres Freund
    
    
    
  6. Re: BUG #15471: psql 11 array concatenation in CASE takes on values from the CASE expression when using enum_range

    Andres Freund <andres@anarazel.de> — 2018-10-30T17:44:11Z

    Hi,
    
    On 2018-10-30 09:30:54 -0400, Tom Lane wrote:
    > Good guess but I don't think that changed anything.  It looks to me
    > like the culprit is commit c12d570fa, so it's my bug not Andres' :-(.
    
    :)
    
    
    
    > This is all some more fuel for the idea that we need a less messy
    > substitute for CaseTestExpr.  As it happens, I was just fooling with
    > that yesterday, and hope to have something to post soon.  But it'll
    > be too invasive to back-patch.  I'll try to fix this particular
    > problem more locally.
    
    Yea, I agree we need something better there.  I happened to also play
    around with that (more from the angle that we'd want similar
    infrastructure for the SQL function inlining case we talked about) on
    the long flights back from .eu yesterday, but didn't get that far. Will
    wait till you have posted something.
    
    Greetings,
    
    Andres Freund