Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)