PostgreSQL在何处处理 sql查询之三十三
接前面,在 PortalStart 中调用了 ExecutorStart,ExecutorStart 会调用 InitPlan:
/* ---------------------------------------------------------------- * InitPlan * * Initializes the query plan: open files, allocate storage * and start up the rule manager * ---------------------------------------------------------------- */ static void InitPlan(QueryDesc *queryDesc, int eflags) { CmdType operation = queryDesc->operation; PlannedStmt *plannedstmt = queryDesc->plannedstmt; Plan *plan = plannedstmt->planTree; List *rangeTable = plannedstmt->rtable; EState *estate = queryDesc->estate; PlanState *planstate; TupleDesc tupType; ListCell *l; int i; /* * Do permissions checks */ ExecCheckRTPerms(rangeTable, true); /* * initialize the node's execution state */ estate->es_range_table = rangeTable; estate->es_plannedstmt = plannedstmt; /* * initialize result relation stuff, and open/lock the result rels. * * We must do this before initializing the plan tree, else we might try to * do a lock upgrade if a result rel is also a source rel. */ if (plannedstmt->resultRelations) { List *resultRelations = plannedstmt->resultRelations; int numResultRelations = list_length(resultRelations); ResultRelInfo *resultRelInfos; ResultRelInfo *resultRelInfo; resultRelInfos = (ResultRelInfo *) palloc(numResultRelations * sizeof(ResultRelInfo)); resultRelInfo = resultRelInfos; foreach(l, resultRelations) { Index resultRelationIndex = lfirst_int(l); Oid resultRelationOid; Relation resultRelation; resultRelationOid = getrelid(resultRelationIndex, rangeTable); resultRelation = heap_open(resultRelationOid, RowExclusiveLock); InitResultRelInfo(resultRelInfo, resultRelation, resultRelationIndex, estate->es_instrument); resultRelInfo++; } estate->es_result_relations = resultRelInfos; estate->es_num_result_relations = numResultRelations; /* es_result_relation_info is NULL except when within ModifyTable */ estate->es_result_relation_info = NULL; } else { /* * if no result relation, then set state appropriately */ estate->es_result_relations = NULL; estate->es_num_result_relations = 0; estate->es_result_relation_info = NULL; } /* * Similarly, we have to lock relations selected FOR UPDATE/FOR SHARE * before we initialize the plan tree, else we'd be risking lock upgrades. * While we are at it, build the ExecRowMark list. */ estate->es_rowMarks = NIL; foreach(l, plannedstmt->rowMarks) { PlanRowMark *rc = (PlanRowMark *) lfirst(l); Oid relid; Relation relation; ExecRowMark *erm; /* ignore "parent" rowmarks; they are irrelevant at runtime */ if (rc->isParent) continue; switch (rc->markType) { case ROW_MARK_EXCLUSIVE: case ROW_MARK_SHARE: relid = getrelid(rc->rti, rangeTable); relation = heap_open(relid, RowShareLock); break; case ROW_MARK_REFERENCE: relid = getrelid(rc->rti, rangeTable); relation = heap_open(relid, AccessShareLock); break; case ROW_MARK_COPY: /* there's no real table here ... */ relation = NULL; break; default: elog(ERROR, "unrecognized markType: %d", rc->markType); relation = NULL; /* keep compiler quiet */ break; } /* Check that relation is a legal target for marking */ if (relation) CheckValidRowMarkRel(relation, rc->markType); erm = (ExecRowMark *) palloc(sizeof(ExecRowMark)); erm->relation = relation; erm->rti = rc->rti; erm->prti = rc->prti; erm->rowmarkId = rc->rowmarkId; erm->markType = rc->markType; erm->noWait = rc->noWait; ItemPointerSetInvalid(&(erm->curCtid)); estate->es_rowMarks = lappend(estate->es_rowMarks, erm); } /* * Initialize the executor's tuple table to empty. */ estate->es_tupleTable = NIL; estate->es_trig_tuple_slot = NULL; estate->es_trig_oldtup_slot = NULL; estate->es_trig_newtup_slot = NULL; /* mark EvalPlanQual not active */ estate->es_epqTuple = NULL; estate->es_epqTupleSet = NULL; estate->es_epqScanDone = NULL; /* * Initialize private state information for each SubPlan. We must do this * before running ExecInitNode on the main query tree, since * ExecInitSubPlan expects to be able to find these entries. */ Assert(estate->es_subplanstates == NIL); i = 1; /* subplan indices count from 1 */ foreach(l, plannedstmt->subplans) { Plan *subplan = (Plan *) lfirst(l); PlanState *subplanstate; int sp_eflags; /* * A subplan will never need to do BACKWARD scan nor MARK/RESTORE. If * it is a parameterless subplan (not initplan), we suggest that it be * prepared to handle REWIND efficiently; otherwise there is no need. */ sp_eflags = eflags & EXEC_FLAG_EXPLAIN_ONLY; if (bms_is_member(i, plannedstmt->rewindPlanIDs)) sp_eflags |= EXEC_FLAG_REWIND; subplanstate = ExecInitNode(subplan, estate, sp_eflags); estate->es_subplanstates = lappend(estate->es_subplanstates, subplanstate); i++; } /* * Initialize the private state information for all the nodes in the query * tree. This opens files, allocates storage and leaves us ready to start * processing tuples. */ planstate = ExecInitNode(plan, estate, eflags); /* * Get the tuple descriptor describing the type of tuples to return. */ tupType = ExecGetResultType(planstate); /* * Initialize the junk filter if needed. SELECT queries need a filter if * there are any junk attrs in the top-level tlist. */ if (operation == CMD_SELECT) { bool junk_filter_needed = false; ListCell *tlist; foreach(tlist, plan->targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(tlist); if (tle->resjunk) { junk_filter_needed = true; break; } } if (junk_filter_needed) { JunkFilter *j; j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, ExecInitExtraTupleSlot(estate)); estate->es_junkFilter = j; /* Want to return the cleaned tuple type */ tupType = j->jf_cleanTupType; } } queryDesc->tupDesc = tupType; queryDesc->planstate = planstate; }
先从 tupDesc 来入手吧:
/* * This struct is passed around within the backend to describe the structure * of tuples. For tuples coming from on-disk relations, the information is * collected from the pg_attribute, pg_attrdef, and pg_constraint catalogs. * Transient row types (such as the result of a join query) have anonymous * TupleDesc structs that generally omit any constraint info; therefore the * structure is designed to let the constraints be omitted efficiently. * * Note that only user attributes, not system attributes, are mentioned in * TupleDesc; with the exception that tdhasoid indicates if OID is present. * * If the tupdesc is known to correspond to a named rowtype (such as a table's * rowtype) then tdtypeid identifies that type and tdtypmod is -1. Otherwise * tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous * row type, or a value >= 0 to allow the rowtype to be looked up in the * typcache.c type cache. * * Tuple descriptors that live in caches (relcache or typcache, at present) * are reference-counted: they can be deleted when their reference count goes * to zero. Tuple descriptors created by the executor need no reference * counting, however: they are simply created in the appropriate memory * context and go away when the context is freed. We set the tdrefcount * field of such a descriptor to -1, while reference-counted descriptors * always have tdrefcount >= 0. */ typedef struct tupleDesc { int natts; /* number of attributes in the tuple */ Form_pg_attribute *attrs; /* attrs[N] is a pointer to the description of Attribute Number N+1 */ TupleConstr *constr; /* constraints, or NULL if none */ Oid tdtypeid; /* composite type ID for tuple type */ int32 tdtypmod; /* typmod for tuple type */ bool tdhasoid; /* tuple has oid attribute in its header */ int tdrefcount; /* reference count, or -1 if not counting */ } *TupleDesc;
对InitPlan进行简化和进一步分析:
static void InitPlan(QueryDesc *queryDesc, int eflags) { ... /* * initialize result relation stuff, and open/lock the result rels. * * We must do this before initializing the plan tree, else we might try to * do a lock upgrade if a result rel is also a source rel. */ if (plannedstmt->resultRelations) { ... } else { /* * if no result relation, then set state appropriately */ estate->es_result_relations = NULL; estate->es_num_result_relations = 0; estate->es_result_relation_info = NULL; } ... queryDesc->tupDesc = tupType; queryDesc->planstate = planstate; }
实际测试后发现 select * from tst01 这样的SQL文,
得到的 (plannedstmt->resultRelations) 判断值为false。
然后,在 foreach(l, plannedstmt->rowMarks) 之前加点判断:
l= list_head(plannedstmt->rowMarks); if (l != NULL) fprintf(stderr, "l is not null\n"); else fprintf(stderr,"l is null\n");
发现,l 是空值。
/* ---------------------------------------------------------------- * InitPlan * * Initializes the query plan: open files, allocate storage * and start up the rule manager * ---------------------------------------------------------------- */ static void InitPlan(QueryDesc *queryDesc, int eflags) { ... if (plannedstmt->resultRelations) { ... } else { /* * if no result relation, then set state appropriately */ estate->es_result_relations = NULL; estate->es_num_result_relations = 0; estate->es_result_relation_info = NULL; } /* * Similarly, we have to lock relations selected FOR UPDATE/FOR SHARE * before we initialize the plan tree, else we'd be risking lock upgrades. * While we are at it, build the ExecRowMark list. */ estate->es_rowMarks = NIL; foreach(l, plannedstmt->rowMarks) { ... } /* * Initialize the executor's tuple table to empty. */ estate->es_tupleTable = NIL; estate->es_trig_tuple_slot = NULL; estate->es_trig_oldtup_slot = NULL; estate->es_trig_newtup_slot = NULL; /* mark EvalPlanQual not active */ estate->es_epqTuple = NULL; estate->es_epqTupleSet = NULL; estate->es_epqScanDone = NULL; ... }
接着往下分析:
发现运行 select * from test01 where id<10 这样的sql文的时候,
foreach(l, plannedstmt->subplans) 也一次没有得到执行。
static void InitPlan(QueryDesc *queryDesc, int eflags) { ... if (plannedstmt->resultRelations) { ... } else { /* * if no result relation, then set state appropriately */ estate->es_result_relations = NULL; estate->es_num_result_relations = 0; estate->es_result_relation_info = NULL; } ... foreach(l, plannedstmt->rowMarks) { ... } /* * Initialize the executor's tuple table to empty. */ estate->es_tupleTable = NIL; estate->es_trig_tuple_slot = NULL; estate->es_trig_oldtup_slot = NULL; estate->es_trig_newtup_slot = NULL; /* mark EvalPlanQual not active */ estate->es_epqTuple = NULL; estate->es_epqTupleSet = NULL; estate->es_epqScanDone = NULL; ... i = 1; /* subplan indices count from 1 */ foreach(l, plannedstmt->subplans) { ... } /* * Initialize the private state information for all the nodes in the query * tree. This opens files, allocates storage and leaves us ready to start * processing tuples. */ planstate = ExecInitNode(plan, estate, eflags); /* * Get the tuple descriptor describing the type of tuples to return. */ tupType = ExecGetResultType(planstate); ... }
接下来看 tupType到底是什么,也就是表的记录的属性信息。
typedef struct tupleDesc { int natts; /* number of attributes in the tuple */ Form_pg_attribute *attrs; /* attrs[N] is a pointer to the description of Attribute Number N+1 */ TupleConstr *constr; /* constraints, or NULL if none */ Oid tdtypeid; /* composite type ID for tuple type */ int32 tdtypmod; /* typmod for tuple type */ bool tdhasoid; /* tuple has oid attribute in its header */ int tdrefcount; /* reference count, or -1 if not counting */ } *TupleDesc;
重新整理一下:
static void InitPlan(QueryDesc *queryDesc, int eflags) { ... if (plannedstmt->resultRelations) { ... } else { estate->es_result_relations = NULL; estate->es_num_result_relations = 0; estate->es_result_relation_info = NULL; } ... /* * Initialize the private state information for all the nodes in the query * tree. This opens files, allocates storage and leaves us ready to start * processing tuples. */ planstate = ExecInitNode(plan, estate, eflags); /* * Get the tuple descriptor describing the type of tuples to return. */ tupType = ExecGetResultType(planstate); /* * Initialize the junk filter if needed. SELECT queries need a filter if * there are any junk attrs in the top-level tlist. */ if (operation == CMD_SELECT) { bool junk_filter_needed = false; ListCell *tlist; foreach(tlist, plan->targetlist) { fprintf(stderr,"In foreach (tlist, plan->targetlist) \n"); TargetEntry *tle = (TargetEntry *) lfirst(tlist); if (tle->resjunk) { junk_filter_needed = true; break; } } ... } queryDesc->tupDesc = tupType; queryDesc->planstate = planstate; }
有趣的事情来了:这一段,
如果我 select id from tst04; for 循环执行一次
如果我 select id,val from tst04; for 循环执行二次。
也就是说 plan->targelist 的长度,就是 select 列表里字段的个数。
foreach(tlist, plan->targetlist) { fprintf(stderr,"In foreach (tlist, plan->targetlist) \n"); TargetEntry *tle = (TargetEntry *) lfirst(tlist); if (tle->resjunk) { junk_filter_needed = true; break; } }
事实上,这个在 调用 InitPlan之前,就已经准备好了:
Plan *plan = plannedstmt->planTree;