这篇文章主要介绍“PostgreSQL中exec_simple_query函数的实现逻辑是什么”,在日常操作中,相信很多人在PostgreSQL中exec_simple_query函数的实现逻辑是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中exec_simple_query函数的实现逻辑是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
一、源码解读
exec_simple_query函数,顾名思义,执行简单“查询”(包括INSERT/UPDATE/DELETE等语句)
/* * exec_simple_query * * Execute a "simple Query" protocol message. */ /* 输入: query_string-SQL语句 输出: 无 */ static void exec_simple_query(const char *query_string) { CommandDest dest = whereToSendOutput;//输出到哪里的定义 MemoryContext oldcontext;//存储原内存上下文 List *parsetree_list;//分析树列表 ListCell *parsetree_item;//分析树中的ITEM bool save_log_statement_stats = log_statement_stats;//是否保存统计信息,false bool was_logged = false;//Log? 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);//解析输入的查询语句,获得分析树List(元素是RawStmt nodes) /* 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);//如果分析树条目>1,使用隐式事务块(多条SQL语句在同一个事务中) /* * Run through the raw parsetree(s) and process each one. */ foreach(parsetree_item, parsetree_list)//对分析树中的每一个条目进行处理 { RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);//分析树List中的元素为RawStmt指针类型 bool snapshot_set = false;//是否设置快照? const char *commandTag;//命令标识 char completionTag[COMPLETION_TAG_BUFSIZE];//完成标记,如INSERT 0 1之类的字符串 List *querytree_list,//查询树List *plantree_list;//执行计划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);//创建命令标记,插入数据则为INSERT set_ps_display(commandTag, false); BeginCommand(commandTag, dest);//do Nothing! /* * 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 querytrees (again, * these must outlive the execution context). */ oldcontext = MemoryContextSwitchTo(MessageContext);//切换内存上下文 querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0, NULL);//根据分析树获得查询树,返回List(元素为Query) plantree_list = pg_plan_queries(querytree_list, CURSOR_OPT_PARALLEL_OK, NULL);//根据查询树获取计划树,返回List(元素为PlannedStmt) /* 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(); /* * Create unnamed portal to run the query or queries in. If there * already is one, silently drop it. */ portal = CreatePortal("", true, true);//创建匿名Portal变量 /* 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, which will outlive the * portal anyway. */ PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL);//给Portal变量赋值 /* * Start the portal. No parameters here. */ PortalStart(portal, NULL, 0, InvalidSnapshot);//为PortalRun作准备 /* * 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);//设置结果返回的格式,默认为TEXT /* * Now we can create the destination receiver object. */ receiver = CreateDestReceiver(dest);//创建目标接收器(如使用psql则为:printtup DestReceiver) if (dest == DestRemote) SetRemoteDestReceiverParams(receiver, portal); /* * Switch back to transaction context for execution. */ MemoryContextSwitchTo(oldcontext);//切换回原内存上下文 /* * Run the portal to completion, and then drop it (and the receiver). */ (void) PortalRun(portal, FETCH_ALL, true, /* always top level */ true, receiver, receiver, completionTag);//执行 receiver->rDestroy(receiver);//执行完毕,销毁接收器 PortalDrop(portal, false);//清除Portal中的资源&Portal if (lnext(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))//事务语句?BEGIN/COMMIT/ABORT... { /* * 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();//命令+1(对应Tuple中的cid) } /* * 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.) */ EndCommand(completionTag, dest);//命令Done } /* 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) NullCommand(dest); /* * 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; }
二、基础信息
exec_simple_query函数使用的数据结构、宏定义以及依赖的函数等。
数据结构/宏定义
*1、whereToSendOutput *
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */ CommandDest whereToSendOutput = DestDebug; /* ---------------- * CommandDest is a simplistic means of identifying the desired * destination. Someday this will probably need to be improved. * * Note: only the values DestNone, DestDebug, DestRemote are legal for the * global variable whereToSendOutput. The other values may be used * as the destination for individual commands. * ---------------- */ typedef enum { DestNone, /* results are discarded */ DestDebug, /* results go to debugging output */ DestRemote, /* results sent to frontend process */ DestRemoteExecute, /* sent to frontend, in Execute command */ DestRemoteSimple, /* sent to frontend, w/no catalog access */ DestSPI, /* results sent to SPI manager */ DestTuplestore, /* results sent to Tuplestore */ DestIntoRel, /* results sent to relation (SELECT INTO) */ DestCopyOut, /* results sent to COPY TO code */ DestSQLFunction, /* results sent to SQL-language func mgr */ DestTransientRel, /* results sent to transient relation */ DestTupleQueue /* results sent to tuple queue */ } CommandDest;
2、RawStmt
/* * RawStmt --- container for any one statement's raw parse tree * * Parse analysis converts a raw parse tree headed by a RawStmt node into * an analyzed statement headed by a Query node. For optimizable statements, * the conversion is complex. For utility statements, the parser usually just * transfers the raw parse tree (sans RawStmt) into the utilityStmt field of * the Query node, and all the useful work happens at execution time. * * stmt_location/stmt_len identify the portion of the source text string * containing this raw statement (useful for multi-statement strings). */ typedef struct RawStmt { NodeTag type; Node *stmt; /* raw parse tree */ int stmt_location; /* start location, or -1 if unknown */ int stmt_len; /* length in bytes; 0 means "rest of string" */ } RawStmt;
3、Query
//在解析查询语句时再深入解析 /***************************************************************************** * Query Tree *****************************************************************************/ /* * Query - * Parse analysis turns all statements into a Query tree * for further processing by the rewriter and planner. * * Utility statements (i.e. non-optimizable statements) have the * utilityStmt field set, and the rest of the Query is mostly dummy. * * Planning converts a Query tree into a Plan tree headed by a PlannedStmt * node --- the Query structure is not used by the executor. */ typedef struct Query { NodeTag type; CmdType commandType; /* select|insert|update|delete|utility */ QuerySource querySource; /* where did I come from? */ uint64 queryId; /* query identifier (can be set by plugins) */ bool canSetTag; /* do I set the command result tag? */ Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */ int resultRelation; /* rtable index of target relation for * INSERT/UPDATE/DELETE; 0 for SELECT */ bool hasAggs; /* has aggregates in tlist or havingQual */ bool hasWindowFuncs; /* has window functions in tlist */ bool hasTargetSRFs; /* has set-returning functions in tlist */ bool hasSubLinks; /* has subquery SubLink */ bool hasDistinctOn; /* distinctClause is from DISTINCT ON */ bool hasRecursive; /* WITH RECURSIVE was specified */ bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */ bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */ bool hasRowSecurity; /* rewriter has applied some RLS policy */ List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ OverridingKind override; /* OVERRIDING clause */ OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */ List *returningList; /* return-values list (of TargetEntry) */ List *groupClause; /* a list of SortGroupClause's */ List *groupingSets; /* a list of GroupingSet's if present */ Node *havingQual; /* qualifications applied to groups */ List *windowClause; /* a list of WindowClause's */ List *distinctClause; /* a list of SortGroupClause's */ List *sortClause; /* a list of SortGroupClause's */ Node *limitOffset; /* # of result tuples to skip (int8 expr) */ Node *limitCount; /* # of result tuples to return (int8 expr) */ List *rowMarks; /* a list of RowMarkClause's */ Node *setOperations; /* set-operation tree if this is top level of * a UNION/INTERSECT/EXCEPT query */ List *constraintDeps; /* a list of pg_constraint OIDs that the query * depends on to be semantically valid */ List *withCheckOptions; /* a list of WithCheckOption's, which are * only added during rewrite and therefore * are not written out as part of Query. */ /* * The following two fields identify the portion of the source text string * containing this query. They are typically only populated in top-level * Queries, not in sub-queries. When not set, they might both be zero, or * both be -1 meaning "unknown". */ int stmt_location; /* start location, or -1 if unknown */ int stmt_len; /* length in bytes; 0 means "rest of string" */ } Query;
4、ParseState
/* * Function signatures for parser hooks */ typedef struct ParseState ParseState; typedef Node *(*PreParseColumnRefHook) (ParseState *pstate, ColumnRef *cref); typedef Node *(*PostParseColumnRefHook) (ParseState *pstate, ColumnRef *cref, Node *var); typedef Node *(*ParseParamRefHook) (ParseState *pstate, ParamRef *pref); typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, Oid targetTypeId, int32 targetTypeMod, int location); /* * State information used during parse analysis * * parentParseState: NULL in a top-level ParseState. When parsing a subquery, * links to current parse state of outer query. * * p_sourcetext: source string that generated the raw parsetree being * analyzed, or NULL if not available. (The string is used only to * generate cursor positions in error messages: we need it to convert * byte-wise locations in parse structures to character-wise cursor * positions.) * * p_rtable: list of RTEs that will become the rangetable of the query. * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. * * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that * will become the fromlist of the query's top-level FromExpr node. * * p_namespace: list of ParseNamespaceItems that represents the current * namespace for table and column lookup. (The RTEs listed here may be just * a subset of the whole rtable. See ParseNamespaceItem comments below.) * * p_lateral_active: true if we are currently parsing a LATERAL subexpression * of this parse level. This makes p_lateral_only namespace items visible, * whereas they are not visible when p_lateral_active is FALSE. * * p_ctenamespace: list of CommonTableExprs (WITH items) that are visible * at the moment. This is entirely different from p_namespace because a CTE * is not an RTE, rather "visibility" means you could make an RTE from it. * * p_future_ctes: list of CommonTableExprs (WITH items) that are not yet * visible due to scope rules. This is used to help improve error messages. * * p_parent_cte: CommonTableExpr that immediately contains the current query, * if any. * * p_target_relation: target relation, if query is INSERT, UPDATE, or DELETE. * * p_target_rangetblentry: target relation's entry in the rtable list. * * p_is_insert: true to process assignment expressions like INSERT, false * to process them like UPDATE. (Note this can change intra-statement, for * cases like INSERT ON CONFLICT UPDATE.) * * p_windowdefs: list of WindowDefs representing WINDOW and OVER clauses. * We collect these while transforming expressions and then transform them * afterwards (so that any resjunk tlist items needed for the sort/group * clauses end up at the end of the query tlist). A WindowDef's location in * this list, counting from 1, is the winref number to use to reference it. * * p_expr_kind: kind of expression we're currently parsing, as per enum above; * EXPR_KIND_NONE when not in an expression. * * p_next_resno: next TargetEntry.resno to assign, starting from 1. * * p_multiassign_exprs: partially-processed MultiAssignRef source expressions. * * p_locking_clause: query's FOR UPDATE/FOR SHARE clause, if any. * * p_locked_from_parent: true if parent query level applies FOR UPDATE/SHARE * to this subquery as a whole. * * p_resolve_unknowns: resolve unknown-type SELECT output columns as type TEXT * (this is true by default). * * p_hasAggs, p_hasWindowFuncs, etc: true if we've found any of the indicated * constructs in the query. * * p_last_srf: the set-returning FuncExpr or OpExpr most recently found in * the query, or NULL if none. * * p_pre_columnref_hook, etc: optional parser hook functions for modifying the * interpretation of ColumnRefs and ParamRefs. * * p_ref_hook_state: passthrough state for the parser hook functions. */ struct ParseState { struct ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ List *p_namespace; /* currently-referenceable RTEs (List of * ParseNamespaceItem) */ bool p_lateral_active; /* p_lateral_only items visible? */ List *p_ctenamespace; /* current namespace for common table exprs */ List *p_future_ctes; /* common table exprs not yet in namespace */ CommonTableExpr *p_parent_cte; /* this query's containing CTE */ Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */ RangeTblEntry *p_target_rangetblentry; /* target rel's RTE */ bool p_is_insert; /* process assignment like INSERT not UPDATE */ List *p_windowdefs; /* raw representations of window clauses */ ParseExprKind p_expr_kind; /* what kind of expression we're parsing */ int p_next_resno; /* next targetlist resno to assign */ List *p_multiassign_exprs; /* junk tlist entries for multiassign */ List *p_locking_clause; /* raw FOR UPDATE/FOR SHARE info */ bool p_locked_from_parent; /* parent has marked this subquery * with FOR UPDATE/FOR SHARE */ bool p_resolve_unknowns; /* resolve unknown-type SELECT outputs as * type text */ QueryEnvironment *p_queryEnv; /* curr env, incl refs to enclosing env */ /* Flags telling about things found in the query: */ bool p_hasAggs; bool p_hasWindowFuncs; bool p_hasTargetSRFs; bool p_hasSubLinks; bool p_hasModifyingCTE; Node *p_last_srf; /* most recent set-returning func/op found */ /* * Optional hook functions for parser callbacks. These are null unless * set up by the caller of make_parsestate. */ PreParseColumnRefHook p_pre_columnref_hook; PostParseColumnRefHook p_post_columnref_hook; ParseParamRefHook p_paramref_hook; CoerceParamHook p_coerce_param_hook; void *p_ref_hook_state; /* common passthrough link for above */ };
5、RangeTblEntry
/*-------------------- * RangeTblEntry - * A range table is a List of RangeTblEntry nodes. * * A range table entry may represent a plain relation, a sub-select in * FROM, or the result of a JOIN clause. (Only explicit JOIN syntax * produces an RTE, not the implicit join resulting from multiple FROM * items. This is because we only need the RTE to deal with SQL features * like outer joins and join-output-column aliasing.) Other special * RTE types also exist, as indicated by RTEKind. * * Note that we consider RTE_RELATION to cover anything that has a pg_class * entry. relkind distinguishes the sub-cases. * * alias is an Alias node representing the AS alias-clause attached to the * FROM expression, or NULL if no clause. * * eref is the table reference name and column reference names (either * real or aliases). Note that system columns (OID etc) are not included * in the column list. * eref->aliasname is required to be present, and should generally be used * to identify the RTE for error messages etc. * * In RELATION RTEs, the colnames in both alias and eref are indexed by * physical attribute number; this means there must be colname entries for * dropped columns. When building an RTE we insert empty strings ("") for * dropped columns. Note however that a stored rule may have nonempty * colnames for columns dropped since the rule was created (and for that * matter the colnames might be out of date due to column renamings). * The same comments apply to FUNCTION RTEs when a function's return type * is a named composite type. * * In JOIN RTEs, the colnames in both alias and eref are one-to-one with * joinaliasvars entries. A JOIN RTE will omit columns of its inputs when * those columns are known to be dropped at parse time. Again, however, * a stored rule might contain entries for columns dropped since the rule * was created. (This is only possible for columns not actually referenced * in the rule.) When loading a stored rule, we replace the joinaliasvars * items for any such columns with null pointers. (We can't simply delete * them from the joinaliasvars list, because that would affect the attnums * of Vars referencing the rest of the list.) * * inh is true for relation references that should be expanded to include * inheritance children, if the rel has any. This *must* be false for * RTEs other than RTE_RELATION entries. * * inFromCl marks those range variables that are listed in the FROM clause. * It's false for RTEs that are added to a query behind the scenes, such * as the NEW and OLD variables for a rule, or the subqueries of a UNION. * This flag is not used anymore during parsing, since the parser now uses * a separate "namespace" data structure to control visibility, but it is * needed by ruleutils.c to determine whether RTEs should be shown in * decompiled queries. * * requiredPerms and checkAsUser specify run-time access permissions * checks to be performed at query startup. The user must have *all* * of the permissions that are OR'd together in requiredPerms (zero * indicates no permissions checking). If checkAsUser is not zero, * then do the permissions checks using the access rights of that user, * not the current effective user ID. (This allows rules to act as * setuid gateways.) Permissions checks only apply to RELATION RTEs. * * For SELECT/INSERT/UPDATE permissions, if the user doesn't have * table-wide permissions then it is sufficient to have the permissions * on all columns identified in selectedCols (for SELECT) and/or * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, * which cannot have negative integer members, so we subtract * FirstLowInvalidHeapAttributeNumber from column numbers before storing * them in these fields. A whole-row Var reference is represented by * setting the bit for InvalidAttrNumber. * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the * rewriter to implement security-barrier views and/or row-level security. * Note that the planner turns each boolean expression into an implicitly * AND'ed sublist, as is its usual habit with qualification expressions. *-------------------- */ typedef enum RTEKind { RTE_RELATION, /* ordinary relation reference */ RTE_SUBQUERY, /* subquery in FROM */ RTE_JOIN, /* join */ RTE_FUNCTION, /* function in FROM */ RTE_TABLEFUNC, /* TableFunc(.., column list) */ RTE_VALUES, /* VALUES (<exprlist>), (<exprlist>), ... */ RTE_CTE, /* common table expr (WITH list element) */ RTE_NAMEDTUPLESTORE /* tuplestore, e.g. for AFTER triggers */ } RTEKind; typedef struct RangeTblEntry { NodeTag type; RTEKind rtekind; /* see above */ /* * XXX the fields applicable to only some rte kinds should be merged into * a union. I didn't do this yet because the diffs would impact a lot of * code that is being actively worked on. FIXME someday. */ /* * Fields valid for a plain relation RTE (else zero): * * As a special case, RTE_NAMEDTUPLESTORE can also set relid to indicate * that the tuple format of the tuplestore is the same as the referenced * relation. This allows plans referencing AFTER trigger transition * tables to be invalidated if the underlying table is altered. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ /* * Fields valid for a subquery RTE (else NULL): */ Query *subquery; /* the sub-query */ bool security_barrier; /* is from security_barrier view? */ /* * Fields valid for a join RTE (else NULL/zero): * * joinaliasvars is a list of (usually) Vars corresponding to the columns * of the join result. An alias Var referencing column K of the join * result can be replaced by the K'th element of joinaliasvars --- but to * simplify the task of reverse-listing aliases correctly, we do not do * that until planning time. In detail: an element of joinaliasvars can * be a Var of one of the join's input relations, or such a Var with an * implicit coercion to the join's output column type, or a COALESCE * expression containing the two input column Vars (possibly coerced). * Within a Query loaded from a stored rule, it is also possible for * joinaliasvars items to be null pointers, which are placeholders for * (necessarily unreferenced) columns dropped since the rule was made. * Also, once planning begins, joinaliasvars items can be almost anything, * as a result of subquery-flattening substitutions. */ JoinType jointype; /* type of join */ List *joinaliasvars; /* list of alias-var expansions */ /* * Fields valid for a function RTE (else NIL/zero): * * When funcordinality is true, the eref->colnames list includes an alias * for the ordinality column. The ordinality column is otherwise * implicit, and must be accounted for "by hand" in places such as * expandRTE(). */ List *functions; /* list of RangeTblFunction nodes */ bool funcordinality; /* is this called WITH ORDINALITY? */ /* * Fields valid for a TableFunc RTE (else NULL): */ TableFunc *tablefunc; /* * Fields valid for a values RTE (else NIL): */ List *values_lists; /* list of expression lists */ /* * Fields valid for a CTE RTE (else NULL/zero): */ char *ctename; /* name of the WITH list item */ Index ctelevelsup; /* number of query levels up */ bool self_reference; /* is this a recursive self-reference? */ /* * Fields valid for table functions, values, CTE and ENR RTEs (else NIL): * * We need these for CTE RTEs so that the types of self-referential * columns are well-defined. For VALUES RTEs, storing these explicitly * saves having to re-determine the info by scanning the values_lists. For * ENRs, we store the types explicitly here (we could get the information * from the catalogs if 'relid' was supplied, but we'd still need these * for TupleDesc-based ENRs, so we might as well always store the type * info here). * * For ENRs only, we have to consider the possibility of dropped columns. * A dropped column is included in these lists, but it will have zeroes in * all three lists (as well as an empty-string entry in eref). Testing * for zero coltype is the standard way to detect a dropped column. */ List *coltypes; /* OID list of column type OIDs */ List *coltypmods; /* integer list of column typmods */ List *colcollations; /* OID list of column collation OIDs */ /* * Fields valid for ENR RTEs (else NULL/zero): */ char *enrname; /* name of ephemeral named relation */ double enrtuples; /* estimated or actual from caller */ /* * Fields valid in all RTEs: */ Alias *alias; /* user-written alias clause, if any */ Alias *eref; /* expanded reference names */ bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ List *securityQuals; /* security barrier quals to apply, if any */ } RangeTblEntry;
6、TargetEntry
/*-------------------- * TargetEntry - * a target entry (used in query target lists) * * Strictly speaking, a TargetEntry isn't an expression node (since it can't * be evaluated by ExecEvalExpr). But we treat it as one anyway, since in * very many places it's convenient to process a whole query targetlist as a * single expression tree. * * In a SELECT's targetlist, resno should always be equal to the item's * ordinal position (counting from 1). However, in an INSERT or UPDATE * targetlist, resno represents the attribute number of the destination * column for the item; so there may be missing or out-of-order resnos. * It is even legal to have duplicated resnos; consider * UPDATE table SET arraycol[1] = ..., arraycol[2] = ..., ... * The two meanings come together in the executor, because the planner * transforms INSERT/UPDATE tlists into a normalized form with exactly * one entry for each column of the destination table. Before that's * happened, however, it is risky to assume that resno == position. * Generally get_tle_by_resno() should be used rather than list_nth() * to fetch tlist entries by resno, and only in SELECT should you assume * that resno is a unique identifier. * * resname is required to represent the correct column name in non-resjunk * entries of top-level SELECT targetlists, since it will be used as the * column title sent to the frontend. In most other contexts it is only * a debugging aid, and may be wrong or even NULL. (In particular, it may * be wrong in a tlist from a stored rule, if the referenced column has been * renamed by ALTER TABLE since the rule was made. Also, the planner tends * to store NULL rather than look up a valid name for tlist entries in * non-toplevel plan nodes.) In resjunk entries, resname should be either * a specific system-generated name (such as "ctid") or NULL; anything else * risks confusing ExecGetJunkAttribute! * * ressortgroupref is used in the representation of ORDER BY, GROUP BY, and * DISTINCT items. Targetlist entries with ressortgroupref=0 are not * sort/group items. If ressortgroupref>0, then this item is an ORDER BY, * GROUP BY, and/or DISTINCT target value. No two entries in a targetlist * may have the same nonzero ressortgroupref --- but there is no particular * meaning to the nonzero values, except as tags. (For example, one must * not assume that lower ressortgroupref means a more significant sort key.) * The order of the associated SortGroupClause lists determine the semantics. * * resorigtbl/resorigcol identify the source of the column, if it is a * simple reference to a column of a base table (or view). If it is not * a simple reference, these fields are zeroes. * * If resjunk is true then the column is a working column (such as a sort key) * that should be removed from the final output of the query. Resjunk columns * must have resnos that cannot duplicate any regular column's resno. Also * note that there are places that assume resjunk columns come after non-junk * columns. *-------------------- */ typedef struct TargetEntry { Expr xpr; Expr *expr; /* expression to evaluate */ AttrNumber resno; /* attribute number (see notes above) */ char *resname; /* name of the column (could be NULL) */ Index ressortgroupref; /* nonzero if referenced by a sort/group * clause */ Oid resorigtbl; /* OID of column's source table */ AttrNumber resorigcol; /* column's number in source table */ bool resjunk; /* set to true to eliminate the attribute from * final target list */ } TargetEntry;
7、全局变量定义
bool log_parser_stats = false; bool log_planner_stats = false; bool log_executor_stats = false; bool log_statement_stats = false; /* this is sort of all three above * together */
依赖的函数
1、start_xact_command
/* * 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(); } /* * StartTransactionCommand */ void StartTransactionCommand(void) { TransactionState s = CurrentTransactionState; switch (s->blockState) { /* * if we aren't in a transaction block, we just do our usual start * transaction. */ case TBLOCK_DEFAULT: StartTransaction(); s->blockState = TBLOCK_STARTED; break; /* * We are somewhere in a transaction block or subtransaction and * about to start a new command. For now we do nothing, but * someday we may do command-local resource initialization. (Note * that any needed CommandCounterIncrement was done by the * previous CommitTransactionCommand.) */ case TBLOCK_INPROGRESS: case TBLOCK_IMPLICIT_INPROGRESS: case TBLOCK_SUBINPROGRESS: break; /* * Here we are in a failed transaction block (one of the commands * caused an abort) so we do nothing but remain in the abort * state. Eventually we will get a ROLLBACK command which will * get us out of this state. (It is up to other code to ensure * that no commands other than ROLLBACK will be processed in these * states.) */ case TBLOCK_ABORT: case TBLOCK_SUBABORT: break; /* These cases are invalid. */ case TBLOCK_STARTED: case TBLOCK_BEGIN: case TBLOCK_PARALLEL_INPROGRESS: case TBLOCK_SUBBEGIN: case TBLOCK_END: case TBLOCK_SUBRELEASE: case TBLOCK_SUBCOMMIT: case TBLOCK_ABORT_END: case TBLOCK_SUBABORT_END: case TBLOCK_ABORT_PENDING: case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBRESTART: case TBLOCK_SUBABORT_RESTART: case TBLOCK_PREPARE: elog(ERROR, "StartTransactionCommand: unexpected state %s", BlockStateAsString(s->blockState)); break; } /* * We must switch to CurTransactionContext before returning. This is * already done if we called StartTransaction, otherwise not. */ Assert(CurTransactionContext != NULL); MemoryContextSwitchTo(CurTransactionContext);//内存上下文切换至当前事务上下文 }
2、drop_unnamed_stmt
/* 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); } } /* * 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;
3、pg_parse_query
//执行语句解析,返回RawStmt nodes(List) /* * Do raw parsing (only). * * A list of parsetrees (RawStmt nodes) is returned, since there might be * multiple commands in the given string. * * NOTE: for interactive queries, it is important to keep this routine * separate from the analysis & rewrite stages. Analysis and rewriting * cannot be done in an aborted transaction, since they require access to * database tables. So, we rely on the raw parser to determine whether * we've seen a COMMIT or ABORT command; when we are in abort state, other * commands are not processed any further than the raw parse stage. */ List * pg_parse_query(const char *query_string) { List *raw_parsetree_list; TRACE_POSTGRESQL_QUERY_PARSE_START(query_string); if (log_parser_stats) ResetUsage(); raw_parsetree_list = raw_parser(query_string); if (log_parser_stats) ShowUsage("PARSER STATISTICS"); #ifdef COPY_PARSE_PLAN_TREES /* Optional debugging check: pass raw parsetrees through copyObject() */ { List *new_list = copyObject(raw_parsetree_list); /* This checks both copyObject() and the equal() routines... */ if (!equal(new_list, raw_parsetree_list)) elog(WARNING, "copyObject() failed to produce an equal raw parse tree"); else raw_parsetree_list = new_list; } #endif TRACE_POSTGRESQL_QUERY_PARSE_DONE(query_string); return raw_parsetree_list; }
4、raw_parser
//执行词法和语法分析,返回raw parse trees(List,其中的元素是RawStmt) /* * raw_parser * Given a query in string form, do lexical and grammatical analysis. * * Returns a list of raw (un-analyzed) parse trees. The immediate elements * of the list are always RawStmt nodes. */ List * raw_parser(const char *str) { core_yyscan_t yyscanner; base_yy_extra_type yyextra; int yyresult; /* initialize the flex scanner */ yyscanner = scanner_init(str, &yyextra.core_yy_extra, ScanKeywords, NumScanKeywords); /* base_yylex() only needs this much initialization */ yyextra.have_lookahead = false; /* initialize the bison parser */ parser_init(&yyextra); /* Parse! */ yyresult = base_yyparse(yyscanner); /* Clean up (release memory) */ scanner_finish(yyscanner); if (yyresult) /* error */ return NIL; return yyextra.parsetree; }
5、CreateCommandTag
//创建命令Tag //基本上,所有的PG命令类型都可以在这里找到 /* * CreateCommandTag * utility to get a string representation of the command operation, * given either a raw (un-analyzed) parsetree, an analyzed Query, * or a PlannedStmt. * * This must handle all command types, but since the vast majority * of 'em are utility commands, it seems sensible to keep it here. * * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE. * Also, the result must point at a true constant (permanent storage). */ const char * CreateCommandTag(Node *parsetree) { const char *tag; switch (nodeTag(parsetree)) { /* recurse if we're given a RawStmt */ case T_RawStmt: tag = CreateCommandTag(((RawStmt *) parsetree)->stmt); break; /* raw plannable queries */ case T_InsertStmt: tag = "INSERT"; break; case T_DeleteStmt: tag = "DELETE"; break; case T_UpdateStmt: tag = "UPDATE"; break; case T_SelectStmt: tag = "SELECT"; break; /* utility statements --- same whether raw or cooked */ case T_TransactionStmt: { TransactionStmt *stmt = (TransactionStmt *) parsetree; switch (stmt->kind) { case TRANS_STMT_BEGIN: tag = "BEGIN"; break; case TRANS_STMT_START: tag = "START TRANSACTION"; break; case TRANS_STMT_COMMIT: tag = "COMMIT"; break; case TRANS_STMT_ROLLBACK: case TRANS_STMT_ROLLBACK_TO: tag = "ROLLBACK"; break; case TRANS_STMT_SAVEPOINT: tag = "SAVEPOINT"; break; case TRANS_STMT_RELEASE: tag = "RELEASE"; break; case TRANS_STMT_PREPARE: tag = "PREPARE TRANSACTION"; break; case TRANS_STMT_COMMIT_PREPARED: tag = "COMMIT PREPARED"; break; case TRANS_STMT_ROLLBACK_PREPARED: tag = "ROLLBACK PREPARED"; break; default: tag = "???"; break; } } break; case T_DeclareCursorStmt: tag = "DECLARE CURSOR"; break; case T_ClosePortalStmt: { ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree; if (stmt->portalname == NULL) tag = "CLOSE CURSOR ALL"; else tag = "CLOSE CURSOR"; } break; case T_FetchStmt: { FetchStmt *stmt = (FetchStmt *) parsetree; tag = (stmt->ismove) ? "MOVE" : "FETCH"; } break; case T_CreateDomainStmt: tag = "CREATE DOMAIN"; break; case T_CreateSchemaStmt: tag = "CREATE SCHEMA"; break; case T_CreateStmt: tag = "CREATE TABLE"; break; case T_CreateTableSpaceStmt: tag = "CREATE TABLESPACE"; break; case T_DropTableSpaceStmt: tag = "DROP TABLESPACE"; break; case T_AlterTableSpaceOptionsStmt: tag = "ALTER TABLESPACE"; break; case T_CreateExtensionStmt: tag = "CREATE EXTENSION"; break; case T_AlterExtensionStmt: tag = "ALTER EXTENSION"; break; case T_AlterExtensionContentsStmt: tag = "ALTER EXTENSION"; break; case T_CreateFdwStmt: tag = "CREATE FOREIGN DATA WRAPPER"; break; case T_AlterFdwStmt: tag = "ALTER FOREIGN DATA WRAPPER"; break; case T_CreateForeignServerStmt: tag = "CREATE SERVER"; break; case T_AlterForeignServerStmt: tag = "ALTER SERVER"; break; case T_CreateUserMappingStmt: tag = "CREATE USER MAPPING"; break; case T_AlterUserMappingStmt: tag = "ALTER USER MAPPING"; break; case T_DropUserMappingStmt: tag = "DROP USER MAPPING"; break; case T_CreateForeignTableStmt: tag = "CREATE FOREIGN TABLE"; break; case T_ImportForeignSchemaStmt: tag = "IMPORT FOREIGN SCHEMA"; break; case T_DropStmt: switch (((DropStmt *) parsetree)->removeType) { case OBJECT_TABLE: tag = "DROP TABLE"; break; case OBJECT_SEQUENCE: tag = "DROP SEQUENCE"; break; case OBJECT_VIEW: tag = "DROP VIEW"; break; case OBJECT_MATVIEW: tag = "DROP MATERIALIZED VIEW"; break; case OBJECT_INDEX: tag = "DROP INDEX"; break; case OBJECT_TYPE: tag = "DROP TYPE"; break; case OBJECT_DOMAIN: tag = "DROP DOMAIN"; break; case OBJECT_COLLATION: tag = "DROP COLLATION"; break; case OBJECT_CONVERSION: tag = "DROP CONVERSION"; break; case OBJECT_SCHEMA: tag = "DROP SCHEMA"; break; case OBJECT_TSPARSER: tag = "DROP TEXT SEARCH PARSER"; break; case OBJECT_TSDICTIONARY: tag = "DROP TEXT SEARCH DICTIONARY"; break; case OBJECT_TSTEMPLATE: tag = "DROP TEXT SEARCH TEMPLATE"; break; case OBJECT_TSCONFIGURATION: tag = "DROP TEXT SEARCH CONFIGURATION"; break; case OBJECT_FOREIGN_TABLE: tag = "DROP FOREIGN TABLE"; break; case OBJECT_EXTENSION: tag = "DROP EXTENSION"; break; case OBJECT_FUNCTION: tag = "DROP FUNCTION"; break; case OBJECT_PROCEDURE: tag = "DROP PROCEDURE"; break; case OBJECT_ROUTINE: tag = "DROP ROUTINE"; break; case OBJECT_AGGREGATE: tag = "DROP AGGREGATE"; break; case OBJECT_OPERATOR: tag = "DROP OPERATOR"; break; case OBJECT_LANGUAGE: tag = "DROP LANGUAGE"; break; case OBJECT_CAST: tag = "DROP CAST"; break; case OBJECT_TRIGGER: tag = "DROP TRIGGER"; break; case OBJECT_EVENT_TRIGGER: tag = "DROP EVENT TRIGGER"; break; case OBJECT_RULE: tag = "DROP RULE"; break; case OBJECT_FDW: tag = "DROP FOREIGN DATA WRAPPER"; break; case OBJECT_FOREIGN_SERVER: tag = "DROP SERVER"; break; case OBJECT_OPCLASS: tag = "DROP OPERATOR CLASS"; break; case OBJECT_OPFAMILY: tag = "DROP OPERATOR FAMILY"; break; case OBJECT_POLICY: tag = "DROP POLICY"; break; case OBJECT_TRANSFORM: tag = "DROP TRANSFORM"; break; case OBJECT_ACCESS_METHOD: tag = "DROP ACCESS METHOD"; break; case OBJECT_PUBLICATION: tag = "DROP PUBLICATION"; break; case OBJECT_STATISTIC_EXT: tag = "DROP STATISTICS"; break; default: tag = "???"; } break; case T_TruncateStmt: tag = "TRUNCATE TABLE"; break; case T_CommentStmt: tag = "COMMENT"; break; case T_SecLabelStmt: tag = "SECURITY LABEL"; break; case T_CopyStmt: tag = "COPY"; break; case T_RenameStmt: tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType); break; case T_AlterObjectDependsStmt: tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType); break; case T_AlterObjectSchemaStmt: tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType); break; case T_AlterOwnerStmt: tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType); break; case T_AlterTableMoveAllStmt: tag = AlterObjectTypeCommandTag(((AlterTableMoveAllStmt *) parsetree)->objtype); break; case T_AlterTableStmt: tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind); break; case T_AlterDomainStmt: tag = "ALTER DOMAIN"; break; case T_AlterFunctionStmt: switch (((AlterFunctionStmt *) parsetree)->objtype) { case OBJECT_FUNCTION: tag = "ALTER FUNCTION"; break; case OBJECT_PROCEDURE: tag = "ALTER PROCEDURE"; break; case OBJECT_ROUTINE: tag = "ALTER ROUTINE"; break; default: tag = "???"; } break; case T_GrantStmt: { GrantStmt *stmt = (GrantStmt *) parsetree; tag = (stmt->is_grant) ? "GRANT" : "REVOKE"; } break; case T_GrantRoleStmt: { GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree; tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE"; } break; case T_AlterDefaultPrivilegesStmt: tag = "ALTER DEFAULT PRIVILEGES"; break; case T_DefineStmt: switch (((DefineStmt *) parsetree)->kind) { case OBJECT_AGGREGATE: tag = "CREATE AGGREGATE"; break; case OBJECT_OPERATOR: tag = "CREATE OPERATOR"; break; case OBJECT_TYPE: tag = "CREATE TYPE"; break; case OBJECT_TSPARSER: tag = "CREATE TEXT SEARCH PARSER"; break; case OBJECT_TSDICTIONARY: tag = "CREATE TEXT SEARCH DICTIONARY"; break; case OBJECT_TSTEMPLATE: tag = "CREATE TEXT SEARCH TEMPLATE"; break; case OBJECT_TSCONFIGURATION: tag = "CREATE TEXT SEARCH CONFIGURATION"; break; case OBJECT_COLLATION: tag = "CREATE COLLATION"; break; case OBJECT_ACCESS_METHOD: tag = "CREATE ACCESS METHOD"; break; default: tag = "???"; } break; case T_CompositeTypeStmt: tag = "CREATE TYPE"; break; case T_CreateEnumStmt: tag = "CREATE TYPE"; break; case T_CreateRangeStmt: tag = "CREATE TYPE"; break; case T_AlterEnumStmt: tag = "ALTER TYPE"; break; case T_ViewStmt: tag = "CREATE VIEW"; break; case T_CreateFunctionStmt: if (((CreateFunctionStmt *) parsetree)->is_procedure) tag = "CREATE PROCEDURE"; else tag = "CREATE FUNCTION"; break; case T_IndexStmt: tag = "CREATE INDEX"; break; case T_RuleStmt: tag = "CREATE RULE"; break; case T_CreateSeqStmt: tag = "CREATE SEQUENCE"; break; case T_AlterSeqStmt: tag = "ALTER SEQUENCE"; break; case T_DoStmt: tag = "DO"; break; case T_CreatedbStmt: tag = "CREATE DATABASE"; break; case T_AlterDatabaseStmt: tag = "ALTER DATABASE"; break; case T_AlterDatabaseSetStmt: tag = "ALTER DATABASE"; break; case T_DropdbStmt: tag = "DROP DATABASE"; break; case T_NotifyStmt: tag = "NOTIFY"; break; case T_ListenStmt: tag = "LISTEN"; break; case T_UnlistenStmt: tag = "UNLISTEN"; break; case T_LoadStmt: tag = "LOAD"; break; case T_CallStmt: tag = "CALL"; break; case T_ClusterStmt: tag = "CLUSTER"; break; case T_VacuumStmt: if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM) tag = "VACUUM"; else tag = "ANALYZE"; break; case T_ExplainStmt: tag = "EXPLAIN"; break; case T_CreateTableAsStmt: switch (((CreateTableAsStmt *) parsetree)->relkind) { case OBJECT_TABLE: if (((CreateTableAsStmt *) parsetree)->is_select_into) tag = "SELECT INTO"; else tag = "CREATE TABLE AS"; break; case OBJECT_MATVIEW: tag = "CREATE MATERIALIZED VIEW"; break; default: tag = "???"; } break; case T_RefreshMatViewStmt: tag = "REFRESH MATERIALIZED VIEW"; break; case T_AlterSystemStmt: tag = "ALTER SYSTEM"; break; case T_VariableSetStmt: switch (((VariableSetStmt *) parsetree)->kind) { case VAR_SET_VALUE: case VAR_SET_CURRENT: case VAR_SET_DEFAULT: case VAR_SET_MULTI: tag = "SET"; break; case VAR_RESET: case VAR_RESET_ALL: tag = "RESET"; break; default: tag = "???"; } break; case T_VariableShowStmt: tag = "SHOW"; break; case T_DiscardStmt: switch (((DiscardStmt *) parsetree)->target) { case DISCARD_ALL: tag = "DISCARD ALL"; break; case DISCARD_PLANS: tag = "DISCARD PLANS"; break; case DISCARD_TEMP: tag = "DISCARD TEMP"; break; case DISCARD_SEQUENCES: tag = "DISCARD SEQUENCES"; break; default: tag = "???"; } break; case T_CreateTransformStmt: tag = "CREATE TRANSFORM"; break; case T_CreateTrigStmt: tag = "CREATE TRIGGER"; break; case T_CreateEventTrigStmt: tag = "CREATE EVENT TRIGGER"; break; case T_AlterEventTrigStmt: tag = "ALTER EVENT TRIGGER"; break; case T_CreatePLangStmt: tag = "CREATE LANGUAGE"; break; case T_CreateRoleStmt: tag = "CREATE ROLE"; break; case T_AlterRoleStmt: tag = "ALTER ROLE"; break; case T_AlterRoleSetStmt: tag = "ALTER ROLE"; break; case T_DropRoleStmt: tag = "DROP ROLE"; break; case T_DropOwnedStmt: tag = "DROP OWNED"; break; case T_ReassignOwnedStmt: tag = "REASSIGN OWNED"; break; case T_LockStmt: tag = "LOCK TABLE"; break; case T_ConstraintsSetStmt: tag = "SET CONSTRAINTS"; break; case T_CheckPointStmt: tag = "CHECKPOINT"; break; case T_ReindexStmt: tag = "REINDEX"; break; case T_CreateConversionStmt: tag = "CREATE CONVERSION"; break; case T_CreateCastStmt: tag = "CREATE CAST"; break; case T_CreateOpClassStmt: tag = "CREATE OPERATOR CLASS"; break; case T_CreateOpFamilyStmt: tag = "CREATE OPERATOR FAMILY"; break; case T_AlterOpFamilyStmt: tag = "ALTER OPERATOR FAMILY"; break; case T_AlterOperatorStmt: tag = "ALTER OPERATOR"; break; case T_AlterTSDictionaryStmt: tag = "ALTER TEXT SEARCH DICTIONARY"; break; case T_AlterTSConfigurationStmt: tag = "ALTER TEXT SEARCH CONFIGURATION"; break; case T_CreatePolicyStmt: tag = "CREATE POLICY"; break; case T_AlterPolicyStmt: tag = "ALTER POLICY"; break; case T_CreateAmStmt: tag = "CREATE ACCESS METHOD"; break; case T_CreatePublicationStmt: tag = "CREATE PUBLICATION"; break; case T_AlterPublicationStmt: tag = "ALTER PUBLICATION"; break; case T_CreateSubscriptionStmt: tag = "CREATE SUBSCRIPTION"; break; case T_AlterSubscriptionStmt: tag = "ALTER SUBSCRIPTION"; break; case T_DropSubscriptionStmt: tag = "DROP SUBSCRIPTION"; break; case T_AlterCollationStmt: tag = "ALTER COLLATION"; break; case T_PrepareStmt: tag = "PREPARE"; break; case T_ExecuteStmt: tag = "EXECUTE"; break; case T_CreateStatsStmt: tag = "CREATE STATISTICS"; break; case T_DeallocateStmt: { DeallocateStmt *stmt = (DeallocateStmt *) parsetree; if (stmt->name == NULL) tag = "DEALLOCATE ALL"; else tag = "DEALLOCATE"; } break; /* already-planned queries */ case T_PlannedStmt: { PlannedStmt *stmt = (PlannedStmt *) parsetree; switch (stmt->commandType) { case CMD_SELECT: /* * We take a little extra care here so that the result * will be useful for complaints about read-only * statements */ if (stmt->rowMarks != NIL) { /* not 100% but probably close enough */ switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength) { case LCS_FORKEYSHARE: tag = "SELECT FOR KEY SHARE"; break; case LCS_FORSHARE: tag = "SELECT FOR SHARE"; break; case LCS_FORNOKEYUPDATE: tag = "SELECT FOR NO KEY UPDATE"; break; case LCS_FORUPDATE: tag = "SELECT FOR UPDATE"; break; default: tag = "SELECT"; break; } } else tag = "SELECT"; break; case CMD_UPDATE: tag = "UPDATE"; break; case CMD_INSERT: tag = "INSERT"; break; case CMD_DELETE: tag = "DELETE"; break; case CMD_UTILITY: tag = CreateCommandTag(stmt->utilityStmt); break; default: elog(WARNING, "unrecognized commandType: %d", (int) stmt->commandType); tag = "???"; break; } } break; /* parsed-and-rewritten-but-not-planned queries */ case T_Query: { Query *stmt = (Query *) parsetree; switch (stmt->commandType) { case CMD_SELECT: /* * We take a little extra care here so that the result * will be useful for complaints about read-only * statements */ if (stmt->rowMarks != NIL) { /* not 100% but probably close enough */ switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength) { case LCS_FORKEYSHARE: tag = "SELECT FOR KEY SHARE"; break; case LCS_FORSHARE: tag = "SELECT FOR SHARE"; break; case LCS_FORNOKEYUPDATE: tag = "SELECT FOR NO KEY UPDATE"; break; case LCS_FORUPDATE: tag = "SELECT FOR UPDATE"; break; default: tag = "???"; break; } } else tag = "SELECT"; break; case CMD_UPDATE: tag = "UPDATE"; break; case CMD_INSERT: tag = "INSERT"; break; case CMD_DELETE: tag = "DELETE"; break; case CMD_UTILITY: tag = CreateCommandTag(stmt->utilityStmt); break; default: elog(WARNING, "unrecognized commandType: %d", (int) stmt->commandType); tag = "???"; break; } } break; default: elog(WARNING, "unrecognized node type: %d", (int) nodeTag(parsetree)); tag = "???"; break; } return tag; }
6、BeginCommand
/* ---------------- * BeginCommand - initialize the destination at start of command * ---------------- */ void BeginCommand(const char *commandTag, CommandDest dest) { /* Nothing to do at present */ }
7、analyze_requires_snapshot
//是否需要快照? //增删改查均需要 /* * analyze_requires_snapshot * Returns true if a snapshot must be set before doing parse analysis * on the given raw parse tree. * * Classification here should match transformStmt(). */ bool analyze_requires_snapshot(RawStmt *parseTree) { bool result; switch (nodeTag(parseTree->stmt)) { /* * Optimizable statements */ case T_InsertStmt: case T_DeleteStmt: case T_UpdateStmt: case T_SelectStmt: result = true; break; /* * Special cases */ case T_DeclareCursorStmt: case T_ExplainStmt: case T_CreateTableAsStmt: /* yes, because we must analyze the contained statement */ result = true; break; default: /* other utility statements don't have any real parse analysis */ result = false; break; } return result; }
8、pg_analyze_and_rewrite
/* * Given a raw parsetree (gram.y output), and optionally information about * types of parameter symbols ($n), perform parse analysis and rule rewriting. * * A list of Query nodes is returned, since either the analyzer or the * rewriter might expand one query to several. * * NOTE: for reasons mentioned above, this must be separate from raw parsing. */ List * pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string, Oid *paramTypes, int numParams, QueryEnvironment *queryEnv) { Query *query; List *querytree_list; TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string); /* * (1) Perform parse analysis. */ if (log_parser_stats) ResetUsage(); query = parse_analyze(parsetree, query_string, paramTypes, numParams, queryEnv);//解析&分析 if (log_parser_stats) ShowUsage("PARSE ANALYSIS STATISTICS"); /* * (2) Rewrite the queries, as necessary */ querytree_list = pg_rewrite_query(query);//查询重写 TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string); return querytree_list; } /* * parse_analyze * Analyze a raw parse tree and transform it to Query form. * * Optionally, information about $n parameter types can be supplied. * References to $n indexes not defined by paramTypes[] are disallowed. * * The result is a Query node. Optimizable statements require considerable * transformation, while utility-type statements are simply hung off * a dummy CMD_UTILITY Query node. */ Query * parse_analyze(RawStmt *parseTree, const char *sourceText, Oid *paramTypes, int numParams, QueryEnvironment *queryEnv) { ParseState *pstate = make_parsestate(NULL); Query *query; Assert(sourceText != NULL); /* required as of 8.4 */ pstate->p_sourcetext = sourceText; if (numParams > 0) parse_fixed_parameters(pstate, paramTypes, numParams); pstate->p_queryEnv = queryEnv; query = transformTopLevelStmt(pstate, parseTree); if (post_parse_analyze_hook) (*post_parse_analyze_hook) (pstate, query); free_parsestate(pstate); return query; } /* * make_parsestate * Allocate and initialize a new ParseState. * * Caller should eventually release the ParseState via free_parsestate(). */ ParseState * make_parsestate(ParseState *parentParseState) { ParseState *pstate; pstate = palloc0(sizeof(ParseState)); pstate->parentParseState = parentParseState; /* Fill in fields that don't start at null/false/zero */ pstate->p_next_resno = 1; pstate->p_resolve_unknowns = true; if (parentParseState) { pstate->p_sourcetext = parentParseState->p_sourcetext; /* all hooks are copied from parent */ pstate->p_pre_columnref_hook = parentParseState->p_pre_columnref_hook; pstate->p_post_columnref_hook = parentParseState->p_post_columnref_hook; pstate->p_paramref_hook = parentParseState->p_paramref_hook; pstate->p_coerce_param_hook = parentParseState->p_coerce_param_hook; pstate->p_ref_hook_state = parentParseState->p_ref_hook_state; /* query environment stays in context for the whole parse analysis */ pstate->p_queryEnv = parentParseState->p_queryEnv; } return pstate; } /* * transformTopLevelStmt - * transform a Parse tree into a Query tree. * * This function is just responsible for transferring statement location data * from the RawStmt into the finished Query. */ Query * transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree) { Query *result; /* We're at top level, so allow SELECT INTO */ result = transformOptionalSelectInto(pstate, parseTree->stmt); result->stmt_location = parseTree->stmt_location; result->stmt_len = parseTree->stmt_len; return result; } /* * transformOptionalSelectInto - * If SELECT has INTO, convert it to CREATE TABLE AS. * * The only thing we do here that we don't do in transformStmt() is to * convert SELECT ... INTO into CREATE TABLE AS. Since utility statements * aren't allowed within larger statements, this is only allowed at the top * of the parse tree, and so we only try it before entering the recursive * transformStmt() processing. */ static Query * transformOptionalSelectInto(ParseState *pstate, Node *parseTree) { if (IsA(parseTree, SelectStmt)) { SelectStmt *stmt = (SelectStmt *) parseTree; /* If it's a set-operation tree, drill down to leftmost SelectStmt */ while (stmt && stmt->op != SETOP_NONE) stmt = stmt->larg; Assert(stmt && IsA(stmt, SelectStmt) &&stmt->larg == NULL); if (stmt->intoClause) { CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); ctas->query = parseTree; ctas->into = stmt->intoClause; ctas->relkind = OBJECT_TABLE; ctas->is_select_into = true; /* * Remove the intoClause from the SelectStmt. This makes it safe * for transformSelectStmt to complain if it finds intoClause set * (implying that the INTO appeared in a disallowed place). */ stmt->intoClause = NULL; parseTree = (Node *) ctas; } } return transformStmt(pstate, parseTree); } /* * transformStmt - * recursively transform a Parse tree into a Query tree. */ Query * transformStmt(ParseState *pstate, Node *parseTree) { Query *result; /* * We apply RAW_EXPRESSION_COVERAGE_TEST testing to basic DML statements; * we can't just run it on everything because raw_expression_tree_walker() * doesn't claim to handle utility statements. */ #ifdef RAW_EXPRESSION_COVERAGE_TEST switch (nodeTag(parseTree)) { case T_SelectStmt: case T_InsertStmt: case T_UpdateStmt: case T_DeleteStmt: (void) test_raw_expression_coverage(parseTree, NULL); break; default: break; } #endif /* RAW_EXPRESSION_COVERAGE_TEST */ switch (nodeTag(parseTree)) { /* * Optimizable statements */ case T_InsertStmt: result = transformInsertStmt(pstate, (InsertStmt *) parseTree); break; case T_DeleteStmt: result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree); break; case T_UpdateStmt: result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree); break; case T_SelectStmt: { SelectStmt *n = (SelectStmt *) parseTree; if (n->valuesLists) result = transformValuesClause(pstate, n); else if (n->op == SETOP_NONE) result = transformSelectStmt(pstate, n); else result = transformSetOperationStmt(pstate, n); } break; /* * Special cases */ case T_DeclareCursorStmt: result = transformDeclareCursorStmt(pstate, (DeclareCursorStmt *) parseTree); break; case T_ExplainStmt: result = transformExplainStmt(pstate, (ExplainStmt *) parseTree); break; case T_CreateTableAsStmt: result = transformCreateTableAsStmt(pstate, (CreateTableAsStmt *) parseTree); break; case T_CallStmt: result = transformCallStmt(pstate, (CallStmt *) parseTree); break; default: /* * other statements don't require any transformation; just return * the original parsetree with a Query node plastered on top. */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) parseTree; break; } /* Mark as original query until we learn differently */ result->querySource = QSRC_ORIGINAL; result->canSetTag = true; return result; } /* * transformInsertStmt - * transform an Insert Statement */ static Query * transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { Query *qry = makeNode(Query); SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt; List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; List *sub_namespace; List *icolumns; List *attrnos; RangeTblEntry *rte; RangeTblRef *rtr; ListCell *icols; ListCell *attnos; ListCell *lc; bool isOnConflictUpdate; AclMode targetPerms; /* There can't be any outer WITH to worry about */ Assert(pstate->p_ctenamespace == NIL); qry->commandType = CMD_INSERT; pstate->p_is_insert = true; /* process the WITH clause independently of all else */ if (stmt->withClause) { qry->hasRecursive = stmt->withClause->recursive; qry->cteList = transformWithClause(pstate, stmt->withClause); qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } qry->override = stmt->override; isOnConflictUpdate = (stmt->onConflictClause && stmt->onConflictClause->action == ONCONFLICT_UPDATE); /* * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL), * VALUES list, or general SELECT input. We special-case VALUES, both for * efficiency and so we can handle DEFAULT specifications. * * The grammar allows attaching ORDER BY, LIMIT, FOR UPDATE, or WITH to a * VALUES clause. If we have any of those, treat it as a general SELECT; * so it will work, but you can't use DEFAULT items together with those. */ isGeneralSelect = (selectStmt && (selectStmt->valuesLists == NIL || selectStmt->sortClause != NIL || selectStmt->limitOffset != NULL || selectStmt->limitCount != NULL || selectStmt->lockingClause != NIL || selectStmt->withClause != NULL)); /* * If a non-nil rangetable/namespace was passed in, and we are doing * INSERT/SELECT, arrange to pass the rangetable/namespace down to the * SELECT. This can only happen if we are inside a CREATE RULE, and in * that case we want the rule's OLD and NEW rtable entries to appear as * part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ if (isGeneralSelect) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } else { sub_rtable = NIL; /* not used, but keep compiler quiet */ sub_namespace = NIL; } /* * Must get write lock on INSERT target table before scanning SELECT, else * we will grab the wrong kind of initial lock if the target table is also * mentioned in the SELECT part. Note that the target table is not added * to the joinlist or namespace. */ targetPerms = ACL_INSERT; if (isOnConflictUpdate) targetPerms |= ACL_UPDATE; qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false, targetPerms); /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); Assert(list_length(icolumns) == list_length(attrnos)); /* * Determine which variant of INSERT we have. */ if (selectStmt == NULL) { /* * We have INSERT ... DEFAULT VALUES. We can handle this case by * emitting an empty targetlist --- all columns will be defaulted when * the planner expands the targetlist. */ exprList = NIL; } else if (isGeneralSelect) { /* * We make the sub-pstate a child of the outer pstate so that it can * see any Param definitions supplied from above. Since the outer * pstate's rtable and namespace are presently empty, there are no * side-effects of exposing names the sub-SELECT shouldn't be able to * see. */ ParseState *sub_pstate = make_parsestate(pstate); Query *selectQuery; /* * Process the source SELECT. * * It is important that this be handled just like a standalone SELECT; * otherwise the behavior of SELECT within INSERT might be different * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had * bugs of just that nature...) * * The sole exception is that we prevent resolving unknown-type * outputs as TEXT. This does not change the semantics since if the * column type matters semantically, it would have been resolved to * something else anyway. Doing this lets us resolve such outputs as * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; selectQuery = transformStmt(sub_pstate, stmt->selectStmt); free_parsestate(sub_pstate); /* The grammar should have produced a SELECT */ if (!IsA(selectQuery, Query) || selectQuery->commandType != CMD_SELECT) elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT"); /* * Make the source be a subquery in the INSERT's rangetable, and add * it to the INSERT's joinlist. */ rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias("*SELECT*", NIL), false, false); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); /*---------- * Generate an expression list for the INSERT that selects all the * non-resjunk columns from the subquery. (INSERT's tlist must be * separate from the subquery's tlist because we may add columns, * insert datatype coercions, etc.) * * HACK: unknown-type constants and params in the SELECT's targetlist * are copied up as-is rather than being referenced as subquery * outputs. This is to ensure that when we try to coerce them to * the target column's datatype, the right things happen (see * special cases in coerce_type). Otherwise, this fails: * INSERT INTO foo SELECT 'bar', ... FROM baz *---------- */ exprList = NIL; foreach(lc, selectQuery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(lc); Expr *expr; if (tle->resjunk) continue; if (tle->expr && (IsA(tle->expr, Const) ||IsA(tle->expr, Param)) && exprType((Node *) tle->expr) == UNKNOWNOID) expr = tle->expr; else { Var *var = makeVarFromTargetEntry(rtr->rtindex, tle); var->location = exprLocation((Node *) tle->expr); expr = (Expr *) var; } exprList = lappend(exprList, expr); } /* Prepare row for assignment to target table */ exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos, false); } else if (list_length(selectStmt->valuesLists) > 1) { /* * Process INSERT ... VALUES with multiple VALUES sublists. We * generate a VALUES RTE holding the transformed expression lists, and * build up a targetlist containing Vars that reference the VALUES * RTE. */ List *exprsLists = NIL; List *coltypes = NIL; List *coltypmods = NIL; List *colcollations = NIL; int sublist_length = -1; bool lateral = false; Assert(selectStmt->intoClause == NULL); foreach(lc, selectStmt->valuesLists) { List *sublist = (List *) lfirst(lc); /* * Do basic expression transformation (same as a ROW() expr, but * allow SetToDefault at top level) */ sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES, true); /* * All the sublists must be the same length, *after* * transformation (which might expand '*' into multiple items). * The VALUES RTE can't handle anything different. */ if (sublist_length < 0) { /* Remember post-transformation length of first sublist */ sublist_length = list_length(sublist); } else if (sublist_length != list_length(sublist)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("VALUES lists must all be the same length"), parser_errposition(pstate, exprLocation((Node *) sublist)))); } /* * Prepare row for assignment to target table. We process any * indirection on the target column specs normally but then strip * off the resulting field/array assignment nodes, since we don't * want the parsed statement to contain copies of those in each * VALUES row. (It's annoying to have to transform the * indirection specs over and over like this, but avoiding it * would take some really messy refactoring of * transformAssignmentIndirection.) */ sublist = transformInsertRow(pstate, sublist, stmt->cols, icolumns, attrnos, true); /* * We must assign collations now because assign_query_collations * doesn't process rangetable entries. We just assign all the * collations independently in each row, and don't worry about * whether they are consistent vertically. The outer INSERT query * isn't going to care about the collations of the VALUES columns, * so it's not worth the effort to identify a common collation for * each one here. (But note this does have one user-visible * consequence: INSERT ... VALUES won't complain about conflicting * explicit COLLATEs in a column, whereas the same VALUES * construct in another context would complain.) */ assign_list_collations(pstate, sublist); exprsLists = lappend(exprsLists, sublist); } /* * Construct column type/typmod/collation lists for the VALUES RTE. * Every expression in each column has been coerced to the type/typmod * of the corresponding target column or subfield, so it's sufficient * to look at the exprType/exprTypmod of the first row. We don't care * about the collation labeling, so just fill in InvalidOid for that. */ foreach(lc, (List *) linitial(exprsLists)) { Node *val = (Node *) lfirst(lc); coltypes = lappend_oid(coltypes, exprType(val)); coltypmods = lappend_int(coltypmods, exprTypmod(val)); colcollations = lappend_oid(colcollations, InvalidOid); } /* * Ordinarily there can't be any current-level Vars in the expression * lists, because the namespace was empty ... but if we're inside * CREATE RULE, then NEW/OLD references might appear. In that case we * have to mark the VALUES RTE as LATERAL. */ if (list_length(pstate->p_rtable) != 1 && contain_vars_of_level((Node *) exprsLists, 0)) lateral = true; /* * Generate the VALUES RTE */ rte = addRangeTableEntryForValues(pstate, exprsLists, coltypes, coltypmods, colcollations, NULL, lateral, true); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); /* * Generate list of Vars referencing the RTE */ expandRTE(rte, rtr->rtindex, 0, -1, false, NULL, &exprList); /* * Re-apply any indirection on the target column specs to the Vars */ exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos, false); } else { /* * Process INSERT ... VALUES with a single VALUES sublist. We treat * this case separately for efficiency. The sublist is just computed * directly as the Query's targetlist, with no VALUES RTE. So it * works just like a SELECT without any FROM. */ List *valuesLists = selectStmt->valuesLists; Assert(list_length(valuesLists) == 1); Assert(selectStmt->intoClause == NULL); /* * Do basic expression transformation (same as a ROW() expr, but allow * SetToDefault at top level) */ exprList = transformExpressionList(pstate, (List *) linitial(valuesLists), EXPR_KIND_VALUES_SINGLE, true); /* Prepare row for assignment to target table */ exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos, false); } /* * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ rte = pstate->p_target_rangetblentry; qry->targetList = NIL; icols = list_head(icolumns); attnos = list_head(attrnos); foreach(lc, exprList) { Expr *expr = (Expr *) lfirst(lc); ResTarget *col; AttrNumber attr_num; TargetEntry *tle; col = lfirst_node(ResTarget, icols); attr_num = (AttrNumber) lfirst_int(attnos); tle = makeTargetEntry(expr, attr_num, col->name, false); qry->targetList = lappend(qry->targetList, tle); rte->insertedCols = bms_add_member(rte->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); icols = lnext(icols); attnos = lnext(attnos); } /* Process ON CONFLICT, if any. */ if (stmt->onConflictClause) qry->onConflict = transformOnConflictClause(pstate, stmt->onConflictClause); /* * If we have a RETURNING clause, we need to add the target relation to * the query namespace before processing it, so that Var references in * RETURNING will work. Also, remove any namespace entries added in a * sub-SELECT or VALUES list. */ if (stmt->returningList) { pstate->p_namespace = NIL; addRTEtoQuery(pstate, pstate->p_target_rangetblentry, false, true, true); qry->returningList = transformReturningList(pstate, stmt->returningList); } /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); return qry; }
9、pg_plan_queries
/* * Generate plans for a list of already-rewritten queries. * * For normal optimizable statements, invoke the planner. For utility * statements, just make a wrapper PlannedStmt node. * * The result is a list of PlannedStmt nodes. */ List * pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams) { List *stmt_list = NIL; ListCell *query_list; foreach(query_list, querytrees) { Query *query = lfirst_node(Query, query_list); PlannedStmt *stmt; if (query->commandType == CMD_UTILITY) { /* Utility commands require no planning. */ stmt = makeNode(PlannedStmt); stmt->commandType = CMD_UTILITY; stmt->canSetTag = query->canSetTag; stmt->utilityStmt = query->utilityStmt; stmt->stmt_location = query->stmt_location; stmt->stmt_len = query->stmt_len; } else { stmt = pg_plan_query(query, cursorOptions, boundParams); } stmt_list = lappend(stmt_list, stmt); } return stmt_list; } /* * Generate a plan for a single already-rewritten query. * This is a thin wrapper around planner() and takes the same parameters. */ PlannedStmt * pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams) { PlannedStmt *plan; /* Utility commands have no plans. */ if (querytree->commandType == CMD_UTILITY) return NULL; /* Planner must have a snapshot in case it calls user-defined functions. */ Assert(ActiveSnapshotSet()); TRACE_POSTGRESQL_QUERY_PLAN_START(); if (log_planner_stats) ResetUsage(); /* call the optimizer */ plan = planner(querytree, cursorOptions, boundParams); if (log_planner_stats) ShowUsage("PLANNER STATISTICS"); #ifdef COPY_PARSE_PLAN_TREES /* Optional debugging check: pass plan output through copyObject() */ { PlannedStmt *new_plan = copyObject(plan); /* * equal() currently does not have routines to compare Plan nodes, so * don't try to test equality here. Perhaps fix someday? */ #ifdef NOT_USED /* This checks both copyObject() and the equal() routines... */ if (!equal(new_plan, plan)) elog(WARNING, "copyObject() failed to produce an equal plan tree"); else #endif plan = new_plan; } #endif /* * Print plan if debugging. */ if (Debug_print_plan) elog_node_display(LOG, "plan", plan, Debug_pretty_print); TRACE_POSTGRESQL_QUERY_PLAN_DONE(); return plan; } /***************************************************************************** * * Query optimizer entry point * * To support loadable plugins that monitor or modify planner behavior, * we provide a hook variable that lets a plugin get control before and * after the standard planning process. The plugin would normally call * standard_planner(). * * Note to plugin authors: standard_planner() scribbles on its Query input, * so you'd better copy that data structure if you want to plan more than once. * *****************************************************************************/ PlannedStmt * planner(Query *parse, int cursorOptions, ParamListInfo boundParams) { PlannedStmt *result; if (planner_hook) result = (*planner_hook) (parse, cursorOptions, boundParams); else result = standard_planner(parse, cursorOptions, boundParams); return result; }
10、CreatePortal
/* * CreatePortal * Returns a new portal given a name. * * allowDup: if true, automatically drop any pre-existing portal of the * same name (if false, an error is raised). * * dupSilent: if true, don't even emit a WARNING. */ Portal CreatePortal(const char *name, bool allowDup, bool dupSilent) { Portal portal; AssertArg(PointerIsValid(name)); portal = GetPortalByName(name); if (PortalIsValid(portal)) { if (!allowDup) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_CURSOR), errmsg("cursor /"%s/" already exists", name))); if (!dupSilent) ereport(WARNING, (errcode(ERRCODE_DUPLICATE_CURSOR), errmsg("closing existing cursor /"%s/"", name))); PortalDrop(portal, false); } /* make new portal structure */ portal = (Portal) MemoryContextAllocZero(TopPortalContext, sizeof *portal); /* initialize portal context; typically it won't store much */ portal->portalContext = AllocSetContextCreate(TopPortalContext, "PortalContext", ALLOCSET_SMALL_SIZES); /* create a resource owner for the portal */ portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner, "Portal"); /* initialize portal fields that don't start off zero */ portal->status = PORTAL_NEW; portal->cleanup = PortalCleanup; portal->createSubid = GetCurrentSubTransactionId(); portal->activeSubid = portal->createSubid; portal->strategy = PORTAL_MULTI_QUERY; portal->cursorOptions = CURSOR_OPT_NO_SCROLL; portal->atStart = true; portal->atEnd = true; /* disallow fetches until query is set */ portal->visible = true; portal->creation_time = GetCurrentStatementStartTimestamp(); /* put portal in table (sets portal->name) */ PortalHashTableInsert(portal, name); /* reuse portal->name copy */ MemoryContextSetIdentifier(portal->portalContext, portal->name); return portal; }
11、PortalDefineQuery
/* * PortalDefineQuery * A simple subroutine to establish a portal's query. * * Notes: as of PG 8.4, caller MUST supply a sourceText string; it is not * allowed anymore to pass NULL. (If you really don't have source text, * you can pass a constant string, perhaps "(query not available)".) * * commandTag shall be NULL if and only if the original query string * (before rewriting) was an empty string. Also, the passed commandTag must * be a pointer to a constant string, since it is not copied. * * If cplan is provided, then it is a cached plan containing the stmts, and * the caller must have done GetCachedPlan(), causing a refcount increment. * The refcount will be released when the portal is destroyed. * * If cplan is NULL, then it is the caller's responsibility to ensure that * the passed plan trees have adequate lifetime. Typically this is done by * copying them into the portal's context. * * The caller is also responsible for ensuring that the passed prepStmtName * (if not NULL) and sourceText have adequate lifetime. * * NB: this function mustn't do much beyond storing the passed values; in * particular don't do anything that risks elog(ERROR). If that were to * happen here before storing the cplan reference, we'd leak the plancache * refcount that the caller is trying to hand off to us. */ void PortalDefineQuery(Portal portal, const char *prepStmtName, const char *sourceText, const char *commandTag, List *stmts, CachedPlan *cplan) { AssertArg(PortalIsValid(portal)); AssertState(portal->status == PORTAL_NEW); AssertArg(sourceText != NULL); AssertArg(commandTag != NULL || stmts == NIL); portal->prepStmtName = prepStmtName; portal->sourceText = sourceText; portal->commandTag = commandTag; portal->stmts = stmts; portal->cplan = cplan; portal->status = PORTAL_DEFINED; }
12、PortalStart
/* * PortalStart * Prepare a portal for execution. * * Caller must already have created the portal, done PortalDefineQuery(), * and adjusted portal options if needed. * * If parameters are needed by the query, they must be passed in "params" * (caller is responsible for giving them appropriate lifetime). * * The caller can also provide an initial set of "eflags" to be passed to * ExecutorStart (but note these can be modified internally, and they are * currently only honored for PORTAL_ONE_SELECT portals). Most callers * should simply pass zero. * * The caller can optionally pass a snapshot to be used; pass InvalidSnapshot * for the normal behavior of setting a new snapshot. This parameter is * presently ignored for non-PORTAL_ONE_SELECT portals (it's only intended * to be used for cursors). * * On return, portal is ready to accept PortalRun() calls, and the result * tupdesc (if any) is known. */ void PortalStart(Portal portal, ParamListInfo params, int eflags, Snapshot snapshot) { Portal saveActivePortal; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext oldContext; QueryDesc *queryDesc; int myeflags; AssertArg(PortalIsValid(portal)); AssertState(portal->status == PORTAL_DEFINED); /* * Set up global portal context pointers. */ saveActivePortal = ActivePortal; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; if (portal->resowner) CurrentResourceOwner = portal->resowner; PortalContext = portal->portalContext; oldContext = MemoryContextSwitchTo(PortalContext); /* Must remember portal param list, if any */ portal->portalParams = params; /* * Determine the portal execution strategy */ portal->strategy = ChoosePortalStrategy(portal->stmts); /* * Fire her up according to the strategy */ switch (portal->strategy) { case PORTAL_ONE_SELECT: /* Must set snapshot before starting executor. */ if (snapshot) PushActiveSnapshot(snapshot); else PushActiveSnapshot(GetTransactionSnapshot()); /* * Create QueryDesc in portal's context; for the moment, set * the destination to DestNone. */ queryDesc = CreateQueryDesc(linitial_node(PlannedStmt, portal->stmts), portal->sourceText, GetActiveSnapshot(), InvalidSnapshot, None_Receiver, params, portal->queryEnv, 0); /* * If it's a scrollable cursor, executor needs to support * REWIND and backwards scan, as well as whatever the caller * might've asked for. */ if (portal->cursorOptions & CURSOR_OPT_SCROLL) myeflags = eflags | EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD; else myeflags = eflags; /* * Call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, myeflags); /* * This tells PortalCleanup to shut down the executor */ portal->queryDesc = queryDesc; /* * Remember tuple descriptor (computed by ExecutorStart) */ portal->tupDesc = queryDesc->tupDesc; /* * Reset cursor position data to "start of query" */ portal->atStart = true; portal->atEnd = false; /* allow fetches */ portal->portalPos = 0; PopActiveSnapshot(); break; case PORTAL_ONE_RETURNING: case PORTAL_ONE_MOD_WITH: /* * We don't start the executor until we are told to run the * portal. We do need to set up the result tupdesc. */ { PlannedStmt *pstmt; pstmt = PortalGetPrimaryStmt(portal); portal->tupDesc = ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); } /* * Reset cursor position data to "start of query" */ portal->atStart = true; portal->atEnd = false; /* allow fetches */ portal->portalPos = 0; break; case PORTAL_UTIL_SELECT: /* * We don't set snapshot here, because PortalRunUtility will * take care of it if needed. */ { PlannedStmt *pstmt = PortalGetPrimaryStmt(portal); Assert(pstmt->commandType == CMD_UTILITY); portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt); } /* * Reset cursor position data to "start of query" */ portal->atStart = true; portal->atEnd = false; /* allow fetches */ portal->portalPos = 0; break; case PORTAL_MULTI_QUERY: /* Need do nothing now */ portal->tupDesc = NULL; break; } } PG_CATCH(); { /* Uncaught error while executing portal: mark it dead */ MarkPortalFailed(portal); /* Restore global vars and propagate error */ ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; PG_RE_THROW(); } PG_END_TRY(); MemoryContextSwitchTo(oldContext); ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; portal->status = PORTAL_READY; }
13、PortalSetResultFormat
/* * PortalSetResultFormat * Select the format codes for a portal's output. * * This must be run after PortalStart for a portal that will be read by * a DestRemote or DestRemoteExecute destination. It is not presently needed * for other destination types. * * formats[] is the client format request, as per Bind message conventions. */ void PortalSetResultFormat(Portal portal, int nFormats, int16 *formats) { int natts; int i; /* Do nothing if portal won't return tuples */ if (portal->tupDesc == NULL) return; natts = portal->tupDesc->natts; portal->formats = (int16 *) MemoryContextAlloc(portal->portalContext, natts * sizeof(int16)); if (nFormats > 1) { /* format specified for each column */ if (nFormats != natts) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message has %d result formats but query has %d columns", nFormats, natts))); memcpy(portal->formats, formats, natts * sizeof(int16)); } else if (nFormats > 0) { /* single format specified, use for all columns */ int16 format1 = formats[0]; for (i = 0; i < natts; i++) portal->formats[i] = format1; } else { /* use default format for all columns */ for (i = 0; i < natts; i++) portal->formats[i] = 0; } }
14、CreateDestReceiver
/* ---------------- * CreateDestReceiver - return appropriate receiver function set for dest * ---------------- */ DestReceiver * CreateDestReceiver(CommandDest dest) { switch (dest) { case DestRemote: case DestRemoteExecute: return printtup_create_DR(dest); case DestRemoteSimple: return &printsimpleDR; case DestNone: return &donothingDR; case DestDebug: return &debugtupDR; case DestSPI: return &spi_printtupDR; case DestTuplestore: return CreateTuplestoreDestReceiver(); case DestIntoRel: return CreateIntoRelDestReceiver(NULL); case DestCopyOut: return CreateCopyDestReceiver(); case DestSQLFunction: return CreateSQLFunctionDestReceiver(); case DestTransientRel: return CreateTransientRelDestReceiver(InvalidOid); case DestTupleQueue: return CreateTupleQueueDestReceiver(NULL); } /* should never get here */ return &donothingDR; }
15、printtup_create_DR
/* ---------------- * Initialize: create a DestReceiver for printtup * ---------------- */ DestReceiver * printtup_create_DR(CommandDest dest) { DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup)); self->pub.receiveSlot = printtup; /* might get changed later */ self->pub.rStartup = printtup_startup; self->pub.rShutdown = printtup_shutdown; self->pub.rDestroy = printtup_destroy; self->pub.mydest = dest; /* * Send T message automatically if DestRemote, but not if * DestRemoteExecute */ self->sendDescrip = (dest == DestRemote); self->attrinfo = NULL; self->nattrs = 0; self->myinfo = NULL; self->tmpcontext = NULL; return (DestReceiver *) self; }
16、PortalDrop
/* * PortalDrop * Destroy the portal. */ void PortalDrop(Portal portal, bool isTopCommit) { AssertArg(PortalIsValid(portal)); /* * Don't allow dropping a pinned portal, it's still needed by whoever * pinned it. */ if (portal->portalPinned) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE), errmsg("cannot drop pinned portal /"%s/"", portal->name))); /* * Not sure if the PORTAL_ACTIVE case can validly happen or not... */ if (portal->status == PORTAL_ACTIVE) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE), errmsg("cannot drop active portal /"%s/"", portal->name))); /* * Allow portalcmds.c to clean up the state it knows about, in particular * shutting down the executor if still active. This step potentially runs * user-defined code so failure has to be expected. It's the cleanup * hook's responsibility to not try to do that more than once, in the case * that failure occurs and then we come back to drop the portal again * during transaction abort. * * Note: in most paths of control, this will have been done already in * MarkPortalDone or MarkPortalFailed. We're just making sure. */ if (PointerIsValid(portal->cleanup)) { portal->cleanup(portal); portal->cleanup = NULL; } /* * Remove portal from hash table. Because we do this here, we will not * come back to try to remove the portal again if there's any error in the * subsequent steps. Better to leak a little memory than to get into an * infinite error-recovery loop. */ PortalHashTableDelete(portal); /* drop cached plan reference, if any */ PortalReleaseCachedPlan(portal); /* * If portal has a snapshot protecting its data, release that. This needs * a little care since the registration will be attached to the portal's * resowner; if the portal failed, we will already have released the * resowner (and the snapshot) during transaction abort. */ if (portal->holdSnapshot) { if (portal->resowner) UnregisterSnapshotFromOwner(portal->holdSnapshot, portal->resowner); portal->holdSnapshot = NULL; } /* * Release any resources still attached to the portal. There are several * cases being covered here: * * Top transaction commit (indicated by isTopCommit): normally we should * do nothing here and let the regular end-of-transaction resource * releasing mechanism handle these resources too. However, if we have a * FAILED portal (eg, a cursor that got an error), we'd better clean up * its resources to avoid resource-leakage warning messages. * * Sub transaction commit: never comes here at all, since we don't kill * any portals in AtSubCommit_Portals(). * * Main or sub transaction abort: we will do nothing here because * portal->resowner was already set NULL; the resources were already * cleaned up in transaction abort. * * Ordinary portal drop: must release resources. However, if the portal * is not FAILED then we do not release its locks. The locks become the * responsibility of the transaction's ResourceOwner (since it is the * parent of the portal's owner) and will be released when the transaction * eventually ends. */ if (portal->resowner && (!isTopCommit || portal->status == PORTAL_FAILED)) { bool isCommit = (portal->status != PORTAL_FAILED); ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_BEFORE_LOCKS, isCommit, false); ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_LOCKS, isCommit, false); ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_AFTER_LOCKS, isCommit, false); ResourceOwnerDelete(portal->resowner); } portal->resowner = NULL; /* * Delete tuplestore if present. We should do this even under error * conditions; since the tuplestore would have been using cross- * transaction storage, its temp files need to be explicitly deleted. */ if (portal->holdStore) { MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(portal->holdContext); tuplestore_end(portal->holdStore); MemoryContextSwitchTo(oldcontext); portal->holdStore = NULL; } /* delete tuplestore storage, if any */ if (portal->holdContext) MemoryContextDelete(portal->holdContext); /* release subsidiary storage */ MemoryContextDelete(portal->portalContext); /* release portal struct (it's in TopPortalContext) */ pfree(portal); }
17、EndImplicitTransactionBlock
/* * EndImplicitTransactionBlock * End an implicit transaction block, if we're in one. * * Like EndTransactionBlock, we just make any needed blockState change here. * The real work will be done in the upcoming CommitTransactionCommand(). */ void EndImplicitTransactionBlock(void) { TransactionState s = CurrentTransactionState; /* * If we are in IMPLICIT_INPROGRESS state, switch back to STARTED state, * allowing CommitTransactionCommand to commit whatever happened during * the implicit transaction block as though it were a single statement. * * 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_IMPLICIT_INPROGRESS) s->blockState = TBLOCK_STARTED; }
18、finish_xact_command
static void finish_xact_command(void) { /* cancel active statement timeout after each command */ disable_statement_timeout(); if (xact_started) { CommitTransactionCommand(); #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; } }
19、CommandCounterIncrement
/* * CommandCounterIncrement */ void CommandCounterIncrement(void) { /* * If the current value of the command counter hasn't been "used" to mark * tuples, we need not increment it, since there's no need to distinguish * a read-only command from others. This helps postpone command counter * overflow, and keeps no-op CommandCounterIncrement operations cheap. */ if (currentCommandIdUsed) { /* * Workers synchronize transaction state at the beginning of each * parallel operation, so we can't account for new commands after that * point. */ if (IsInParallelMode() || IsParallelWorker()) elog(ERROR, "cannot start commands during a parallel operation"); currentCommandId += 1; if (currentCommandId == InvalidCommandId) { currentCommandId -= 1; ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("cannot have more than 2^32-2 commands in a transaction"))); } currentCommandIdUsed = false; /* Propagate new command ID into static snapshots */ SnapshotSetCommandId(currentCommandId); /* * Make any catalog changes done by the just-completed command visible * in the local syscache. We obviously don't need to do this after a * read-only command. (But see hacks in inval.c to make real sure we * don't think a command that queued inval messages was read-only.) */ AtCCI_LocalCache(); } }
20、EndCommand
/* ---------------- * EndCommand - clean up the destination at end of command * ---------------- */ void EndCommand(const char *commandTag, CommandDest dest) { switch (dest) { case DestRemote: case DestRemoteExecute: case DestRemoteSimple: /* * We assume the commandTag is plain ASCII and therefore requires * no encoding conversion. */ pq_putmessage('C', commandTag, strlen(commandTag) + 1); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: case DestSQLFunction: case DestTransientRel: case DestTupleQueue: break; } }
三、跟踪分析
插入测试数据:
testdb=# -- 获取pid testdb=# select pg_backend_pid(); pg_backend_pid ---------------- 1893 (1 row) testdb=# -- 插入1行 testdb=# insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query'); (挂起)
启动gdb,跟踪调试:
[root@localhost ~]# gdb -p 1893 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7 Copyright (C) 2013 Free Software Foundation, Inc. ... (gdb) b exec_simple_query Breakpoint 1 at 0x84cad8: file postgres.c, line 893. (gdb) c Continuing. Breakpoint 1, exec_simple_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:893 893 CommandDest dest = whereToSendOutput; #输入参数 #query_string (gdb) p query_string $1 = 0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');" #单步调试 938 oldcontext = MemoryContextSwitchTo(MessageContext); (gdb) p *MessageContext $2 = {type = T_AllocSetContext, isReset = false, allowInCritSection = false, methods = 0xb8c720 <AllocSetMethods>, parent = 0x1503ba0, firstchild = 0x0, prevchild = 0x157c3c0, nextchild = 0x15956d0, name = 0xb4e87c "MessageContext", ident = 0x0, reset_cbs = 0x0} (gdb) n 944 parsetree_list = pg_parse_query(query_string); (gdb) p oldcontext $3 = (MemoryContext) 0x15a8320 (gdb) p *oldcontext $4 = {type = T_AllocSetContext, isReset = true, allowInCritSection = false, methods = 0xb8c720 <AllocSetMethods>, parent = 0x1503ba0, firstchild = 0x0, prevchild = 0x0, nextchild = 0x157c3c0, name = 0xa1b4ff "TopTransactionContext", ident = 0x0, reset_cbs = 0x0} (gdb) step pg_parse_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:615 615 if (log_parser_stats) (gdb) 618 raw_parsetree_list = raw_parser(query_string); #进入raw_parser (gdb) step raw_parser (str=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at parser.c:43 43 yyscanner = scanner_init(str, &yyextra.core_yy_extra, (gdb) ... 61 return yyextra.parsetree; (gdb) p yyextra $8 = {core_yy_extra = {scanbuf = 0x1509820 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');", scanbuflen = 92, keywords = 0xbb8d40 <ScanKeywords>, num_keywords = 440, backslash_quote = 2, escape_string_warning = true, standard_conforming_strings = true, literalbuf = 0x1509300 "exec_simple_query", literallen = 17, literalalloc = 1024, xcdepth = 1087033144, dolqstart = 0x0, utf16_first_part = 16777215, warn_on_first_escape = true, saw_non_ascii = false}, have_lookahead = false, lookahead_token = 32765, lookahead_yylval = { ival = 10027008, str = 0x300990000 <Address 0x300990000 out of bounds>, keyword = 0x300990000 <Address 0x300990000 out of bounds>}, lookahead_yylloc = 2015867616, lookahead_end = 0xa1b62b "StartTransaction", lookahead_hold_char = 16 '/020', parsetree = 0x1509d88} (gdb) p *(yyextra.parsetree) $10 = {type = T_List, length = 1, head = 0x1509d68, tail = 0x1509d68} #解析树中的内容 (gdb) p *((RawStmt*)(yyextra.parsetree->head->data.ptr_value)) $25 = {type = T_RawStmt, stmt = 0x1509ce8, stmt_location = 0, stmt_len = 91} (gdb) p *(((RawStmt*)(yyextra.parsetree->head->data.ptr_value))->stmt) $27 = {type = T_InsertStmt} #跳出子函数,重新进入主函数 (gdb) exec_simple_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:947 947 if (check_log_statement(parsetree_list)) ... #解析树只有一个元素 (gdb) n 974 foreach(parsetree_item, parsetree_list) (gdb) p list_length(parsetree_list) $30 = 1 (gdb) n 976 RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item); (gdb) 977 bool snapshot_set = false; (gdb) p *parsetree $31 = {type = T_RawStmt, stmt = 0x1509ce8, stmt_location = 0, stmt_len = 91} (gdb) p *(parsetree->stmt) $32 = {type = T_InsertStmt} #commandTag (gdb) n 992 commandTag = CreateCommandTag(parsetree->stmt); (gdb) 994 set_ps_display(commandTag, false); (gdb) p commandTag $33 = 0xb50908 "INSERT" #进入分析&查询重写 1047 querytree_list = pg_analyze_and_rewrite(parsetree, query_string, (gdb) step pg_analyze_and_rewrite (parsetree=0x1509d38, query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');", paramTypes=0x0, numParams=0, queryEnv=0x0) at postgres.c:663 663 if (log_parser_stats) ... #分析后的Query数据结构 (gdb) p *query $34 = {type = T_Query, commandType = CMD_INSERT, querySource = QSRC_ORIGINAL, queryId = 0, canSetTag = true, utilityStmt = 0x0, resultRelation = 1, hasAggs = false, hasWindowFuncs = false, hasTargetSRFs = false, hasSubLinks = false, hasDistinctOn = false, hasRecursive = false, hasModifyingCTE = false, hasForUpdate = false, hasRowSecurity = false, cteList = 0x0, rtable = 0x150a788, jointree = 0x152cf40, targetList = 0x152cda8, override = OVERRIDING_NOT_SET, onConflict = 0x0, returningList = 0x0, groupClause = 0x0, groupingSets = 0x0, havingQual = 0x0, windowClause = 0x0, distinctClause = 0x0, sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, rowMarks = 0x0, setOperations = 0x0, constraintDeps = 0x0, withCheckOptions = 0x0, stmt_location = 0, stmt_len = 91} ... #回到主函数 (gdb) exec_simple_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:1050 1050 plantree_list = pg_plan_queries(querytree_list, (gdb) 1054 if (snapshot_set) (gdb) 1055 PopActiveSnapshot(); (gdb) 1058 CHECK_FOR_INTERRUPTS(); (gdb) 1064 portal = CreatePortal("", true, true); (gdb) p *plantree_list $36 = {type = T_List, length = 1, head = 0x15c03e8, tail = 0x15c03e8} (gdb) p (PlannedStmt*)(plantree_list->head->data.ptr_value) $37 = (PlannedStmt *) 0x150a4a8 (gdb) p *((PlannedStmt*)(plantree_list->head->data.ptr_value)) $38 = {type = T_PlannedStmt, commandType = CMD_INSERT, queryId = 0, hasReturning = false, hasModifyingCTE = false, canSetTag = true, transientPlan = false, dependsOnRole = false, parallelModeNeeded = false, jitFlags = 0, planTree = 0x150a028, rtable = 0x15c0318, resultRelations = 0x15c03b8, nonleafResultRelations = 0x0, rootResultRelations = 0x0, subplans = 0x0, rewindPlanIDs = 0x0, rowMarks = 0x0, relationOids = 0x15c0368, invalItems = 0x0, paramExecTypes = 0x152e720, utilityStmt = 0x0, stmt_location = 0, stmt_len = 91} #Portal ... (gdb) p *portal $40 = {name = 0x1571e98 "", prepStmtName = 0x0, portalContext = 0x152c3d0, resowner = 0x1539d10, cleanup = 0x62f15c <PortalCleanup>, createSubid = 1, activeSubid = 1, sourceText = 0x0, commandTag = 0x0, stmts = 0x0, cplan = 0x0, portalParams = 0x0, queryEnv = 0x0, strategy = PORTAL_MULTI_QUERY, cursorOptions = 4, run_once = false, status = PORTAL_NEW, portalPinned = false, autoHeld = false, queryDesc = 0x0, tupDesc = 0x0, formats = 0x0, holdStore = 0x0, holdContext = 0x0, holdSnapshot = 0x0, atStart = true, atEnd = true, portalPos = 0, creation_time = 587101481469205, visible = true} (gdb) n 1073 PortalDefineQuery(portal, (gdb) 1083 PortalStart(portal, NULL, 0, InvalidSnapshot); (gdb) 1091 format = 0; /* TEXT is default */ (gdb) 1092 if (IsA(parsetree->stmt, FetchStmt)) (gdb) p *portal $41 = {name = 0x1571e98 "", prepStmtName = 0x0, portalContext = 0x152c3d0, resowner = 0x1539d10, cleanup = 0x62f15c <PortalCleanup>, createSubid = 1, activeSubid = 1, sourceText = 0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');", commandTag = 0xb50908 "INSERT", stmts = 0x15c0408, cplan = 0x0, portalParams = 0x0, queryEnv = 0x0, strategy = PORTAL_MULTI_QUERY, cursorOptions = 4, run_once = false, status = PORTAL_READY, portalPinned = false, autoHeld = false, queryDesc = 0x0, tupDesc = 0x0, formats = 0x0, holdStore = 0x0, holdContext = 0x0, holdSnapshot = 0x0, atStart = true, atEnd = true, portalPos = 0, creation_time = 587101481469205, visible = false} #Receiver 1110 receiver = CreateDestReceiver(dest); (gdb) 1111 if (dest == DestRemote) (gdb) p *receiver $42 = {receiveSlot = 0x4857ad <printtup>, rStartup = 0x485196 <printtup_startup>, rShutdown = 0x485bad <printtup_shutdown>, rDestroy = 0x485c21 <printtup_destroy>, mydest = DestRemote} (gdb) #执行 ... 1122 (void) PortalRun(portal, (gdb) ... #DONE! (gdb) PostgresMain (argc=1, argv=0x1532aa8, dbname=0x1532990 "testdb", username=0x1532978 "xdb") at postgres.c:4155 4155 send_ready_for_query = true;
到此,关于“PostgreSQL中exec_simple_query函数的实现逻辑是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/205349.html