Re: FATAL: ReleaseSavepoint: unexpected state STARTED
От | Robert Haas |
---|---|
Тема | Re: FATAL: ReleaseSavepoint: unexpected state STARTED |
Дата | |
Msg-id | CA+Tgmoa74n5BFgD2MxjfPpy0a-kOjcTS_AQkw0uQwjGLfYrmgw@mail.gmail.com обсуждение исходный текст |
Ответ на | Re: FATAL: ReleaseSavepoint: unexpected state STARTED (Marcin Mańk <marcin.mank@gmail.com>) |
Ответы |
Re: FATAL: ReleaseSavepoint: unexpected state STARTED
(Tom Lane <tgl@sss.pgh.pa.us>)
|
Список | pgsql-hackers |
On Thu, Aug 18, 2011 at 3:57 AM, Marcin Mańk <marcin.mank@gmail.com> wrote: > On Wed, Aug 17, 2011 at 11:30 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> Marcin Mańk <marcin.mank@gmail.com> writes: >>> psql -c 'release q; prepare q(int) as select 1' >>> FATAL: ReleaseSavepoint: unexpected state STARTED >> >> Can't get terribly excited about that, seeing that the statement is >> surely going to draw an error and abort processing the rest of the >> command string in any case ... > > Oh, I thought FATAL was disconnectiong other sessions. Yeah, that was > not bad enough. > > Here is a better one: > > psql postgres -c 'savepoint q; select 1' > FATAL: DefineSavepoint: unexpected state STARTED > server closed the connection unexpectedly > This probably means the server terminated abnormally > before or while processing the request. I spent some time looking at this afternoon and it appears that the root of this problem is that we're a bit schizophrenic about whether a multi-query command string constitutes a transaction or not. For example, DECLARE CURSOR works fine in that context, and you can fetch from the cursor. Since that command normally only works from within a transaction, you might reasonably conclude "hey, we're in a transaction". Furthermore, commands that can't be run from with a transaction context are fenced out here, too - e.g. VACUUM. The error message that you get there shows that someone thought about this specific case: [rhaas pgsql]$ psql -c 'SELECT 1;VACUUM' ERROR: VACUUM cannot be executed from a function or multi-command string The transaction control commands are not on the same page. BEGIN thinks we're not in a transaction, so you can use BEGIN to start one. If you do that then things are pretty normal. Phew. But let's say you don't use BEGIN. ROLLBACK claims that you aren't in a transaction and therefore you can't roll back: [rhaas pgsql]$ psql -c 'create table xyz(); rollback' NOTICE: there is no transaction in progress ROLLBACK But if there really were no transaction in progress at the point where ROLLBACK was issued, then the rollback wouldn't do anything. In fact, it does do something: it prevents xyz from getting created. Crazily enough, it does this without aborting the execution of the remaining commands: [rhaas pgsql]$ psql -c 'create table xyz(); rollback; select 1' NOTICE: there is no transaction in progress?column? ---------- 1 (1 row) So, from ROLLBACK's perspective, you are sort of in a transaction, and sort of not in a transaction. SAVEPOINT and ROLLBACK TO SAVEPOINT are normally called after RequireTransactionChain(), so that they can only be called from within a transaction block. But exec_simple_query is passing down isTopLevel = true, so RequireTransactionChain() eventually gets that value in the mail and says "oh, good. we're in a transaction. these commands can be allowed to work!" But then when the functions actually implementing those commands are invoked, they view TBLOCK_STARTED as unacceptable, and a FATAL error results. If backward compatibility were no object, I'd be tempted to deal with this by turning a multi-query command string into a full-fledged transaction. But that would break things like psql -c 'BEGIN; ...do stuff...; COMMIT; BEGIN; ...do other stuff...; COMMIT', which is probably common enough that we shouldn't indiscriminately break it. So I'm inclined to think that the problem - at least as far as SAVEPOINT and ROLLBACK TO SAVEPOINT are concerned - is that RequireTransactionChain() really can't just be the mirror image of PreventTransactionChain(). When you're in a transaction, PreventTransactionChain() should be unhappy; when you're outside one, RequireTransactionChain() should be unhappy; and when you're in this intermediate state inside of a multi-command string, BOTH of them should be unhappy. I'm not sure whether we need to go through and replace isTopLevel with a three-valued flag, or whether we can just delete the isTopLevel test from RequireTransactionChain() altogether. The latter seems like it might do the trick, but I haven't puzzled through all the logic yet. As for ROLLBACK, I think it should chuck an error instead of doing this funny emit-a-warning-and-silently-arrange-for-the-transaction-to-be-aborted-later thing. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
В списке pgsql-hackers по дате отправления: