From: Yoshiyuki Asaba Date: Wed, 25 Jul 2007 07:01:05 +0000 (+0000) Subject: Fix hang up when a SELECT query has error inside transaction block. It X-Git-Tag: V3_4~16 X-Git-Url: http://git.postgresql.org/gitweb/edit?a=commitdiff_plain;h=71c1640a77d0205ee81ad787f3e1706379859148;p=pgpool1.git Fix hang up when a SELECT query has error inside transaction block. It occurs only in simple query protocol. We reproduce the bug with the following operation. BEGIN; SELECT * FROM non_exist_table; <-- ERROR SELECT 1; --- diff --git a/pool_process_query.c b/pool_process_query.c index af4ce1f..428817a 100644 --- a/pool_process_query.c +++ b/pool_process_query.c @@ -124,6 +124,7 @@ static int load_balance_enabled(POOL_CONNECTION_POOL *backend, char *sql); static void start_load_balance(POOL_CONNECTION_POOL *backend); static void end_load_balance(POOL_CONNECTION_POOL *backend); static POOL_STATUS do_command(POOL_CONNECTION *backend, char *query, int protoMajor, int no_ready_for_query); +static POOL_STATUS do_error_command(POOL_CONNECTION *backend, int protoMajor); static int need_insert_lock(POOL_CONNECTION_POOL *backend, char *query); static POOL_STATUS insert_lock(POOL_CONNECTION_POOL *backend, char *query); static char *get_insert_command_table_name(char *query); @@ -2658,13 +2659,11 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend, POOL_C if (select_in_transaction) { - do_command(SECONDARY(backend), "send invalid query from pgpool to abort transaction.", - PROTO_MAJOR_V3, 0); - pool_write(SECONDARY(backend), "S", 1); - res1 = htonl(4); - if (pool_write_and_flush(SECONDARY(backend), &res1, sizeof(res1)) < 0) + if (TSTATE(backend) != 'E') { - return POOL_END; + in_load_balance = 0; + REPLICATION = 1; + do_error_command(SECONDARY(backend), PROTO_MAJOR_V3); } select_in_transaction = 0; } @@ -3371,6 +3370,68 @@ static POOL_STATUS do_command(POOL_CONNECTION *backend, char *query, int protoMa return deadlock_detected ? POOL_DEADLOCK : POOL_CONTINUE; } +/* + * Send syntax error query to abort transaction. + * We need to sync transaction status in transaction block. + * SELECT query is sended to master only. + * If SELECT is error, we must abort transaction on other nodes. + */ +static POOL_STATUS do_error_command(POOL_CONNECTION *backend, int protoMajor) +{ + int len; + int status; + char kind; + char *string; + char *error_query = "send invalid query from pgpool to abort transaction"; + + /* send the query to the backend */ + pool_write(backend, "Q", 1); + len = strlen(error_query)+1; + + if (protoMajor == PROTO_MAJOR_V3) + { + int sendlen = htonl(len + 4); + pool_write(backend, &sendlen, sizeof(sendlen)); + } + + if (pool_write_and_flush(backend, error_query, len) < 0) + { + return POOL_END; + } + + /* + * Expecting CompleteCommand + */ + status = pool_read(backend, &kind, sizeof(kind)); + if (status < 0) + { + pool_error("do_command: error while reading message kind"); + return POOL_END; + } + + /* + * read ErrorResponse message + */ + if (protoMajor == PROTO_MAJOR_V3) + { + if (pool_read(backend, &len, sizeof(len)) < 0) + return POOL_END; + len = ntohl(len) - 4; + string = pool_read2(backend, len); + if (string == NULL) + return POOL_END; + pool_debug("command tag: %s", string); + } + else + { + string = pool_read_string(backend, &len, 0); + if (string == NULL) + return POOL_END; + } + + return POOL_CONTINUE; +} + /* * judge if we need to lock the table * to keep SERIAL consistency among servers