微前端架构如何改变企业的开发模式与效率提升
362
2024-01-03
这篇文章主要介绍“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(constchar *query_string) { CommandDest dest = whereToSendOutput;//输出到哪里的定义 MemoryContext oldcontext;//存储原内存上下文 List *parsetree_list;//分析树列表 ListCell *parsetree_item;//分析树中的ITEMbool 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 doesnt report incorrect * results because ResetUsage wasnt 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 were 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变量 /* Dont display the portal in pg_cursors */ portal->visible = false; /* * We dont 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 were 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 were 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 statements 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 */ boolhasRowSecurity;/* rewriter has applied some RLS policy */ List *cteList; /* WITH list (of CommonTableExprs) */ 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 SortGroupClauses */ List *groupingSets; /* a list of GroupingSets if present */ Node *havingQual; /* qualifications applied to groups */ List *windowClause; /* a list of WindowClauses */List *distinctClause;/* a list of SortGroupClauses */ List *sortClause; /* a list of SortGroupClauses */ Node *limitOffset; /* # of result tuples to skip (int8 expr) */ Node *limitCount; /* # of result tuples to return (int8 expr) */ List *rowMarks; /* a list of RowMarkClauses */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 WithCheckOptions, 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; typedefNode *(*PreParseColumnRefHook) (ParseState *pstate, ColumnRef *cref);typedefNode *(*PostParseColumnRefHook) (ParseState *pstate, ColumnRef *cref, Node *var);typedef Node *(*ParseParamRefHook) (ParseState *pstate, ParamRef *pref); typedefNode *(*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) arent joins. * * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that * will become the fromlist of the querys 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 relations 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 WindowDefs location in * this list, counting from 1, is the winref number to use to reference it. * * p_expr_kind: kind of expression were 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: querys 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 weve 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 * nodes 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 querys containing CTE */ Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */ RangeTblEntry *p_target_rangetblentry; /* target rels 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 were 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; boolp_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 functions 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 cant 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. * Its 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 ORd 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 doesnt 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 * ANDed 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{ NodeTagtype; RTEKind rtekind; /* see above */ /* * XXX the fields applicable to only some rte kinds should be merged into * a union. I didnt 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 Kth 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 joins input relations, or such a Var with an * implicit coercion to the joins 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 wed 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 isnt an expression node (since it cant * be evaluated by ExecEvalExpr). But we treat it as one anyway, since in * very many places its convenient to process a whole query targetlist as a * single expression tree. * * In a SELECTs targetlist, resno should always be equal to the items * 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 thats * 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 columns 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 columns source table */ AttrNumber resorigcol; /* columns number in source table */ bool resjunk; /* set to true to eliminate the attribute from * final target list */ } TargetEntry;7、全局变量定义
bool log_parser_stats = false; boollog_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 thisll 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 thats * not desired, the timeout has to be disabled explicitly. */ enable_statement_timeout(); } /* * StartTransactionCommand */ void StartTransactionCommand(void) { TransactionState s = CurrentTransactionState;switch (s->blockState) { /* * if we arent in a transaction block, we just do our usual start * transaction. */ caseTBLOCK_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: caseTBLOCK_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: caseTBLOCK_SUBABORT:break; /* These cases are invalid. */ case TBLOCK_STARTED: case TBLOCK_BEGIN: caseTBLOCK_PARALLEL_INPROGRESS:case TBLOCK_SUBBEGIN: case TBLOCK_END: case TBLOCK_SUBRELEASE: case TBLOCK_SUBCOMMIT: caseTBLOCK_ABORT_END:case TBLOCK_SUBABORT_END: case TBLOCK_ABORT_PENDING: case TBLOCK_SUBABORT_PENDING: caseTBLOCK_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, its 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 * weve 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"); elseraw_parsetree_list = new_list; }#endif TRACE_POSTGRESQL_QUERY_PARSE_DONE(query_string); returnraw_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 */ returnNIL;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 were given a RawStmt */ caseT_RawStmt: tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);break; /* raw plannable queries */ caseT_InsertStmt: tag ="INSERT"; break; case T_DeleteStmt: tag = "DELETE"; break; case T_UpdateStmt: tag = "UPDATE"; break; caseT_SelectStmt: tag ="SELECT"; break; /* utility statements --- same whether raw or cooked */ caseT_TransactionStmt: { TransactionStmt *stmt = (TransactionStmt *) parsetree;switch (stmt->kind) { caseTRANS_STMT_BEGIN: tag ="BEGIN"; break; case TRANS_STMT_START: tag = "START TRANSACTION"; break; case TRANS_STMT_COMMIT: tag = "COMMIT"; break; caseTRANS_STMT_ROLLBACK:case TRANS_STMT_ROLLBACK_TO: tag = "ROLLBACK"; break; caseTRANS_STMT_SAVEPOINT: tag ="SAVEPOINT"; break; caseTRANS_STMT_RELEASE: tag ="RELEASE"; break; caseTRANS_STMT_PREPARE: tag ="PREPARE TRANSACTION"; break; caseTRANS_STMT_COMMIT_PREPARED: tag ="COMMIT PREPARED"; break; caseTRANS_STMT_ROLLBACK_PREPARED: tag ="ROLLBACK PREPARED"; break; default: tag = "-"; break; } }break; caseT_DeclareCursorStmt: tag ="DECLARE CURSOR"; break; caseT_ClosePortalStmt: { ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;if (stmt->portalname == NULL) tag ="CLOSE CURSOR ALL"; else tag = "CLOSE CURSOR"; } break; caseT_FetchStmt: { FetchStmt *stmt = (FetchStmt *) parsetree; tag = (stmt->ismove) ?"MOVE" : "FETCH"; } break; caseT_CreateDomainStmt: tag ="CREATE DOMAIN"; break; caseT_CreateSchemaStmt: tag ="CREATE SCHEMA"; break; case T_CreateStmt: tag = "CREATE TABLE"; break; case T_CreateTableSpaceStmt: tag = "CREATE TABLESPACE"; break; caseT_DropTableSpaceStmt: tag ="DROP TABLESPACE"; break; caseT_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; caseT_CreateFdwStmt: tag ="CREATE FOREIGN DATA WRAPPER"; break; caseT_AlterFdwStmt: tag ="ALTER FOREIGN DATA WRAPPER"; break; caseT_CreateForeignServerStmt: tag ="CREATE SERVER"; break; case T_AlterForeignServerStmt: tag = "ALTER SERVER"; break; case T_CreateUserMappingStmt: tag = "CREATE USER MAPPING"; break; caseT_AlterUserMappingStmt: tag ="ALTER USER MAPPING"; break; caseT_DropUserMappingStmt: tag ="DROP USER MAPPING"; break; caseT_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; caseOBJECT_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; caseOBJECT_TYPE: tag ="DROP TYPE"; break; case OBJECT_DOMAIN: tag = "DROP DOMAIN"; break; case OBJECT_COLLATION: tag = "DROP COLLATION"; break; caseOBJECT_CONVERSION: tag ="DROP CONVERSION"; break; caseOBJECT_SCHEMA: tag ="DROP SCHEMA"; break; caseOBJECT_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; caseOBJECT_FUNCTION: tag ="DROP FUNCTION"; break; caseOBJECT_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; caseOBJECT_LANGUAGE: tag ="DROP LANGUAGE"; break; caseOBJECT_CAST: tag ="DROP CAST"; break; case OBJECT_TRIGGER: tag = "DROP TRIGGER"; break; caseOBJECT_EVENT_TRIGGER: tag ="DROP EVENT TRIGGER"; break; caseOBJECT_RULE: tag ="DROP RULE"; break; caseOBJECT_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; caseOBJECT_POLICY: tag ="DROP POLICY"; break; caseOBJECT_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; caseT_TruncateStmt: tag ="TRUNCATE TABLE"; break; case T_CommentStmt: tag = "COMMENT"; break; caseT_SecLabelStmt: tag ="SECURITY LABEL"; break; caseT_CopyStmt: tag ="COPY"; break; caseT_RenameStmt: tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType);break; caseT_AlterObjectDependsStmt: tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType);break; caseT_AlterObjectSchemaStmt: tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType);break; caseT_AlterOwnerStmt: tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType);break; caseT_AlterTableMoveAllStmt: tag = AlterObjectTypeCommandTag(((AlterTableMoveAllStmt *) parsetree)->objtype);break; caseT_AlterTableStmt: tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind);break; case T_AlterDomainStmt: tag = "ALTER DOMAIN"; break; case T_AlterFunctionStmt: switch(((AlterFunctionStmt *) parsetree)->objtype) {caseOBJECT_FUNCTION: tag ="ALTER FUNCTION"; break; caseOBJECT_PROCEDURE: tag ="ALTER PROCEDURE"; break; case OBJECT_ROUTINE: tag = "ALTER ROUTINE"; break;default: tag = "-"; } break; caseT_GrantStmt: { GrantStmt *stmt = (GrantStmt *) parsetree; tag = (stmt->is_grant) ?"GRANT" : "REVOKE"; } break; caseT_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; caseOBJECT_OPERATOR: tag ="CREATE OPERATOR"; break; caseOBJECT_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; caseOBJECT_ACCESS_METHOD: tag ="CREATE ACCESS METHOD"; break; default: tag ="-"; } break; caseT_CompositeTypeStmt: tag ="CREATE TYPE"; break; case T_CreateEnumStmt: tag = "CREATE TYPE"; break;case T_CreateRangeStmt: tag = "CREATE TYPE"; break; caseT_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; caseT_IndexStmt: tag ="CREATE INDEX"; break; case T_RuleStmt: tag = "CREATE RULE"; break; caseT_CreateSeqStmt: tag ="CREATE SEQUENCE"; break; caseT_AlterSeqStmt: tag ="ALTER SEQUENCE"; break; case T_DoStmt: tag = "DO"; break; caseT_CreatedbStmt: tag ="CREATE DATABASE"; break; case T_AlterDatabaseStmt: tag = "ALTER DATABASE"; break; case T_AlterDatabaseSetStmt: tag = "ALTER DATABASE"; break; caseT_DropdbStmt: tag ="DROP DATABASE"; break; caseT_NotifyStmt: tag ="NOTIFY"; break; case T_ListenStmt: tag = "LISTEN"; break; caseT_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; caseOBJECT_MATVIEW: tag ="CREATE MATERIALIZED VIEW"; break; default: tag = "-"; }break; case T_RefreshMatViewStmt: tag = "REFRESH MATERIALIZED VIEW"; break; case T_AlterSystemStmt: tag = "ALTER SYSTEM"; break; caseT_VariableSetStmt:switch (((VariableSetStmt *) parsetree)->kind) { case VAR_SET_VALUE: caseVAR_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; caseT_VariableShowStmt: tag ="SHOW"; break; case T_DiscardStmt: switch(((DiscardStmt *) parsetree)->target) {case DISCARD_ALL: tag = "DISCARD ALL"; break; caseDISCARD_PLANS: tag ="DISCARD PLANS"; break; caseDISCARD_TEMP: tag ="DISCARD TEMP"; break; case DISCARD_SEQUENCES: tag = "DISCARD SEQUENCES";break; default: tag = "-"; } break; caseT_CreateTransformStmt: tag ="CREATE TRANSFORM"; break; caseT_CreateTrigStmt: tag ="CREATE TRIGGER"; break; case T_CreateEventTrigStmt: tag = "CREATE EVENT TRIGGER"; break; case T_AlterEventTrigStmt: tag = "ALTER EVENT TRIGGER"; break; caseT_CreatePLangStmt: tag ="CREATE LANGUAGE"; break; caseT_CreateRoleStmt: tag ="CREATE ROLE"; break; case T_AlterRoleStmt: tag = "ALTER ROLE"; break; case T_AlterRoleSetStmt: tag = "ALTER ROLE"; break; caseT_DropRoleStmt: tag ="DROP ROLE"; break; case T_DropOwnedStmt: tag = "DROP OWNED"; break; case T_ReassignOwnedStmt: tag = "REASSIGN OWNED"; break; caseT_LockStmt: tag ="LOCK TABLE"; break; case T_ConstraintsSetStmt: tag = "SET CONSTRAINTS"; break; case T_CheckPointStmt: tag = "CHECKPOINT"; break; caseT_ReindexStmt: tag ="REINDEX"; break; case T_CreateConversionStmt: tag = "CREATE CONVERSION"; break; case T_CreateCastStmt: tag = "CREATE CAST"; break; caseT_CreateOpClassStmt: tag ="CREATE OPERATOR CLASS"; break; caseT_CreateOpFamilyStmt: tag ="CREATE OPERATOR FAMILY"; break; caseT_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; caseT_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; caseT_CreateSubscriptionStmt: tag ="CREATE SUBSCRIPTION"; break; caseT_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; caseT_ExecuteStmt: tag ="EXECUTE"; break; case T_CreateStatsStmt: tag = "CREATE STATISTICS"; break; caseT_DeallocateStmt: { DeallocateStmt *stmt = (DeallocateStmt *) parsetree;if (stmt->name == NULL) tag = "DEALLOCATE ALL"; else tag = "DEALLOCATE"; } break; /* already-planned queries */ caseT_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; caseCMD_UPDATE: tag ="UPDATE"; break; caseCMD_INSERT: tag ="INSERT"; break; case CMD_DELETE: tag = "DELETE"; break; caseCMD_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 */ caseT_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) {caseLCS_FORKEYSHARE: tag ="SELECT FOR KEY SHARE"; break; caseLCS_FORSHARE: tag ="SELECT FOR SHARE"; break; caseLCS_FORNOKEYUPDATE: tag ="SELECT FOR NO KEY UPDATE"; break; caseLCS_FORUPDATE: tag ="SELECT FOR UPDATE"; break; default: tag = "-";break; } } else tag = "SELECT"; break; case CMD_UPDATE: tag = "UPDATE"; break; caseCMD_INSERT: tag ="INSERT"; break; case CMD_DELETE: tag = "DELETE"; break; caseCMD_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: caseT_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 dont 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,constchar *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, constchar *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 dont 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;/* Were 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 dont do in transformStmt() is to * convert SELECT ... INTO into CREATE TABLE AS. Since utility statements * arent 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. */ staticQuery * transformOptionalSelectInto(ParseState *pstate, Node *parseTree) {if(IsA(parseTree, SelectStmt)) { SelectStmt *stmt = (SelectStmt *) parseTree;/* If its 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 cant just run it on everything because raw_expression_tree_walker() * doesnt claim to handle utility statements. */ #ifdef RAW_EXPRESSION_COVERAGE_TEST switch (nodeTag(parseTree)) { case T_SelectStmt: caseT_InsertStmt:case T_UpdateStmt: caseT_DeleteStmt: (void) test_raw_expression_coverage(parseTree,NULL); break; default: break; } #endif /* RAW_EXPRESSION_COVERAGE_TEST */ switch (nodeTag(parseTree)) { /* * Optimizable statements */ caseT_InsertStmt: result = transformInsertStmt(pstate, (InsertStmt *) parseTree);break; caseT_DeleteStmt: result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);break; caseT_UpdateStmt: result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);break; caseT_SelectStmt: { SelectStmt *n = (SelectStmt *) parseTree;if(n->valuesLists) result = transformValuesClause(pstate, n);else if(n->op == SETOP_NONE) result = transformSelectStmt(pstate, n);elseresult = transformSetOperationStmt(pstate, n); }break; /* * Special cases */ caseT_DeclareCursorStmt: result = transformDeclareCursorStmt(pstate, (DeclareCursorStmt *) parseTree);break; caseT_ExplainStmt: result = transformExplainStmt(pstate, (ExplainStmt *) parseTree);break; caseT_CreateTableAsStmt: result = transformCreateTableAsStmt(pstate, (CreateTableAsStmt *) parseTree);break; caseT_CallStmt: result = transformCallStmt(pstate, (CallStmt *) parseTree);break; default: /* * other statements dont 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 */ staticQuery * 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 cant 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 cant 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 rules OLD and NEW rtable entries to appear as * part of the SELECTs rtable, not as outer references for it. (Kluge!) * The SELECTs joinlist is not affected however. We must do this before * adding the target table to the INSERTs 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 * pstates rtable and namespace are presently empty, there are no * side-effects of exposing names the sub-SELECT shouldnt 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 columns 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 INSERTs rangetable, and add * it to the INSERTs 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. (INSERTs tlist must be * separate from the subquerys tlist because we may add columns, * insert datatype coercions, etc.) * * HACK: unknown-type constants and params in the SELECTs 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 columns 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 cant 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 dont * want the parsed statement to contain copies of those in each * VALUES row. (Its 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 * doesnt process rangetable entries. We just assign all the * collations independently in each row, and dont worry about * whether they are consistent vertically. The outer INSERT query * isnt going to care about the collations of the VALUES columns, * so its not worth the effort to identify a common collation for * each one here. (But note this does have one user-visible * consequence: INSERT ... VALUES wont 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 its sufficient * to look at the exprType/exprTypmod of the first row. We dont 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 cant be any current-level Vars in the expression * lists, because the namespace was empty ... but if were 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 Querys 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 querys 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);returnqry; }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 * dont 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 youd 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); returnresult; }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, dont even emit a WARNING. */ Portal CreatePortal(const char*name,bool allowDup, booldupSilent) { 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 wont 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 dont 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 portals query. * * Notes: as of PG 8.4, caller MUST supply a sourceText string; it is not * allowed anymore to pass NULL. (If you really dont 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 callers responsibility to ensure that * the passed plan trees have adequate lifetime. Typically this is done by * copying them into the portals context. * * The caller is also responsible for ensuring that the passed prepStmtName * (if not NULL) and sourceText have adequate lifetime. * * NB: this function mustnt do much beyond storing the passed values; in * particular dont do anything that risks elog(ERROR). If that were to * happen here before storing the cplan reference, wed leak the plancache * refcount that the caller is trying to hand off to us. */ void PortalDefineQuery(Portal portal, constchar *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 (its 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 portals 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 its a scrollable cursor, executor needs to support * REWIND and backwards scan, as well as whatever the caller * mightve 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 dont 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 dont 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 portals 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 wont 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) { caseDestRemote:case DestRemoteExecute: return printtup_create_DR(dest); case DestRemoteSimple: return &printsimpleDR; case DestNone: return &donothingDR; case DestDebug: return &debugtupDR; case DestSPI: return &spi_printtupDR; caseDestTuplestore:return CreateTuplestoreDestReceiver(); case DestIntoRel: return CreateIntoRelDestReceiver(NULL); case DestCopyOut: return CreateCopyDestReceiver(); case DestSQLFunction: returnCreateSQLFunctionDestReceiver();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));/* * Dont allow dropping a pinned portal, its 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. Its the cleanup * hooks 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. Were 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 theres 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 portals * 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), wed better clean up * its resources to avoid resource-leakage warning messages. * * Sub transaction commit: never comes here at all, since we dont 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 transactions ResourceOwner (since it is the * parent of the portals 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 (its in TopPortalContext) */ pfree(portal); }17、EndImplicitTransactionBlock
/* * EndImplicitTransactionBlock * End an implicit transaction block, if were 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(); #ifdefMEMORY_CONTEXT_CHECKING /* Check all memory contexts that werent freed during commit */ /* (those that were, were checked before being deleted) */ MemoryContextCheck(TopMemoryContext); #endif #ifdefSHOW_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 hasnt been "used" to mark * tuples, we need not increment it, since theres 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 cant 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 dont need to do this after a * read-only command. (But see hacks in inval.c to make real sure we * dont 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: caseDestRemoteSimple:/* * 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: caseDestIntoRel:case DestCopyOut: case DestSQLFunction: case DestTransientRel: case DestTupleQueue: break; } }三、跟踪分析插入测试数据:
testdb=# -- 获取pid testdb=# selectpg_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函数的实现逻辑是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~