PostgreSQL ExecInitIndexScan 函数解析

发布于:2025-07-15 ⋅ 阅读:(18) ⋅ 点赞:(0)

该函数初始化索引扫描状态信息,创建扫描键,并打开表和索引的relation

测试表结构:

test=# \d poly100w
                                                     数据表 "sde.poly100w"
       栏位        |         类型          | 校对规则 |  可空的  |                            预设
-------------------+-----------------------+----------+----------+-------------------------------------------------------------
 objectid          | integer               |          | not null |
 globalid          | character varying(38) |          | not null | '{00000000-0000-0000-0000-000000000000}'::character varying
 gdb_geomattr_data | bytea                 |          |          |
 shape             | geometry              |          |          |
索引:
    "a2_ix1" gist (shape)
    "r13_sde_rowid_uk" UNIQUE, btree (objectid) WITH (fillfactor='75')
    "uuid_13" UNIQUE, btree (globalid) WITH (fillfactor='75')
检查约束限制
    "enforce_srid_shape" CHECK (st_srid(shape) = 3857)
select length(st_astext(shape)) from poly100w where objectid=100;

  代码段1 

IndexScanState *
ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
{
	IndexScanState *indexstate;
	Relation	currentRelation;
	LOCKMODE	lockmode;

	/*
	 * create state structure
	 */
    /*
        当前内存上下文为ExecutorState
    */
	indexstate = makeNode(IndexScanState);

    /*
        p *(Plan *) node
$52 = {type = T_IndexScan, startup_cost = 0.42749999999999999, total_cost = 9.0724999999999998, plan_rows = 1, plan_width = 4, parallel_aware = false, parallel_safe = true,async_capable = false, plan_node_id = 0, targetlist = 0x25ee420, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}
    */
	indexstate->ss.ps.plan = (Plan *) node;
	indexstate->ss.ps.state = estate;

    /*
      p indexstate->ss.ps.ExecProcNode
$56 = (ExecProcNodeMtd) 0x756c87 <ExecIndexScan>
    */
	indexstate->ss.ps.ExecProcNode = ExecIndexScan;

	/*
	 * Miscellaneous initialization
	 *
	 * create expression context for node
	 */

    /*创建ExprContext,并赋予planstate的ps_ExprContext变量
      并在estate->es_query_cxt中创建ExprContext上下文,并赋予econtext->ecxt_per_tuple_memory,具体实现参考下面的代码段2
   */
	ExecAssignExprContext(estate, &indexstate->ss.ps);

	/*
	 * open the scan relation(表,poly100w)
       具体看下面代码3部分
	 */
	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);

    //赋予scanstatede ss_currentRelation
	indexstate->ss.ss_currentRelation = currentRelation;
	indexstate->ss.ss_currentScanDesc = NULL;	/* no heap scan here */

	/*
	 * get the scan type from the relation descriptor.
	 */

    //见下面的代码4部分
	ExecInitScanTupleSlot(estate, &indexstate->ss,
						  RelationGetDescr(currentRelation),
						  table_slot_callbacks(currentRelation));

	/*
	 * Initialize result type and projection.
	 */

    /*
       void
        ExecInitResultTypeTL(PlanState *planstate)
        {

             /*p *planstate->plan->targetlist
$69 = {type = T_List, length = 1, max_length = 5, elements = 0x25ee438, initial_elements = 0x25ee438}*/
            //具体查看代码5
	        TupleDesc	tupDesc = ExecTypeFromTL(planstate->plan->targetlist);

	        planstate->ps_ResultTupleDesc = tupDesc;
        }
    */
	ExecInitResultTypeTL(&indexstate->ss.ps);

    //具体代码看https://blog.csdn.net/liufeng1980423/article/details/149253663?spm=1001.2014.3001.5501
	ExecAssignScanProjectionInfo(&indexstate->ss);

	/*
	 * initialize child expressions
	 *
	 * Note: we don't initialize all of the indexqual expression, only the
	 * sub-parts corresponding to runtime keys (see below).  Likewise for
	 * indexorderby, if any.  But the indexqualorig expression is always
	 * initialized even though it will only be used in some uncommon cases ---
	 * would be nice to improve that.  (Problem is that any SubPlans present
	 * in the expression must be found now...)
	 */
	indexstate->ss.ps.qual =
		ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
	indexstate->indexqualorig =
		ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
	indexstate->indexorderbyorig =
		ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);

	/*
	 * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
	 * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
	 * references to nonexistent indexes.
	 */
	if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
		return indexstate;

	/* Open the index relation. */
	lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
	indexstate->iss_RelationDesc = index_open(node->indexid, lockmode);

	/*
	 * Initialize index-specific scan state
	 */
	indexstate->iss_RuntimeKeysReady = false;
	indexstate->iss_RuntimeKeys = NULL;
	indexstate->iss_NumRuntimeKeys = 0;

	/*
	 * build the index scan keys from the index qualification
	 */
	ExecIndexBuildScanKeys((PlanState *) indexstate,
						   indexstate->iss_RelationDesc,
						   node->indexqual,
						   false,
						   &indexstate->iss_ScanKeys,
						   &indexstate->iss_NumScanKeys,
						   &indexstate->iss_RuntimeKeys,
						   &indexstate->iss_NumRuntimeKeys,
						   NULL,	/* no ArrayKeys */
						   NULL);

	/*
	 * any ORDER BY exprs have to be turned into scankeys in the same way
	 */
	ExecIndexBuildScanKeys((PlanState *) indexstate,
						   indexstate->iss_RelationDesc,
						   node->indexorderby,
						   true,
						   &indexstate->iss_OrderByKeys,
						   &indexstate->iss_NumOrderByKeys,
						   &indexstate->iss_RuntimeKeys,
						   &indexstate->iss_NumRuntimeKeys,
						   NULL,	/* no ArrayKeys */
						   NULL);

	/* Initialize sort support, if we need to re-check ORDER BY exprs */
	if (indexstate->iss_NumOrderByKeys > 0)
	{
		int			numOrderByKeys = indexstate->iss_NumOrderByKeys;
		int			i;
		ListCell   *lco;
		ListCell   *lcx;

		/*
		 * Prepare sort support, and look up the data type for each ORDER BY
		 * expression.
		 */
		Assert(numOrderByKeys == list_length(node->indexorderbyops));
		Assert(numOrderByKeys == list_length(node->indexorderbyorig));
		indexstate->iss_SortSupport = (SortSupportData *)
			palloc0(numOrderByKeys * sizeof(SortSupportData));
		indexstate->iss_OrderByTypByVals = (bool *)
			palloc(numOrderByKeys * sizeof(bool));
		indexstate->iss_OrderByTypLens = (int16 *)
			palloc(numOrderByKeys * sizeof(int16));
		i = 0;
		forboth(lco, node->indexorderbyops, lcx, node->indexorderbyorig)
		{
			Oid			orderbyop = lfirst_oid(lco);
			Node	   *orderbyexpr = (Node *) lfirst(lcx);
			Oid			orderbyType = exprType(orderbyexpr);
			Oid			orderbyColl = exprCollation(orderbyexpr);
			SortSupport orderbysort = &indexstate->iss_SortSupport[i];

			/* Initialize sort support */
			orderbysort->ssup_cxt = CurrentMemoryContext;
			orderbysort->ssup_collation = orderbyColl;
			/* See cmp_orderbyvals() comments on NULLS LAST */
			orderbysort->ssup_nulls_first = false;
			/* ssup_attno is unused here and elsewhere */
			orderbysort->ssup_attno = 0;
			/* No abbreviation */
			orderbysort->abbreviate = false;
			PrepareSortSupportFromOrderingOp(orderbyop, orderbysort);

			get_typlenbyval(orderbyType,
							&indexstate->iss_OrderByTypLens[i],
							&indexstate->iss_OrderByTypByVals[i]);
			i++;
		}

		/* allocate arrays to hold the re-calculated distances */
		indexstate->iss_OrderByValues = (Datum *)
			palloc(numOrderByKeys * sizeof(Datum));
		indexstate->iss_OrderByNulls = (bool *)
			palloc(numOrderByKeys * sizeof(bool));

		/* and initialize the reorder queue */
		indexstate->iss_ReorderQueue = pairingheap_allocate(reorderqueue_cmp,
															indexstate);
	}

	/*
	 * If we have runtime keys, we need an ExprContext to evaluate them. The
	 * node's standard context won't do because we want to reset that context
	 * for every tuple.  So, build another context just like the other one...
	 * -tgl 7/11/00
	 */
	if (indexstate->iss_NumRuntimeKeys != 0)
	{
		ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;

		ExecAssignExprContext(estate, &indexstate->ss.ps);
		indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
		indexstate->ss.ps.ps_ExprContext = stdecontext;
	}
	else
	{
		indexstate->iss_RuntimeContext = NULL;
	}

	/*
	 * all done.
	 */
	return indexstate;
}

  代码段2 

static ExprContext *
CreateExprContextInternal(EState *estate, Size minContextSize,
						  Size initBlockSize, Size maxBlockSize)
{
	ExprContext *econtext;
	MemoryContext oldcontext;


    /*
       当前内存上下文为ExecutorState
    */
	/* Create the ExprContext node within the per-query memory context */
	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);

	econtext = makeNode(ExprContext);

	/* Initialize fields of ExprContext */
    //单表的扫描tuple
	econtext->ecxt_scantuple = NULL;
    //关联内表的扫描tuple
	econtext->ecxt_innertuple = NULL;
    //关联外表的扫描tuple
	econtext->ecxt_outertuple = NULL;

	econtext->ecxt_per_query_memory = estate->es_query_cxt;

	/*
	 * Create working memory for expression evaluation in this context.
	 */
	econtext->ecxt_per_tuple_memory =
		AllocSetContextCreate(estate->es_query_cxt,
							  "ExprContext",
							  minContextSize,
							  initBlockSize,
							  maxBlockSize);

	econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
	econtext->ecxt_param_list_info = estate->es_param_list_info;

	econtext->ecxt_aggvalues = NULL;
	econtext->ecxt_aggnulls = NULL;

	econtext->caseValue_datum = (Datum) 0;
	econtext->caseValue_isNull = true;

	econtext->domainValue_datum = (Datum) 0;
	econtext->domainValue_isNull = true;

	econtext->ecxt_estate = estate;

	econtext->ecxt_callbacks = NULL;

	/*
	 * Link the ExprContext into the EState to ensure it is shut down when the
	 * EState is freed.  Because we use lcons(), shutdowns will occur in
	 * reverse order of creation, which may not be essential but can't hurt.
	 */

    /*estate->es_exprcontexts
$59 = {type = T_List, length = 1, max_length = 5, elements = 0x25e1418, initial_elements = 0x25e1418}*/
	estate->es_exprcontexts = lcons(econtext, estate->es_exprcontexts);

	MemoryContextSwitchTo(oldcontext);

	return econtext;
}

  代码段3:


Relation
ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
{
	Relation	rel;

	/* Open the relation. */
	rel = ExecGetRangeTableRelation(estate, scanrelid);

	/*
	 * Complain if we're attempting a scan of an unscannable relation, except
	 * when the query won't actually be run.  This is a slightly klugy place
	 * to do this, perhaps, but there is no better place.
	 */
	if ((eflags & (EXEC_FLAG_EXPLAIN_ONLY | EXEC_FLAG_WITH_NO_DATA)) == 0 &&
		!RelationIsScannable(rel))
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("materialized view \"%s\" has not been populated",
						RelationGetRelationName(rel)),
				 errhint("Use the REFRESH MATERIALIZED VIEW command.")));

	return rel;
}

Relation
ExecGetRangeTableRelation(EState *estate, Index rti)
{
	Relation	rel;

	Assert(rti > 0 && rti <= estate->es_range_table_size);

	rel = estate->es_relations[rti - 1];
	if (rel == NULL)
	{
		/* First time through, so open the relation */

        /*static inline RangeTblEntry *
        exec_rt_fetch(Index rti, EState *estate)
        {
	        return (RangeTblEntry *) list_nth(estate->es_range_table, rti - 1);
        }*/

		RangeTblEntry *rte = exec_rt_fetch(rti, estate);

		Assert(rte->rtekind == RTE_RELATION);

		if (!IsParallelWorker())
		{
			/*
			 * In a normal query, we should already have the appropriate lock,
			 * but verify that through an Assert.  Since there's already an
			 * Assert inside table_open that insists on holding some lock, it
			 * seems sufficient to check this only when rellockmode is higher
			 * than the minimum.
			 */
			rel = table_open(rte->relid, NoLock);
			Assert(rte->rellockmode == AccessShareLock ||
				   CheckRelationLockedByMe(rel, rte->rellockmode, false));
		}
		else
		{
			/*
			 * If we are a parallel worker, we need to obtain our own local
			 * lock on the relation.  This ensures sane behavior in case the
			 * parent process exits before we do.
			 */
			rel = table_open(rte->relid, rte->rellockmode);
		}

		estate->es_relations[rti - 1] = rel;
	}

	return rel;
}

代码段4

void
ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
					  TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
{
    /*
       p *tupledesc
$64 = {natts = 4, tdtypeid = 6789769, tdtypmod = -1, tdrefcount = 1, constr = 0x7f085fc8eea8, attrs = 0x7f085fc8ecb8}
    */
	scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
													 tupledesc, tts_ops);
	scanstate->ps.scandesc = tupledesc;
	scanstate->ps.scanopsfixed = tupledesc != NULL;
	scanstate->ps.scanops = tts_ops;
	scanstate->ps.scanopsset = true;
}

TupleTableSlot *
ExecAllocTableSlot(List **tupleTable, TupleDesc desc,
				   const TupleTableSlotOps *tts_ops)
{
	TupleTableSlot *slot = MakeTupleTableSlot(desc, tts_ops);

	*tupleTable = lappend(*tupleTable, slot);

	return slot;
}


/* --------------------------------
 *		MakeTupleTableSlot
 *
 *		Basic routine to make an empty TupleTableSlot of given
 *		TupleTableSlotType. If tupleDesc is specified the slot's descriptor is
 *		fixed for its lifetime, gaining some efficiency. If that's
 *		undesirable, pass NULL.
 * --------------------------------
 */
TupleTableSlot *
MakeTupleTableSlot(TupleDesc tupleDesc,
				   const TupleTableSlotOps *tts_ops)
{
	Size		basesz,
				allocsz;
	TupleTableSlot *slot;

	basesz = tts_ops->base_slot_size;

	/*
	 * When a fixed descriptor is specified, we can reduce overhead by
	 * allocating the entire slot in one go.
	 */
	if (tupleDesc)
		allocsz = MAXALIGN(basesz) +
			MAXALIGN(tupleDesc->natts * sizeof(Datum)) +
			MAXALIGN(tupleDesc->natts * sizeof(bool));
	else
		allocsz = basesz;
    //CurrentMemoryText: ExecutorState
	slot = palloc0(allocsz);
	/* const for optimization purposes, OK to modify at allocation time */
	*((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops;
	slot->type = T_TupleTableSlot;
	slot->tts_flags |= TTS_FLAG_EMPTY;
	if (tupleDesc != NULL)
		slot->tts_flags |= TTS_FLAG_FIXED;
	slot->tts_tupleDescriptor = tupleDesc;
	slot->tts_mcxt = CurrentMemoryContext;
	slot->tts_nvalid = 0;

	if (tupleDesc != NULL)
	{
		slot->tts_values = (Datum *)
			(((char *) slot)
			 + MAXALIGN(basesz));
		slot->tts_isnull = (bool *)
			(((char *) slot)
			 + MAXALIGN(basesz)
			 + MAXALIGN(tupleDesc->natts * sizeof(Datum)));

		PinTupleDesc(tupleDesc);
	}

	/*
	 * And allow slot type specific initialization.
	 */
	slot->tts_ops->init(slot);

	return slot;
}

代码5

/* ----------------------------------------------------------------
 *		ExecTypeFromTL
 *
 *		Generate a tuple descriptor for the result tuple of a targetlist.
 *		(A parse/plan tlist must be passed, not an ExprState tlist.)
 *		Note that resjunk columns, if any, are included in the result.
 *
 *		Currently there are about 4 different places where we create
 *		TupleDescriptors.  They should all be merged, or perhaps
 *		be rewritten to call BuildDesc().
 * ----------------------------------------------------------------
 */
TupleDesc
ExecTypeFromTL(List *targetList)
{
	return ExecTypeFromTLInternal(targetList, false);
}

static TupleDesc
ExecTypeFromTLInternal(List *targetList, bool skipjunk)
{
	TupleDesc	typeInfo;
	ListCell   *l;
	int			len;
	int			cur_resno = 1;

	if (skipjunk)
		len = ExecCleanTargetListLength(targetList);
	else
		len = ExecTargetListLength(targetList);
	typeInfo = CreateTemplateTupleDesc(len);

	foreach(l, targetList)
	{
		TargetEntry *tle = lfirst(l);

		if (skipjunk && tle->resjunk)
			continue;

        /*
              length
              *(FuncExpr*) tle->expr
$84 = {xpr = {type = T_FuncExpr}, funcid = 1317, funcresulttype = 23, funcretset = false, funcvariadic = false, funcformat = COERCE_EXPLICIT_CALL, funccollid = 0,
  inputcollid = 100, args = 0x25281a0, location = 7}
          select 1317::regprocedure;
             regprocedure
             --------------
             length(text)
           
        */
		TupleDescInitEntry(typeInfo,
						   cur_resno,
						   tle->resname,
						   exprType((Node *) tle->expr),
						   exprTypmod((Node *) tle->expr),
						   0);
		TupleDescInitEntryCollation(typeInfo,
									cur_resno,
									exprCollation((Node *) tle->expr));
		cur_resno++;
	}

	return typeInfo;
}


网站公告

今日签到

点亮在社区的每一天
去签到