千家信息网

PostgreSQL中exec_simple_query函数的实现逻辑是什么

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要介绍"PostgreSQL中exec_simple_query函数的实现逻辑是什么",在日常操作中,相信很多人在PostgreSQL中exec_simple_query函数的实现逻辑是什么
千家信息网最后更新 2026年11月01日PostgreSQL中exec_simple_query函数的实现逻辑是什么

这篇文章主要介绍"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 voidexec_simple_query(const char *query_string){    CommandDest dest = whereToSendOutput;//输出到哪里的定义    MemoryContext oldcontext;//存储原内存上下文    List       *parsetree_list;//分析树列表    ListCell   *parsetree_item;//分析树中的ITEM    bool        save_log_statement_stats = log_statement_stats;//是否保存统计信息,false    bool        was_logged = false;//Log?    bool        use_implicit_block;//是否使用隐式事务块    char        msec_str[32];    /*     * Report query to various monitoring facilities.     */    debug_query_string = query_string;    pgstat_report_activity(STATE_RUNNING, query_string);//统计信息    TRACE_POSTGRESQL_QUERY_START(query_string);    /*     * We use save_log_statement_stats so ShowUsage doesn't report incorrect     * results because ResetUsage wasn't called.     */    if (save_log_statement_stats)        ResetUsage();    /*     * Start up a transaction command.  All queries generated by the     * query_string will be in this same command block, *unless* we find a     * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after     * one of those, else bad things will happen in xact.c. (Note that this     * will normally change current memory context.)     */    start_xact_command();//启动事务    /*     * Zap any pre-existing unnamed statement.  (While not strictly necessary,     * it seems best to define simple-Query mode as if it used the unnamed     * statement and portal; this ensures we recover any storage used by prior     * unnamed operations.)     */    drop_unnamed_stmt();//清除匿名语句    /*     * Switch to appropriate context for constructing parsetrees.     */    oldcontext = MemoryContextSwitchTo(MessageContext);//切换内存上下文    /*     * Do basic parsing of the query or queries (this should be safe even if     * we are in aborted transaction state!)     */    parsetree_list = pg_parse_query(query_string);//解析输入的查询语句,获得分析树List(元素是RawStmt nodes)    /* Log immediately if dictated by log_statement */    if (check_log_statement(parsetree_list))//日志记录    {        ereport(LOG,                (errmsg("statement: %s", query_string),                 errhidestmt(true),                 errdetail_execute(parsetree_list)));        was_logged = true;    }    /*     * Switch back to transaction context to enter the loop.     */    MemoryContextSwitchTo(oldcontext);//切换回原内存上下文    /*     * For historical reasons, if multiple SQL statements are given in a     * single "simple Query" message, we execute them as a single transaction,     * unless explicit transaction control commands are included to make     * portions of the list be separate transactions.  To represent this     * behavior properly in the transaction machinery, we use an "implicit"     * transaction block.     */    use_implicit_block = (list_length(parsetree_list) > 1);//如果分析树条目>1,使用隐式事务块(多条SQL语句在同一个事务中)    /*     * Run through the raw parsetree(s) and process each one.     */    foreach(parsetree_item, parsetree_list)//对分析树中的每一个条目进行处理    {        RawStmt    *parsetree = lfirst_node(RawStmt, parsetree_item);//分析树List中的元素为RawStmt指针类型        bool        snapshot_set = false;//是否设置快照?        const char *commandTag;//命令标识        char        completionTag[COMPLETION_TAG_BUFSIZE];//完成标记,如INSERT 0 1之类的字符串        List       *querytree_list,//查询树List                   *plantree_list;//执行计划List        Portal      portal;//"门户"变量        DestReceiver *receiver;//目标接收端        int16       format;//        /*         * Get the command name for use in status display (it also becomes the         * default completion tag, down inside PortalRun).  Set ps_status and         * do any special start-of-SQL-command processing needed by the         * destination.         */        commandTag = CreateCommandTag(parsetree->stmt);//创建命令标记,插入数据则为INSERT        set_ps_display(commandTag, false);        BeginCommand(commandTag, dest);//do Nothing!        /*         * If we are in an aborted transaction, reject all commands except         * COMMIT/ABORT.  It is important that this test occur before we try         * to do parse analysis, rewrite, or planning, since all those phases         * try to do database accesses, which may fail in abort state. (It         * might be safe to allow some additional utility commands in this         * state, but not many...)         */        if (IsAbortedTransactionBlockState() &&            !IsTransactionExitStmt(parsetree->stmt))            ereport(ERROR,                    (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),                     errmsg("current transaction is aborted, "                            "commands ignored until end of transaction block"),                     errdetail_abort()));        /* Make sure we are in a transaction command */        start_xact_command();//确认在事务中        /*         * If using an implicit transaction block, and we're not already in a         * transaction block, start an implicit block to force this statement         * to be grouped together with any following ones.  (We must do this         * each time through the loop; otherwise, a COMMIT/ROLLBACK in the         * list would cause later statements to not be grouped.)         */        if (use_implicit_block)            BeginImplicitTransactionBlock();//隐式事务,进入事务块        /* If we got a cancel signal in parsing or prior command, quit */        CHECK_FOR_INTERRUPTS();        /*         * Set up a snapshot if parse analysis/planning will need one.         */        if (analyze_requires_snapshot(parsetree))//是否需要快照进行分析?增删改查均需要        {            PushActiveSnapshot(GetTransactionSnapshot());//            snapshot_set = true;        }        /*         * OK to analyze, rewrite, and plan this query.         *         * Switch to appropriate context for constructing querytrees (again,         * these must outlive the execution context).         */        oldcontext = MemoryContextSwitchTo(MessageContext);//切换内存上下文        querytree_list = pg_analyze_and_rewrite(parsetree, query_string,                                                NULL, 0, NULL);//根据分析树获得查询树,返回List(元素为Query)        plantree_list = pg_plan_queries(querytree_list,                                        CURSOR_OPT_PARALLEL_OK, NULL);//根据查询树获取计划树,返回List(元素为PlannedStmt)        /* Done with the snapshot used for parsing/planning */        if (snapshot_set)            PopActiveSnapshot();//        /* If we got a cancel signal in analysis or planning, quit */        CHECK_FOR_INTERRUPTS();        /*         * Create unnamed portal to run the query or queries in. If there         * already is one, silently drop it.         */        portal = CreatePortal("", true, true);//创建匿名Portal变量        /* Don't display the portal in pg_cursors */        portal->visible = false;        /*         * We don't have to copy anything into the portal, because everything         * we are passing here is in MessageContext, which will outlive the         * portal anyway.         */        PortalDefineQuery(portal,                          NULL,                          query_string,                          commandTag,                          plantree_list,                          NULL);//给Portal变量赋值        /*         * Start the portal.  No parameters here.         */        PortalStart(portal, NULL, 0, InvalidSnapshot);//为PortalRun作准备        /*         * Select the appropriate output format: text unless we are doing a         * FETCH from a binary cursor.  (Pretty grotty to have to do this here         * --- but it avoids grottiness in other places.  Ah, the joys of         * backward compatibility...)         */        format = 0;             /* TEXT is default */        if (IsA(parsetree->stmt, FetchStmt))        {            FetchStmt  *stmt = (FetchStmt *) parsetree->stmt;            if (!stmt->ismove)            {                Portal      fportal = GetPortalByName(stmt->portalname);                if (PortalIsValid(fportal) &&                    (fportal->cursorOptions & CURSOR_OPT_BINARY))                    format = 1; /* BINARY */            }        }        PortalSetResultFormat(portal, 1, &format);//设置结果返回的格式,默认为TEXT        /*         * Now we can create the destination receiver object.         */        receiver = CreateDestReceiver(dest);//创建目标接收器(如使用psql则为:printtup DestReceiver)        if (dest == DestRemote)            SetRemoteDestReceiverParams(receiver, portal);        /*         * Switch back to transaction context for execution.         */        MemoryContextSwitchTo(oldcontext);//切换回原内存上下文        /*         * Run the portal to completion, and then drop it (and the receiver).         */        (void) PortalRun(portal,                         FETCH_ALL,                         true,  /* always top level */                         true,                         receiver,                         receiver,                         completionTag);//执行        receiver->rDestroy(receiver);//执行完毕,销毁接收器        PortalDrop(portal, false);//清除Portal中的资源&Portal        if (lnext(parsetree_item) == NULL)//所有语句已执行完毕        {            /*             * If this is the last parsetree of the query string, close down             * transaction statement before reporting command-complete.  This             * is so that any end-of-transaction errors are reported before             * the command-complete message is issued, to avoid confusing             * clients who will expect either a command-complete message or an             * error, not one and then the other.  Also, if we're using an             * implicit transaction block, we must close that out first.             */            if (use_implicit_block)                EndImplicitTransactionBlock();//结束事务            finish_xact_command();//结束事务        }        else if (IsA(parsetree->stmt, TransactionStmt))//事务语句?BEGIN/COMMIT/ABORT...        {            /*             * If this was a transaction control statement, commit it. We will             * start a new xact command for the next command.             */            finish_xact_command();        }        else        {            /*             * We need a CommandCounterIncrement after every query, except             * those that start or end a transaction block.             */            CommandCounterIncrement();//命令+1(对应Tuple中的cid)        }        /*         * Tell client that we're done with this query.  Note we emit exactly         * one EndCommand report for each raw parsetree, thus one for each SQL         * command the client sent, regardless of rewriting. (But a command         * aborted by error will not send an EndCommand report at all.)         */        EndCommand(completionTag, dest);//命令Done    }                           /* end loop over parsetrees */        //所有语句结束    /*     * Close down transaction statement, if one is open.  (This will only do     * something if the parsetree list was empty; otherwise the last loop     * iteration already did it.)     */    finish_xact_command();    /*     * If there were no parsetrees, return EmptyQueryResponse message.     */    if (!parsetree_list)        NullCommand(dest);    /*     * Emit duration logging if appropriate.     */    switch (check_log_duration(msec_str, was_logged))    {        case 1:            ereport(LOG,                    (errmsg("duration: %s ms", msec_str),                     errhidestmt(true)));            break;        case 2:            ereport(LOG,                    (errmsg("duration: %s ms  statement: %s",                            msec_str, query_string),                     errhidestmt(true),                     errdetail_execute(parsetree_list)));            break;    }    if (save_log_statement_stats)        ShowUsage("QUERY STATISTICS");    TRACE_POSTGRESQL_QUERY_DONE(query_string);    debug_query_string = NULL;}

二、基础信息

exec_simple_query函数使用的数据结构、宏定义以及依赖的函数等。
数据结构/宏定义
*1、whereToSendOutput *

 /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */ CommandDest whereToSendOutput = DestDebug; /* ----------------  *      CommandDest is a simplistic means of identifying the desired  *      destination.  Someday this will probably need to be improved.  *  * Note: only the values DestNone, DestDebug, DestRemote are legal for the  * global variable whereToSendOutput.   The other values may be used  * as the destination for individual commands.  * ----------------  */ typedef enum {     DestNone,                   /* results are discarded */     DestDebug,                  /* results go to debugging output */     DestRemote,                 /* results sent to frontend process */     DestRemoteExecute,          /* sent to frontend, in Execute command */     DestRemoteSimple,           /* sent to frontend, w/no catalog access */     DestSPI,                    /* results sent to SPI manager */     DestTuplestore,             /* results sent to Tuplestore */     DestIntoRel,                /* results sent to relation (SELECT INTO) */     DestCopyOut,                /* results sent to COPY TO code */     DestSQLFunction,            /* results sent to SQL-language func mgr */     DestTransientRel,           /* results sent to transient relation */     DestTupleQueue              /* results sent to tuple queue */ } CommandDest;

2、RawStmt

  /*  *      RawStmt --- container for any one statement's raw parse tree  *  * Parse analysis converts a raw parse tree headed by a RawStmt node into  * an analyzed statement headed by a Query node.  For optimizable statements,  * the conversion is complex.  For utility statements, the parser usually just  * transfers the raw parse tree (sans RawStmt) into the utilityStmt field of  * the Query node, and all the useful work happens at execution time.  *  * stmt_location/stmt_len identify the portion of the source text string  * containing this raw statement (useful for multi-statement strings).  */ typedef struct RawStmt {     NodeTag     type;     Node       *stmt;           /* raw parse tree */     int         stmt_location;  /* start location, or -1 if unknown */     int         stmt_len;       /* length in bytes; 0 means "rest of string" */ } RawStmt;

3、Query

//在解析查询语句时再深入解析/*****************************************************************************  *  Query Tree  *****************************************************************************/  /*  * Query -  *    Parse analysis turns all statements into a Query tree  *    for further processing by the rewriter and planner.  *  *    Utility statements (i.e. non-optimizable statements) have the  *    utilityStmt field set, and the rest of the Query is mostly dummy.  *  *    Planning converts a Query tree into a Plan tree headed by a PlannedStmt  *    node --- the Query structure is not used by the executor.  */ typedef struct Query {     NodeTag     type;      CmdType     commandType;    /* select|insert|update|delete|utility */      QuerySource querySource;    /* where did I come from? */      uint64      queryId;        /* query identifier (can be set by plugins) */      bool        canSetTag;      /* do I set the command result tag? */      Node       *utilityStmt;    /* non-null if commandType == CMD_UTILITY */      int         resultRelation; /* rtable index of target relation for                                  * INSERT/UPDATE/DELETE; 0 for SELECT */      bool        hasAggs;        /* has aggregates in tlist or havingQual */     bool        hasWindowFuncs; /* has window functions in tlist */     bool        hasTargetSRFs;  /* has set-returning functions in tlist */     bool        hasSubLinks;    /* has subquery SubLink */     bool        hasDistinctOn;  /* distinctClause is from DISTINCT ON */     bool        hasRecursive;   /* WITH RECURSIVE was specified */     bool        hasModifyingCTE;    /* has INSERT/UPDATE/DELETE in WITH */     bool        hasForUpdate;   /* FOR [KEY] UPDATE/SHARE was specified */     bool        hasRowSecurity; /* rewriter has applied some RLS policy */      List       *cteList;        /* WITH list (of CommonTableExpr's) */      List       *rtable;         /* list of range table entries */     FromExpr   *jointree;       /* table join tree (FROM and WHERE clauses) */      List       *targetList;     /* target list (of TargetEntry) */      OverridingKind override;    /* OVERRIDING clause */      OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */      List       *returningList;  /* return-values list (of TargetEntry) */      List       *groupClause;    /* a list of SortGroupClause's */      List       *groupingSets;   /* a list of GroupingSet's if present */      Node       *havingQual;     /* qualifications applied to groups */      List       *windowClause;   /* a list of WindowClause's */      List       *distinctClause; /* a list of SortGroupClause's */      List       *sortClause;     /* a list of SortGroupClause's */      Node       *limitOffset;    /* # of result tuples to skip (int8 expr) */     Node       *limitCount;     /* # of result tuples to return (int8 expr) */      List       *rowMarks;       /* a list of RowMarkClause's */      Node       *setOperations;  /* set-operation tree if this is top level of                                  * a UNION/INTERSECT/EXCEPT query */      List       *constraintDeps; /* a list of pg_constraint OIDs that the query                                  * depends on to be semantically valid */      List       *withCheckOptions;   /* a list of WithCheckOption's, which are                                      * only added during rewrite and therefore                                      * are not written out as part of Query. */      /*      * The following two fields identify the portion of the source text string      * containing this query.  They are typically only populated in top-level      * Queries, not in sub-queries.  When not set, they might both be zero, or      * both be -1 meaning "unknown".      */     int         stmt_location;  /* start location, or -1 if unknown */     int         stmt_len;       /* length in bytes; 0 means "rest of string" */ } Query;

4、ParseState

 /*  * Function signatures for parser hooks  */ typedef struct ParseState ParseState;  typedef Node *(*PreParseColumnRefHook) (ParseState *pstate, ColumnRef *cref); typedef Node *(*PostParseColumnRefHook) (ParseState *pstate, ColumnRef *cref, Node *var); typedef Node *(*ParseParamRefHook) (ParseState *pstate, ParamRef *pref); typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,                                   Oid targetTypeId, int32 targetTypeMod,                                   int location);   /*  * State information used during parse analysis  *  * parentParseState: NULL in a top-level ParseState.  When parsing a subquery,  * links to current parse state of outer query.  *  * p_sourcetext: source string that generated the raw parsetree being  * analyzed, or NULL if not available.  (The string is used only to  * generate cursor positions in error messages: we need it to convert  * byte-wise locations in parse structures to character-wise cursor  * positions.)  *  * p_rtable: list of RTEs that will become the rangetable of the query.  * Note that neither relname nor refname of these entries are necessarily  * unique; searching the rtable by name is a bad idea.  *  * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries.  * This is one-for-one with p_rtable, but contains NULLs for non-join  * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins.  *  * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that  * will become the fromlist of the query's top-level FromExpr node.  *  * p_namespace: list of ParseNamespaceItems that represents the current  * namespace for table and column lookup.  (The RTEs listed here may be just  * a subset of the whole rtable.  See ParseNamespaceItem comments below.)  *  * p_lateral_active: true if we are currently parsing a LATERAL subexpression  * of this parse level.  This makes p_lateral_only namespace items visible,  * whereas they are not visible when p_lateral_active is FALSE.  *  * p_ctenamespace: list of CommonTableExprs (WITH items) that are visible  * at the moment.  This is entirely different from p_namespace because a CTE  * is not an RTE, rather "visibility" means you could make an RTE from it.  *  * p_future_ctes: list of CommonTableExprs (WITH items) that are not yet  * visible due to scope rules.  This is used to help improve error messages.  *  * p_parent_cte: CommonTableExpr that immediately contains the current query,  * if any.  *  * p_target_relation: target relation, if query is INSERT, UPDATE, or DELETE.  *  * p_target_rangetblentry: target relation's entry in the rtable list.  *  * p_is_insert: true to process assignment expressions like INSERT, false  * to process them like UPDATE.  (Note this can change intra-statement, for  * cases like INSERT ON CONFLICT UPDATE.)  *  * p_windowdefs: list of WindowDefs representing WINDOW and OVER clauses.  * We collect these while transforming expressions and then transform them  * afterwards (so that any resjunk tlist items needed for the sort/group  * clauses end up at the end of the query tlist).  A WindowDef's location in  * this list, counting from 1, is the winref number to use to reference it.  *  * p_expr_kind: kind of expression we're currently parsing, as per enum above;  * EXPR_KIND_NONE when not in an expression.  *  * p_next_resno: next TargetEntry.resno to assign, starting from 1.  *  * p_multiassign_exprs: partially-processed MultiAssignRef source expressions.  *  * p_locking_clause: query's FOR UPDATE/FOR SHARE clause, if any.  *  * p_locked_from_parent: true if parent query level applies FOR UPDATE/SHARE  * to this subquery as a whole.  *  * p_resolve_unknowns: resolve unknown-type SELECT output columns as type TEXT  * (this is true by default).  *  * p_hasAggs, p_hasWindowFuncs, etc: true if we've found any of the indicated  * constructs in the query.  *  * p_last_srf: the set-returning FuncExpr or OpExpr most recently found in  * the query, or NULL if none.  *  * p_pre_columnref_hook, etc: optional parser hook functions for modifying the  * interpretation of ColumnRefs and ParamRefs.  *  * p_ref_hook_state: passthrough state for the parser hook functions.  */ struct ParseState {     struct ParseState *parentParseState;    /* stack link */     const char *p_sourcetext;   /* source text, or NULL if not available */     List       *p_rtable;       /* range table so far */     List       *p_joinexprs;    /* JoinExprs for RTE_JOIN p_rtable entries */     List       *p_joinlist;     /* join items so far (will become FromExpr                                  * node's fromlist) */     List       *p_namespace;    /* currently-referenceable RTEs (List of                                  * ParseNamespaceItem) */     bool        p_lateral_active;   /* p_lateral_only items visible? */     List       *p_ctenamespace; /* current namespace for common table exprs */     List       *p_future_ctes;  /* common table exprs not yet in namespace */     CommonTableExpr *p_parent_cte;  /* this query's containing CTE */     Relation    p_target_relation;  /* INSERT/UPDATE/DELETE target rel */     RangeTblEntry *p_target_rangetblentry;  /* target rel's RTE */     bool        p_is_insert;    /* process assignment like INSERT not UPDATE */     List       *p_windowdefs;   /* raw representations of window clauses */     ParseExprKind p_expr_kind;  /* what kind of expression we're parsing */     int         p_next_resno;   /* next targetlist resno to assign */     List       *p_multiassign_exprs;    /* junk tlist entries for multiassign */     List       *p_locking_clause;   /* raw FOR UPDATE/FOR SHARE info */     bool        p_locked_from_parent;   /* parent has marked this subquery                                          * with FOR UPDATE/FOR SHARE */     bool        p_resolve_unknowns; /* resolve unknown-type SELECT outputs as                                      * type text */      QueryEnvironment *p_queryEnv;   /* curr env, incl refs to enclosing env */      /* Flags telling about things found in the query: */     bool        p_hasAggs;     bool        p_hasWindowFuncs;     bool        p_hasTargetSRFs;     bool        p_hasSubLinks;     bool        p_hasModifyingCTE;      Node       *p_last_srf;     /* most recent set-returning func/op found */      /*      * Optional hook functions for parser callbacks.  These are null unless      * set up by the caller of make_parsestate.      */     PreParseColumnRefHook p_pre_columnref_hook;     PostParseColumnRefHook p_post_columnref_hook;     ParseParamRefHook p_paramref_hook;     CoerceParamHook p_coerce_param_hook;     void       *p_ref_hook_state;   /* common passthrough link for above */ };

5、RangeTblEntry

 /*--------------------  * RangeTblEntry -  *    A range table is a List of RangeTblEntry nodes.  *  *    A range table entry may represent a plain relation, a sub-select in  *    FROM, or the result of a JOIN clause.  (Only explicit JOIN syntax  *    produces an RTE, not the implicit join resulting from multiple FROM  *    items.  This is because we only need the RTE to deal with SQL features  *    like outer joins and join-output-column aliasing.)  Other special  *    RTE types also exist, as indicated by RTEKind.  *  *    Note that we consider RTE_RELATION to cover anything that has a pg_class  *    entry.  relkind distinguishes the sub-cases.  *  *    alias is an Alias node representing the AS alias-clause attached to the  *    FROM expression, or NULL if no clause.  *  *    eref is the table reference name and column reference names (either  *    real or aliases).  Note that system columns (OID etc) are not included  *    in the column list.  *    eref->aliasname is required to be present, and should generally be used  *    to identify the RTE for error messages etc.  *  *    In RELATION RTEs, the colnames in both alias and eref are indexed by  *    physical attribute number; this means there must be colname entries for  *    dropped columns.  When building an RTE we insert empty strings ("") for  *    dropped columns.  Note however that a stored rule may have nonempty  *    colnames for columns dropped since the rule was created (and for that  *    matter the colnames might be out of date due to column renamings).  *    The same comments apply to FUNCTION RTEs when a function's return type  *    is a named composite type.  *  *    In JOIN RTEs, the colnames in both alias and eref are one-to-one with  *    joinaliasvars entries.  A JOIN RTE will omit columns of its inputs when  *    those columns are known to be dropped at parse time.  Again, however,  *    a stored rule might contain entries for columns dropped since the rule  *    was created.  (This is only possible for columns not actually referenced  *    in the rule.)  When loading a stored rule, we replace the joinaliasvars  *    items for any such columns with null pointers.  (We can't simply delete  *    them from the joinaliasvars list, because that would affect the attnums  *    of Vars referencing the rest of the list.)  *  *    inh is true for relation references that should be expanded to include  *    inheritance children, if the rel has any.  This *must* be false for  *    RTEs other than RTE_RELATION entries.  *  *    inFromCl marks those range variables that are listed in the FROM clause.  *    It's false for RTEs that are added to a query behind the scenes, such  *    as the NEW and OLD variables for a rule, or the subqueries of a UNION.  *    This flag is not used anymore during parsing, since the parser now uses  *    a separate "namespace" data structure to control visibility, but it is  *    needed by ruleutils.c to determine whether RTEs should be shown in  *    decompiled queries.  *  *    requiredPerms and checkAsUser specify run-time access permissions  *    checks to be performed at query startup.  The user must have *all*  *    of the permissions that are OR'd together in requiredPerms (zero  *    indicates no permissions checking).  If checkAsUser is not zero,  *    then do the permissions checks using the access rights of that user,  *    not the current effective user ID.  (This allows rules to act as  *    setuid gateways.)  Permissions checks only apply to RELATION RTEs.  *  *    For SELECT/INSERT/UPDATE permissions, if the user doesn't have  *    table-wide permissions then it is sufficient to have the permissions  *    on all columns identified in selectedCols (for SELECT) and/or  *    insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may  *    have all 3).  selectedCols, insertedCols and updatedCols are bitmapsets,  *    which cannot have negative integer members, so we subtract  *    FirstLowInvalidHeapAttributeNumber from column numbers before storing  *    them in these fields.  A whole-row Var reference is represented by  *    setting the bit for InvalidAttrNumber.  *  *    securityQuals is a list of security barrier quals (boolean expressions),  *    to be tested in the listed order before returning a row from the  *    relation.  It is always NIL in parser output.  Entries are added by the  *    rewriter to implement security-barrier views and/or row-level security.  *    Note that the planner turns each boolean expression into an implicitly  *    AND'ed sublist, as is its usual habit with qualification expressions.  *--------------------  */ typedef enum RTEKind {     RTE_RELATION,               /* ordinary relation reference */     RTE_SUBQUERY,               /* subquery in FROM */     RTE_JOIN,                   /* join */     RTE_FUNCTION,               /* function in FROM */     RTE_TABLEFUNC,              /* TableFunc(.., column list) */     RTE_VALUES,                 /* VALUES (), (), ... */     RTE_CTE,                    /* common table expr (WITH list element) */     RTE_NAMEDTUPLESTORE         /* tuplestore, e.g. for AFTER triggers */ } RTEKind;  typedef struct RangeTblEntry {     NodeTag     type;      RTEKind     rtekind;        /* see above */      /*      * XXX the fields applicable to only some rte kinds should be merged into      * a union.  I didn't do this yet because the diffs would impact a lot of      * code that is being actively worked on.  FIXME someday.      */      /*      * Fields valid for a plain relation RTE (else zero):      *      * As a special case, RTE_NAMEDTUPLESTORE can also set relid to indicate      * that the tuple format of the tuplestore is the same as the referenced      * relation.  This allows plans referencing AFTER trigger transition      * tables to be invalidated if the underlying table is altered.      */     Oid         relid;          /* OID of the relation */     char        relkind;        /* relation kind (see pg_class.relkind) */     struct TableSampleClause *tablesample;  /* sampling info, or NULL */      /*      * Fields valid for a subquery RTE (else NULL):      */     Query      *subquery;       /* the sub-query */     bool        security_barrier;   /* is from security_barrier view? */      /*      * Fields valid for a join RTE (else NULL/zero):      *      * joinaliasvars is a list of (usually) Vars corresponding to the columns      * of the join result.  An alias Var referencing column K of the join      * result can be replaced by the K'th element of joinaliasvars --- but to      * simplify the task of reverse-listing aliases correctly, we do not do      * that until planning time.  In detail: an element of joinaliasvars can      * be a Var of one of the join's input relations, or such a Var with an      * implicit coercion to the join's output column type, or a COALESCE      * expression containing the two input column Vars (possibly coerced).      * Within a Query loaded from a stored rule, it is also possible for      * joinaliasvars items to be null pointers, which are placeholders for      * (necessarily unreferenced) columns dropped since the rule was made.      * Also, once planning begins, joinaliasvars items can be almost anything,      * as a result of subquery-flattening substitutions.      */     JoinType    jointype;       /* type of join */     List       *joinaliasvars;  /* list of alias-var expansions */      /*      * Fields valid for a function RTE (else NIL/zero):      *      * When funcordinality is true, the eref->colnames list includes an alias      * for the ordinality column.  The ordinality column is otherwise      * implicit, and must be accounted for "by hand" in places such as      * expandRTE().      */     List       *functions;      /* list of RangeTblFunction nodes */     bool        funcordinality; /* is this called WITH ORDINALITY? */      /*      * Fields valid for a TableFunc RTE (else NULL):      */     TableFunc  *tablefunc;      /*      * Fields valid for a values RTE (else NIL):      */     List       *values_lists;   /* list of expression lists */      /*      * Fields valid for a CTE RTE (else NULL/zero):      */     char       *ctename;        /* name of the WITH list item */     Index       ctelevelsup;    /* number of query levels up */     bool        self_reference; /* is this a recursive self-reference? */      /*      * Fields valid for table functions, values, CTE and ENR RTEs (else NIL):      *      * We need these for CTE RTEs so that the types of self-referential      * columns are well-defined.  For VALUES RTEs, storing these explicitly      * saves having to re-determine the info by scanning the values_lists. For      * ENRs, we store the types explicitly here (we could get the information      * from the catalogs if 'relid' was supplied, but we'd still need these      * for TupleDesc-based ENRs, so we might as well always store the type      * info here).      *      * For ENRs only, we have to consider the possibility of dropped columns.      * A dropped column is included in these lists, but it will have zeroes in      * all three lists (as well as an empty-string entry in eref).  Testing      * for zero coltype is the standard way to detect a dropped column.      */     List       *coltypes;       /* OID list of column type OIDs */     List       *coltypmods;     /* integer list of column typmods */     List       *colcollations;  /* OID list of column collation OIDs */      /*      * Fields valid for ENR RTEs (else NULL/zero):      */     char       *enrname;        /* name of ephemeral named relation */     double      enrtuples;      /* estimated or actual from caller */      /*      * Fields valid in all RTEs:      */     Alias      *alias;          /* user-written alias clause, if any */     Alias      *eref;           /* expanded reference names */     bool        lateral;        /* subquery, function, or values is LATERAL? */     bool        inh;            /* inheritance requested? */     bool        inFromCl;       /* present in FROM clause? */     AclMode     requiredPerms;  /* bitmask of required access permissions */     Oid         checkAsUser;    /* if valid, check access as this role */     Bitmapset  *selectedCols;   /* columns needing SELECT permission */     Bitmapset  *insertedCols;   /* columns needing INSERT permission */     Bitmapset  *updatedCols;    /* columns needing UPDATE permission */     List       *securityQuals;  /* security barrier quals to apply, if any */ } RangeTblEntry;

6、TargetEntry

 /*--------------------  * TargetEntry -  *     a target entry (used in query target lists)  *  * Strictly speaking, a TargetEntry isn't an expression node (since it can't  * be evaluated by ExecEvalExpr).  But we treat it as one anyway, since in  * very many places it's convenient to process a whole query targetlist as a  * single expression tree.  *  * In a SELECT's targetlist, resno should always be equal to the item's  * ordinal position (counting from 1).  However, in an INSERT or UPDATE  * targetlist, resno represents the attribute number of the destination  * column for the item; so there may be missing or out-of-order resnos.  * It is even legal to have duplicated resnos; consider  *      UPDATE table SET arraycol[1] = ..., arraycol[2] = ..., ...  * The two meanings come together in the executor, because the planner  * transforms INSERT/UPDATE tlists into a normalized form with exactly  * one entry for each column of the destination table.  Before that's  * happened, however, it is risky to assume that resno == position.  * Generally get_tle_by_resno() should be used rather than list_nth()  * to fetch tlist entries by resno, and only in SELECT should you assume  * that resno is a unique identifier.  *  * resname is required to represent the correct column name in non-resjunk  * entries of top-level SELECT targetlists, since it will be used as the  * column title sent to the frontend.  In most other contexts it is only  * a debugging aid, and may be wrong or even NULL.  (In particular, it may  * be wrong in a tlist from a stored rule, if the referenced column has been  * renamed by ALTER TABLE since the rule was made.  Also, the planner tends  * to store NULL rather than look up a valid name for tlist entries in  * non-toplevel plan nodes.)  In resjunk entries, resname should be either  * a specific system-generated name (such as "ctid") or NULL; anything else  * risks confusing ExecGetJunkAttribute!  *  * ressortgroupref is used in the representation of ORDER BY, GROUP BY, and  * DISTINCT items.  Targetlist entries with ressortgroupref=0 are not  * sort/group items.  If ressortgroupref>0, then this item is an ORDER BY,  * GROUP BY, and/or DISTINCT target value.  No two entries in a targetlist  * may have the same nonzero ressortgroupref --- but there is no particular  * meaning to the nonzero values, except as tags.  (For example, one must  * not assume that lower ressortgroupref means a more significant sort key.)  * The order of the associated SortGroupClause lists determine the semantics.  *  * resorigtbl/resorigcol identify the source of the column, if it is a  * simple reference to a column of a base table (or view).  If it is not  * a simple reference, these fields are zeroes.  *  * If resjunk is true then the column is a working column (such as a sort key)  * that should be removed from the final output of the query.  Resjunk columns  * must have resnos that cannot duplicate any regular column's resno.  Also  * note that there are places that assume resjunk columns come after non-junk  * columns.  *--------------------  */ typedef struct TargetEntry {     Expr        xpr;     Expr       *expr;           /* expression to evaluate */     AttrNumber  resno;          /* attribute number (see notes above) */     char       *resname;        /* name of the column (could be NULL) */     Index       ressortgroupref;    /* nonzero if referenced by a sort/group                                      * clause */     Oid         resorigtbl;     /* OID of column's source table */     AttrNumber  resorigcol;     /* column's number in source table */     bool        resjunk;        /* set to true to eliminate the attribute from                                  * final target list */ } TargetEntry;

7、全局变量定义

 bool        log_parser_stats = false; bool        log_planner_stats = false; bool        log_executor_stats = false; bool        log_statement_stats = false;    /* this is sort of all three above                                              * together */

依赖的函数
1、start_xact_command

 /*  * Convenience routines for starting/committing a single command.  */ static void start_xact_command(void) {     if (!xact_started)     {         StartTransactionCommand();//开启事务          xact_started = true;     }      /*      * Start statement timeout if necessary.  Note that this'll intentionally      * not reset the clock on an already started timeout, to avoid the timing      * overhead when start_xact_command() is invoked repeatedly, without an      * interceding finish_xact_command() (e.g. parse/bind/execute).  If that's      * not desired, the timeout has to be disabled explicitly.      */     enable_statement_timeout(); } /*  *  StartTransactionCommand  */ void StartTransactionCommand(void) {     TransactionState s = CurrentTransactionState;      switch (s->blockState)     {             /*              * if we aren't in a transaction block, we just do our usual start              * transaction.              */         case TBLOCK_DEFAULT:             StartTransaction();             s->blockState = TBLOCK_STARTED;             break;              /*              * We are somewhere in a transaction block or subtransaction and              * about to start a new command.  For now we do nothing, but              * someday we may do command-local resource initialization. (Note              * that any needed CommandCounterIncrement was done by the              * previous CommitTransactionCommand.)              */         case TBLOCK_INPROGRESS:         case TBLOCK_IMPLICIT_INPROGRESS:         case TBLOCK_SUBINPROGRESS:             break;              /*              * Here we are in a failed transaction block (one of the commands              * caused an abort) so we do nothing but remain in the abort              * state.  Eventually we will get a ROLLBACK command which will              * get us out of this state.  (It is up to other code to ensure              * that no commands other than ROLLBACK will be processed in these              * states.)              */         case TBLOCK_ABORT:         case TBLOCK_SUBABORT:             break;              /* These cases are invalid. */         case TBLOCK_STARTED:         case TBLOCK_BEGIN:         case TBLOCK_PARALLEL_INPROGRESS:         case TBLOCK_SUBBEGIN:         case TBLOCK_END:         case TBLOCK_SUBRELEASE:         case TBLOCK_SUBCOMMIT:         case TBLOCK_ABORT_END:         case TBLOCK_SUBABORT_END:         case TBLOCK_ABORT_PENDING:         case TBLOCK_SUBABORT_PENDING:         case TBLOCK_SUBRESTART:         case TBLOCK_SUBABORT_RESTART:         case TBLOCK_PREPARE:             elog(ERROR, "StartTransactionCommand: unexpected state %s",                  BlockStateAsString(s->blockState));             break;     }      /*      * We must switch to CurTransactionContext before returning. This is      * already done if we called StartTransaction, otherwise not.      */     Assert(CurTransactionContext != NULL);     MemoryContextSwitchTo(CurTransactionContext);//内存上下文切换至当前事务上下文 }

2、drop_unnamed_stmt

 /* Release any existing unnamed prepared statement */ static void drop_unnamed_stmt(void) {     /* paranoia to avoid a dangling pointer in case of error */     if (unnamed_stmt_psrc)     {         CachedPlanSource *psrc = unnamed_stmt_psrc;          unnamed_stmt_psrc = NULL;         DropCachedPlan(psrc);     } } /*  * If an unnamed prepared statement exists, it's stored here.  * We keep it separate from the hashtable kept by commands/prepare.c  * in order to reduce overhead for short-lived queries.  */ static CachedPlanSource *unnamed_stmt_psrc = NULL;

3、pg_parse_query

//执行语句解析,返回RawStmt  nodes(List) /*  * Do raw parsing (only).  *  * A list of parsetrees (RawStmt nodes) is returned, since there might be  * multiple commands in the given string.  *  * NOTE: for interactive queries, it is important to keep this routine  * separate from the analysis & rewrite stages.  Analysis and rewriting  * cannot be done in an aborted transaction, since they require access to  * database tables.  So, we rely on the raw parser to determine whether  * we've seen a COMMIT or ABORT command; when we are in abort state, other  * commands are not processed any further than the raw parse stage.  */ List * pg_parse_query(const char *query_string) {     List       *raw_parsetree_list;      TRACE_POSTGRESQL_QUERY_PARSE_START(query_string);      if (log_parser_stats)         ResetUsage();      raw_parsetree_list = raw_parser(query_string);      if (log_parser_stats)         ShowUsage("PARSER STATISTICS");  #ifdef COPY_PARSE_PLAN_TREES     /* Optional debugging check: pass raw parsetrees through copyObject() */     {         List       *new_list = copyObject(raw_parsetree_list);          /* This checks both copyObject() and the equal() routines... */         if (!equal(new_list, raw_parsetree_list))             elog(WARNING, "copyObject() failed to produce an equal raw parse tree");         else             raw_parsetree_list = new_list;     } #endif      TRACE_POSTGRESQL_QUERY_PARSE_DONE(query_string);      return raw_parsetree_list; }

4、raw_parser

//执行词法和语法分析,返回raw parse trees(List,其中的元素是RawStmt) /*  * raw_parser  *      Given a query in string form, do lexical and grammatical analysis.  *  * Returns a list of raw (un-analyzed) parse trees.  The immediate elements  * of the list are always RawStmt nodes.  */ List * raw_parser(const char *str) {     core_yyscan_t yyscanner;     base_yy_extra_type yyextra;     int         yyresult;      /* initialize the flex scanner */     yyscanner = scanner_init(str, &yyextra.core_yy_extra,                              ScanKeywords, NumScanKeywords);      /* base_yylex() only needs this much initialization */     yyextra.have_lookahead = false;      /* initialize the bison parser */     parser_init(&yyextra);      /* Parse! */     yyresult = base_yyparse(yyscanner);      /* Clean up (release memory) */     scanner_finish(yyscanner);      if (yyresult)               /* error */         return NIL;      return yyextra.parsetree; }

5、CreateCommandTag

//创建命令Tag//基本上,所有的PG命令类型都可以在这里找到 /*  * CreateCommandTag  *      utility to get a string representation of the command operation,  *      given either a raw (un-analyzed) parsetree, an analyzed Query,  *      or a PlannedStmt.  *  * This must handle all command types, but since the vast majority  * of 'em are utility commands, it seems sensible to keep it here.  *  * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.  * Also, the result must point at a true constant (permanent storage).  */ const char * CreateCommandTag(Node *parsetree) {     const char *tag;      switch (nodeTag(parsetree))     {             /* recurse if we're given a RawStmt */         case T_RawStmt:             tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);             break;              /* raw plannable queries */         case T_InsertStmt:             tag = "INSERT";             break;          case T_DeleteStmt:             tag = "DELETE";             break;          case T_UpdateStmt:             tag = "UPDATE";             break;          case T_SelectStmt:             tag = "SELECT";             break;              /* utility statements --- same whether raw or cooked */         case T_TransactionStmt:             {                 TransactionStmt *stmt = (TransactionStmt *) parsetree;                  switch (stmt->kind)                 {                     case TRANS_STMT_BEGIN:                         tag = "BEGIN";                         break;                      case TRANS_STMT_START:                         tag = "START TRANSACTION";                         break;                      case TRANS_STMT_COMMIT:                         tag = "COMMIT";                         break;                      case TRANS_STMT_ROLLBACK:                     case TRANS_STMT_ROLLBACK_TO:                         tag = "ROLLBACK";                         break;                      case TRANS_STMT_SAVEPOINT:                         tag = "SAVEPOINT";                         break;                      case TRANS_STMT_RELEASE:                         tag = "RELEASE";                         break;                      case TRANS_STMT_PREPARE:                         tag = "PREPARE TRANSACTION";                         break;                      case TRANS_STMT_COMMIT_PREPARED:                         tag = "COMMIT PREPARED";                         break;                      case TRANS_STMT_ROLLBACK_PREPARED:                         tag = "ROLLBACK PREPARED";                         break;                      default:                         tag = "???";                         break;                 }             }             break;          case T_DeclareCursorStmt:             tag = "DECLARE CURSOR";             break;          case T_ClosePortalStmt:             {                 ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;                  if (stmt->portalname == NULL)                     tag = "CLOSE CURSOR ALL";                 else                     tag = "CLOSE CURSOR";             }             break;          case T_FetchStmt:             {                 FetchStmt  *stmt = (FetchStmt *) parsetree;                  tag = (stmt->ismove) ? "MOVE" : "FETCH";             }             break;          case T_CreateDomainStmt:             tag = "CREATE DOMAIN";             break;          case T_CreateSchemaStmt:             tag = "CREATE SCHEMA";             break;          case T_CreateStmt:             tag = "CREATE TABLE";             break;          case T_CreateTableSpaceStmt:             tag = "CREATE TABLESPACE";             break;          case T_DropTableSpaceStmt:             tag = "DROP TABLESPACE";             break;          case T_AlterTableSpaceOptionsStmt:             tag = "ALTER TABLESPACE";             break;          case T_CreateExtensionStmt:             tag = "CREATE EXTENSION";             break;          case T_AlterExtensionStmt:             tag = "ALTER EXTENSION";             break;          case T_AlterExtensionContentsStmt:             tag = "ALTER EXTENSION";             break;          case T_CreateFdwStmt:             tag = "CREATE FOREIGN DATA WRAPPER";             break;          case T_AlterFdwStmt:             tag = "ALTER FOREIGN DATA WRAPPER";             break;          case T_CreateForeignServerStmt:             tag = "CREATE SERVER";             break;          case T_AlterForeignServerStmt:             tag = "ALTER SERVER";             break;          case T_CreateUserMappingStmt:             tag = "CREATE USER MAPPING";             break;          case T_AlterUserMappingStmt:             tag = "ALTER USER MAPPING";             break;          case T_DropUserMappingStmt:             tag = "DROP USER MAPPING";             break;          case T_CreateForeignTableStmt:             tag = "CREATE FOREIGN TABLE";             break;          case T_ImportForeignSchemaStmt:             tag = "IMPORT FOREIGN SCHEMA";             break;          case T_DropStmt:             switch (((DropStmt *) parsetree)->removeType)             {                 case OBJECT_TABLE:                     tag = "DROP TABLE";                     break;                 case OBJECT_SEQUENCE:                     tag = "DROP SEQUENCE";                     break;                 case OBJECT_VIEW:                     tag = "DROP VIEW";                     break;                 case OBJECT_MATVIEW:                     tag = "DROP MATERIALIZED VIEW";                     break;                 case OBJECT_INDEX:                     tag = "DROP INDEX";                     break;                 case OBJECT_TYPE:                     tag = "DROP TYPE";                     break;                 case OBJECT_DOMAIN:                     tag = "DROP DOMAIN";                     break;                 case OBJECT_COLLATION:                     tag = "DROP COLLATION";                     break;                 case OBJECT_CONVERSION:                     tag = "DROP CONVERSION";                     break;                 case OBJECT_SCHEMA:                     tag = "DROP SCHEMA";                     break;                 case OBJECT_TSPARSER:                     tag = "DROP TEXT SEARCH PARSER";                     break;                 case OBJECT_TSDICTIONARY:                     tag = "DROP TEXT SEARCH DICTIONARY";                     break;                 case OBJECT_TSTEMPLATE:                     tag = "DROP TEXT SEARCH TEMPLATE";                     break;                 case OBJECT_TSCONFIGURATION:                     tag = "DROP TEXT SEARCH CONFIGURATION";                     break;                 case OBJECT_FOREIGN_TABLE:                     tag = "DROP FOREIGN TABLE";                     break;                 case OBJECT_EXTENSION:                     tag = "DROP EXTENSION";                     break;                 case OBJECT_FUNCTION:                     tag = "DROP FUNCTION";                     break;                 case OBJECT_PROCEDURE:                     tag = "DROP PROCEDURE";                     break;                 case OBJECT_ROUTINE:                     tag = "DROP ROUTINE";                     break;                 case OBJECT_AGGREGATE:                     tag = "DROP AGGREGATE";                     break;                 case OBJECT_OPERATOR:                     tag = "DROP OPERATOR";                     break;                 case OBJECT_LANGUAGE:                     tag = "DROP LANGUAGE";                     break;                 case OBJECT_CAST:                     tag = "DROP CAST";                     break;                 case OBJECT_TRIGGER:                     tag = "DROP TRIGGER";                     break;                 case OBJECT_EVENT_TRIGGER:                     tag = "DROP EVENT TRIGGER";                     break;                 case OBJECT_RULE:                     tag = "DROP RULE";                     break;                 case OBJECT_FDW:                     tag = "DROP FOREIGN DATA WRAPPER";                     break;                 case OBJECT_FOREIGN_SERVER:                     tag = "DROP SERVER";                     break;                 case OBJECT_OPCLASS:                     tag = "DROP OPERATOR CLASS";                     break;                 case OBJECT_OPFAMILY:                     tag = "DROP OPERATOR FAMILY";                     break;                 case OBJECT_POLICY:                     tag = "DROP POLICY";                     break;                 case OBJECT_TRANSFORM:                     tag = "DROP TRANSFORM";                     break;                 case OBJECT_ACCESS_METHOD:                     tag = "DROP ACCESS METHOD";                     break;                 case OBJECT_PUBLICATION:                     tag = "DROP PUBLICATION";                     break;                 case OBJECT_STATISTIC_EXT:                     tag = "DROP STATISTICS";                     break;                 default:                     tag = "???";             }             break;          case T_TruncateStmt:             tag = "TRUNCATE TABLE";             break;          case T_CommentStmt:             tag = "COMMENT";             break;          case T_SecLabelStmt:             tag = "SECURITY LABEL";             break;          case T_CopyStmt:             tag = "COPY";             break;          case T_RenameStmt:             tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType);             break;          case T_AlterObjectDependsStmt:             tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType);             break;          case T_AlterObjectSchemaStmt:             tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType);             break;          case T_AlterOwnerStmt:             tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType);             break;          case T_AlterTableMoveAllStmt:             tag = AlterObjectTypeCommandTag(((AlterTableMoveAllStmt *) parsetree)->objtype);             break;          case T_AlterTableStmt:             tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind);             break;          case T_AlterDomainStmt:             tag = "ALTER DOMAIN";             break;          case T_AlterFunctionStmt:             switch (((AlterFunctionStmt *) parsetree)->objtype)             {                 case OBJECT_FUNCTION:                     tag = "ALTER FUNCTION";                     break;                 case OBJECT_PROCEDURE:                     tag = "ALTER PROCEDURE";                     break;                 case OBJECT_ROUTINE:                     tag = "ALTER ROUTINE";                     break;                 default:                     tag = "???";             }             break;          case T_GrantStmt:             {                 GrantStmt  *stmt = (GrantStmt *) parsetree;                  tag = (stmt->is_grant) ? "GRANT" : "REVOKE";             }             break;          case T_GrantRoleStmt:             {                 GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;                  tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";             }             break;          case T_AlterDefaultPrivilegesStmt:             tag = "ALTER DEFAULT PRIVILEGES";             break;          case T_DefineStmt:             switch (((DefineStmt *) parsetree)->kind)             {                 case OBJECT_AGGREGATE:                     tag = "CREATE AGGREGATE";                     break;                 case OBJECT_OPERATOR:                     tag = "CREATE OPERATOR";                     break;                 case OBJECT_TYPE:                     tag = "CREATE TYPE";                     break;                 case OBJECT_TSPARSER:                     tag = "CREATE TEXT SEARCH PARSER";                     break;                 case OBJECT_TSDICTIONARY:                     tag = "CREATE TEXT SEARCH DICTIONARY";                     break;                 case OBJECT_TSTEMPLATE:                     tag = "CREATE TEXT SEARCH TEMPLATE";                     break;                 case OBJECT_TSCONFIGURATION:                     tag = "CREATE TEXT SEARCH CONFIGURATION";                     break;                 case OBJECT_COLLATION:                     tag = "CREATE COLLATION";                     break;                 case OBJECT_ACCESS_METHOD:                     tag = "CREATE ACCESS METHOD";                     break;                 default:                     tag = "???";             }             break;          case T_CompositeTypeStmt:             tag = "CREATE TYPE";             break;          case T_CreateEnumStmt:             tag = "CREATE TYPE";             break;          case T_CreateRangeStmt:             tag = "CREATE TYPE";             break;          case T_AlterEnumStmt:             tag = "ALTER TYPE";             break;          case T_ViewStmt:             tag = "CREATE VIEW";             break;          case T_CreateFunctionStmt:             if (((CreateFunctionStmt *) parsetree)->is_procedure)                 tag = "CREATE PROCEDURE";             else                 tag = "CREATE FUNCTION";             break;          case T_IndexStmt:             tag = "CREATE INDEX";             break;          case T_RuleStmt:             tag = "CREATE RULE";             break;          case T_CreateSeqStmt:             tag = "CREATE SEQUENCE";             break;          case T_AlterSeqStmt:             tag = "ALTER SEQUENCE";             break;          case T_DoStmt:             tag = "DO";             break;          case T_CreatedbStmt:             tag = "CREATE DATABASE";             break;          case T_AlterDatabaseStmt:             tag = "ALTER DATABASE";             break;          case T_AlterDatabaseSetStmt:             tag = "ALTER DATABASE";             break;          case T_DropdbStmt:             tag = "DROP DATABASE";             break;          case T_NotifyStmt:             tag = "NOTIFY";             break;          case T_ListenStmt:             tag = "LISTEN";             break;          case T_UnlistenStmt:             tag = "UNLISTEN";             break;          case T_LoadStmt:             tag = "LOAD";             break;          case T_CallStmt:             tag = "CALL";             break;          case T_ClusterStmt:             tag = "CLUSTER";             break;          case T_VacuumStmt:             if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM)                 tag = "VACUUM";             else                 tag = "ANALYZE";             break;          case T_ExplainStmt:             tag = "EXPLAIN";             break;          case T_CreateTableAsStmt:             switch (((CreateTableAsStmt *) parsetree)->relkind)             {                 case OBJECT_TABLE:                     if (((CreateTableAsStmt *) parsetree)->is_select_into)                         tag = "SELECT INTO";                     else                         tag = "CREATE TABLE AS";                     break;                 case OBJECT_MATVIEW:                     tag = "CREATE MATERIALIZED VIEW";                     break;                 default:                     tag = "???";             }             break;          case T_RefreshMatViewStmt:             tag = "REFRESH MATERIALIZED VIEW";             break;          case T_AlterSystemStmt:             tag = "ALTER SYSTEM";             break;          case T_VariableSetStmt:             switch (((VariableSetStmt *) parsetree)->kind)             {                 case VAR_SET_VALUE:                 case VAR_SET_CURRENT:                 case VAR_SET_DEFAULT:                 case VAR_SET_MULTI:                     tag = "SET";                     break;                 case VAR_RESET:                 case VAR_RESET_ALL:                     tag = "RESET";                     break;                 default:                     tag = "???";             }             break;          case T_VariableShowStmt:             tag = "SHOW";             break;          case T_DiscardStmt:             switch (((DiscardStmt *) parsetree)->target)             {                 case DISCARD_ALL:                     tag = "DISCARD ALL";                     break;                 case DISCARD_PLANS:                     tag = "DISCARD PLANS";                     break;                 case DISCARD_TEMP:                     tag = "DISCARD TEMP";                     break;                 case DISCARD_SEQUENCES:                     tag = "DISCARD SEQUENCES";                     break;                 default:                     tag = "???";             }             break;          case T_CreateTransformStmt:             tag = "CREATE TRANSFORM";             break;          case T_CreateTrigStmt:             tag = "CREATE TRIGGER";             break;          case T_CreateEventTrigStmt:             tag = "CREATE EVENT TRIGGER";             break;          case T_AlterEventTrigStmt:             tag = "ALTER EVENT TRIGGER";             break;          case T_CreatePLangStmt:             tag = "CREATE LANGUAGE";             break;          case T_CreateRoleStmt:             tag = "CREATE ROLE";             break;          case T_AlterRoleStmt:             tag = "ALTER ROLE";             break;          case T_AlterRoleSetStmt:             tag = "ALTER ROLE";             break;          case T_DropRoleStmt:             tag = "DROP ROLE";             break;          case T_DropOwnedStmt:             tag = "DROP OWNED";             break;          case T_ReassignOwnedStmt:             tag = "REASSIGN OWNED";             break;          case T_LockStmt:             tag = "LOCK TABLE";             break;          case T_ConstraintsSetStmt:             tag = "SET CONSTRAINTS";             break;          case T_CheckPointStmt:             tag = "CHECKPOINT";             break;          case T_ReindexStmt:             tag = "REINDEX";             break;          case T_CreateConversionStmt:             tag = "CREATE CONVERSION";             break;          case T_CreateCastStmt:             tag = "CREATE CAST";             break;          case T_CreateOpClassStmt:             tag = "CREATE OPERATOR CLASS";             break;          case T_CreateOpFamilyStmt:             tag = "CREATE OPERATOR FAMILY";             break;          case T_AlterOpFamilyStmt:             tag = "ALTER OPERATOR FAMILY";             break;          case T_AlterOperatorStmt:             tag = "ALTER OPERATOR";             break;          case T_AlterTSDictionaryStmt:             tag = "ALTER TEXT SEARCH DICTIONARY";             break;          case T_AlterTSConfigurationStmt:             tag = "ALTER TEXT SEARCH CONFIGURATION";             break;          case T_CreatePolicyStmt:             tag = "CREATE POLICY";             break;          case T_AlterPolicyStmt:             tag = "ALTER POLICY";             break;          case T_CreateAmStmt:             tag = "CREATE ACCESS METHOD";             break;          case T_CreatePublicationStmt:             tag = "CREATE PUBLICATION";             break;          case T_AlterPublicationStmt:             tag = "ALTER PUBLICATION";             break;          case T_CreateSubscriptionStmt:             tag = "CREATE SUBSCRIPTION";             break;          case T_AlterSubscriptionStmt:             tag = "ALTER SUBSCRIPTION";             break;          case T_DropSubscriptionStmt:             tag = "DROP SUBSCRIPTION";             break;          case T_AlterCollationStmt:             tag = "ALTER COLLATION";             break;          case T_PrepareStmt:             tag = "PREPARE";             break;          case T_ExecuteStmt:             tag = "EXECUTE";             break;          case T_CreateStatsStmt:             tag = "CREATE STATISTICS";             break;          case T_DeallocateStmt:             {                 DeallocateStmt *stmt = (DeallocateStmt *) parsetree;                  if (stmt->name == NULL)                     tag = "DEALLOCATE ALL";                 else                     tag = "DEALLOCATE";             }             break;              /* already-planned queries */         case T_PlannedStmt:             {                 PlannedStmt *stmt = (PlannedStmt *) parsetree;                  switch (stmt->commandType)                 {                     case CMD_SELECT:                          /*                          * We take a little extra care here so that the result                          * will be useful for complaints about read-only                          * statements                          */                         if (stmt->rowMarks != NIL)                         {                             /* not 100% but probably close enough */                             switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)                             {                                 case LCS_FORKEYSHARE:                                     tag = "SELECT FOR KEY SHARE";                                     break;                                 case LCS_FORSHARE:                                     tag = "SELECT FOR SHARE";                                     break;                                 case LCS_FORNOKEYUPDATE:                                     tag = "SELECT FOR NO KEY UPDATE";                                     break;                                 case LCS_FORUPDATE:                                     tag = "SELECT FOR UPDATE";                                     break;                                 default:                                     tag = "SELECT";                                     break;                             }                         }                         else                             tag = "SELECT";                         break;                     case CMD_UPDATE:                         tag = "UPDATE";                         break;                     case CMD_INSERT:                         tag = "INSERT";                         break;                     case CMD_DELETE:                         tag = "DELETE";                         break;                     case CMD_UTILITY:                         tag = CreateCommandTag(stmt->utilityStmt);                         break;                     default:                         elog(WARNING, "unrecognized commandType: %d",                              (int) stmt->commandType);                         tag = "???";                         break;                 }             }             break;              /* parsed-and-rewritten-but-not-planned queries */         case T_Query:             {                 Query      *stmt = (Query *) parsetree;                  switch (stmt->commandType)                 {                     case CMD_SELECT:                          /*                          * We take a little extra care here so that the result                          * will be useful for complaints about read-only                          * statements                          */                         if (stmt->rowMarks != NIL)                         {                             /* not 100% but probably close enough */                             switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)                             {                                 case LCS_FORKEYSHARE:                                     tag = "SELECT FOR KEY SHARE";                                     break;                                 case LCS_FORSHARE:                                     tag = "SELECT FOR SHARE";                                     break;                                 case LCS_FORNOKEYUPDATE:                                     tag = "SELECT FOR NO KEY UPDATE";                                     break;                                 case LCS_FORUPDATE:                                     tag = "SELECT FOR UPDATE";                                     break;                                 default:                                     tag = "???";                                     break;                             }                         }                         else                             tag = "SELECT";                         break;                     case CMD_UPDATE:                         tag = "UPDATE";                         break;                     case CMD_INSERT:                         tag = "INSERT";                         break;                     case CMD_DELETE:                         tag = "DELETE";                         break;                     case CMD_UTILITY:                         tag = CreateCommandTag(stmt->utilityStmt);                         break;                     default:                         elog(WARNING, "unrecognized commandType: %d",                              (int) stmt->commandType);                         tag = "???";                         break;                 }             }             break;          default:             elog(WARNING, "unrecognized node type: %d",                  (int) nodeTag(parsetree));             tag = "???";             break;     }      return tag; }

6、BeginCommand

 /* ----------------  *      BeginCommand - initialize the destination at start of command  * ----------------  */ void BeginCommand(const char *commandTag, CommandDest dest) {     /* Nothing to do at present */ }

7、analyze_requires_snapshot

//是否需要快照?//增删改查均需要 /*  * analyze_requires_snapshot  *      Returns true if a snapshot must be set before doing parse analysis  *      on the given raw parse tree.  *  * Classification here should match transformStmt().  */ bool analyze_requires_snapshot(RawStmt *parseTree) {     bool        result;      switch (nodeTag(parseTree->stmt))     {             /*              * Optimizable statements              */         case T_InsertStmt:         case T_DeleteStmt:         case T_UpdateStmt:         case T_SelectStmt:             result = true;             break;              /*              * Special cases              */         case T_DeclareCursorStmt:         case T_ExplainStmt:         case T_CreateTableAsStmt:             /* yes, because we must analyze the contained statement */             result = true;             break;          default:             /* other utility statements don't have any real parse analysis */             result = false;             break;     }      return result; }

8、pg_analyze_and_rewrite

 /*  * Given a raw parsetree (gram.y output), and optionally information about  * types of parameter symbols ($n), perform parse analysis and rule rewriting.  *  * A list of Query nodes is returned, since either the analyzer or the  * rewriter might expand one query to several.  *  * NOTE: for reasons mentioned above, this must be separate from raw parsing.  */ List * pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,                        Oid *paramTypes, int numParams,                        QueryEnvironment *queryEnv) {     Query      *query;     List       *querytree_list;      TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);      /*      * (1) Perform parse analysis.      */     if (log_parser_stats)         ResetUsage();      query = parse_analyze(parsetree, query_string, paramTypes, numParams,                           queryEnv);//解析&分析      if (log_parser_stats)         ShowUsage("PARSE ANALYSIS STATISTICS");      /*      * (2) Rewrite the queries, as necessary      */     querytree_list = pg_rewrite_query(query);//查询重写      TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string);      return querytree_list; }  /*  * parse_analyze  *      Analyze a raw parse tree and transform it to Query form.  *  * Optionally, information about $n parameter types can be supplied.  * References to $n indexes not defined by paramTypes[] are disallowed.  *  * The result is a Query node.  Optimizable statements require considerable  * transformation, while utility-type statements are simply hung off  * a dummy CMD_UTILITY Query node.  */ Query * parse_analyze(RawStmt *parseTree, const char *sourceText,               Oid *paramTypes, int numParams,               QueryEnvironment *queryEnv) {     ParseState *pstate = make_parsestate(NULL);     Query      *query;      Assert(sourceText != NULL); /* required as of 8.4 */      pstate->p_sourcetext = sourceText;      if (numParams > 0)         parse_fixed_parameters(pstate, paramTypes, numParams);      pstate->p_queryEnv = queryEnv;      query = transformTopLevelStmt(pstate, parseTree);      if (post_parse_analyze_hook)         (*post_parse_analyze_hook) (pstate, query);      free_parsestate(pstate);      return query; } /*  * make_parsestate  *      Allocate and initialize a new ParseState.  *  * Caller should eventually release the ParseState via free_parsestate().  */ ParseState * make_parsestate(ParseState *parentParseState) {     ParseState *pstate;      pstate = palloc0(sizeof(ParseState));      pstate->parentParseState = parentParseState;      /* Fill in fields that don't start at null/false/zero */     pstate->p_next_resno = 1;     pstate->p_resolve_unknowns = true;      if (parentParseState)     {         pstate->p_sourcetext = parentParseState->p_sourcetext;         /* all hooks are copied from parent */         pstate->p_pre_columnref_hook = parentParseState->p_pre_columnref_hook;         pstate->p_post_columnref_hook = parentParseState->p_post_columnref_hook;         pstate->p_paramref_hook = parentParseState->p_paramref_hook;         pstate->p_coerce_param_hook = parentParseState->p_coerce_param_hook;         pstate->p_ref_hook_state = parentParseState->p_ref_hook_state;         /* query environment stays in context for the whole parse analysis */         pstate->p_queryEnv = parentParseState->p_queryEnv;     }      return pstate; } /*  * transformTopLevelStmt -  *    transform a Parse tree into a Query tree.  *  * This function is just responsible for transferring statement location data  * from the RawStmt into the finished Query.  */ Query * transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree) {     Query      *result;      /* We're at top level, so allow SELECT INTO */     result = transformOptionalSelectInto(pstate, parseTree->stmt);      result->stmt_location = parseTree->stmt_location;     result->stmt_len = parseTree->stmt_len;      return result; }/*  * transformOptionalSelectInto -  *    If SELECT has INTO, convert it to CREATE TABLE AS.  *  * The only thing we do here that we don't do in transformStmt() is to  * convert SELECT ... INTO into CREATE TABLE AS.  Since utility statements  * aren't allowed within larger statements, this is only allowed at the top  * of the parse tree, and so we only try it before entering the recursive  * transformStmt() processing.  */ static Query * transformOptionalSelectInto(ParseState *pstate, Node *parseTree) {     if (IsA(parseTree, SelectStmt))     {         SelectStmt *stmt = (SelectStmt *) parseTree;          /* If it's a set-operation tree, drill down to leftmost SelectStmt */         while (stmt && stmt->op != SETOP_NONE)             stmt = stmt->larg;         Assert(stmt && IsA(stmt, SelectStmt) &&stmt->larg == NULL);          if (stmt->intoClause)         {             CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);              ctas->query = parseTree;             ctas->into = stmt->intoClause;             ctas->relkind = OBJECT_TABLE;             ctas->is_select_into = true;              /*              * Remove the intoClause from the SelectStmt.  This makes it safe              * for transformSelectStmt to complain if it finds intoClause set              * (implying that the INTO appeared in a disallowed place).              */             stmt->intoClause = NULL;              parseTree = (Node *) ctas;         }     }      return transformStmt(pstate, parseTree); }  /*  * transformStmt -  *    recursively transform a Parse tree into a Query tree.  */ Query * transformStmt(ParseState *pstate, Node *parseTree) {     Query      *result;      /*      * We apply RAW_EXPRESSION_COVERAGE_TEST testing to basic DML statements;      * we can't just run it on everything because raw_expression_tree_walker()      * doesn't claim to handle utility statements.      */ #ifdef RAW_EXPRESSION_COVERAGE_TEST     switch (nodeTag(parseTree))     {         case T_SelectStmt:         case T_InsertStmt:         case T_UpdateStmt:         case T_DeleteStmt:             (void) test_raw_expression_coverage(parseTree, NULL);             break;         default:             break;     } #endif                          /* RAW_EXPRESSION_COVERAGE_TEST */      switch (nodeTag(parseTree))     {             /*              * Optimizable statements              */         case T_InsertStmt:             result = transformInsertStmt(pstate, (InsertStmt *) parseTree);             break;          case T_DeleteStmt:             result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);             break;          case T_UpdateStmt:             result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);             break;          case T_SelectStmt:             {                 SelectStmt *n = (SelectStmt *) parseTree;                  if (n->valuesLists)                     result = transformValuesClause(pstate, n);                 else if (n->op == SETOP_NONE)                     result = transformSelectStmt(pstate, n);                 else                     result = transformSetOperationStmt(pstate, n);             }             break;              /*              * Special cases              */         case T_DeclareCursorStmt:             result = transformDeclareCursorStmt(pstate,                                                 (DeclareCursorStmt *) parseTree);             break;          case T_ExplainStmt:             result = transformExplainStmt(pstate,                                           (ExplainStmt *) parseTree);             break;          case T_CreateTableAsStmt:             result = transformCreateTableAsStmt(pstate,                                                 (CreateTableAsStmt *) parseTree);             break;          case T_CallStmt:             result = transformCallStmt(pstate,                                        (CallStmt *) parseTree);             break;          default:              /*              * other statements don't require any transformation; just return              * the original parsetree with a Query node plastered on top.              */             result = makeNode(Query);             result->commandType = CMD_UTILITY;             result->utilityStmt = (Node *) parseTree;             break;     }      /* Mark as original query until we learn differently */     result->querySource = QSRC_ORIGINAL;     result->canSetTag = true;      return result; } /*  * transformInsertStmt -  *    transform an Insert Statement  */ static Query * transformInsertStmt(ParseState *pstate, InsertStmt *stmt) {     Query      *qry = makeNode(Query);     SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;     List       *exprList = NIL;     bool        isGeneralSelect;     List       *sub_rtable;     List       *sub_namespace;     List       *icolumns;     List       *attrnos;     RangeTblEntry *rte;     RangeTblRef *rtr;     ListCell   *icols;     ListCell   *attnos;     ListCell   *lc;     bool        isOnConflictUpdate;     AclMode     targetPerms;      /* There can't be any outer WITH to worry about */     Assert(pstate->p_ctenamespace == NIL);      qry->commandType = CMD_INSERT;     pstate->p_is_insert = true;      /* process the WITH clause independently of all else */     if (stmt->withClause)     {         qry->hasRecursive = stmt->withClause->recursive;         qry->cteList = transformWithClause(pstate, stmt->withClause);         qry->hasModifyingCTE = pstate->p_hasModifyingCTE;     }      qry->override = stmt->override;      isOnConflictUpdate = (stmt->onConflictClause &&                           stmt->onConflictClause->action == ONCONFLICT_UPDATE);      /*      * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),      * VALUES list, or general SELECT input.  We special-case VALUES, both for      * efficiency and so we can handle DEFAULT specifications.      *      * The grammar allows attaching ORDER BY, LIMIT, FOR UPDATE, or WITH to a      * VALUES clause.  If we have any of those, treat it as a general SELECT;      * so it will work, but you can't use DEFAULT items together with those.      */     isGeneralSelect = (selectStmt && (selectStmt->valuesLists == NIL ||                                       selectStmt->sortClause != NIL ||                                       selectStmt->limitOffset != NULL ||                                       selectStmt->limitCount != NULL ||                                       selectStmt->lockingClause != NIL ||                                       selectStmt->withClause != NULL));      /*      * If a non-nil rangetable/namespace was passed in, and we are doing      * INSERT/SELECT, arrange to pass the rangetable/namespace down to the      * SELECT.  This can only happen if we are inside a CREATE RULE, and in      * that case we want the rule's OLD and NEW rtable entries to appear as      * part of the SELECT's rtable, not as outer references for it.  (Kluge!)      * The SELECT's joinlist is not affected however.  We must do this before      * adding the target table to the INSERT's rtable.      */     if (isGeneralSelect)     {         sub_rtable = pstate->p_rtable;         pstate->p_rtable = NIL;         sub_namespace = pstate->p_namespace;         pstate->p_namespace = NIL;     }     else     {         sub_rtable = NIL;       /* not used, but keep compiler quiet */         sub_namespace = NIL;     }      /*      * Must get write lock on INSERT target table before scanning SELECT, else      * we will grab the wrong kind of initial lock if the target table is also      * mentioned in the SELECT part.  Note that the target table is not added      * to the joinlist or namespace.      */     targetPerms = ACL_INSERT;     if (isOnConflictUpdate)         targetPerms |= ACL_UPDATE;     qry->resultRelation = setTargetTable(pstate, stmt->relation,                                          false, false, targetPerms);      /* Validate stmt->cols list, or build default list if no list given */     icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);     Assert(list_length(icolumns) == list_length(attrnos));      /*      * Determine which variant of INSERT we have.      */     if (selectStmt == NULL)     {         /*          * We have INSERT ... DEFAULT VALUES.  We can handle this case by          * emitting an empty targetlist --- all columns will be defaulted when          * the planner expands the targetlist.          */         exprList = NIL;     }     else if (isGeneralSelect)     {         /*          * We make the sub-pstate a child of the outer pstate so that it can          * see any Param definitions supplied from above.  Since the outer          * pstate's rtable and namespace are presently empty, there are no          * side-effects of exposing names the sub-SELECT shouldn't be able to          * see.          */         ParseState *sub_pstate = make_parsestate(pstate);         Query      *selectQuery;          /*          * Process the source SELECT.          *          * It is important that this be handled just like a standalone SELECT;          * otherwise the behavior of SELECT within INSERT might be different          * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had          * bugs of just that nature...)          *          * The sole exception is that we prevent resolving unknown-type          * outputs as TEXT.  This does not change the semantics since if the          * column type matters semantically, it would have been resolved to          * something else anyway.  Doing this lets us resolve such outputs as          * the target column's type, which we handle below.          */         sub_pstate->p_rtable = sub_rtable;         sub_pstate->p_joinexprs = NIL;  /* sub_rtable has no joins */         sub_pstate->p_namespace = sub_namespace;         sub_pstate->p_resolve_unknowns = false;          selectQuery = transformStmt(sub_pstate, stmt->selectStmt);          free_parsestate(sub_pstate);          /* The grammar should have produced a SELECT */         if (!IsA(selectQuery, Query) ||             selectQuery->commandType != CMD_SELECT)             elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");          /*          * Make the source be a subquery in the INSERT's rangetable, and add          * it to the INSERT's joinlist.          */         rte = addRangeTableEntryForSubquery(pstate,                                             selectQuery,                                             makeAlias("*SELECT*", NIL),                                             false,                                             false);         rtr = makeNode(RangeTblRef);         /* assume new rte is at end */         rtr->rtindex = list_length(pstate->p_rtable);         Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));         pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);          /*----------          * Generate an expression list for the INSERT that selects all the          * non-resjunk columns from the subquery.  (INSERT's tlist must be          * separate from the subquery's tlist because we may add columns,          * insert datatype coercions, etc.)          *          * HACK: unknown-type constants and params in the SELECT's targetlist          * are copied up as-is rather than being referenced as subquery          * outputs.  This is to ensure that when we try to coerce them to          * the target column's datatype, the right things happen (see          * special cases in coerce_type).  Otherwise, this fails:          *      INSERT INTO foo SELECT 'bar', ... FROM baz          *----------          */         exprList = NIL;         foreach(lc, selectQuery->targetList)         {             TargetEntry *tle = (TargetEntry *) lfirst(lc);             Expr       *expr;              if (tle->resjunk)                 continue;             if (tle->expr &&                 (IsA(tle->expr, Const) ||IsA(tle->expr, Param)) &&                 exprType((Node *) tle->expr) == UNKNOWNOID)                 expr = tle->expr;             else             {                 Var        *var = makeVarFromTargetEntry(rtr->rtindex, tle);                  var->location = exprLocation((Node *) tle->expr);                 expr = (Expr *) var;             }             exprList = lappend(exprList, expr);         }          /* Prepare row for assignment to target table */         exprList = transformInsertRow(pstate, exprList,                                       stmt->cols,                                       icolumns, attrnos,                                       false);     }     else if (list_length(selectStmt->valuesLists) > 1)     {         /*          * Process INSERT ... VALUES with multiple VALUES sublists. We          * generate a VALUES RTE holding the transformed expression lists, and          * build up a targetlist containing Vars that reference the VALUES          * RTE.          */         List       *exprsLists = NIL;         List       *coltypes = NIL;         List       *coltypmods = NIL;         List       *colcollations = NIL;         int         sublist_length = -1;         bool        lateral = false;          Assert(selectStmt->intoClause == NULL);          foreach(lc, selectStmt->valuesLists)         {             List       *sublist = (List *) lfirst(lc);              /*              * Do basic expression transformation (same as a ROW() expr, but              * allow SetToDefault at top level)              */             sublist = transformExpressionList(pstate, sublist,                                               EXPR_KIND_VALUES, true);              /*              * All the sublists must be the same length, *after*              * transformation (which might expand '*' into multiple items).              * The VALUES RTE can't handle anything different.              */             if (sublist_length < 0)             {                 /* Remember post-transformation length of first sublist */                 sublist_length = list_length(sublist);             }             else if (sublist_length != list_length(sublist))             {                 ereport(ERROR,                         (errcode(ERRCODE_SYNTAX_ERROR),                          errmsg("VALUES lists must all be the same length"),                          parser_errposition(pstate,                                             exprLocation((Node *) sublist))));             }              /*              * Prepare row for assignment to target table.  We process any              * indirection on the target column specs normally but then strip              * off the resulting field/array assignment nodes, since we don't              * want the parsed statement to contain copies of those in each              * VALUES row.  (It's annoying to have to transform the              * indirection specs over and over like this, but avoiding it              * would take some really messy refactoring of              * transformAssignmentIndirection.)              */             sublist = transformInsertRow(pstate, sublist,                                          stmt->cols,                                          icolumns, attrnos,                                          true);              /*              * We must assign collations now because assign_query_collations              * doesn't process rangetable entries.  We just assign all the              * collations independently in each row, and don't worry about              * whether they are consistent vertically.  The outer INSERT query              * isn't going to care about the collations of the VALUES columns,              * so it's not worth the effort to identify a common collation for              * each one here.  (But note this does have one user-visible              * consequence: INSERT ... VALUES won't complain about conflicting              * explicit COLLATEs in a column, whereas the same VALUES              * construct in another context would complain.)              */             assign_list_collations(pstate, sublist);              exprsLists = lappend(exprsLists, sublist);         }          /*          * Construct column type/typmod/collation lists for the VALUES RTE.          * Every expression in each column has been coerced to the type/typmod          * of the corresponding target column or subfield, so it's sufficient          * to look at the exprType/exprTypmod of the first row.  We don't care          * about the collation labeling, so just fill in InvalidOid for that.          */         foreach(lc, (List *) linitial(exprsLists))         {             Node       *val = (Node *) lfirst(lc);              coltypes = lappend_oid(coltypes, exprType(val));             coltypmods = lappend_int(coltypmods, exprTypmod(val));             colcollations = lappend_oid(colcollations, InvalidOid);         }          /*          * Ordinarily there can't be any current-level Vars in the expression          * lists, because the namespace was empty ... but if we're inside          * CREATE RULE, then NEW/OLD references might appear.  In that case we          * have to mark the VALUES RTE as LATERAL.          */         if (list_length(pstate->p_rtable) != 1 &&             contain_vars_of_level((Node *) exprsLists, 0))             lateral = true;          /*          * Generate the VALUES RTE          */         rte = addRangeTableEntryForValues(pstate, exprsLists,                                           coltypes, coltypmods, colcollations,                                           NULL, lateral, true);         rtr = makeNode(RangeTblRef);         /* assume new rte is at end */         rtr->rtindex = list_length(pstate->p_rtable);         Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));         pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);          /*          * Generate list of Vars referencing the RTE          */         expandRTE(rte, rtr->rtindex, 0, -1, false, NULL, &exprList);          /*          * Re-apply any indirection on the target column specs to the Vars          */         exprList = transformInsertRow(pstate, exprList,                                       stmt->cols,                                       icolumns, attrnos,                                       false);     }     else     {         /*          * Process INSERT ... VALUES with a single VALUES sublist.  We treat          * this case separately for efficiency.  The sublist is just computed          * directly as the Query's targetlist, with no VALUES RTE.  So it          * works just like a SELECT without any FROM.          */         List       *valuesLists = selectStmt->valuesLists;          Assert(list_length(valuesLists) == 1);         Assert(selectStmt->intoClause == NULL);          /*          * Do basic expression transformation (same as a ROW() expr, but allow          * SetToDefault at top level)          */         exprList = transformExpressionList(pstate,                                            (List *) linitial(valuesLists),                                            EXPR_KIND_VALUES_SINGLE,                                            true);          /* Prepare row for assignment to target table */         exprList = transformInsertRow(pstate, exprList,                                       stmt->cols,                                       icolumns, attrnos,                                       false);     }      /*      * Generate query's target list using the computed list of expressions.      * Also, mark all the target columns as needing insert permissions.      */     rte = pstate->p_target_rangetblentry;     qry->targetList = NIL;     icols = list_head(icolumns);     attnos = list_head(attrnos);     foreach(lc, exprList)     {         Expr       *expr = (Expr *) lfirst(lc);         ResTarget  *col;         AttrNumber  attr_num;         TargetEntry *tle;          col = lfirst_node(ResTarget, icols);         attr_num = (AttrNumber) lfirst_int(attnos);          tle = makeTargetEntry(expr,                               attr_num,                               col->name,                               false);         qry->targetList = lappend(qry->targetList, tle);          rte->insertedCols = bms_add_member(rte->insertedCols,                                            attr_num - FirstLowInvalidHeapAttributeNumber);          icols = lnext(icols);         attnos = lnext(attnos);     }      /* Process ON CONFLICT, if any. */     if (stmt->onConflictClause)         qry->onConflict = transformOnConflictClause(pstate,                                                     stmt->onConflictClause);      /*      * If we have a RETURNING clause, we need to add the target relation to      * the query namespace before processing it, so that Var references in      * RETURNING will work.  Also, remove any namespace entries added in a      * sub-SELECT or VALUES list.      */     if (stmt->returningList)     {         pstate->p_namespace = NIL;         addRTEtoQuery(pstate, pstate->p_target_rangetblentry,                       false, true, true);         qry->returningList = transformReturningList(pstate,                                                     stmt->returningList);     }      /* done building the range table and jointree */     qry->rtable = pstate->p_rtable;     qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);      qry->hasTargetSRFs = pstate->p_hasTargetSRFs;     qry->hasSubLinks = pstate->p_hasSubLinks;      assign_query_collations(pstate, qry);      return qry; }

9、pg_plan_queries

 /*  * Generate plans for a list of already-rewritten queries.  *  * For normal optimizable statements, invoke the planner.  For utility  * statements, just make a wrapper PlannedStmt node.  *  * The result is a list of PlannedStmt nodes.  */ List * pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams) {     List       *stmt_list = NIL;     ListCell   *query_list;      foreach(query_list, querytrees)     {         Query      *query = lfirst_node(Query, query_list);         PlannedStmt *stmt;          if (query->commandType == CMD_UTILITY)         {             /* Utility commands require no planning. */             stmt = makeNode(PlannedStmt);             stmt->commandType = CMD_UTILITY;             stmt->canSetTag = query->canSetTag;             stmt->utilityStmt = query->utilityStmt;             stmt->stmt_location = query->stmt_location;             stmt->stmt_len = query->stmt_len;         }         else         {             stmt = pg_plan_query(query, cursorOptions, boundParams);         }          stmt_list = lappend(stmt_list, stmt);     }      return stmt_list; } /*  * Generate a plan for a single already-rewritten query.  * This is a thin wrapper around planner() and takes the same parameters.  */ PlannedStmt * pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams) {     PlannedStmt *plan;      /* Utility commands have no plans. */     if (querytree->commandType == CMD_UTILITY)         return NULL;      /* Planner must have a snapshot in case it calls user-defined functions. */     Assert(ActiveSnapshotSet());      TRACE_POSTGRESQL_QUERY_PLAN_START();      if (log_planner_stats)         ResetUsage();      /* call the optimizer */     plan = planner(querytree, cursorOptions, boundParams);      if (log_planner_stats)         ShowUsage("PLANNER STATISTICS");  #ifdef COPY_PARSE_PLAN_TREES     /* Optional debugging check: pass plan output through copyObject() */     {         PlannedStmt *new_plan = copyObject(plan);          /*          * equal() currently does not have routines to compare Plan nodes, so          * don't try to test equality here.  Perhaps fix someday?          */ #ifdef NOT_USED         /* This checks both copyObject() and the equal() routines... */         if (!equal(new_plan, plan))             elog(WARNING, "copyObject() failed to produce an equal plan tree");         else #endif             plan = new_plan;     } #endif      /*      * Print plan if debugging.      */     if (Debug_print_plan)         elog_node_display(LOG, "plan", plan, Debug_pretty_print);      TRACE_POSTGRESQL_QUERY_PLAN_DONE();      return plan; } /*****************************************************************************  *  *     Query optimizer entry point  *  * To support loadable plugins that monitor or modify planner behavior,  * we provide a hook variable that lets a plugin get control before and  * after the standard planning process.  The plugin would normally call  * standard_planner().  *  * Note to plugin authors: standard_planner() scribbles on its Query input,  * so you'd better copy that data structure if you want to plan more than once.  *  *****************************************************************************/ PlannedStmt * planner(Query *parse, int cursorOptions, ParamListInfo boundParams) {     PlannedStmt *result;      if (planner_hook)         result = (*planner_hook) (parse, cursorOptions, boundParams);     else         result = standard_planner(parse, cursorOptions, boundParams);     return result; }

10、CreatePortal

 /*  * CreatePortal  *      Returns a new portal given a name.  *  * allowDup: if true, automatically drop any pre-existing portal of the  * same name (if false, an error is raised).  *  * dupSilent: if true, don't even emit a WARNING.  */ Portal CreatePortal(const char *name, bool allowDup, bool dupSilent) {     Portal      portal;      AssertArg(PointerIsValid(name));      portal = GetPortalByName(name);     if (PortalIsValid(portal))     {         if (!allowDup)             ereport(ERROR,                     (errcode(ERRCODE_DUPLICATE_CURSOR),                      errmsg("cursor \"%s\" already exists", name)));         if (!dupSilent)             ereport(WARNING,                     (errcode(ERRCODE_DUPLICATE_CURSOR),                      errmsg("closing existing cursor \"%s\"",                             name)));         PortalDrop(portal, false);     }      /* make new portal structure */     portal = (Portal) MemoryContextAllocZero(TopPortalContext, sizeof *portal);      /* initialize portal context; typically it won't store much */     portal->portalContext = AllocSetContextCreate(TopPortalContext,                                                   "PortalContext",                                                   ALLOCSET_SMALL_SIZES);      /* create a resource owner for the portal */     portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,                                            "Portal");      /* initialize portal fields that don't start off zero */     portal->status = PORTAL_NEW;     portal->cleanup = PortalCleanup;     portal->createSubid = GetCurrentSubTransactionId();     portal->activeSubid = portal->createSubid;     portal->strategy = PORTAL_MULTI_QUERY;     portal->cursorOptions = CURSOR_OPT_NO_SCROLL;     portal->atStart = true;     portal->atEnd = true;       /* disallow fetches until query is set */     portal->visible = true;     portal->creation_time = GetCurrentStatementStartTimestamp();      /* put portal in table (sets portal->name) */     PortalHashTableInsert(portal, name);      /* reuse portal->name copy */     MemoryContextSetIdentifier(portal->portalContext, portal->name);      return portal; }

11、PortalDefineQuery

 /*  * PortalDefineQuery  *      A simple subroutine to establish a portal's query.  *  * Notes: as of PG 8.4, caller MUST supply a sourceText string; it is not  * allowed anymore to pass NULL.  (If you really don't have source text,  * you can pass a constant string, perhaps "(query not available)".)  *  * commandTag shall be NULL if and only if the original query string  * (before rewriting) was an empty string.  Also, the passed commandTag must  * be a pointer to a constant string, since it is not copied.  *  * If cplan is provided, then it is a cached plan containing the stmts, and  * the caller must have done GetCachedPlan(), causing a refcount increment.  * The refcount will be released when the portal is destroyed.  *  * If cplan is NULL, then it is the caller's responsibility to ensure that  * the passed plan trees have adequate lifetime.  Typically this is done by  * copying them into the portal's context.  *  * The caller is also responsible for ensuring that the passed prepStmtName  * (if not NULL) and sourceText have adequate lifetime.  *  * NB: this function mustn't do much beyond storing the passed values; in  * particular don't do anything that risks elog(ERROR).  If that were to  * happen here before storing the cplan reference, we'd leak the plancache  * refcount that the caller is trying to hand off to us.  */ void PortalDefineQuery(Portal portal,                   const char *prepStmtName,                   const char *sourceText,                   const char *commandTag,                   List *stmts,                   CachedPlan *cplan) {     AssertArg(PortalIsValid(portal));     AssertState(portal->status == PORTAL_NEW);      AssertArg(sourceText != NULL);     AssertArg(commandTag != NULL || stmts == NIL);      portal->prepStmtName = prepStmtName;     portal->sourceText = sourceText;     portal->commandTag = commandTag;     portal->stmts = stmts;     portal->cplan = cplan;     portal->status = PORTAL_DEFINED; }

12、PortalStart

 /*  * PortalStart  *      Prepare a portal for execution.  *  * Caller must already have created the portal, done PortalDefineQuery(),  * and adjusted portal options if needed.  *  * If parameters are needed by the query, they must be passed in "params"  * (caller is responsible for giving them appropriate lifetime).  *  * The caller can also provide an initial set of "eflags" to be passed to  * ExecutorStart (but note these can be modified internally, and they are  * currently only honored for PORTAL_ONE_SELECT portals).  Most callers  * should simply pass zero.  *  * The caller can optionally pass a snapshot to be used; pass InvalidSnapshot  * for the normal behavior of setting a new snapshot.  This parameter is  * presently ignored for non-PORTAL_ONE_SELECT portals (it's only intended  * to be used for cursors).  *  * On return, portal is ready to accept PortalRun() calls, and the result  * tupdesc (if any) is known.  */ void PortalStart(Portal portal, ParamListInfo params,             int eflags, Snapshot snapshot) {     Portal      saveActivePortal;     ResourceOwner saveResourceOwner;     MemoryContext savePortalContext;     MemoryContext oldContext;     QueryDesc  *queryDesc;     int         myeflags;      AssertArg(PortalIsValid(portal));     AssertState(portal->status == PORTAL_DEFINED);      /*      * Set up global portal context pointers.      */     saveActivePortal = ActivePortal;     saveResourceOwner = CurrentResourceOwner;     savePortalContext = PortalContext;     PG_TRY();     {         ActivePortal = portal;         if (portal->resowner)             CurrentResourceOwner = portal->resowner;         PortalContext = portal->portalContext;          oldContext = MemoryContextSwitchTo(PortalContext);          /* Must remember portal param list, if any */         portal->portalParams = params;          /*          * Determine the portal execution strategy          */         portal->strategy = ChoosePortalStrategy(portal->stmts);          /*          * Fire her up according to the strategy          */         switch (portal->strategy)         {             case PORTAL_ONE_SELECT:                  /* Must set snapshot before starting executor. */                 if (snapshot)                     PushActiveSnapshot(snapshot);                 else                     PushActiveSnapshot(GetTransactionSnapshot());                  /*                  * Create QueryDesc in portal's context; for the moment, set                  * the destination to DestNone.                  */                 queryDesc = CreateQueryDesc(linitial_node(PlannedStmt, portal->stmts),                                             portal->sourceText,                                             GetActiveSnapshot(),                                             InvalidSnapshot,                                             None_Receiver,                                             params,                                             portal->queryEnv,                                             0);                  /*                  * If it's a scrollable cursor, executor needs to support                  * REWIND and backwards scan, as well as whatever the caller                  * might've asked for.                  */                 if (portal->cursorOptions & CURSOR_OPT_SCROLL)                     myeflags = eflags | EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD;                 else                     myeflags = eflags;                  /*                  * Call ExecutorStart to prepare the plan for execution                  */                 ExecutorStart(queryDesc, myeflags);                  /*                  * This tells PortalCleanup to shut down the executor                  */                 portal->queryDesc = queryDesc;                  /*                  * Remember tuple descriptor (computed by ExecutorStart)                  */                 portal->tupDesc = queryDesc->tupDesc;                  /*                  * Reset cursor position data to "start of query"                  */                 portal->atStart = true;                 portal->atEnd = false;  /* allow fetches */                 portal->portalPos = 0;                  PopActiveSnapshot();                 break;              case PORTAL_ONE_RETURNING:             case PORTAL_ONE_MOD_WITH:                  /*                  * We don't start the executor until we are told to run the                  * portal.  We do need to set up the result tupdesc.                  */                 {                     PlannedStmt *pstmt;                      pstmt = PortalGetPrimaryStmt(portal);                     portal->tupDesc =                         ExecCleanTypeFromTL(pstmt->planTree->targetlist,                                             false);                 }                  /*                  * Reset cursor position data to "start of query"                  */                 portal->atStart = true;                 portal->atEnd = false;  /* allow fetches */                 portal->portalPos = 0;                 break;              case PORTAL_UTIL_SELECT:                  /*                  * We don't set snapshot here, because PortalRunUtility will                  * take care of it if needed.                  */                 {                     PlannedStmt *pstmt = PortalGetPrimaryStmt(portal);                      Assert(pstmt->commandType == CMD_UTILITY);                     portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt);                 }                  /*                  * Reset cursor position data to "start of query"                  */                 portal->atStart = true;                 portal->atEnd = false;  /* allow fetches */                 portal->portalPos = 0;                 break;              case PORTAL_MULTI_QUERY:                 /* Need do nothing now */                 portal->tupDesc = NULL;                 break;         }     }     PG_CATCH();     {         /* Uncaught error while executing portal: mark it dead */         MarkPortalFailed(portal);          /* Restore global vars and propagate error */         ActivePortal = saveActivePortal;         CurrentResourceOwner = saveResourceOwner;         PortalContext = savePortalContext;          PG_RE_THROW();     }     PG_END_TRY();      MemoryContextSwitchTo(oldContext);      ActivePortal = saveActivePortal;     CurrentResourceOwner = saveResourceOwner;     PortalContext = savePortalContext;      portal->status = PORTAL_READY; }

13、PortalSetResultFormat

 /*  * PortalSetResultFormat  *      Select the format codes for a portal's output.  *  * This must be run after PortalStart for a portal that will be read by  * a DestRemote or DestRemoteExecute destination.  It is not presently needed  * for other destination types.  *  * formats[] is the client format request, as per Bind message conventions.  */ void PortalSetResultFormat(Portal portal, int nFormats, int16 *formats) {     int         natts;     int         i;      /* Do nothing if portal won't return tuples */     if (portal->tupDesc == NULL)         return;     natts = portal->tupDesc->natts;     portal->formats = (int16 *)         MemoryContextAlloc(portal->portalContext,                            natts * sizeof(int16));     if (nFormats > 1)     {         /* format specified for each column */         if (nFormats != natts)             ereport(ERROR,                     (errcode(ERRCODE_PROTOCOL_VIOLATION),                      errmsg("bind message has %d result formats but query has %d columns",                             nFormats, natts)));         memcpy(portal->formats, formats, natts * sizeof(int16));     }     else if (nFormats > 0)     {         /* single format specified, use for all columns */         int16       format1 = formats[0];          for (i = 0; i < natts; i++)             portal->formats[i] = format1;     }     else     {         /* use default format for all columns */         for (i = 0; i < natts; i++)             portal->formats[i] = 0;     } }

14、CreateDestReceiver

 /* ----------------  *      CreateDestReceiver - return appropriate receiver function set for dest  * ----------------  */ DestReceiver * CreateDestReceiver(CommandDest dest) {     switch (dest)     {         case DestRemote:         case DestRemoteExecute:             return printtup_create_DR(dest);          case DestRemoteSimple:             return &printsimpleDR;          case DestNone:             return &donothingDR;          case DestDebug:             return &debugtupDR;          case DestSPI:             return &spi_printtupDR;          case DestTuplestore:             return CreateTuplestoreDestReceiver();          case DestIntoRel:             return CreateIntoRelDestReceiver(NULL);          case DestCopyOut:             return CreateCopyDestReceiver();          case DestSQLFunction:             return CreateSQLFunctionDestReceiver();          case DestTransientRel:             return CreateTransientRelDestReceiver(InvalidOid);          case DestTupleQueue:             return CreateTupleQueueDestReceiver(NULL);     }      /* should never get here */     return &donothingDR; }

15、printtup_create_DR

 /* ----------------  *      Initialize: create a DestReceiver for printtup  * ----------------  */ DestReceiver * printtup_create_DR(CommandDest dest) {     DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));      self->pub.receiveSlot = printtup;   /* might get changed later */     self->pub.rStartup = printtup_startup;     self->pub.rShutdown = printtup_shutdown;     self->pub.rDestroy = printtup_destroy;     self->pub.mydest = dest;      /*      * Send T message automatically if DestRemote, but not if      * DestRemoteExecute      */     self->sendDescrip = (dest == DestRemote);      self->attrinfo = NULL;     self->nattrs = 0;     self->myinfo = NULL;     self->tmpcontext = NULL;      return (DestReceiver *) self; }

16、PortalDrop

/*  * PortalDrop  *      Destroy the portal.  */ void PortalDrop(Portal portal, bool isTopCommit) {     AssertArg(PortalIsValid(portal));      /*      * Don't allow dropping a pinned portal, it's still needed by whoever      * pinned it.      */     if (portal->portalPinned)         ereport(ERROR,                 (errcode(ERRCODE_INVALID_CURSOR_STATE),                  errmsg("cannot drop pinned portal \"%s\"", portal->name)));      /*      * Not sure if the PORTAL_ACTIVE case can validly happen or not...      */     if (portal->status == PORTAL_ACTIVE)         ereport(ERROR,                 (errcode(ERRCODE_INVALID_CURSOR_STATE),                  errmsg("cannot drop active portal \"%s\"", portal->name)));      /*      * Allow portalcmds.c to clean up the state it knows about, in particular      * shutting down the executor if still active.  This step potentially runs      * user-defined code so failure has to be expected.  It's the cleanup      * hook's responsibility to not try to do that more than once, in the case      * that failure occurs and then we come back to drop the portal again      * during transaction abort.      *      * Note: in most paths of control, this will have been done already in      * MarkPortalDone or MarkPortalFailed.  We're just making sure.      */     if (PointerIsValid(portal->cleanup))     {         portal->cleanup(portal);         portal->cleanup = NULL;     }      /*      * Remove portal from hash table.  Because we do this here, we will not      * come back to try to remove the portal again if there's any error in the      * subsequent steps.  Better to leak a little memory than to get into an      * infinite error-recovery loop.      */     PortalHashTableDelete(portal);      /* drop cached plan reference, if any */     PortalReleaseCachedPlan(portal);      /*      * If portal has a snapshot protecting its data, release that.  This needs      * a little care since the registration will be attached to the portal's      * resowner; if the portal failed, we will already have released the      * resowner (and the snapshot) during transaction abort.      */     if (portal->holdSnapshot)     {         if (portal->resowner)             UnregisterSnapshotFromOwner(portal->holdSnapshot,                                         portal->resowner);         portal->holdSnapshot = NULL;     }      /*      * Release any resources still attached to the portal.  There are several      * cases being covered here:      *      * Top transaction commit (indicated by isTopCommit): normally we should      * do nothing here and let the regular end-of-transaction resource      * releasing mechanism handle these resources too.  However, if we have a      * FAILED portal (eg, a cursor that got an error), we'd better clean up      * its resources to avoid resource-leakage warning messages.      *      * Sub transaction commit: never comes here at all, since we don't kill      * any portals in AtSubCommit_Portals().      *      * Main or sub transaction abort: we will do nothing here because      * portal->resowner was already set NULL; the resources were already      * cleaned up in transaction abort.      *      * Ordinary portal drop: must release resources.  However, if the portal      * is not FAILED then we do not release its locks.  The locks become the      * responsibility of the transaction's ResourceOwner (since it is the      * parent of the portal's owner) and will be released when the transaction      * eventually ends.      */     if (portal->resowner &&         (!isTopCommit || portal->status == PORTAL_FAILED))     {         bool        isCommit = (portal->status != PORTAL_FAILED);          ResourceOwnerRelease(portal->resowner,                              RESOURCE_RELEASE_BEFORE_LOCKS,                              isCommit, false);         ResourceOwnerRelease(portal->resowner,                              RESOURCE_RELEASE_LOCKS,                              isCommit, false);         ResourceOwnerRelease(portal->resowner,                              RESOURCE_RELEASE_AFTER_LOCKS,                              isCommit, false);         ResourceOwnerDelete(portal->resowner);     }     portal->resowner = NULL;      /*      * Delete tuplestore if present.  We should do this even under error      * conditions; since the tuplestore would have been using cross-      * transaction storage, its temp files need to be explicitly deleted.      */     if (portal->holdStore)     {         MemoryContext oldcontext;          oldcontext = MemoryContextSwitchTo(portal->holdContext);         tuplestore_end(portal->holdStore);         MemoryContextSwitchTo(oldcontext);         portal->holdStore = NULL;     }      /* delete tuplestore storage, if any */     if (portal->holdContext)         MemoryContextDelete(portal->holdContext);      /* release subsidiary storage */     MemoryContextDelete(portal->portalContext);      /* release portal struct (it's in TopPortalContext) */     pfree(portal); }

17、EndImplicitTransactionBlock

 /*  * EndImplicitTransactionBlock  *      End an implicit transaction block, if we're in one.  *  * Like EndTransactionBlock, we just make any needed blockState change here.  * The real work will be done in the upcoming CommitTransactionCommand().  */ void EndImplicitTransactionBlock(void) {     TransactionState s = CurrentTransactionState;      /*      * If we are in IMPLICIT_INPROGRESS state, switch back to STARTED state,      * allowing CommitTransactionCommand to commit whatever happened during      * the implicit transaction block as though it were a single statement.      *      * For caller convenience, we consider all other transaction states as      * legal here; otherwise the caller would need its own state check, which      * seems rather pointless.      */     if (s->blockState == TBLOCK_IMPLICIT_INPROGRESS)         s->blockState = TBLOCK_STARTED; }

18、finish_xact_command

 static void finish_xact_command(void) {     /* cancel active statement timeout after each command */     disable_statement_timeout();      if (xact_started)     {         CommitTransactionCommand();  #ifdef MEMORY_CONTEXT_CHECKING         /* Check all memory contexts that weren't freed during commit */         /* (those that were, were checked before being deleted) */         MemoryContextCheck(TopMemoryContext); #endif  #ifdef SHOW_MEMORY_STATS         /* Print mem stats after each commit for leak tracking */         MemoryContextStats(TopMemoryContext); #endif          xact_started = false;     } }

19、CommandCounterIncrement

/*  *  CommandCounterIncrement  */ void CommandCounterIncrement(void) {     /*      * If the current value of the command counter hasn't been "used" to mark      * tuples, we need not increment it, since there's no need to distinguish      * a read-only command from others.  This helps postpone command counter      * overflow, and keeps no-op CommandCounterIncrement operations cheap.      */     if (currentCommandIdUsed)     {         /*          * Workers synchronize transaction state at the beginning of each          * parallel operation, so we can't account for new commands after that          * point.          */         if (IsInParallelMode() || IsParallelWorker())             elog(ERROR, "cannot start commands during a parallel operation");          currentCommandId += 1;         if (currentCommandId == InvalidCommandId)         {             currentCommandId -= 1;             ereport(ERROR,                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),                      errmsg("cannot have more than 2^32-2 commands in a transaction")));         }         currentCommandIdUsed = false;          /* Propagate new command ID into static snapshots */         SnapshotSetCommandId(currentCommandId);          /*          * Make any catalog changes done by the just-completed command visible          * in the local syscache.  We obviously don't need to do this after a          * read-only command.  (But see hacks in inval.c to make real sure we          * don't think a command that queued inval messages was read-only.)          */         AtCCI_LocalCache();     } }

20、EndCommand

/* ----------------  *      EndCommand - clean up the destination at end of command  * ----------------  */ void EndCommand(const char *commandTag, CommandDest dest) {     switch (dest)     {         case DestRemote:         case DestRemoteExecute:         case DestRemoteSimple:              /*              * We assume the commandTag is plain ASCII and therefore requires              * no encoding conversion.              */             pq_putmessage('C', commandTag, strlen(commandTag) + 1);             break;          case DestNone:         case DestDebug:         case DestSPI:         case DestTuplestore:         case DestIntoRel:         case DestCopyOut:         case DestSQLFunction:         case DestTransientRel:         case DestTupleQueue:             break;     } }

三、跟踪分析

插入测试数据:

testdb=# -- 获取pidtestdb=# select pg_backend_pid(); pg_backend_pid ----------------           1893(1 row)testdb=# -- 插入1行testdb=# insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');(挂起)

启动gdb,跟踪调试:

[root@localhost ~]# gdb -p 1893GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7Copyright (C) 2013 Free Software Foundation, Inc....(gdb) b exec_simple_queryBreakpoint 1 at 0x84cad8: file postgres.c, line 893.(gdb) cContinuing.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:893893     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 , parent = 0x1503ba0, firstchild = 0x0, prevchild = 0x157c3c0, nextchild = 0x15956d0,   name = 0xb4e87c "MessageContext", ident = 0x0, reset_cbs = 0x0}(gdb) n944     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 , parent = 0x1503ba0, firstchild = 0x0, prevchild = 0x0, nextchild = 0x157c3c0,   name = 0xa1b4ff "TopTransactionContext", ident = 0x0, reset_cbs = 0x0}(gdb) steppg_parse_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:615615     if (log_parser_stats)(gdb) 618     raw_parsetree_list = raw_parser(query_string);#进入raw_parser(gdb) stepraw_parser (str=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at parser.c:4343      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 ,     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 
, keyword = 0x300990000
}, 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:947947 if (check_log_statement(parsetree_list))...#解析树只有一个元素(gdb) n974 foreach(parsetree_item, parsetree_list)(gdb) p list_length(parsetree_list)$30 = 1(gdb) n976 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) n992 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) steppg_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:663663 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:10501050 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 , 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) n1073 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 , 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}#Receiver1110 receiver = CreateDestReceiver(dest);(gdb) 1111 if (dest == DestRemote)(gdb) p *receiver$42 = {receiveSlot = 0x4857ad , rStartup = 0x485196 , rShutdown = 0x485bad , rDestroy = 0x485c21 , mydest = DestRemote}(gdb) #执行...1122 (void) PortalRun(portal,(gdb)...#DONE!(gdb) PostgresMain (argc=1, argv=0x1532aa8, dbname=0x1532990 "testdb", username=0x1532978 "xdb") at postgres.c:41554155 send_ready_for_query = true;

到此,关于"PostgreSQL中exec_simple_query函数的实现逻辑是什么"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

分析 事务 函数 语句 查询 上下 上下文 元素 内存 命令 数据 切换 逻辑 变量 学习 信息 快照 数据结构 结构 输入 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 上海挚品互联网科技有限公司 华服互联网科技靠谱吗 伟宏互联网科技 亿人帮互联网科技有限公司 深圳九邦互联网科技有限公司 重生之互联网科技帝国 深圳恒合互联网络科技有限公司 A股互联网金融科技公司 互联网科技会 美国互联网科技 互联网科技公司怎么样 上游互联网科技 多迪互联网科技有限公司 佳云科技和互联网广告 广州市恰铎互联网科技有限公司 济南有互联网科技公司吗 广东工业互联网大赛深圳学泰科技 互联网科技股为什么大涨 互联网科技包装股份有限公司上市 万物互联网智能科技 有格互联网科技 互联网的科技 朗新科技与工业互联网 狮子座互联网科技培训 甘肃拓世互联网科技有限公司 青岛中康互联网医疗科技有限公司 玉溪互联网科技怎么选 国产互联网科技公司 互联网科技企业商标注意事项 华人运通互联网科技公司
0