#include #include #include "include.h" enum PIPES {READ, WRITE}; static int pipes[2]; static int StatementTimeoutMy; static int stderr_fd; /* * Flag to keep track of whether we have started a transaction. * For extended query protocol this has to be remembered across messages. */ bool xact_started = false; /* * If an unnamed prepared statement exists, it's stored here. * We keep it separate from the hashtable kept by commands/prepare.c * in order to reduce overhead for short-lived queries. */ static CachedPlanSource *unnamed_stmt_psrc = NULL; static bool check_log_statement(List *stmt_list); static int errdetail_execute(List *raw_parsetree_list); static int errdetail_abort(void); static void start_xact_command(void); static void finish_xact_command(void); static bool IsTransactionExitStmt(Node *parsetree); static void drop_unnamed_stmt(void); static void enable_statement_timeout(void); static void disable_statement_timeout(void); static void capture_stderr_start(void) { if (pipe(pipes) < 0) E("pipe < 0"); if ((stderr_fd = dup(STDERR_FILENO)) < 0) E("dup < 0"); if (dup2(pipes[WRITE], STDERR_FILENO) < 0) E("dup2 < 0"); } static void capture_stderr_stop(Task *task) { char buffer[1024 + 1]; int nread; if (fflush(stderr)) E("fflush"); if (dup2(stderr_fd, STDERR_FILENO) < 0) E("dup2 < 0"); if (close(stderr_fd) < 0) E("close < 0"); if (close(pipes[WRITE]) < 0) E("close < 0"); while ((nread = read(pipes[READ], buffer, sizeof(buffer))) > 0) { if (!task->error.data) initStringInfoMy(TopMemoryContext, &task->error); buffer[nread] = '\0'; appendStringInfoString(&task->error, buffer); if (fwrite(buffer, nread, 1, stderr) != 1) E("fwrite"); } if (close(pipes[READ]) < 0) E("close < 0"); } /* * exec_simple_query * * Execute a "simple Query" protocol message. */ void exec_simple_query_my(Task *task) { const char *query_string = task->input; CommandDest dest = whereToSendOutput = DestDebug; MemoryContext oldcontext; List *parsetree_list; ListCell *parsetree_item; bool save_log_statement_stats = log_statement_stats; bool was_logged = false; bool use_implicit_block; char msec_str[32]; StatementTimeoutMy = task->timeout; SetConfigOption("config.append_type_to_column_name", task->append ? "true" : NULL, PGC_USERSET, PGC_S_SESSION); /* * Report query to various monitoring facilities. */ debug_query_string = query_string; pgstat_report_activity(STATE_RUNNING, query_string); TRACE_POSTGRESQL_QUERY_START(query_string); /* * We use save_log_statement_stats so ShowUsage doesn't report incorrect * results because ResetUsage wasn't called. */ if (save_log_statement_stats) ResetUsage(); /* * 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 * one of those, else bad things will happen in xact.c. (Note that this * will normally change current memory context.) */ start_xact_command(); /* * Zap any pre-existing unnamed statement. (While not strictly necessary, * it seems best to define simple-Query mode as if it used the unnamed * statement and portal; this ensures we recover any storage used by prior * unnamed operations.) */ drop_unnamed_stmt(); /* * Switch to appropriate context for constructing parsetrees. */ oldcontext = MemoryContextSwitchTo(MessageContext); /* * Do basic parsing of the query or queries (this should be safe even if * we are in aborted transaction state!) */ parsetree_list = pg_parse_query(query_string); task->length = list_length(parsetree_list); /* Log immediately if dictated by log_statement */ if (check_log_statement(parsetree_list)) { ereport(LOG, (errmsg("statement: %s", query_string), errhidestmt(true), errdetail_execute(parsetree_list))); was_logged = true; } /* * Switch back to transaction context to enter the loop. */ MemoryContextSwitchTo(oldcontext); /* * For historical reasons, if multiple SQL statements are given in a * single "simple Query" message, we execute them as a single transaction, * unless explicit transaction control commands are included to make * portions of the list be separate transactions. To represent this * behavior properly in the transaction machinery, we use an "implicit" * transaction block. */ use_implicit_block = (list_length(parsetree_list) > 1); /* * Run through the raw parsetree(s) and process each one. */ foreach(parsetree_item, parsetree_list) { RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item); bool snapshot_set = false; CommandTag commandTag; QueryCompletion qc; MemoryContext per_parsetree_context = NULL; List *querytree_list, *plantree_list; Portal portal; DestReceiver *receiver; int16 format; /* * 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->stmt); set_ps_display(GetCommandTagName(commandTag)); BeginCommandMy(commandTag, task); /* * If we are in an aborted transaction, reject all commands except * COMMIT/ABORT. It is important that this test occur before we try * to do parse analysis, rewrite, or planning, since all those phases * try to do database accesses, which may fail in abort state. (It * might be safe to allow some additional utility commands in this * state, but not many...) */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmt(parsetree->stmt)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block"), errdetail_abort())); /* Make sure we are in a transaction command */ start_xact_command(); /* * If using an implicit transaction block, and we're not already in a * transaction block, start an implicit block to force this statement * to be grouped together with any following ones. (We must do this * each time through the loop; otherwise, a COMMIT/ROLLBACK in the * list would cause later statements to not be grouped.) */ if (use_implicit_block) BeginImplicitTransactionBlock(); /* If we got a cancel signal in parsing or prior command, quit */ CHECK_FOR_INTERRUPTS(); /* * 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. * * Switch to appropriate context for constructing query and plan trees * (these can't be in the transaction context, as that will get reset * when the command is COMMIT/ROLLBACK). If we have multiple * parsetrees, we use a separate context for each one, so that we can * free that memory before moving on to the next one. But for the * last (or only) parsetree, just use MessageContext, which will be * reset shortly after completion anyway. In event of an error, the * per_parsetree_context will be deleted when MessageContext is reset. */ if (lnext(parsetree_list, parsetree_item) != NULL) { per_parsetree_context = AllocSetContextCreate(MessageContext, "per-parsetree message context", ALLOCSET_DEFAULT_SIZES); oldcontext = MemoryContextSwitchTo(per_parsetree_context); } else oldcontext = MemoryContextSwitchTo(MessageContext); querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0, NULL); plantree_list = pg_plan_queries(querytree_list, query_string, CURSOR_OPT_PARALLEL_OK, NULL); /* * Done with the snapshot used for parsing/planning. * * While it looks promising to reuse the same snapshot for query * execution (at least for simple protocol), unfortunately it causes * execution to use a snapshot that has been acquired before locking * any of the tables mentioned in the query. This creates user- * visible anomalies, so refrain. Refer to * https://postgr.es/m/flat/5075D8DF.6050500@fuzzy.cz for details. */ if (snapshot_set) PopActiveSnapshot(); /* If we got a cancel signal in analysis or planning, quit */ CHECK_FOR_INTERRUPTS(); /* * Create unnamed portal to run the query or queries in. If there * already is one, silently drop it. */ portal = CreatePortal("", true, true); /* Don't display the portal in pg_cursors */ portal->visible = false; /* * We don't have to copy anything into the portal, because everything * we are passing here is in MessageContext or the * per_parsetree_context, and so will outlive the portal anyway. */ PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL); /* * Start the portal. No parameters here. */ PortalStart(portal, NULL, 0, InvalidSnapshot); /* * Select the appropriate output format: text unless we are doing a * FETCH from a binary cursor. (Pretty grotty to have to do this here * --- but it avoids grottiness in other places. Ah, the joys of * backward compatibility...) */ format = 0; /* TEXT is default */ if (IsA(parsetree->stmt, FetchStmt)) { FetchStmt *stmt = (FetchStmt *) parsetree->stmt; if (!stmt->ismove) { Portal fportal = GetPortalByName(stmt->portalname); if (PortalIsValid(fportal) && (fportal->cursorOptions & CURSOR_OPT_BINARY)) format = 1; /* BINARY */ } } PortalSetResultFormat(portal, 1, &format); /* * Now we can create the destination receiver object. */ receiver = CreateDestReceiverMy(task); if (dest == DestRemote) SetRemoteDestReceiverParams(receiver, portal); /* * Switch back to transaction context for execution. */ MemoryContextSwitchTo(oldcontext); capture_stderr_start(); /* * Run the portal to completion, and then drop it (and the receiver). */ (void) PortalRun(portal, FETCH_ALL, true, /* always top level */ true, receiver, receiver, &qc); capture_stderr_stop(task); receiver->rDestroy(receiver); PortalDrop(portal, false); if (lnext(parsetree_list, parsetree_item) == NULL) { /* * If this is the last parsetree of the query string, close down * transaction statement before reporting command-complete. This * is so that any end-of-transaction errors are reported before * the command-complete message is issued, to avoid confusing * clients who will expect either a command-complete message or an * error, not one and then the other. Also, if we're using an * implicit transaction block, we must close that out first. */ if (use_implicit_block) EndImplicitTransactionBlock(); finish_xact_command(); } else if (IsA(parsetree->stmt, TransactionStmt)) { /* * If this was a transaction control statement, commit it. We will * start a new xact command for the next command. */ finish_xact_command(); } else { /* * We need a CommandCounterIncrement after every query, except * those that start or end a transaction block. */ CommandCounterIncrement(); /* * Disable statement timeout between queries of a multi-query * string, so that the timeout applies separately to each query. * (Our next loop iteration will start a fresh timeout.) */ disable_statement_timeout(); } /* * Tell client that we're done with this query. Note we emit exactly * one EndCommand report for each raw parsetree, thus one for each SQL * command the client sent, regardless of rewriting. (But a command * aborted by error will not send an EndCommand report at all.) */ EndCommandMy(&qc, task, false); /* Now we may drop the per-parsetree context, if one was created. */ if (per_parsetree_context) MemoryContextDelete(per_parsetree_context); } /* end loop over parsetrees */ /* * Close down transaction statement, if one is open. (This will only do * something if the parsetree list was empty; otherwise the last loop * iteration already did it.) */ finish_xact_command(); /* * If there were no parsetrees, return EmptyQueryResponse message. */ if (!parsetree_list) NullCommandMy(task); /* * Emit duration logging if appropriate. */ switch (check_log_duration(msec_str, was_logged)) { case 1: ereport(LOG, (errmsg("duration: %s ms", msec_str), errhidestmt(true))); break; case 2: ereport(LOG, (errmsg("duration: %s ms statement: %s", msec_str, query_string), errhidestmt(true), errdetail_execute(parsetree_list))); break; } if (save_log_statement_stats) ShowUsage("QUERY STATISTICS"); TRACE_POSTGRESQL_QUERY_DONE(query_string); debug_query_string = NULL; } /* * check_log_statement * Determine whether command should be logged because of log_statement * * stmt_list can be either raw grammar output or a list of planned * statements */ static bool check_log_statement(List *stmt_list) { ListCell *stmt_item; if (log_statement == LOGSTMT_NONE) return false; if (log_statement == LOGSTMT_ALL) return true; /* Else we have to inspect the statement(s) to see whether to log */ foreach(stmt_item, stmt_list) { Node *stmt = (Node *) lfirst(stmt_item); if (GetCommandLogLevel(stmt) <= log_statement) return true; } return false; } /* * errdetail_execute * * Add an errdetail() line showing the query referenced by an EXECUTE, if any. * The argument is the raw parsetree list. */ static int errdetail_execute(List *raw_parsetree_list) { ListCell *parsetree_item; foreach(parsetree_item, raw_parsetree_list) { RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item); if (IsA(parsetree->stmt, ExecuteStmt)) { ExecuteStmt *stmt = (ExecuteStmt *) parsetree->stmt; PreparedStatement *pstmt; pstmt = FetchPreparedStatement(stmt->name, false); if (pstmt) { errdetail("prepare: %s", pstmt->plansource->query_string); return 0; } } } return 0; } /* * errdetail_abort * * Add an errdetail() line showing abort reason, if any. */ static int errdetail_abort(void) { if (MyProc->recoveryConflictPending) errdetail("abort reason: recovery conflict"); return 0; } /* * Convenience routines for starting/committing a single command. */ static void start_xact_command(void) { if (!xact_started) { StartTransactionCommand(); xact_started = true; } /* * Start statement timeout if necessary. Note that this'll intentionally * not reset the clock on an already started timeout, to avoid the timing * overhead when start_xact_command() is invoked repeatedly, without an * interceding finish_xact_command() (e.g. parse/bind/execute). If that's * not desired, the timeout has to be disabled explicitly. */ enable_statement_timeout(); } static void finish_xact_command(void) { /* cancel active statement timeout after each command */ disable_statement_timeout(); if (xact_started) { CommitTransactionCommand(); ProcessCompletedNotifies(); #ifdef MEMORY_CONTEXT_CHECKING /* Check all memory contexts that weren't freed during commit */ /* (those that were, were checked before being deleted) */ MemoryContextCheck(TopMemoryContext); #endif #ifdef SHOW_MEMORY_STATS /* Print mem stats after each commit for leak tracking */ MemoryContextStats(TopMemoryContext); #endif xact_started = false; } } /* * Convenience routines for checking whether a statement is one of the * ones that we allow in transaction-aborted state. */ /* Test a bare parsetree */ static bool IsTransactionExitStmt(Node *parsetree) { if (parsetree && IsA(parsetree, TransactionStmt)) { TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || stmt->kind == TRANS_STMT_PREPARE || stmt->kind == TRANS_STMT_ROLLBACK || stmt->kind == TRANS_STMT_ROLLBACK_TO) return true; } return false; } /* Release any existing unnamed prepared statement */ static void drop_unnamed_stmt(void) { /* paranoia to avoid a dangling pointer in case of error */ if (unnamed_stmt_psrc) { CachedPlanSource *psrc = unnamed_stmt_psrc; unnamed_stmt_psrc = NULL; DropCachedPlan(psrc); } } /* * Start statement timeout timer, if enabled. * * If there's already a timeout running, don't restart the timer. That * enables compromises between accuracy of timeouts and cost of starting a * timeout. */ static void enable_statement_timeout(void) { /* must be within an xact */ Assert(xact_started); if (StatementTimeoutMy > 0) { if (!get_timeout_active(STATEMENT_TIMEOUT)) enable_timeout_after(STATEMENT_TIMEOUT, StatementTimeoutMy); } else { if (get_timeout_active(STATEMENT_TIMEOUT)) disable_timeout(STATEMENT_TIMEOUT, false); } } /* * Disable statement timeout, if active. */ static void disable_statement_timeout(void) { if (get_timeout_active(STATEMENT_TIMEOUT)) disable_timeout(STATEMENT_TIMEOUT, false); }