Thread

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Minor cosmetic tweaks

  2. Simplify coding in ProcessQuery

  3. Remove assertion from PortalRunMulti

  4. Represent command completion tags as structs

  1. BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    PG Bug reporting form <noreply@postgresql.org> — 2025-07-14T19:00:02Z

    The following bug has been logged on the website:
    
    Bug reference:      18984
    Logged by:          Alexander Lakhin
    Email address:      exclusion@gmail.com
    PostgreSQL version: 18beta1
    Operating system:   Ubuntu 24.04
    Description:        
    
    The following psql script:
    \parse s
    
    execute s;
    
    invokes:
    TRAP: failed Assert("qc->commandTag != CMDTAG_UNKNOWN"), File: "pquery.c",
    Line: 1369, PID: 1123863
    ExceptionalCondition at assert.c:52:13
    PortalRunMulti at pquery.c:1371:1
    PortalRun at pquery.c:792:5
    ExecuteQuery at prepare.c:260:2
    standard_ProcessUtility at utility.c:757:4
    ProcessUtility at utility.c:523:3
    PortalRunUtility at pquery.c:1153:2
    PortalRunMulti at pquery.c:1310:5
    PortalRun at pquery.c:792:5
    exec_simple_query at postgres.c:1280:11
    ...
    
    Reproduced starting from d55322b0d.
    
    
  2. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-14T19:23:12Z

    PG Bug reporting form <noreply@postgresql.org> writes:
    > The following psql script:
    > \parse s
    
    > execute s;
    
    > invokes:
    > TRAP: failed Assert("qc->commandTag != CMDTAG_UNKNOWN"), File: "pquery.c",
    > Line: 1369, PID: 1123863
    
    What this is complaining about, essentially, is that we don't know how
    to construct a command tag to send back to the client.  Maybe we could
    invent some new command tag out of whole cloth, but I'm inclined to
    think it'd be better to forbid empty prepared statements.  Not sure
    where that should be enforced, but probably on the server side.
    
    			regards, tom lane
    
    
    
    
  3. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-14T19:36:01Z

    PG Bug reporting form <noreply@postgresql.org> writes:
    > Reproduced starting from d55322b0d.
    
    BTW, d55322b0d simply made it easy to trigger the bug from psql.
    If I use current psql to connect to older servers, I can demonstrate
    crashes (or sometimes "stack depth limit exceeded") back to v12.
    Pre-v12 is immune because it just returns a command tag of EXECUTE
    regardless of what is in the prepared statement.
    
    That being the case, maybe we should band-aid this by returning
    EXECUTE if the prepared statement is empty.
    
    			regards, tom lane
    
    
    
    
  4. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Aleksander Alekseev <aleksander@tigerdata.com> — 2025-07-14T21:11:31Z

    Hi,
    
    > That being the case, maybe we should band-aid this by returning
    > EXECUTE if the prepared statement is empty.
    
    This sounds pretty straightforward and seems to solve the problem.
    Patch attached.
    
    ```
    eax=# \parse s
    eax=# execute s;
    ERROR:  EXECUTE can't execute an empty query
    eax=# select 1 \parse s2
    
    eax=# execute s2;
     ?column?
    ----------
            1
    (1 row)
    ```
    
  5. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Aleksander Alekseev <aleksander@tigerdata.com> — 2025-07-14T21:33:11Z

    Hi,
    
    > This sounds pretty straightforward and seems to solve the problem.
    > Patch attached.
    
    Interestingly enough EXPLAIN EXECUTE is not affected, thus there is no
    need to modify ExplainExecuteQuery.
    
    ```
    eax=# explain execute s;
     QUERY PLAN
    ------------
    (0 rows)
    ```
    
    
    
    
  6. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-14T21:40:22Z

    Aleksander Alekseev <aleksander@tigerdata.com> writes:
    >> That being the case, maybe we should band-aid this by returning
    >> EXECUTE if the prepared statement is empty.
    
    > This sounds pretty straightforward and seems to solve the problem.
    > Patch attached.
    
    Hmm, throwing an error is exactly not what I was suggesting above.
    The attached quick draft (needs attention to comments) results in
    
    regression=# \parse s
    
    regression=# execute s;
    EXECUTE
    
    which matches the behavior of pre-v12 servers.
    
    (BTW, I wonder why psql is emitting an extra newline after \parse.)
    
    			regards, tom lane
    
    
  7. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-14T22:05:50Z

    I wrote:
    > The attached quick draft (needs attention to comments) results in
    > regression=# \parse s
    > regression=# execute s;
    > EXECUTE
    > which matches the behavior of pre-v12 servers.
    
    Another way that's perhaps a bit less magical is to make
    ExecuteQuery substitute the right thing, as attached.
    
    I'm not entirely sure whether this leaves any other code
    paths that can reach that Assert; but on the other hand,
    if there are any, CMDTAG_EXECUTE is probably wrong for them.
    
    BTW, the reason why EXPLAIN EXECUTE isn't at risk is that it's
    going to return CMDTAG_EXPLAIN.
    
    			regards, tom lane
    
    
  8. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-14T23:17:47Z

    I wrote:
    > BTW, d55322b0d simply made it easy to trigger the bug from psql.
    > If I use current psql to connect to older servers, I can demonstrate
    > crashes (or sometimes "stack depth limit exceeded") back to v12.
    
    I think I fat-fingered something, because now I can only reproduce back
    to v13.  Bisecting says the crash started at the v13-era commit
    
    commit 2f9661311b83dc481fc19f6e3bda015392010a40
    Author: Alvaro Herrera <alvherre@alvh.no-ip.org>
    Date:   Mon Mar 2 18:19:51 2020 -0300
    
        Represent command completion tags as structs
    
    which is unsurprising because that's what introduced the
    Assert we're hitting.
    
    BTW, with Asserts disabled it works fine:
    
    $ psql postgres
    psql (19devel)
    Type "help" for help.
    
    postgres=# \parse s
    
    postgres=# execute s;
    EXECUTE
    
    which is another reason not to start throwing an error.
    (I did not look into how come that works... the correct
    command tag must be getting filled in later, but where?)
    
    			regards, tom lane
    
    
    
    
  9. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Michael Paquier <michael@paquier.xyz> — 2025-07-15T03:54:46Z

    On Mon, Jul 14, 2025 at 03:36:01PM -0400, Tom Lane wrote:
    > BTW, d55322b0d simply made it easy to trigger the bug from psql.
    > If I use current psql to connect to older servers, I can demonstrate
    > crashes (or sometimes "stack depth limit exceeded") back to v12.
    > Pre-v12 is immune because it just returns a command tag of EXECUTE
    > regardless of what is in the prepared statement.
    
    Oh.  It's pretty cool to see that these new meta-commands are able to
    find such problems more easily.  That was one of the reasons to have
    them in v18.
    --
    Michael
    
  10. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Michael Paquier <michael@paquier.xyz> — 2025-07-15T04:52:37Z

    On Mon, Jul 14, 2025 at 07:17:47PM -0400, Tom Lane wrote:
    > I think I fat-fingered something, because now I can only reproduce back
    > to v13.  Bisecting says the crash started at the v13-era commit
    > 
    > which is unsurprising because that's what introduced the
    > Assert we're hitting.
    
    Yep.
    
    > which is another reason not to start throwing an error.
    > (I did not look into how come that works... the correct
    > command tag must be getting filled in later, but where?)
    
    With some extra logs:
    WARNING:  01000: BOOBOO exec_simple_query commandTag = EXECUTE
    LOCATION:  exec_simple_query, postgres.c:1120
    WARNING:  01000: BOOBOO PortalDefineQuery commandTag = EXECUTE
    LOCATION:  PortalDefineQuery, portalmem.c:301
    WARNING:  01000: BOOBOO PortalDefineQuery commandTag = ???
    LOCATION:  PortalDefineQuery, portalmem.c:301
    
    The first pass in PortalDefineQuery() is done in exec_simple_query().
    The second pass of PortalDefineQuery() is the relevant one, I guess:
    #0  PortalDefineQuery (portal=0x564cf7cc9240, prepStmtName=0x0,
    sourceText=0x564cf7c74d80 "", commandTag=CMDTAG_UNKNOWN, stmts=0x0,
    cplan=0x564cf7c817a8)     at portalmem.c:289
    #1  0x0000564cd98d4328 in ExecuteQuery (pstate=0x564cf7c7b740,
    stmt=0x564cf7c48d38, intoClause=0x0, params=0x0, dest=0x564cf7c48de0,
    qc=0x7fffb8e7b4a0) at prepare.c:203
    #2  0x0000564cda3f6427 in standard_ProcessUtility
    (pstmt=0x564cf7c48550, queryString=0x564cf7c47590 "execute s;",
    readOnlyTree=false, context=PROCESS_UTILITY_TOPLEVEL,      params=0x0,
    queryEnv=0x0, dest=0x564cf7c48de0, qc=0x7fffb8e7b4a0) at utility.c:759
    #3  0x0000564cda3f541f in ProcessUtility (pstmt=0x564cf7c48550,
    queryString=0x564cf7c47590 "execute s;", readOnlyTree=false,
    context=PROCESS_UTILITY_TOPLEVEL, params=0x0,      queryEnv=0x0,
    dest=0x564cf7c48de0, qc=0x7fffb8e7b4a0) at utility.c:523
    #4  0x0000564cda3f2214 in PortalRunUtility (portal=0x564cf7cc9130,
    pstmt=0x564cf7c48550, isTopLevel=true, setHoldSnapshot=false,
    dest=0x564cf7c48de0, qc=0x7fffb8e7b4a0)     at pquery.c:1153
    #5  0x0000564cda3f2a40 in PortalRunMulti (portal=0x564cf7cc9130,
    isTopLevel=true, setHoldSnapshot=false, dest=0x564cf7c48de0,
    altdest=0x564cf7c48de0, qc=0x7fffb8e7b4a0)     at pquery.c:1310
    #6  0x0000564cda3f09a4 in PortalRun (portal=0x564cf7cc9130,
    count=9223372036854775807, isTopLevel=true, dest=0x564cf7c48de0,
    altdest=0x564cf7c48de0, qc=0x7fffb8e7b4a0)     at pquery.c:788
    #7  0x0000564cda3e240a in exec_simple_query
    (query_string=0x564cf7c47590 "execute s;") at postgres.c:1274
    #8  0x0000564cda3eb78f in PostgresMain (dbname=0x564cf7c84730
    "ioltas", username=0x564cf7c84718 "ioltas") at postgres.c:4767
    
    So the QueryCompletion given to PortalRunMulti() is initialized at the
    beginning of PortalRun with an unknown tag, and we miss to fill it
    when running the individual queries because CopyQueryCompletion()
    happens at the end of PortalRunMulti().  We can be more proactive when
    updating the tag by doing one in ExecuteQuery() as you are suggesting,
    but I am wondering if we should not just nuke the assertion at the end
    of PortalRunMulti() instead, relying on the same check done at the
    beginning of ProcessUtility().
    --
    Michael
    
  11. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-15T04:59:26Z

    Michael Paquier <michael@paquier.xyz> writes:
    > ... I am wondering if we should not just nuke the assertion at the end
    > of PortalRunMulti() instead, relying on the same check done at the
    > beginning of ProcessUtility().
    
    Yeah, I was starting to think about that solution too.  Removing
    code seems nicer than adding more.
    
    CC'ing Alvaro as the original author...
    
    			regards, tom lane
    
    
    
    
  12. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-07-15T12:33:14Z

    On 2025-Jul-15, Tom Lane wrote:
    
    > Michael Paquier <michael@paquier.xyz> writes:
    > > ... I am wondering if we should not just nuke the assertion at the end
    > > of PortalRunMulti() instead, relying on the same check done at the
    > > beginning of ProcessUtility().
    > 
    > Yeah, I was starting to think about that solution too.  Removing
    > code seems nicer than adding more.
    
    Yeah, this makes sense to me too.  I'd rewrite the comment while at it,
    because what's being described as "printing 0 0" no longer occurs in
    this form in this place anymore.  Maybe we could discuss adding
    some commentary to EndCommand where this now happens, but I don't think
    we really need it.
    
    -- 
    Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
    
  13. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-15T14:57:54Z

    =?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@kurilemu.de> writes:
    > On 2025-Jul-15, Tom Lane wrote:
    >> Yeah, I was starting to think about that solution too.  Removing
    >> code seems nicer than adding more.
    
    > Yeah, this makes sense to me too.  I'd rewrite the comment while at it,
    > because what's being described as "printing 0 0" no longer occurs in
    > this form in this place anymore.  Maybe we could discuss adding
    > some commentary to EndCommand where this now happens, but I don't think
    > we really need it.
    
    Right, I was giving that comment the side eye too.  I agree that its
    second para is no longer useful: the logic it describes certainly
    isn't here anymore, and there doesn't seem to be an identifiable place
    where we could move it to.  (I think the concern it describes is now
    baked into the table design for command tags, specifically that any
    given CMDTAG either has or doesn't have a count.)  I might write the
    replacement comment more like
    
         * If query completion data is requested and not yet filled in,
         * and the portal has a default command tag, copy it from there.
         * See QueryRewrite(), step 3, for motivation.
    
    			regards, tom lane
    
    
    
    
  14. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-15T16:05:34Z

    I wrote:
    > BTW, with Asserts disabled it works fine:
    > ...
    > (I did not look into how come that works... the correct
    > command tag must be getting filled in later, but where?)
    
    I traced through this, and the answer is that there are two levels
    of Portal, an outer one that holds the EXECUTE command (and has
    portal->qc.commandTag = CMDTAG_EXECUTE) and an inner one that runs
    the prepared statement's list (and has portal->qc.commandTag =
    CMDTAG_UNKNOWN, because that list is empty).  But ExecuteQuery
    passes down the outer "qc" parameter to the inner recursion level.
    So without the Assert, the inner invocation of PortalRunMulti leaves
    qc->commandTag = CMDTAG_UNKNOWN, and then the very same stanza
    of PortalRunMulti replaces that with CMDTAG_EXECUTE in the outer
    recursion level.
    
    So it's just wrong for PortalRunMulti to assert that the
    computation of qc->commandTag is necessarily complete.
    
    If we wanted to replace that, it might be appropriate to have an
    assertion at the top level (exec_simple_query) but I'm not very sure
    what that would buy us.  It'll be apparent enough what happened from
    the reported "???" command tag, and the top-level assertion would
    contribute exactly nada to identifying where we'd missed setting it.
    
    In short, Alvaro's patch definitely seems like the right one,
    modulo quibbles about rewriting the comment.  Perhaps we should
    add something about the possibility that an outer Portal level
    can supply a default command tag if we lack one now?
    
    			regards, tom lane
    
    
    
    
  15. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Michael Paquier <michael@paquier.xyz> — 2025-07-15T23:32:00Z

    On Tue, Jul 15, 2025 at 12:05:34PM -0400, Tom Lane wrote:
    > In short, Alvaro's patch definitely seems like the right one,
    > modulo quibbles about rewriting the comment.
    
    +++ b/src/backend/tcop/pquery.c
    @@ -1350,24 +1350,14 @@ PortalRunMulti(Portal portal,
    [...]
    -    if (qc && qc->commandTag == CMDTAG_UNKNOWN)
    -    {
    -        if (portal->qc.commandTag != CMDTAG_UNKNOWN)
    -            CopyQueryCompletion(qc, &portal->qc);
    -        /* If the caller supplied a qc, we should have set it by now. */
    -        Assert(qc->commandTag != CMDTAG_UNKNOWN);
    -    }
    +    if (qc &&
    +        qc->commandTag == CMDTAG_UNKNOWN &&
    +        portal->qc.commandTag != CMDTAG_UNKNOWN)
    +        CopyQueryCompletion(qc, &portal->qc);
    
    Indeed this change looks OK.
    
    > Perhaps we should
    > add something about the possibility that an outer Portal level
    > can supply a default command tag if we lack one now?
    
    You mean as an extra assertion?  I am not sure that this is strongly
    necessary but why not.
    --
    Michael
    
  16. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-15T23:40:19Z

    Michael Paquier <michael@paquier.xyz> writes:
    > On Tue, Jul 15, 2025 at 12:05:34PM -0400, Tom Lane wrote:
    >> Perhaps we should
    >> add something about the possibility that an outer Portal level
    >> can supply a default command tag if we lack one now?
    
    > You mean as an extra assertion?  I am not sure that this is strongly
    > necessary but why not.
    
    No, I meant that the comment could explain that if we fail to assign a
    non-unknown CMDTAG here, there's still a possibility of an outer
    Portal level supplying one.  But I don't see how to make a useful
    assertion about that.
    
    			regards, tom lane
    
    
    
    
  17. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Michael Paquier <michael@paquier.xyz> — 2025-07-16T04:45:55Z

    On Tue, Jul 15, 2025 at 07:40:19PM -0400, Tom Lane wrote:
    > Michael Paquier <michael@paquier.xyz> writes:
    >> You mean as an extra assertion?  I am not sure that this is strongly
    >> necessary but why not.
    > 
    > No, I meant that the comment could explain that if we fail to assign a
    > non-unknown CMDTAG here, there's still a possibility of an outer
    > Portal level supplying one.  But I don't see how to make a useful
    > assertion about that.
    
    Oh, OK, I was not sure about your point here.  Thanks for clarifying.
    --
    Michael
    
  18. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-07-17T12:03:11Z

    On 2025-Jul-15, Tom Lane wrote:
    
    > I might write the replacement comment more like
    > 
    >      * If query completion data is requested and not yet filled in,
    >      * and the portal has a default command tag, copy it from there.
    >      * See QueryRewrite(), step 3, for motivation.
    
    I've been wondering what does the "a default command tag" mean there. 
    I couldn't find a reference for a command tag being default.  Would it
    work to say "... and the portal has acquired a command tag [during
    execution above?], ..." ?
    
    Thanks,
    
    -- 
    Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/
    "Once again, thank you and all of the developers for your hard work on
    PostgreSQL.  This is by far the most pleasant management experience of
    any database I've worked on."                             (Dan Harris)
    http://archives.postgresql.org/pgsql-performance/2006-04/msg00247.php
    
    
    
    
  19. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-17T13:45:34Z

    =?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@kurilemu.de> writes:
    > I've been wondering what does the "a default command tag" mean there. 
    > I couldn't find a reference for a command tag being default.  Would it
    > work to say "... and the portal has acquired a command tag [during
    > execution above?], ..." ?
    
    I think that where the Portal gets a tag is in PortalDefineQuery,
    and that's normally set as a result of parsing.  The code earlier in
    PortalRunMulti is meant to supply run-time-determined tags, such
    as INSERT/UPDATE/DELETE with a row count.  I guess if you hold
    your head at the right angle, the case we're considering now is
    a run-time-determined tag for the outer EXECUTE.
    
    			regards, tom lane
    
    
    
    
  20. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-07-17T15:00:56Z

    I wrote:
    > =?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@kurilemu.de> writes:
    
    > I think that where the Portal gets a tag is in PortalDefineQuery,
    > and that's normally set as a result of parsing.
    
    So rather than using the word "default", maybe like
    
        * If a command tag was requested and we did not fill in a
        * run-time-determined tag above, copy the parse-time tag
        * from the Portal.  (There might not be any tag there either,
        * in edge cases such as empty prepared statements.  That's OK.)
    
    			regards, tom lane
    
    
    
    
  21. Re: BUG #18984: Empty prepared statement from psql \parse triggers assert in PortalRunMulti

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-07-17T15:48:27Z

    On 2025-Jul-17, Tom Lane wrote:
    
    > So rather than using the word "default", maybe like
    > 
    >     * If a command tag was requested and we did not fill in a
    >     * run-time-determined tag above, copy the parse-time tag
    >     * from the Portal.  (There might not be any tag there either,
    >     * in edge cases such as empty prepared statements.  That's OK.)
    
    Oh yeah, this seems very clear.  Pushed that way, thanks.
    
    -- 
    Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
    "Oh, great altar of passive entertainment, bestow upon me thy discordant images
    at such speed as to render linear thought impossible" (Calvin a la TV)