BeginImplicitTransactionBlock函数启动一个隐含事务块,和BeginTransactionBlock不一样,BeginImplicitTransactionBlock是从postgres.c的main loop直接调用的而不是通过Portal。所以我们仅改变事务状态块,也不期望调用者调用CommitTransactionCommand/StartTransactionCommand。
void BeginImplicitTransactionBlock(void) {
TransactionState s = CurrentTransactionState;
/* If we are in STARTED state (that is, no transaction block is open),
* switch to IMPLICIT_INPROGRESS state, creating an implicit transaction
* block.
* For caller convenience, we consider all other transaction states as
* legal here; otherwise the caller would need its own state check, which
* seems rather pointless. */
if (s->blockState == TBLOCK_STARTED)
s->blockState = TBLOCK_IMPLICIT_INPROGRESS;
}
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();
}
调用处在于exec_simple_query函数:
- start_xact_command()建立事务命令。所有由query_strin*生的查询将处理相同的命令块总,除非我们发现一个BEGIN/COMMIT/ABORT语句。我们需要强制执行一个xact命令,否则会发生不好的事情。start_xact_command命令只有是调用StartTransactionCommand函数(StartTransactionCommand函数将事务块状态从TBLOCK_DEFAULT改为TBLOCK_STARTED)。 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.)
- use_implicit_block = (list_length(parsetree_list) > 1); 如果解析树大于1的话,需要使用隐含事务块。这是因为历史原因,在简单查询中多个SQL语句需要在单个事务中执行。显式事务控制命令可以将拆分事务。 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.
- foreach(parsetree_item, parsetree_list)
- start_xact_command() Make sure we are in a transaction command
- if (use_implicit_block) BeginImplicitTransactionBlock() 如果使用隐含事务块,且不在事务块中,启动一个隐含事务块。(BeginImplicitTransactionBlock函数将事务块状态从TBLOCK_STARTED改为TBLOCK_IMPLICIT_INPROGRESS) 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.)
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 use_implicit_block;
char msec_str[32];
/* 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);
/* 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;
const char *commandTag;
char completionTag[COMPLETION_TAG_BUFSIZE];
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(commandTag, false);
BeginCommand(commandTag, dest);
/* 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();