• Tom Lane's avatar
    Disallow set-returning functions inside CASE or COALESCE. · 0436f6bd
    Tom Lane authored
    When we reimplemented SRFs in commit 69f4b9c8, our initial choice was
    to allow the behavior to vary from historical practice in cases where a
    SRF call appeared within a conditional-execution construct (currently,
    only CASE or COALESCE).  But that was controversial to begin with, and
    subsequent discussion has resulted in a consensus that it's better to
    throw an error instead of executing the query differently from before,
    so long as we can provide a reasonably clear error message and a way to
    rewrite the query.
    
    Hence, add a parser mechanism to allow detection of such cases during
    parse analysis.  The mechanism just requires storing, in the ParseState,
    a pointer to the set-returning FuncExpr or OpExpr most recently emitted
    by parse analysis.  Then the parsing functions for CASE and COALESCE can
    detect the presence of a SRF in their arguments by noting whether this
    pointer changes while analyzing their arguments.  Furthermore, if it does,
    it provides a suitable error cursor location for the complaint.  (This
    means that if there's more than one SRF in the arguments, the error will
    point at the last one to be analyzed not the first.  While connoisseurs of
    parsing behavior might find that odd, it's unlikely the average user would
    ever notice.)
    
    While at it, we can also provide more specific error messages than before
    about some pre-existing restrictions, such as no-SRFs-within-aggregates.
    Also, reject at parse time cases where a NULLIF or IS DISTINCT FROM
    construct would need to return a set.  We've never supported that, but the
    restriction is depended on in more subtle ways now, so it seems wise to
    detect it at the start.
    
    Also, provide some documentation about how to rewrite a SRF-within-CASE
    query using a custom wrapper SRF.
    
    It turns out that the information_schema.user_mapping_options view
    contained an instance of exactly the behavior we're now forbidding; but
    rewriting it makes it more clear and safer too.
    
    initdb forced because of user_mapping_options change.
    
    Patch by me, with error message suggestions from Alvaro Herrera and
    Andres Freund, pursuant to a complaint from Regina Obe.
    
    Discussion: https://postgr.es/m/000001d2d5de$d8d66170$8a832450$@pcorp.us
    0436f6bd
catversion.h 2.53 KB