Skip to content

Commit 4ce6be4

Browse files
committed
Defend against crash while processing Describe Statement or Describe Portal
messages, when client attempts to execute these outside a transaction (start one) or in a failed transaction (reject message, except for COMMIT/ROLLBACK statements which we can handle). Per report from Francisco Figueiredo Jr.
1 parent 4262926 commit 4ce6be4

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

src/backend/commands/prepare.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.43 2005/11/29 01:25:49 tgl Exp $
13+
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.44 2005/12/14 17:06:27 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -447,6 +447,30 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
447447
return NULL;
448448
}
449449

450+
/*
451+
* Given a prepared statement, determine whether it will return tuples.
452+
*
453+
* Note: this is used rather than just testing the result of
454+
* FetchPreparedStatementResultDesc() because that routine can fail if
455+
* invoked in an aborted transaction. This one is safe to use in any
456+
* context. Be sure to keep the two routines in sync!
457+
*/
458+
bool
459+
PreparedStatementReturnsTuples(PreparedStatement *stmt)
460+
{
461+
switch (ChoosePortalStrategy(stmt->query_list))
462+
{
463+
case PORTAL_ONE_SELECT:
464+
case PORTAL_UTIL_SELECT:
465+
return true;
466+
467+
case PORTAL_MULTI_QUERY:
468+
/* will not return tuples */
469+
break;
470+
}
471+
return false;
472+
}
473+
450474
/*
451475
* Given a prepared statement that returns tuples, extract the query
452476
* targetlist. Returns NIL if the statement doesn't have a determinable

src/backend/tcop/postgres.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.470 2005/11/22 18:17:21 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.471 2005/12/14 17:06:27 tgl Exp $
1212
*
1313
* NOTES
1414
* this is the "main" module of the postgres backend and
@@ -1849,6 +1849,15 @@ exec_describe_statement_message(const char *stmt_name)
18491849
ListCell *l;
18501850
StringInfoData buf;
18511851

1852+
/*
1853+
* Start up a transaction command. (Note that this will normally change
1854+
* current memory context.) Nothing happens if we are already in one.
1855+
*/
1856+
start_xact_command();
1857+
1858+
/* Switch back to message context */
1859+
MemoryContextSwitchTo(MessageContext);
1860+
18521861
/* Find prepared statement */
18531862
if (stmt_name[0] != '\0')
18541863
pstmt = FetchPreparedStatement(stmt_name, true);
@@ -1862,6 +1871,22 @@ exec_describe_statement_message(const char *stmt_name)
18621871
errmsg("unnamed prepared statement does not exist")));
18631872
}
18641873

1874+
/*
1875+
* If we are in aborted transaction state, we can't safely create a result
1876+
* tupledesc, because that needs catalog accesses. Hence, refuse to
1877+
* Describe statements that return data. (We shouldn't just refuse all
1878+
* Describes, since that might break the ability of some clients to issue
1879+
* COMMIT or ROLLBACK commands, if they use code that blindly Describes
1880+
* whatever it does.) We can Describe parameters without doing anything
1881+
* dangerous, so we don't restrict that.
1882+
*/
1883+
if (IsAbortedTransactionBlockState() &&
1884+
PreparedStatementReturnsTuples(pstmt))
1885+
ereport(ERROR,
1886+
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
1887+
errmsg("current transaction is aborted, "
1888+
"commands ignored until end of transaction block")));
1889+
18651890
if (whereToSendOutput != DestRemote)
18661891
return; /* can't actually do anything... */
18671892

@@ -1902,12 +1927,36 @@ exec_describe_portal_message(const char *portal_name)
19021927
{
19031928
Portal portal;
19041929

1930+
/*
1931+
* Start up a transaction command. (Note that this will normally change
1932+
* current memory context.) Nothing happens if we are already in one.
1933+
*/
1934+
start_xact_command();
1935+
1936+
/* Switch back to message context */
1937+
MemoryContextSwitchTo(MessageContext);
1938+
19051939
portal = GetPortalByName(portal_name);
19061940
if (!PortalIsValid(portal))
19071941
ereport(ERROR,
19081942
(errcode(ERRCODE_UNDEFINED_CURSOR),
19091943
errmsg("portal \"%s\" does not exist", portal_name)));
19101944

1945+
/*
1946+
* If we are in aborted transaction state, we can't run
1947+
* SendRowDescriptionMessage(), because that needs catalog accesses.
1948+
* Hence, refuse to Describe portals that return data. (We shouldn't just
1949+
* refuse all Describes, since that might break the ability of some
1950+
* clients to issue COMMIT or ROLLBACK commands, if they use code that
1951+
* blindly Describes whatever it does.)
1952+
*/
1953+
if (IsAbortedTransactionBlockState() &&
1954+
portal->tupDesc)
1955+
ereport(ERROR,
1956+
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
1957+
errmsg("current transaction is aborted, "
1958+
"commands ignored until end of transaction block")));
1959+
19111960
if (whereToSendOutput != DestRemote)
19121961
return; /* can't actually do anything... */
19131962

src/include/commands/prepare.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*
77
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
88
*
9-
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.15 2005/11/29 01:25:50 tgl Exp $
9+
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.16 2005/12/14 17:06:28 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -60,6 +60,7 @@ extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
6060
extern void DropPreparedStatement(const char *stmt_name, bool showError);
6161
extern List *FetchPreparedStatementParams(const char *stmt_name);
6262
extern TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt);
63+
extern bool PreparedStatementReturnsTuples(PreparedStatement *stmt);
6364
extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
6465

6566
#endif /* PREPARE_H */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy