4.ソース読みに慣れよう - PostgreSQL のソースコードを読む
小さな課題を設定し読み進める事で PostgreSQL のソースを読みに慣れよう。昨日設定した課題は「/tcop/postgres.c にある exec_simple_query(const char *query_string) から大まかに処理を追い、結果をまとめる」。
結果は以下の通り。PostgreSQL のソースは比較的読みやすいと聞いていたが確かに読みやすかった。コメントが多いのが良い。
- pg_parse_query(query_string)
- SQL の parse
- 戻り値は parse された Tree Listらしい
- for-each item in Tree List
- pg_analyze_and_rewrite
- pg_plan_queries
- pg_analyze_and_rewrite の結果を入力として query plan を列挙する
- いや違う。分解された Query block それぞれに対して Query plan を構築する
- 内部で planner 関数を読んでいる。optimizer 。
- planner_hook とかあるよ。
- 戻り値は PlannedStmt
- PortalRun : 実行
- PortalRunSelect
- ExecutorRun : Query plan を実行する
- ExecutePlan : 戻り値は TupleTableSlot
- EndCommand
さて次の課題はどうしようかな。tuple が Storage から fetch される部分を読んでみようか。
Query
typedef struct Query { NodeTag type; CmdType commandType; /* select|insert|update|delete|utility */ QuerySource querySource; /* where did I come from? */ bool canSetTag; /* do I set the command result tag? */ Node *utilityStmt; /* non-null if this is DECLARE CURSOR or a * non-optimizable statement */ int resultRelation; /* rtable index of target relation for * INSERT/UPDATE/DELETE; 0 for SELECT */ IntoClause *intoClause; /* target for SELECT INTO / CREATE TABLE AS */ bool hasAggs; /* has aggregates in tlist or havingQual */ bool hasSubLinks; /* has subquery SubLink */ List *rtable; /* list of range table entries */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ List *returningList; /* return-values list (of TargetEntry) */ List *groupClause; /* a list of GroupClause's */ Node *havingQual; /* qualifications applied to groups */ List *distinctClause; /* a list of SortClause's */ List *sortClause; /* a list of SortClause'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 */ } Query;
PlannedStmt
typedef struct PlannedStmt { NodeTag type; CmdType commandType; /* select|insert|update|delete */ bool canSetTag; /* do I set the command result tag? */ bool transientPlan; /* redo plan when TransactionXmin changes? */ struct Plan *planTree; /* tree of Plan nodes */ List *rtable; /* list of RangeTblEntry nodes */ /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ Node *utilityStmt; /* non-null if this is DECLARE CURSOR */ IntoClause *intoClause; /* target for SELECT INTO / CREATE TABLE AS */ List *subplans; /* Plan trees for SubPlan expressions */ Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */ /* * If the query has a returningList then the planner will store a list of * processed targetlists (one per result relation) here. We must have a * separate RETURNING targetlist for each result rel because column * numbers may vary within an inheritance tree. In the targetlists, Vars * referencing the result relation will have their original varno and * varattno, while Vars referencing other rels will be converted to have * varno OUTER and varattno referencing a resjunk entry in the top plan * node's targetlist. */ List *returningLists; /* list of lists of TargetEntry, or NIL */ List *rowMarks; /* a list of RowMarkClause's */ List *relationOids; /* OIDs of relations the plan depends on */ int nParamExec; /* number of PARAM_EXEC Params used */ } PlannedStmt;
Plan
typedef struct Plan { NodeTag type; /* * estimated execution costs for plan (see costsize.c for more info) */ Cost startup_cost; /* cost expended before fetching any tuples */ Cost total_cost; /* total cost (assuming all tuples fetched) */ /* * planner's estimate of result size of this plan step */ double plan_rows; /* number of rows plan is expected to emit */ int plan_width; /* average row width in bytes */ /* * Common structural data for all Plan types. */ List *targetlist; /* target list to be computed at this node */ List *qual; /* implicitly-ANDed qual conditions */ struct Plan *lefttree; /* input plan tree(s) */ struct Plan *righttree; List *initPlan; /* Init Plan nodes (un-correlated expr * subselects) */ /* * Information for management of parameter-change-driven rescanning * * extParam includes the paramIDs of all external PARAM_EXEC params * affecting this plan node or its children. setParam params from the * node's initPlans are not included, but their extParams are. * * allParam includes all the extParam paramIDs, plus the IDs of local * params that affect the node (i.e., the setParams of its initplans). * These are _all_ the PARAM_EXEC params that affect this node. */ Bitmapset *extParam; Bitmapset *allParam; } Plan;
TupleTableSlot
typedef struct TupleTableSlot { NodeTag type; /* vestigial ... allows IsA tests */ bool tts_isempty; /* true = slot is empty */ bool tts_shouldFree; /* should pfree tuple? */ bool tts_slow; /* saved state for slot_deform_tuple */ HeapTuple tts_tuple; /* physical tuple, or NULL if none */ TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ MemoryContext tts_mcxt; /* slot itself is in this context */ Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */ int tts_nvalid; /* # of valid values in tts_values */ Datum *tts_values; /* current per-attribute values */ bool *tts_isnull; /* current per-attribute isnull flags */ MinimalTuple tts_mintuple; /* set if it's a minimal tuple, else NULL */ HeapTupleData tts_minhdr; /* workspace if it's a minimal tuple */ long tts_off; /* saved state for slot_deform_tuple */ } TupleTableSlot;