Defend against crash while processing Describe Statement or Describe Portal
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Dec 2005 17:07:00 +0000 (17:07 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Dec 2005 17:07:00 +0000 (17:07 +0000)
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.

src/backend/commands/prepare.c
src/backend/tcop/postgres.c
src/include/commands/prepare.h

index babae5e148346b2c2343acaf932b4a313bd56678..b301a75b276a4f757ced9ebc833b328adcac594e 100644 (file)
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.23.4.2 2004/12/13 00:17:52 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.23.4.3 2005/12/14 17:06:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -442,6 +442,30 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
    return NULL;
 }
 
+/*
+ * Given a prepared statement, determine whether it will return tuples.
+ *
+ * Note: this is used rather than just testing the result of
+ * FetchPreparedStatementResultDesc() because that routine can fail if
+ * invoked in an aborted transaction.  This one is safe to use in any
+ * context.  Be sure to keep the two routines in sync!
+ */
+bool
+PreparedStatementReturnsTuples(PreparedStatement *stmt)
+{
+   switch (ChoosePortalStrategy(stmt->query_list))
+   {
+       case PORTAL_ONE_SELECT:
+       case PORTAL_UTIL_SELECT:
+           return true;
+
+       case PORTAL_MULTI_QUERY:
+           /* will not return tuples */
+           break;
+   }
+   return false;
+}
+
 /*
  * Implements the 'DEALLOCATE' utility statement: deletes the
  * specified plan from storage.
index 0526d3c8331eeab4d49891bc1f47e4f673d081a7..6f424f0976b5ce4f00641f59f95b8cc21b0bf321 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.4 2005/11/10 00:31:59 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.5 2005/12/14 17:07:00 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -1644,6 +1644,15 @@ exec_describe_statement_message(const char *stmt_name)
    List       *l;
    StringInfoData buf;
 
+   /*
+    * Start up a transaction command. (Note that this will normally change
+    * current memory context.) Nothing happens if we are already in one.
+    */
+   start_xact_command();
+
+   /* Switch back to message context */
+   MemoryContextSwitchTo(MessageContext);
+
    /* Find prepared statement */
    if (stmt_name[0] != '\0')
        pstmt = FetchPreparedStatement(stmt_name, true);
@@ -1657,6 +1666,22 @@ exec_describe_statement_message(const char *stmt_name)
                   errmsg("unnamed prepared statement does not exist")));
    }
 
+   /*
+    * If we are in aborted transaction state, we can't safely create a result
+    * tupledesc, because that needs catalog accesses.  Hence, refuse to
+    * Describe statements that return data.  (We shouldn't just refuse all
+    * Describes, since that might break the ability of some clients to issue
+    * COMMIT or ROLLBACK commands, if they use code that blindly Describes
+    * whatever it does.)  We can Describe parameters without doing anything
+    * dangerous, so we don't restrict that.
+    */
+   if (IsAbortedTransactionBlockState() &&
+       PreparedStatementReturnsTuples(pstmt))
+       ereport(ERROR,
+               (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+                errmsg("current transaction is aborted, "
+                       "commands ignored until end of transaction block")));
+
    if (whereToSendOutput != Remote)
        return;                 /* can't actually do anything... */
 
@@ -1703,12 +1728,36 @@ exec_describe_portal_message(const char *portal_name)
 {
    Portal      portal;
 
+   /*
+    * Start up a transaction command. (Note that this will normally change
+    * current memory context.) Nothing happens if we are already in one.
+    */
+   start_xact_command();
+
+   /* Switch back to message context */
+   MemoryContextSwitchTo(MessageContext);
+
    portal = GetPortalByName(portal_name);
    if (!PortalIsValid(portal))
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_CURSOR),
                 errmsg("portal \"%s\" does not exist", portal_name)));
 
+   /*
+    * If we are in aborted transaction state, we can't run
+    * SendRowDescriptionMessage(), because that needs catalog accesses.
+    * Hence, refuse to Describe portals that return data.  (We shouldn't just
+    * refuse all Describes, since that might break the ability of some
+    * clients to issue COMMIT or ROLLBACK commands, if they use code that
+    * blindly Describes whatever it does.)
+    */
+   if (IsAbortedTransactionBlockState() &&
+       portal->tupDesc)
+       ereport(ERROR,
+               (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+                errmsg("current transaction is aborted, "
+                       "commands ignored until end of transaction block")));
+
    if (whereToSendOutput != Remote)
        return;                 /* can't actually do anything... */
 
@@ -2715,7 +2764,7 @@ PostgresMain(int argc, char *argv[], const char *username)
    if (!IsUnderPostmaster)
    {
        puts("\nPOSTGRES backend interactive interface ");
-       puts("$Revision: 1.375.2.4 $ $Date: 2005/11/10 00:31:59 $\n");
+       puts("$Revision: 1.375.2.5 $ $Date: 2005/12/14 17:07:00 $\n");
    }
 
    /*
index fdbea2c6083bdb4b2e221d4874b283d9490ef8c3..5c1fcfaa02e4ff8d9bd68ba57c1cc3e33da65ff7 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
- * $Id: prepare.h,v 1.8 2003/08/08 21:42:40 momjian Exp $
+ * $Id: prepare.h,v 1.8.4.1 2005/12/14 17:07:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,5 +58,6 @@ extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
 extern void DropPreparedStatement(const char *stmt_name, bool showError);
 extern List *FetchPreparedStatementParams(const char *stmt_name);
 extern TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt);
+extern bool PreparedStatementReturnsTuples(PreparedStatement *stmt);
 
 #endif   /* PREPARE_H */