Re: [bug fix] Savepoint-related statements terminates connection - Mailing list pgsql-hackers

From Ashutosh Bapat
Subject Re: [bug fix] Savepoint-related statements terminates connection
Date
Msg-id CAFjFpRfVwDQhq4CM5qxHAB-T42hsz3wQQm+prh-ez1E+aEaYEQ@mail.gmail.com
Whole thread Raw
In response to [bug fix] Savepoint-related statements terminates connection  ("Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com>)
List pgsql-hackers
Please add this to the next commitfest.

I think there's some misunderstanding between exec_simple_query() and
the way we manage transaction block state machine.

In exec_simple_query()952      * We'll tell PortalRun it's a top-level command iff there's
exactly one953      * raw parsetree.  If more than one, it's effectively a
transaction block954      * and we want PreventTransactionChain to reject unsafe
commands. (Note:955      * we're assuming that query rewrite cannot add commands that are956      * significant to
PreventTransactionChain.)957     */958     isTopLevel = (list_length(parsetree_list) == 1);
 

it assumes that a multi-statement command is a transaction block. But
for every statement in this multi-statement, we toggle between
TBLOCK_STARTED and TBLOCK_DEFAULT never entering TBLOCK_INPROGRESS as
expected by a transaction block. It looks like we have to fix this
transaction block state machine for multi-statement commands. One way
to fix it is to call finish_xact_command() in exec_simple_query() at
958 when it sees that it's a transaction block. I am not sure if
that's correct. We have to at least fix the comment above or even stop
setting isTopLevel for mult-statement commands.

I don't think the fix in the patch is on the right track, since
RequireTransactionChain() is supposed to do exactly what the patch
intends to do.
3213 /*
3214  *  RequireTransactionChain
3215  *
3216  *  This routine is to be called by statements that must run inside
3217  *  a transaction block, because they have no effects that persist past
3218  *  transaction end (and so calling them outside a transaction block
3219  *  is presumably an error).  DECLARE CURSOR is an example.

Incidently we allow cursor operations in a multi-statement command
psql -d postgres -c "select 1; declare curs cursor for select * from
pg_class; fetch from curs;"  relname    | relnamespace | reltype | reloftype | relowner | relam
| relfilenode | reltablespace | relpages | reltuples | relallvisible |
reltoastre
lid | relhasindex | relisshared | relpersistence | relkind | relnatts
| relchecks | relhasoids | relhaspkey | relhasrules | relhastriggers |
relhassubc
lass | relrowsecurity | relforcerowsecurity | relispopulated |
relreplident | relispartition | relfrozenxid | relminmxid |
relacl
| reloptions | relpartbound

--------------+--------------+---------+-----------+----------+-------+-------------+---------------+----------+-----------+---------------+-----------

----+-------------+-------------+----------------+---------+----------+-----------+------------+------------+-------------+----------------+-----------

-----+----------------+---------------------+----------------+--------------+----------------+--------------+------------+-----------------------------
+------------+--------------pg_statistic |           11 |   11258 |         0 |       10 |     0
|        2619 |             0 |       16 |       388 |            16 |        2
840 | t           | f           | p              | r       |       26
|         0 | f          | f          | f           | f              |
f    | f              | f                   | t              | n    | f              |          547 |          1 |
{ashutosh=arwdDxt/ashutosh}
|            |
(1 row)

Then the question is why not to allow savepoints as well? For that we
have to fix transaction block state machine.

On Fri, Mar 31, 2017 at 12:40 PM, Tsunakawa, Takayuki
<tsunakawa.takay@jp.fujitsu.com> wrote:
> Hello,
>
> I found a trivial bug that terminates the connection.  The attached patch fixes this.
>
>
> PROBLEM
> ========================================
>
> Savepoint-related statements in a multi-command query terminates the connection unexpectedly, as follows.
>
> $ psql -d postgres -c "SELECT 1; SAVEPOINT sp"
> FATAL:  DefineSavepoint: unexpected state STARTED
> server closed the connection unexpectedly
>         This probably means the server terminated abnormally
>         before or while processing the request.
> connection to server was lost
>
>
> CAUSE
> ========================================
>
> 1. In exec_simple_query(), isTopLevel is set to false.
>
>         isTopLevel = (list_length(parsetree_list) == 1);
>
> Then it is passed to PortalRun().
>
>                 (void) PortalRun(portal,
>                                                  FETCH_ALL,
>                                                  isTopLevel,
>                                                  receiver,
>                                                  receiver,
>                                                  completionTag);
>
> 2. The isTopLevel flag is passed through ProcessUtility() to RequireTransactionChain().
>
>                                                         RequireTransactionChain(isTopLevel, "SAVEPOINT");
>
>
> 3. CheckTransactionChain() returns successfully here:
>
>         /*
>          * inside a function call?
>          */
>         if (!isTopLevel)
>                 return;
>
>
> 4. Finally, unexpectedly called DefineSavepoint() reports invalid transaction block state.
>
>                         /* These cases are invalid. */
>                 case TBLOCK_DEFAULT:
>                 case TBLOCK_STARTED:
> ...
>                         elog(FATAL, "DefineSavepoint: unexpected state %s",
>                                  BlockStateAsString(s->blockState));
>
>
>
> SOLUTION
> ========================================
>
> The manual page says "Savepoints can only be established when inside a transaction block."  So just check for it.
>
> https://www.postgresql.org/docs/devel/static/sql-savepoint.html
>
>
> RESULT AFTER FIX
> ========================================
>
> $ psql -d postgres -c "SELECT 1; SAVEPOINT sp"
> ERROR:  SAVEPOINT can only be used in transaction blocks
>
>
> Regards
> Takayuki Tsunakawa
>
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>



-- 
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company



pgsql-hackers by date:

Previous
From: Etsuro Fujita
Date:
Subject: Re: postgres_fdw bug in 9.6
Next
From: "Daniel Verite"
Date:
Subject: Re: Variable substitution in psql backtick expansion