PostgreSQL 原始碼解讀(12)- 插入資料#11(exec_simple_query)

husthxd發表於2018-08-09

本文簡單介紹了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

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2374904/,如需轉載,請註明出處,否則將追究法律責任。

相關文章