美文网首页PostgreSQL
PostgreSQL 源码解读(12)- 插入数据#11(exe

PostgreSQL 源码解读(12)- 插入数据#11(exe

作者: EthanHe | 来源:发表于2018-08-09 12:31 被阅读9次

    本文简单介绍了PG插入数据部分的源码,主要内容包括exec_simple_query函数的实现逻辑,该函数位于src/backend/tcop/postgres.c文件中。

    一、源码解读

    exec_simple_query函数,顾名思义,执行简单“查询”(包括INSERT/UPDATE/DELETE等语句)

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

    二、基础信息

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

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

    2、RawStmt

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

    3、Query

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

    4、ParseState

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

    5、RangeTblEntry

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

    6、TargetEntry

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

    7、全局变量定义

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

    依赖的函数
    1、start_xact_command

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

    2、drop_unnamed_stmt

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

    3、pg_parse_query

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

    4、raw_parser

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

    5、CreateCommandTag

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

    6、BeginCommand

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

    7、analyze_requires_snapshot

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

    8、pg_analyze_and_rewrite

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

    9、pg_plan_queries

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

    10、CreatePortal

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

    11、PortalDefineQuery

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

    12、PortalStart

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

    13、PortalSetResultFormat

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

    14、CreateDestReceiver

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

    15、printtup_create_DR

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

    16、PortalDrop

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

    17、EndImplicitTransactionBlock

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

    18、finish_xact_command

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

    19、CommandCounterIncrement

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

    20、EndCommand

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

    三、跟踪分析

    插入测试数据:

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

    启动gdb,跟踪调试:

    [root@localhost ~]# gdb -p 1893
    GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
    Copyright (C) 2013 Free Software Foundation, Inc.
    ...
    (gdb) b exec_simple_query
    Breakpoint 1 at 0x84cad8: file postgres.c, line 893.
    (gdb) c
    Continuing.
    
    Breakpoint 1, exec_simple_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:893
    893     CommandDest dest = whereToSendOutput;
    #输入参数
    #query_string
    (gdb) p query_string
    $1 = 0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');"
    #单步调试
    938     oldcontext = MemoryContextSwitchTo(MessageContext);
    (gdb) p *MessageContext
    $2 = {type = T_AllocSetContext, isReset = false, allowInCritSection = false, methods = 0xb8c720 <AllocSetMethods>, parent = 0x1503ba0, firstchild = 0x0, prevchild = 0x157c3c0, nextchild = 0x15956d0, 
      name = 0xb4e87c "MessageContext", ident = 0x0, reset_cbs = 0x0}
    (gdb) n
    944     parsetree_list = pg_parse_query(query_string);
    (gdb) p oldcontext
    $3 = (MemoryContext) 0x15a8320
    (gdb) p *oldcontext
    $4 = {type = T_AllocSetContext, isReset = true, allowInCritSection = false, methods = 0xb8c720 <AllocSetMethods>, parent = 0x1503ba0, firstchild = 0x0, prevchild = 0x0, nextchild = 0x157c3c0, 
      name = 0xa1b4ff "TopTransactionContext", ident = 0x0, reset_cbs = 0x0}
    (gdb) step
    pg_parse_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:615
    615     if (log_parser_stats)
    (gdb) 
    618     raw_parsetree_list = raw_parser(query_string);
    #进入raw_parser
    (gdb) step
    raw_parser (str=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at parser.c:43
    43      yyscanner = scanner_init(str, &yyextra.core_yy_extra,
    (gdb) 
    ...
    61      return yyextra.parsetree;
    (gdb) p yyextra
    $8 = {core_yy_extra = {scanbuf = 0x1509820 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');", scanbuflen = 92, keywords = 0xbb8d40 <ScanKeywords>, 
        num_keywords = 440, backslash_quote = 2, escape_string_warning = true, standard_conforming_strings = true, literalbuf = 0x1509300 "exec_simple_query", literallen = 17, literalalloc = 1024, 
        xcdepth = 1087033144, dolqstart = 0x0, utf16_first_part = 16777215, warn_on_first_escape = true, saw_non_ascii = false}, have_lookahead = false, lookahead_token = 32765, lookahead_yylval = {
        ival = 10027008, str = 0x300990000 <Address 0x300990000 out of bounds>, keyword = 0x300990000 <Address 0x300990000 out of bounds>}, lookahead_yylloc = 2015867616, 
      lookahead_end = 0xa1b62b "StartTransaction", lookahead_hold_char = 16 '\020', parsetree = 0x1509d88}
    (gdb) p *(yyextra.parsetree)
    $10 = {type = T_List, length = 1, head = 0x1509d68, tail = 0x1509d68}
    #解析树中的内容
    (gdb) p *((RawStmt*)(yyextra.parsetree->head->data.ptr_value))
    $25 = {type = T_RawStmt, stmt = 0x1509ce8, stmt_location = 0, stmt_len = 91}
    (gdb) p *(((RawStmt*)(yyextra.parsetree->head->data.ptr_value))->stmt)
    $27 = {type = T_InsertStmt}
    #跳出子函数,重新进入主函数
    (gdb) 
    exec_simple_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:947
    947     if (check_log_statement(parsetree_list))
    ...
    #解析树只有一个元素
    (gdb) n
    974     foreach(parsetree_item, parsetree_list)
    (gdb) p list_length(parsetree_list)
    $30 = 1
    (gdb) n
    976         RawStmt    *parsetree = lfirst_node(RawStmt, parsetree_item);
    (gdb) 
    977         bool        snapshot_set = false;
    (gdb) p *parsetree
    $31 = {type = T_RawStmt, stmt = 0x1509ce8, stmt_location = 0, stmt_len = 91}
    (gdb) p *(parsetree->stmt)
    $32 = {type = T_InsertStmt}
    #commandTag
    (gdb) n
    992         commandTag = CreateCommandTag(parsetree->stmt);
    (gdb) 
    994         set_ps_display(commandTag, false);
    (gdb) p commandTag
    $33 = 0xb50908 "INSERT"
    #进入分析&查询重写
    1047            querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
    (gdb) step
    pg_analyze_and_rewrite (parsetree=0x1509d38, query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');", paramTypes=0x0, numParams=0, 
        queryEnv=0x0) at postgres.c:663
    663     if (log_parser_stats)
    ...
    #分析后的Query数据结构
    (gdb) p *query
    $34 = {type = T_Query, commandType = CMD_INSERT, querySource = QSRC_ORIGINAL, queryId = 0, canSetTag = true, utilityStmt = 0x0, resultRelation = 1, hasAggs = false, hasWindowFuncs = false, 
      hasTargetSRFs = false, hasSubLinks = false, hasDistinctOn = false, hasRecursive = false, hasModifyingCTE = false, hasForUpdate = false, hasRowSecurity = false, cteList = 0x0, rtable = 0x150a788, 
      jointree = 0x152cf40, targetList = 0x152cda8, override = OVERRIDING_NOT_SET, onConflict = 0x0, returningList = 0x0, groupClause = 0x0, groupingSets = 0x0, havingQual = 0x0, windowClause = 0x0, 
      distinctClause = 0x0, sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, rowMarks = 0x0, setOperations = 0x0, constraintDeps = 0x0, withCheckOptions = 0x0, stmt_location = 0, stmt_len = 91}
    ...
    #回到主函数
    (gdb) 
    exec_simple_query (query_string=0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');") at postgres.c:1050
    1050            plantree_list = pg_plan_queries(querytree_list,
    (gdb) 
    1054            if (snapshot_set)
    (gdb) 
    1055                PopActiveSnapshot();
    (gdb) 
    1058            CHECK_FOR_INTERRUPTS();
    (gdb) 
    1064            portal = CreatePortal("", true, true);
    (gdb) p *plantree_list
    $36 = {type = T_List, length = 1, head = 0x15c03e8, tail = 0x15c03e8}
    (gdb) p (PlannedStmt*)(plantree_list->head->data.ptr_value)
    $37 = (PlannedStmt *) 0x150a4a8
    (gdb) p *((PlannedStmt*)(plantree_list->head->data.ptr_value))
    $38 = {type = T_PlannedStmt, commandType = CMD_INSERT, queryId = 0, hasReturning = false, hasModifyingCTE = false, canSetTag = true, transientPlan = false, dependsOnRole = false, 
      parallelModeNeeded = false, jitFlags = 0, planTree = 0x150a028, rtable = 0x15c0318, resultRelations = 0x15c03b8, nonleafResultRelations = 0x0, rootResultRelations = 0x0, subplans = 0x0, 
      rewindPlanIDs = 0x0, rowMarks = 0x0, relationOids = 0x15c0368, invalItems = 0x0, paramExecTypes = 0x152e720, utilityStmt = 0x0, stmt_location = 0, stmt_len = 91}
    #Portal
    ...
    (gdb) p *portal
    $40 = {name = 0x1571e98 "", prepStmtName = 0x0, portalContext = 0x152c3d0, resowner = 0x1539d10, cleanup = 0x62f15c <PortalCleanup>, createSubid = 1, activeSubid = 1, sourceText = 0x0, 
      commandTag = 0x0, stmts = 0x0, cplan = 0x0, portalParams = 0x0, queryEnv = 0x0, strategy = PORTAL_MULTI_QUERY, cursorOptions = 4, run_once = false, status = PORTAL_NEW, portalPinned = false, 
      autoHeld = false, queryDesc = 0x0, tupDesc = 0x0, formats = 0x0, holdStore = 0x0, holdContext = 0x0, holdSnapshot = 0x0, atStart = true, atEnd = true, portalPos = 0, creation_time = 587101481469205, 
      visible = true}
    (gdb) n
    1073            PortalDefineQuery(portal,
    (gdb) 
    1083            PortalStart(portal, NULL, 0, InvalidSnapshot);
    (gdb) 
    1091            format = 0;             /* TEXT is default */
    (gdb) 
    1092            if (IsA(parsetree->stmt, FetchStmt))
    (gdb)  p *portal
    $41 = {name = 0x1571e98 "", prepStmtName = 0x0, portalContext = 0x152c3d0, resowner = 0x1539d10, cleanup = 0x62f15c <PortalCleanup>, createSubid = 1, activeSubid = 1, 
      sourceText = 0x1508ef0 "insert into t_insert values(22,'exec_simple_query','exec_simple_query','exec_simple_query');", commandTag = 0xb50908 "INSERT", stmts = 0x15c0408, cplan = 0x0, 
      portalParams = 0x0, queryEnv = 0x0, strategy = PORTAL_MULTI_QUERY, cursorOptions = 4, run_once = false, status = PORTAL_READY, portalPinned = false, autoHeld = false, queryDesc = 0x0, tupDesc = 0x0, 
      formats = 0x0, holdStore = 0x0, holdContext = 0x0, holdSnapshot = 0x0, atStart = true, atEnd = true, portalPos = 0, creation_time = 587101481469205, visible = false}
    #Receiver
    1110            receiver = CreateDestReceiver(dest);
    (gdb) 
    1111            if (dest == DestRemote)
    (gdb) p *receiver
    $42 = {receiveSlot = 0x4857ad <printtup>, rStartup = 0x485196 <printtup_startup>, rShutdown = 0x485bad <printtup_shutdown>, rDestroy = 0x485c21 <printtup_destroy>, mydest = DestRemote}
    (gdb) 
    #执行
    ...
    1122            (void) PortalRun(portal,
    (gdb)
    ...
    #DONE!
    (gdb) 
    PostgresMain (argc=1, argv=0x1532aa8, dbname=0x1532990 "testdb", username=0x1532978 "xdb") at postgres.c:4155
    4155                        send_ready_for_query = true;
    

    四、小结

    1、执行/数据流程:SQL语句->解析树RawStmt>查询结构体Query->规划执行PlannedStmt->执行处理ExecProcNode
    2、重要的数据结构:本节重要的数据结构是RawStmt、Portal和Query
    3、关键的方法:创建Portal“对象”CreatePortal、语句解析pg_parse_query、语句分析&重写pg_analyze_and_rewrite、规划执行pg_plan_queries、规划执行(每个语句)pg_plan_query

    相关文章

      网友评论

        本文标题:PostgreSQL 源码解读(12)- 插入数据#11(exe

        本文链接:https://www.haomeiwen.com/subject/yhhqbftx.html