Author: Jim Nasby Date: Thu, 30 Oct 2014 20:25:34 -0500 Move the bulk of exec_simple_query into exec_query_string() so that pg_backend can also make use of it. I’m not thrilled about the name, and I’m not sure tcopprot.h is the right place to expose this. Also note the XXX comments. --- contrib/pg_background/pg_background.c | 141 ++-------------------------------- src/backend/tcop/postgres.c | 83 ++++++++++++++++---- src/include/tcop/tcopprot.h | 7 ++ 3 files changed, 80 insertions(+), 151 deletions(-) diff --git a/contrib/pg_background/pg_background.c b/contrib/pg_background/pg_background.c index a566ffb..075ecd8 100644 --- a/contrib/pg_background/pg_background.c +++ b/contrib/pg_background/pg_background.c @@ -817,10 +817,6 @@ pg_background_worker_main(Datum main_arg) static void execute_sql_string(const char *sql) { - List *raw_parsetree_list; - ListCell *lc1; - bool isTopLevel; - int commands_remaining; MemoryContext parsecontext; MemoryContext oldcontext; @@ -839,139 +835,16 @@ execute_sql_string(const char *sql) ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - oldcontext = MemoryContextSwitchTo(parsecontext); - raw_parsetree_list = pg_parse_query(sql); - commands_remaining = list_length(raw_parsetree_list); - isTopLevel = commands_remaining == 1; - MemoryContextSwitchTo(oldcontext); /* - * Do parse analysis, rule rewrite, planning, and execution for each raw - * parsetree. We must fully execute each query before beginning parse - * analysis on the next one, since there may be interdependencies. + * Do the real work */ - foreach(lc1, raw_parsetree_list) - { - Node *parsetree = (Node *) lfirst(lc1); - const char *commandTag; - char completionTag[COMPLETION_TAG_BUFSIZE]; - List *querytree_list, - *plantree_list; - bool snapshot_set = false; - Portal portal; - DestReceiver *receiver; - int16 format = 1; - - /* - * We don't allow transaction-control commands like COMMIT and ABORT - * here. The entire SQL statement is executed as a single transaction - * which commits if no errors are encountered. - */ - if (IsA(parsetree, TransactionStmt)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("transaction control statements are not allowed in pg_background"))); - - /* - * Get the command name for use in status display (it also becomes the - * default completion tag, down inside PortalRun). Set ps_status and - * do any special start-of-SQL-command processing needed by the - * destination. - */ - commandTag = CreateCommandTag(parsetree); - set_ps_display(commandTag, false); - BeginCommand(commandTag, DestNone); - - /* Set up a snapshot if parse analysis/planning will need one. */ - if (analyze_requires_snapshot(parsetree)) - { - PushActiveSnapshot(GetTransactionSnapshot()); - snapshot_set = true; - } - - /* - * OK to analyze, rewrite, and plan this query. - * - * As with parsing, we need to make sure this data outlives the - * transaction, because of the possibility that the statement might - * perform internal transaction control. - */ - oldcontext = MemoryContextSwitchTo(parsecontext); - querytree_list = pg_analyze_and_rewrite(parsetree, sql, NULL, 0); - plantree_list = pg_plan_queries(querytree_list, 0, NULL); - - /* Done with the snapshot used for parsing/planning */ - if (snapshot_set) - PopActiveSnapshot(); - - /* If we got a cancel signal in analysis or planning, quit */ - CHECK_FOR_INTERRUPTS(); - - /* - * Execute the query using the unnamed portal. - */ - portal = CreatePortal("", true, true); - /* Don't display the portal in pg_cursors */ - portal->visible = false; - PortalDefineQuery(portal, NULL, sql, commandTag, plantree_list, NULL); - PortalStart(portal, NULL, 0, InvalidSnapshot); - - /* We always use binary format, for efficiency. */ - PortalSetResultFormat(portal, 1, &format); - - /* - * Tuples returned by any command other than the last are simply - * discarded; but those returned by the last (or only) command are - * redirected to the shared memory queue we're using for communication - * with the launching backend. If the launching backend is gone or has - * detached us, these messages will just get dropped on the floor. - */ - --commands_remaining; - if (commands_remaining > 0) - receiver = CreateDestReceiver(DestNone); - else - { - receiver = CreateDestReceiver(DestRemote); - SetRemoteDestReceiverParams(receiver, portal); - } - - /* - * Only once the portal and destreceiver have been established can - * we return to the transaction context. All that stuff needs to - * survive an internal commit inside PortalRun! - */ - MemoryContextSwitchTo(oldcontext); - - /* Here's where we actually execute the command. */ - (void) PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, - completionTag); - - /* Clean up the receiver. */ - (*receiver->rDestroy) (receiver); - - /* Clean up the portal. */ - PortalDrop(portal, false); - - /* - * If this is the last parsetree, close down transaction statement - * before reporting CommandComplete. Otherwise, we need a - * CommandCounterIncrement. - */ - if (lnext(lc1) == NULL) - finish_xact_command(); - else - CommandCounterIncrement(); - - /* - * Send a CommandComplete message even if we suppressed the query - * results. The user backend will report the command tags in the - * absence of any true query results. - */ - EndCommand(completionTag, DestRemote); - } - - /* Make sure there's not still a transaction open. */ - finish_xact_command(); + exec_query_string(query_string, + DestRemote, + &parsecontext, + true, /* allow_transactions */ + true /* last_result_only */ + ); } /* diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index e44f5fa..3b998fc 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -823,13 +823,7 @@ static void exec_simple_query(const char *query_string) { CommandDest dest = whereToSendOutput; - MemoryContext oldcontext; - List *parsetree_list; - ListCell *parsetree_item; bool save_log_statement_stats = log_statement_stats; - bool was_logged = false; - bool isTopLevel; - char msec_str[32]; /* @@ -849,6 +843,46 @@ exec_simple_query(const char *query_string) ResetUsage(); /* + * Do the real work + */ + exec_query_string(query_string, + dest, + MessageContext, + true, /* allow_transactions */ + false /* last_result_only */ + ); + + if (save_log_statement_stats) + ShowUsage("QUERY STATISTICS"); + + TRACE_POSTGRESQL_QUERY_DONE(query_string); + + debug_query_string = NULL; +} + +/* + * exec_query_string + * + * This is the guts of exec_simply_query, but is also used by + * execute_sql_string in contrib/pg_background + */ +void +exec_query_string(const char *query_string, + CommandDest dest, /* Where to send output */ + MemoryContext parsecontext, /* Context we should use to parse query_string */ + bool allow_transactions, /* Should we allow transaction statements? */ + bool last_result_only /* If true, drop all command output except for the last command */ + ) +{ + MemoryContext oldcontext; + List *parsetree_list; + ListCell *parsetree_item; + bool was_logged = false; + bool isTopLevel; + char msec_str[32]; + int commands_remaining; + + /* * Start up a transaction command. All queries generated by the * query_string will be in this same command block, *unless* we find a * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after @@ -875,6 +909,7 @@ exec_simple_query(const char *query_string) * we are in aborted transaction state!) */ parsetree_list = pg_parse_query(query_string); + commands_remaining = list_length(parsetree_list); /* Log immediately if dictated by log_statement */ if (check_log_statement(parsetree_list)) @@ -915,6 +950,13 @@ exec_simple_query(const char *query_string) DestReceiver *receiver; int16 format; + if (!allow_transactions && IsA(parsetree, TransactionStmt)) + /* XXX this needs to be more flexible */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("transaction control statements are not allowed in pg_background"))); + + /* * Get the command name for use in status display (it also becomes the * default completion tag, down inside PortalRun). Set ps_status and @@ -925,6 +967,7 @@ exec_simple_query(const char *query_string) set_ps_display(commandTag, false); + /* XXX what should we do here in the case of last_result_only = true? */ BeginCommand(commandTag, dest); /* @@ -944,7 +987,8 @@ exec_simple_query(const char *query_string) errdetail_abort())); /* Make sure we are in a transaction command */ - start_xact_command(); + if (allow_transactions) + start_xact_command(); /* If we got a cancel signal in parsing or prior command, quit */ CHECK_FOR_INTERRUPTS(); @@ -1027,10 +1071,19 @@ exec_simple_query(const char *query_string) /* * Now we can create the destination receiver object. + * + * If we sending the last result only, throw output away unless it's + * the last command. */ - receiver = CreateDestReceiver(dest); - if (dest == DestRemote) - SetRemoteDestReceiverParams(receiver, portal); + --commands_remaining; + if (last_result_only && commands_remaining > 0) + receiver = CreateDestReceiver(DestNone); + else + { + receiver = CreateDestReceiver(dest); + if (dest == DestRemote) + SetRemoteDestReceiverParams(receiver, portal); + } /* * Switch back to transaction context for execution. @@ -1055,7 +1108,9 @@ exec_simple_query(const char *query_string) { /* * If this was a transaction control statement, commit it. We will - * start a new xact command for the next command (if any). + * start a new xact command for the next command (if any). Note + * that if allow_transactions is false then we should never get to + * this point. */ finish_xact_command(); } @@ -1121,12 +1176,6 @@ exec_simple_query(const char *query_string) break; } - if (save_log_statement_stats) - ShowUsage("QUERY STATISTICS"); - - TRACE_POSTGRESQL_QUERY_DONE(query_string); - - debug_query_string = NULL; } /* diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index fd3df58..39c6a39 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -86,4 +86,11 @@ extern bool set_plan_disabling_options(const char *arg, GucContext context, GucSource source); extern const char *get_stats_option_name(const char *arg); +extern void exec_query_string(const char *query_string, + CommandDest dest, /* Where to send output */ + MemoryContext parsecontext, /* Context we should use to parse query_string */ + bool allow_transactions, /* Should we allow transaction statements? */ + bool last_result_only /* If true, drop all command output except for the last command */ + ); + #endif /* TCOPPROT_H */ -- 2.1.2