天天看點

PostgreSQL查詢引擎——create table xxx(...)基礎建表流程

CREATE TABLE table_name ( [ { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ] } [, ... ] ] )
where column_constraint is:
[ CONSTRAINT constraint_name ] { NOT NULL | NULL | CHECK ( expression ) [ NO INHERIT ] | DEFAULT default_expr | GENERATED ALWAYS AS ( generation_expr ) STORED | GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] | UNIQUE index_parameters | PRIMARY KEY index_parameters | REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
[ DEFERRABLE | NOT DEFERRABLE ] 
[ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]      
PostgreSQL查詢引擎——create table xxx(...)基礎建表流程

​CREATE TABLE films ( code char(5), title varchar(40), did integer, date_prod date, kind varchar(10), len interval hour to minute);​

​建表語句執行parse_analyze函數時進入傳統的transform階段時并沒有執行任何trasform功能的函數,而是直接走transformStmt函數的default分支:建立Query節點,讓原始文法樹設定到其utilityStmt成員。執行pg_rewrite_query函數時不經過QueryRewrite函數,直接将Query節點作為querytree_list清單的元素傳回。在各個點elog列印的文法樹如下所示,可以說是完全一緻的。

PostgreSQL查詢引擎——create table xxx(...)基礎建表流程

如下所示,對于create table基礎建表語句,pg_plan_queries沒有經過pg_plan_query函數,而是直接建立PlannedStmt節點,然後将query->utilityStmt設定到PlannedStmt節點的utilityStmt成員。

PostgreSQL查詢引擎——create table xxx(...)基礎建表流程

Portal流程

​portal = CreatePortal("", true, true);​

​​函數建立PortalData結構體,并初始化其中的部分成員,比如portalContext、resowner等。​

​PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL);​

​​函數主要工作是設定執行指令的commandTag、指令sourceText和執行計劃PlannedStmts list到PortalData結構體相應成員中。​

​PortalStart(portal, NULL, 0, InvalidSnapshot);​

​​首先暫存目前ActivePortal、CurrentResourceOwner、PortalContext,然後切換到建立portal中的資源中;調用ChoosePortalStrategy确定strategy,這裡依舊為PORTAL_MULTI_QUERY,并設定portal->tupDesc成員為NULL;最後切換到暫存資源中。​

​PortalSetResultFormat(portal, 1, &format)​

​​在這裡運作時由于portal->tupDesc成員為NULL,直接return了,沒有任何有效工作。​

​PortalRun(portal, FETCH_ALL, true, /* always top level */ true, receiver, receiver, completionTag);​

​​函數首先調用​

​MarkPortalActive(portal)​

​​設定portal->status為PORTAL_ACTIVE,更新portal->activeSubid;暫存目前TopTransactionResourceOwner、TopTransactionContext、ActivePortal、CurrentResourceOwner、PortalContext、CurrentMemoryContext,切換到建立portal中的資源中;由于strategy為PORTAL_MULTI_QUERY,是以執行PortalRunMulti函數;最終執行​

​MarkPortalDone(portal)​

​​函數,設定portal->status為PORTAL_DONE,并執行cleanup回調函數;切換到暫存資源中。​

​PortalDrop(portal, false)​

​函數最後執行清理portal申請的相應資源。

typedef struct PortalData{
    /* Bookkeeping data */
    const char *name;           /* portal's name */ 
    MemoryContext portalContext;/* subsidiary memory for portal */  // CreatePortal時建立
    ResourceOwner resowner;/* resources owned by portal */ // CreatePortal時建立
    void (*cleanup) (Portal portal);/* cleanup hook */ // CreatePortal時設定為PortalCleanup
    SubTransactionId createSubid;/* the creating subxact */ // CreatePortal時設定為GetCurrentSubTransactionId()
    SubTransactionId activeSubid;/* the last subxact with activity */ // CreatePortal時設定為GetCurrentSubTransactionId()
    const char *prepStmtName;   /* source prepared statement (NULL if none) */ // PortalDefineQuery時設定
    
    /* The query or queries the portal will execute */
    const char *sourceText;     /* text of query (as of 8.4, never NULL) */ // PortalDefineQuery時設定
    const char *commandTag;     /* command tag for original query */ // PortalDefineQuery時設定
    List       *stmts;          /* list of PlannedStmts */ // PortalDefineQuery時設定
    CachedPlan *cplan;          /* CachedPlan, if stmts are from one */ // PortalDefineQuery時設定

    ParamListInfo portalParams; /* params to pass to query */ // PortalStart時設定
    QueryEnvironment *queryEnv; /* environment for query */

    /* Features/options */
    PortalStrategy strategy;// CreatePortal時設定為PORTAL_MULTI_QUERY; PortalStart時調用ChoosePortalStrategy設定
    int         cursorOptions;/* DECLARE CURSOR option bits */ // CreatePortal時設定為CURSOR_OPT_NO_SCROLL
    bool        run_once;       /* portal will only be run once */ // PortalRun時設定

    /* Status data */
    PortalStatus status; // CreatePortal時設定為PORTAL_NEW;PortalDefineQuery時設定PORTAL_DEFINED;PortalStart時設定PORTAL_READY;PortalRun時設定為PORTAL_ACTIVE
    bool        portalPinned;   /* a pinned portal can't be dropped */
    bool        autoHeld;       /* was automatically converted from pinned to held (see HoldPinnedPortals()) */
    
    /* If not NULL, Executor is active; call ExecutorEnd eventually: */
    QueryDesc  *queryDesc;      /* info needed for executor invocation */
    /* If portal returns tuples, this is their tupdesc: */
    TupleDesc   tupDesc;        /* descriptor for result tuples */
    /* and these are the format codes to use for the columns: */
    int16      *formats;        /* a format code for each column */
    /* Where we store tuples for a held cursor or a PORTAL_ONE_RETURNING or PORTAL_UTIL_SELECT query.  (A cursor held past the end of its transaction no longer has any active executor state.) */
    Tuplestorestate *holdStore; /* store for holdable cursors */
    MemoryContext holdContext;  /* memory containing holdStore */

    Snapshot    holdSnapshot;   /* registered snapshot, or NULL if none */

    bool        atStart;// CreatePortal時設定為true
    bool        atEnd;// CreatePortal時設定為true
    uint64      portalPos;
    /* Presentation data, primarily used by the pg_cursors system view */
    TimestampTz creation_time;/* time at which this portal was defined */ // CreatePortal時設定為GetCurrentStatementStartTimestamp
    bool        visible;/* include this portal in pg_cursors? */ // CreatePortal時設定為true,結束CreatePortal後設定為false
}           PortalData;      

把PortalRunMulti函數的執行流程單抽出來看一下,foreach從portal->stmts清單中抽取出PlannedStmt元素,如果utilityStmt不為null,說明是處理的utility函數(比如create, destroy, etc…),是以走else分支;由于canSetTag為true,故運作​

​PortalRunUtility(portal, pstmt, isTopLevel, false, dest, completionTag)​

​函數。

PostgreSQL查詢引擎——create table xxx(...)基礎建表流程

PortalRunUtility函數在運作ProcessUtility(standard_ProcessUtility)之前需要根據傳入的utilityStmt判定一下是否需要擷取快照,以及是否需要将快照設定到portal->holdSnapshot進行暫存,以備portal後續使用。從下圖堆棧可以看出,需要擷取快照,但無需暫存。

PostgreSQL查詢引擎——create table xxx(...)基礎建表流程

ProcessUtilitySlow流程

standard_ProcessUtility函數首先提取出PlannedStmt中的utilityStmt計劃樹,同時确定isTopLevel和isAtomicContext值,建立ParseState結構體,給ProcessUtilitySlow函數暫存中間結果。​

​switch(nodeTag(parsetree))​

​走的是default分支。

PostgreSQL查詢引擎——create table xxx(...)基礎建表流程
ProcessUtilitySlow (pstate=0x236fd00, pstmt=0x2408578,
    queryString=0x234add0 "CREATE TABLE films ( code char(5), title varchar(40), did integer, date_prod date, kind varchar(10), len interval hour t                     o minute);", context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x2408658, completionTag=0x7ffcf8fb0e60 "") at utility.c:958
958             Node       *parsetree = pstmt->utilityStmt;
(gdb) n
959             bool            isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
(gdb) n
960             bool            isCompleteQuery = (context != PROCESS_UTILITY_SUBCOMMAND);
(gdb) n
962             bool            commandCollected = false;
(gdb) n
964             ObjectAddress secondaryObject = InvalidObjectAddress;
(gdb) n
967             needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery();
(gdb) n
970             PG_TRY();
(gdb) n
972                     if (isCompleteQuery)
(gdb) n
973                             EventTriggerDDLCommandStart(parsetree);
(gdb) n
975                     switch (nodeTag(parsetree))
(gdb) n
997                                             RangeVar   *table_rv = NULL;
(gdb) n
1000                                            stmts = transformCreateStmt((CreateStmt *) parsetree,
(gdb) n
1008                                            while (stmts != NIL)
(gdb) n
1010                                                    Node       *stmt = (Node *) linitial(stmts);
(gdb) n
1012                                                    stmts = list_delete_first(stmts);
(gdb) n
1014                                                    if (IsA(stmt, CreateStmt))
(gdb) n
1016                                                            CreateStmt *cstmt = (CreateStmt *) stmt;
(gdb) n
1021                                                            table_rv = cstmt->relation;
(gdb) n
1024                                                            address = DefineRelation(cstmt,
(gdb) n
1028                                                            EventTriggerCollectSimpleCommand(address,
(gdb) n
1036                                                            CommandCounterIncrement();
(gdb) n
1042                                                            toast_options = transformRelOptions((Datum) 0,
(gdb) n
1048                                                            (void) heap_reloptions(RELKIND_TOASTVALUE,
(gdb) n
1052                                                            NewRelationCreateToastTable(address.objectId,
(gdb) n
1115                                                    if (stmts != NIL)
(gdb) n
1008                                            while (stmts != NIL)
(gdb) n
1123                                            commandCollected = true;
(gdb) n
1125                                    break;
(gdb) n
1748                    if (!commandCollected)
(gdb) n
1752                    if (isCompleteQuery)
(gdb) n
1754                            EventTriggerSQLDrop(parsetree);
(gdb) n
1755                            EventTriggerDDLCommandEnd(parsetree);
(gdb) n
1764            PG_END_TRY();
(gdb) n
1766            if (needCleanup)
(gdb) n
1768    }
(gdb) n      
CREATE TABLE films (
 code char(5) CONSTRAINT firstkey PRIMARY KEY,
 title varchar(40) NOT NULL,
 did integer NOT NULL,
 date_prod date,
 kind varchar(10),
 len interval hour to minute
);      
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] 
table_name ( [ { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
                 | table_constraint | LIKE source_table [ like_option ... ] } [, ... ] ] )
[ INHERITS ( parent_table [, ... ] ) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] 
table_name OF type_name [ ( { column_name [ WITH OPTIONS ] [ column_constraint [ ... ] ]
                              | table_constraint } [, ... ] ) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name
 PARTITION OF parent_table [ ( { column_name [ WITH OPTIONS ] [ column_constraint [ ... ] ]
                                 | table_constraint } [, ... ] ) ] { FOR VALUES partition_bound_spec | DEFAULT }
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]      
where column_constraint is:
[ CONSTRAINT constraint_name ] { NOT NULL | NULL | CHECK ( expression ) [ NO INHERIT ] | DEFAULT default_expr | GENERATED ALWAYS AS ( generation_expr ) STORED | GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] | UNIQUE index_parameters | PRIMARY KEY index_parameters | REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]


and table_constraint is:
[ CONSTRAINT constraint_name ]
{ CHECK ( expression ) [ NO INHERIT ] | UNIQUE ( column_name [, ... ] ) index_parameters | PRIMARY KEY ( column_name [, ... ] ) index_parameters | EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] | FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

and like_option is:
{ INCLUDING | EXCLUDING } 
{ COMMENTS | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }

and partition_bound_spec is:
IN ( partition_bound_expr [, ...] ) |
FROM ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] )
 TO ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] ) |
WITH ( MODULUS numeric_literal, REMAINDER numeric_literal )

index_parameters in UNIQUE, PRIMARY KEY, and EXCLUDE constraints are:
[ INCLUDE ( column_name [, ... ] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) ]
[ USING INDEX TABLESPACE tablespace_name ]

exclude_element in an EXCLUDE constraint is:
{ column_name | ( expression ) } 
[ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]