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 ]

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列印的文法樹如下所示,可以說是完全一緻的。
如下所示,對于create table基礎建表語句,pg_plan_queries沒有經過pg_plan_query函數,而是直接建立PlannedStmt節點,然後将query->utilityStmt設定到PlannedStmt節點的utilityStmt成員。
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)
函數。
PortalRunUtility函數在運作ProcessUtility(standard_ProcessUtility)之前需要根據傳入的utilityStmt判定一下是否需要擷取快照,以及是否需要将快照設定到portal->holdSnapshot進行暫存,以備portal後續使用。從下圖堆棧可以看出,需要擷取快照,但無需暫存。
ProcessUtilitySlow流程
standard_ProcessUtility函數首先提取出PlannedStmt中的utilityStmt計劃樹,同時确定isTopLevel和isAtomicContext值,建立ParseState結構體,給ProcessUtilitySlow函數暫存中間結果。
switch(nodeTag(parsetree))
走的是default分支。
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 } ]