diff options
-rw-r--r-- | src/backend/tcop/postgres.c | 315 |
1 files changed, 160 insertions, 155 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 06931e4e880..0526d3c8331 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.3 2005/06/02 21:04:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.4 2005/11/10 00:31:59 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -155,6 +155,9 @@ static int SocketBackend(StringInfo inBuf); static int ReadCommand(StringInfo inBuf); static void start_xact_command(void); static void finish_xact_command(void); +static bool IsTransactionExitStmt(Node *parsetree); +static bool IsTransactionExitStmtList(List *parseTrees); +static bool IsTransactionStmtList(List *parseTrees); static void SigHupHandler(SIGNAL_ARGS); static void FloatExceptionHandler(SIGNAL_ARGS); @@ -826,25 +829,12 @@ exec_simple_query(const char *query_string) * state. (It might be safe to allow some additional utility * commands in this state, but not many...) */ - if (IsAbortedTransactionBlockState()) - { - bool allowit = false; - - if (IsA(parsetree, TransactionStmt)) - { - TransactionStmt *stmt = (TransactionStmt *) parsetree; - - if (stmt->kind == TRANS_STMT_COMMIT || - stmt->kind == TRANS_STMT_ROLLBACK) - allowit = true; - } - - if (!allowit) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " + if (IsAbortedTransactionBlockState() && + !IsTransactionExitStmt(parsetree)) + ereport(ERROR, + (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), + errmsg("current transaction is aborted, " "commands ignored until end of transaction block"))); - } /* Make sure we are in a transaction command */ start_xact_command(); @@ -1146,25 +1136,12 @@ exec_parse_message(const char *query_string, /* string to execute */ * abort state. (It might be safe to allow some additional utility * commands in this state, but not many...) */ - if (IsAbortedTransactionBlockState()) - { - bool allowit = false; - - if (IsA(parsetree, TransactionStmt)) - { - TransactionStmt *stmt = (TransactionStmt *) parsetree; - - if (stmt->kind == TRANS_STMT_COMMIT || - stmt->kind == TRANS_STMT_ROLLBACK) - allowit = true; - } - - if (!allowit) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " + if (IsAbortedTransactionBlockState() && + !IsTransactionExitStmt(parsetree)) + ereport(ERROR, + (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), + errmsg("current transaction is aborted, " "commands ignored until end of transaction block"))); - } /* * OK to analyze, rewrite, and plan this query. Note that the @@ -1343,6 +1320,22 @@ exec_bind_message(StringInfo input_message) numParams, stmt_name, length(pstmt->argtype_list)))); /* + * If we are in aborted transaction state, the only portals we can + * actually run are those containing COMMIT or ROLLBACK commands. + * We disallow binding anything else to avoid problems with infrastructure + * that expects to run inside a valid transaction. We also disallow + * binding any parameters, since we can't risk calling user-defined + * I/O functions. + */ + if (IsAbortedTransactionBlockState() && + (!IsTransactionExitStmtList(pstmt->query_list) || + numParams != 0)) + ereport(ERROR, + (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), + errmsg("current transaction is aborted, " + "commands ignored until end of transaction block"))); + + /* * Create the portal. Allow silent replacement of an existing portal * only if the unnamed portal is specified. */ @@ -1360,14 +1353,9 @@ exec_bind_message(StringInfo input_message) /* * Fetch parameters, if any, and store in the portal's memory context. - * - * In an aborted transaction, we can't risk calling user-defined - * functions, but we can't fail to Bind either, so bind all parameters - * to null values. */ if (numParams > 0) { - bool isaborted = IsAbortedTransactionBlockState(); List *l; MemoryContext oldContext; @@ -1389,100 +1377,91 @@ exec_bind_message(StringInfo input_message) if (!isNull) { const char *pvalue = pq_getmsgbytes(input_message, plength); + int16 pformat; + StringInfoData pbuf; + char csave; + + if (numPFormats > 1) + pformat = pformats[i]; + else if (numPFormats > 0) + pformat = pformats[0]; + else + pformat = 0; /* default = text */ - if (isaborted) + /* + * Rather than copying data around, we just set up a + * phony StringInfo pointing to the correct portion of + * the message buffer. We assume we can scribble on + * the message buffer so as to maintain the convention + * that StringInfos have a trailing null. This is + * grotty but is a big win when dealing with very + * large parameter strings. + */ + pbuf.data = (char *) pvalue; + pbuf.maxlen = plength + 1; + pbuf.len = plength; + pbuf.cursor = 0; + + csave = pbuf.data[plength]; + pbuf.data[plength] = '\0'; + + if (pformat == 0) { - /* We don't bother to check the format in this case */ - isNull = true; + Oid typInput; + Oid typElem; + char *pstring; + + getTypeInputInfo(ptype, &typInput, &typElem); + + /* + * We have to do encoding conversion before + * calling the typinput routine. + */ + pstring = (char *) + pg_client_to_server((unsigned char *) pbuf.data, + plength); + params[i].value = + OidFunctionCall3(typInput, + CStringGetDatum(pstring), + ObjectIdGetDatum(typElem), + Int32GetDatum(-1)); + /* Free result of encoding conversion, if any */ + if (pstring != pbuf.data) + pfree(pstring); } - else + else if (pformat == 1) { - int16 pformat; - StringInfoData pbuf; - char csave; - - if (numPFormats > 1) - pformat = pformats[i]; - else if (numPFormats > 0) - pformat = pformats[0]; - else - pformat = 0; /* default = text */ + Oid typReceive; + Oid typElem; /* - * Rather than copying data around, we just set up a - * phony StringInfo pointing to the correct portion of - * the message buffer. We assume we can scribble on - * the message buffer so as to maintain the convention - * that StringInfos have a trailing null. This is - * grotty but is a big win when dealing with very - * large parameter strings. + * Call the parameter type's binary input + * converter */ - pbuf.data = (char *) pvalue; - pbuf.maxlen = plength + 1; - pbuf.len = plength; - pbuf.cursor = 0; + getTypeBinaryInputInfo(ptype, &typReceive, &typElem); - csave = pbuf.data[plength]; - pbuf.data[plength] = '\0'; + params[i].value = + OidFunctionCall2(typReceive, + PointerGetDatum(&pbuf), + ObjectIdGetDatum(typElem)); - if (pformat == 0) - { - Oid typInput; - Oid typElem; - char *pstring; - - getTypeInputInfo(ptype, &typInput, &typElem); - - /* - * We have to do encoding conversion before - * calling the typinput routine. - */ - pstring = (char *) - pg_client_to_server((unsigned char *) pbuf.data, - plength); - params[i].value = - OidFunctionCall3(typInput, - CStringGetDatum(pstring), - ObjectIdGetDatum(typElem), - Int32GetDatum(-1)); - /* Free result of encoding conversion, if any */ - if (pstring != pbuf.data) - pfree(pstring); - } - else if (pformat == 1) - { - Oid typReceive; - Oid typElem; - - /* - * Call the parameter type's binary input - * converter - */ - getTypeBinaryInputInfo(ptype, &typReceive, &typElem); - - params[i].value = - OidFunctionCall2(typReceive, - PointerGetDatum(&pbuf), - ObjectIdGetDatum(typElem)); - - /* Trouble if it didn't eat the whole buffer */ - if (pbuf.cursor != pbuf.len) - ereport(ERROR, - (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), - errmsg("incorrect binary data format in bind parameter %d", - i + 1))); - } - else - { + /* Trouble if it didn't eat the whole buffer */ + if (pbuf.cursor != pbuf.len) ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unsupported format code: %d", - pformat))); - } - - /* Restore message buffer contents */ - pbuf.data[plength] = csave; + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("incorrect binary data format in bind parameter %d", + i + 1))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unsupported format code: %d", + pformat))); } + + /* Restore message buffer contents */ + pbuf.data[plength] = csave; } params[i].kind = PARAM_NUM; @@ -1538,8 +1517,6 @@ exec_execute_message(const char *portal_name, long max_rows) CommandDest dest; DestReceiver *receiver; Portal portal; - bool is_trans_stmt = false; - bool is_trans_exit = false; bool completed; char completionTag[COMPLETION_TAG_BUFSIZE]; @@ -1580,24 +1557,6 @@ exec_execute_message(const char *portal_name, long max_rows) BeginCommand(portal->commandTag, dest); - /* Check for transaction-control commands */ - if (length(portal->parseTrees) == 1) - { - Query *query = (Query *) lfirst(portal->parseTrees); - - if (query->commandType == CMD_UTILITY && - query->utilityStmt != NULL && - IsA(query->utilityStmt, TransactionStmt)) - { - TransactionStmt *stmt = (TransactionStmt *) query->utilityStmt; - - is_trans_stmt = true; - if (stmt->kind == TRANS_STMT_COMMIT || - stmt->kind == TRANS_STMT_ROLLBACK) - is_trans_exit = true; - } - } - /* * Create dest receiver in MessageContext (we don't want it in * transaction context, because that may get deleted if portal @@ -1615,14 +1574,12 @@ exec_execute_message(const char *portal_name, long max_rows) * If we are in aborted transaction state, the only portals we can * actually run are those containing COMMIT or ROLLBACK commands. */ - if (IsAbortedTransactionBlockState()) - { - if (!is_trans_exit) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); - } + if (IsAbortedTransactionBlockState() && + !IsTransactionExitStmtList(portal->parseTrees)) + ereport(ERROR, + (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), + errmsg("current transaction is aborted, " + "commands ignored until end of transaction block"))); /* Check for cancel signal before we start execution */ CHECK_FOR_INTERRUPTS(); @@ -1643,7 +1600,7 @@ exec_execute_message(const char *portal_name, long max_rows) if (completed) { - if (is_trans_stmt) + if (IsTransactionStmtList(portal->parseTrees)) { /* * If this was a transaction control statement, commit it. We @@ -1825,6 +1782,54 @@ finish_xact_command(void) } +/* + * Convenience routines for checking whether a statement is one of the + * ones that we allow in transaction-aborted state. + */ + +static bool +IsTransactionExitStmt(Node *parsetree) +{ + if (parsetree && IsA(parsetree, TransactionStmt)) + { + TransactionStmt *stmt = (TransactionStmt *) parsetree; + + if (stmt->kind == TRANS_STMT_COMMIT || + stmt->kind == TRANS_STMT_ROLLBACK) + return true; + } + return false; +} + +static bool +IsTransactionExitStmtList(List *parseTrees) +{ + if (length(parseTrees) == 1) + { + Query *query = (Query *) lfirst(parseTrees); + + if (query->commandType == CMD_UTILITY && + IsTransactionExitStmt(query->utilityStmt)) + return true; + } + return false; +} + +static bool +IsTransactionStmtList(List *parseTrees) +{ + if (length(parseTrees) == 1) + { + Query *query = (Query *) lfirst(parseTrees); + + if (query->commandType == CMD_UTILITY && + query->utilityStmt && IsA(query->utilityStmt, TransactionStmt)) + return true; + } + return false; +} + + /* -------------------------------- * signal handler routines used in PostgresMain() * -------------------------------- @@ -2710,7 +2715,7 @@ PostgresMain(int argc, char *argv[], const char *username) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.375.2.3 $ $Date: 2005/06/02 21:04:08 $\n"); + puts("$Revision: 1.375.2.4 $ $Date: 2005/11/10 00:31:59 $\n"); } /* |