Compare commits

..

31 Commits

Author SHA1 Message Date
LYLlyl 8a28dcc5dd Update execMain.cpp 2023-10-04 17:20:10 +08:00
LYLlyl 057cfbb9cb Update execMain.cpp 2023-10-03 23:58:20 +08:00
TerryTongJ de4ed4867e Update execMain.cpp 2023-09-30 17:47:44 +08:00
LYLlyl e93533c452 Update lightProxy.cpp 2023-09-30 16:12:23 +08:00
ljh0804 125526ed46 Update execTuples.cpp 2023-09-27 19:45:13 +08:00
ljh0804 fbd61d8ce3 Update execTuples.cpp 2023-09-27 16:32:41 +08:00
ljh0804 c555e77989 Update execTuples.cpp 2023-09-27 16:25:51 +08:00
ljh0804 cb286262f4 Update execTuples.cpp 2023-09-20 19:56:14 +08:00
ljh0804 d611d9d258 Update execTuples.cpp 2023-09-20 19:25:23 +08:00
ljh0804 4d5adc9646 Update execTuples.cpp 2023-09-10 03:19:07 +08:00
LYLlyl 3535ac2dbd Update lightProxy.cpp 2023-09-06 21:43:50 +08:00
ljh0804 bd5ccf49db Update execTuples.cpp 2023-09-06 20:27:48 +08:00
ljh0804 c986f2fecc Update execScan.cpp 2023-09-05 23:12:01 +08:00
LYLlyl f1a9e2ac18 Update execUtils.cpp 2023-09-05 20:59:57 +08:00
ljh0804 b6594788c8 Update execScan.cpp 2023-09-05 11:01:35 +08:00
TerryTongJ 46f5f7ecfa Update execQual.cpp 2023-09-04 10:52:29 +08:00
ljh0804 20ef956102 Update execReplication.cpp 2023-08-28 20:22:05 +08:00
ljh0804 915182695a Update execReplication.cpp 2023-08-25 02:15:25 +08:00
ljh0804 ffe1c04626 Update execReplication.cpp 2023-08-23 00:47:19 +08:00
ljh0804 41d7a01e33 Update execReplication.cpp 2023-08-21 23:42:15 +08:00
LYLlyl 58d7fc03e6 Update execUtils.cpp 2023-08-20 18:10:07 +08:00
LYLlyl 279749ec0a Update execProcnode.cpp 2023-08-17 21:03:46 +08:00
LYLlyl 39f68e8d2f Update execProcnode.cpp 2023-08-16 20:45:28 +08:00
ljh0804 3db7a14436 Update execGrouping.cpp 2023-08-10 22:15:08 +08:00
ljh0804 44f750e018 Update execCurrent.cpp 2023-08-10 21:14:09 +08:00
ljh0804 50230625f0 Update execClusterResize.cpp 2023-08-10 20:52:45 +08:00
LYLlyl ea34e898c5 Update execMerge.cpp 2023-08-10 18:26:12 +08:00
TerryTongJ 05aa68b9d3 Update execMerge.cpp 2023-08-10 18:25:21 +08:00
TerryTongJ 40d6b07f99 Update execMerge.cpp 2023-08-10 18:22:43 +08:00
LYLlyl 1d58f78f6b Update execMerge.cpp 2023-08-09 19:19:33 +08:00
LYLlyl dd1fb6010f Update execJunk.cpp 2023-08-08 18:44:15 +08:00
13 changed files with 4579 additions and 3222 deletions

View File

@ -3,9 +3,9 @@
* execClusterResize.cpp
* MPPDB ClusterResizing relevant routines
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* c 2020
* c 1996-2012PostgreSQL
* c 1994
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/execClusterResize.cpp
@ -44,10 +44,10 @@
/*
* ---------------------------------------------------------------------------------
* *Local functions/variables declaration fields*
* /*
* ---------------------------------------------------------------------------------
*/
/* delete delta table definition */
/*删除增量表定义 */
#define Natts_pg_delete_delta 3
#define Anum_pg_delete_delta_xcnodeid_and_dntableoid 1
@ -120,12 +120,12 @@ static inline bool redis_ctid_retrive_function(const char* funcname, Oid rettype
/*
* - Brief: Record the given tuple's tupleid into pg_delete_delta table
* - Parameter:
* @rel: target relation of UPDATE/DELETE operation
* @tupleid: tupleid that needs record
* - Return:
* no return value
*pg_delete_delta表中
* -
* @rel/
* @tupleid
*-
*
*/
void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, const Relation deldelta_rel)
{
@ -134,10 +134,10 @@ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, con
HeapTuple tup = NULL;
Assert(deldelta_rel);
/* In redistribution, table delete_delta has 3 or 2 column. */
/*在重新分发中,表 delete_delta 有 3 列或 2 列。 */
Assert(RelationGetDescr(deldelta_rel)->natts <= 3);
/* Iterate through attributes initializing nulls and values */
/*循环访问初始化空值和值的属性 */
for (int i = 0; i < Natts_pg_delete_delta; i++) {
nulls[i] = false;
values[i] = (Datum)0;
@ -149,7 +149,7 @@ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, con
if (BUCKET_NODE_IS_VALID(bucketid)) {
values[Anum_pg_delete_delta_tablebucketid_and_ctid - 1] |= ((uint64)bucketid << 48);
}
/* Record delta */
/* 记录增量 */
tup = heap_form_tuple(RelationGetDescr(deldelta_rel), values, nulls);
(void)simple_heap_insert(deldelta_rel, tup);
@ -157,18 +157,18 @@ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, con
}
/*
* - Brief: Determine if the relation is under cluster resizing operation
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing
* @FALSE: relation is not under cluster resizing
* -
* -
* @rel
* -
* @TRUE
* @FALSE:
*/
bool RelationInClusterResizing(const Relation rel)
{
Assert(rel != NULL);
/* Check relation's append_mode status */
/*检查关系的append_mode状态 */
if (!IsInitdb && RelationInRedistribute(rel))
return true;
@ -176,18 +176,18 @@ bool RelationInClusterResizing(const Relation rel)
}
/*
* - Brief: Determine if the relation is under cluster resizing read only operation
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing read only
* @FALSE: relation is not under cluster resizing read only
* - :
* - :
* @rel:
* - :
* @TRUE:
* @FALSE:
*/
bool RelationInClusterResizingReadOnly(const Relation rel)
{
Assert(rel != NULL);
/* Check relation's append_mode status */
/*检查关系的append_mode状态 */
if (!IsInitdb && RelationInRedistributeReadOnly(rel))
return true;
@ -195,18 +195,18 @@ bool RelationInClusterResizingReadOnly(const Relation rel)
}
/*
* - Brief: Determine if the relation is under cluster resizing read only operation
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing endcatchup(write error)
* @FALSE: relation is not under cluster resizing endcatchup(write error)
* - :
* - :
* @rel:
* - :
* @TRUE: endcatchup()
* @FALSE: endcatchup()
*/
bool RelationInClusterResizingEndCatchup(const Relation rel)
{
Assert(rel != NULL);
/* Check relation's append_mode status */
/* 检查关系的append_mode状态*/
if (!IsInitdb && RelationInRedistributeEndCatchup(rel))
return true;
@ -214,9 +214,9 @@ bool RelationInClusterResizingEndCatchup(const Relation rel)
}
/*
* @Description: check whether relation is in redistribution though range variable.
* @in range_var: range variable which stored relation info.
* @return: true for in redistribution.
* @:
* @range_var:
* @:true
*/
bool CheckRangeVarInRedistribution(const RangeVar* range_var)
{
@ -228,7 +228,7 @@ bool CheckRangeVarInRedistribution(const RangeVar* range_var)
if (OidIsValid(relid)) {
relation = relation_open(relid, NoLock);
/* If the relation is index, we should check the related table is resizing or not. */
/* 如果关系是索引,我们应该检查相关表是否在调整大小。*/
if (RelationIsIndex(relation)) {
Oid heapOid = IndexGetRelation(relid, false);
Relation heapRelation = relation_open(heapOid, AccessShareLock);
@ -245,12 +245,12 @@ bool CheckRangeVarInRedistribution(const RangeVar* range_var)
}
/*
* - Brief: Determine if the table name is delete_delta table.
* - Parameter:
* @relname: name of target table
* - Return:
* @TRUE: the table is delete_delta table
* @FALSE: the table is not delete_delta table
* - :delete_delta table
* - :
* @relname:
* - :
* @TRUE: delete_delta表
* @FALSE: delete_delta表
*/
bool RelationIsDeleteDeltaTable(char* delete_delta_name)
{
@ -292,10 +292,10 @@ bool RelationIsDeleteDeltaTable(char* delete_delta_name)
}
/*
* - Brief: Determine if the Progress is under cluster resizing status
* - Return:
* @TRUE: Progress is under cluster resizing
* @FALSE: Progress is not under cluster resizing
* - :
* - :
* @TRUE:
* @FALSE:
*/
bool ClusterResizingInProgress()
{
@ -329,27 +329,27 @@ bool ClusterResizingInProgress()
}
/*
* - Brief: get the name of delete_delta table
* - Parameter:
* @relname: name of target table
* @delta_delta_name: output value for delete_delta table name
* @isMultiCatchup: multi catchup delta or not
* - Return:
* no return value
* -:delete_delta表的名称
* - :
* @relname:
* @delta_delta_name: delete_delta表名的输出值
* @isMultiCatchup: delta
* - :
*
*/
static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_delta_name, bool isMultiCatchup)
{
int rc = 0;
/* Check if output parameter it not palloc()-ed from caller side */
/* 检查输出参数是否没有从调用方palloc()-ed */
if (delete_delta_name == NULL || rel == NULL) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid parameter in function '%s'", __FUNCTION__)));
}
/*
* Look up Relation's reloptions to get table's cnoid to
* form the name of delete_delta table
* Relation的关联以获得表的id
* delete_delta表的名称
*/
if (!IsInitdb) {
if (RelationInClusterResizing(rel) && !RelationInClusterResizingReadOnly(rel)) {
@ -381,12 +381,12 @@ static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_de
}
/*
* - Brief: get and open delete_delta rel
* - Parameter:
* @rel: target relation of UPDATE/DELETE/TRUNCATE operation
* @lockmode: lock mode
* @isMultiCatchup: multi catchup delta or not
* - Return:
* - :delete_delta rel
* - :
* @rel: UPDATE/DELETE/TRUNCATE操作的目标关系
* @lockmode:
* @isMultiCatchup: delta
* - :
* delete_delta rel
*/
Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool isMultiCatchup)
@ -403,22 +403,22 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is
RelationGetDeleteDeltaTableName(rel, (char*)delete_delta_tablename, isMultiCatchup);
data_redis_namespace = get_namespace_oid("data_redis", false);
/* We are going to fetch the delete delta relation under data_redis schema. */
/* 我们将在data_redis模式下获取delete delta关系。 */
deldelta_relid = get_relname_relid(delete_delta_tablename, data_redis_namespace);
if (!OidIsValid(deldelta_relid)) {
/*
* If multi catchup delta table is not there, just return NULL. We should not
* report error, because it is a valid case. Multi catchup delta table is
* dropped in each catchup iteration.
* NULL We should not
* delta表是( Multi catchup delta table is)
*
*/
if (isMultiCatchup) {
return NULL;
}
/*
* To support Update or Delete during extension, we need to add 2 more columns.
* more columns. Limited by MaxHeapAttributeNumber, if the table already contains too many columns,
* we don't allow update or delete anymore, but insert statement can still proceed.
* 2
* maxheapattributennumber的限制
*
*/
if (((rel->rd_att->natts > (MaxHeapAttributeNumber - (Natts_pg_delete_delta - 1))) &&
!RELATION_IS_PARTITIONED(rel)) ||
@ -429,7 +429,7 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is
RelationGetRelationName(rel)),
errdetail("Can not support online extension, if the table contains too many columns")));
}
/* ERROR case, should never come here */
/* 错误情况下,不应该出现在这里 */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("delete delta table %s is not found when do cluster resizing table \"%s\"",
@ -446,11 +446,11 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is
}
/*
* - Brief: Check the stmtment during online expansion, block unsupported ddl in cluster resizing.
* - Parameter:
* @rel: parsetree of DDL
* - Return:
* no return value
* - :线ddl
* - :
* @rel: DDL的解析树
* -:
*
*/
void BlockUnsupportedDDL(const Node* parsetree)
{
@ -466,11 +466,11 @@ void BlockUnsupportedDDL(const Node* parsetree)
LOCKMODE lockmode_openrel = AccessShareLock;
/*
* Check for shared-cache-inval messages before trying to access the
* relation. This is needed to cover the case where the name
* identifies a rel that has been dropped and recreated since the
* start of our transaction: if we don't flush the old syscache entry,
* then we'll latch onto that entry and suffer an error later.
*
* relation.
* rel
* :syscache条目
*
*/
AcceptInvalidationMessages();
@ -501,13 +501,13 @@ void BlockUnsupportedDDL(const Node* parsetree)
return;
} break;
/* Block CURSOR for while table in cluster resizing */
/* 在集群调整大小时阻塞游标 */
case T_PlannedStmt: {
PlannedStmt* stmt = (PlannedStmt*)parsetree;
relidlist = stmt->relationOids;
} break;
/* Block RENAME while table in cluster resizing */
/* 当表在集群中调整大小时块RENAME */
case T_RenameStmt: {
RenameStmt* stmt = (RenameStmt*)parsetree;
@ -540,11 +540,11 @@ void BlockUnsupportedDDL(const Node* parsetree)
stmt->relation->relname)));
} break;
/* Block ALTER set schema while table in cluster resizing */
/* 当表在集群中调整大小时Block ALTER设置模式 */
case T_AlterObjectSchemaStmt: {
AlterObjectSchemaStmt* stmt = (AlterObjectSchemaStmt*)parsetree;
/* disable alter table set schema when transfer */
/* 在传输时禁用alter table set schema */
if (stmt->relation != NULL) {
Oid relOid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(relOid)) {
@ -567,7 +567,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
stmt->relation->relname)));
} break;
/* Block CREATE index while table in cluster resizing(for row table only) */
/* 当表在集群中调整大小时,阻塞创建索引(仅适用于行表) */
case T_IndexStmt: {
IndexStmt* stmt = (IndexStmt*)parsetree;
if (stmt->relation) {
@ -590,13 +590,13 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* Block REINDEX while table in cluster resizing(for row table only) */
/* 当表在集群中调整大小时块REINDEX(仅适用于行表) */
case T_ReindexStmt: {
ReindexStmt* stmt = (ReindexStmt*)parsetree;
if (stmt->relation) {
relid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(relid)) {
/* release index lock before lock table to avoid deadlock */
/* 在锁表之前释放索引锁以避免死锁 */
UnlockRelationOid(relid, AccessShareLock);
Relation relation = relation_open(relid, NoLock);
@ -622,7 +622,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* Block ALTER-Table while table in cluster resizing */
/* 当表在集群中调整大小时阻塞ALTER-Table */
case T_AlterTableStmt: {
AlterTableStmt* stmt = (AlterTableStmt*)parsetree;
AlterTableCmd* cmd = NULL;
@ -631,13 +631,13 @@ void BlockUnsupportedDDL(const Node* parsetree)
switch (cmd->subtype) {
case AT_TruncatePartition: {
/*
* We do not allow truncate partition when the target is in read only
* mode during online expansion time.
*
*线
*/
if (stmt->relation) {
relid = RangeVarGetRelid(stmt->relation, lockmode_getrelid, true);
if (OidIsValid(relid)) {
/* disable alter table truncate partition during transfer */
/* 禁止在传输过程中截断分区 */
if (CheckRangeVarInRedistribution(stmt->relation)) {
Oid nsOid = GetNamespaceIdbyRelId(relid);
TRANSFER_DISABLE_DDL(nsOid);
@ -704,12 +704,12 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
}
/* If rel option contain append_mode, then not check. */
/* 如果rel选项包含append_mode则不检查。 */
if (opt != NULL) {
break;
}
}
/* fall through */
/* 失败 */
default: {
if (stmt->relation && !u_sess->attr.attr_sql.enable_cluster_resize &&
CheckRangeVarInRedistribution(stmt->relation))
@ -725,7 +725,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
return;
} break;
/* Block CREATE-RULE statements while target table in cluster resizing */
/* 当集群中的目标表调整大小时阻塞CREATE-RULE语句 */
case T_RuleStmt: {
RuleStmt* stmt = (RuleStmt*)parsetree;
if (stmt->relation) {
@ -734,7 +734,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* Block CREATE SEQUENCE set schema while owner table in cluster resizing */
/* 当所有者表在集群中调整大小时Block CREATE SEQUENCE设置模式 */
case T_CreateSeqStmt: {
CreateSeqStmt* stmt = (CreateSeqStmt*)parsetree;
List* owned_by = NULL;
@ -761,7 +761,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* Block ALTER SEQUENCE while owner table in cluster resizing */
/* 当集群中的所有者表调整大小时阻塞ALTER SEQUENCE */
case T_AlterSeqStmt: {
AlterSeqStmt* stmt = (AlterSeqStmt*)parsetree;
List* owned_by = NIL;
@ -788,7 +788,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* Block CLUSTER while table in cluster resizing */
/* 当表在集群中调整大小时阻塞集群 */
case T_ClusterStmt: {
ClusterStmt* stmt = (ClusterStmt*)parsetree;
if (stmt->relation && CheckRangeVarInRedistribution(stmt->relation))
@ -799,7 +799,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
stmt->relation->relname)));
} break;
/* Block VACUUM FULL while table in cluster resizing */
/* 当表在集群中调整大小时,块真空已满 */
case T_VacuumStmt: {
VacuumStmt* stmt = (VacuumStmt*)parsetree;
if ((stmt->options & VACOPT_VACUUM) || (stmt->options & VACOPT_MERGE)) {
@ -822,7 +822,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* Block truncate DDL when the target table is read only in cluster resizing */
/* 在集群调整大小时当目标表为只读时块截断DDL */
case T_TruncateStmt: {
ListCell* cell = NULL;
TruncateStmt* stmt = (TruncateStmt*)parsetree;
@ -857,7 +857,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
DropStmt* stmt = (DropStmt*)parsetree;
switch (stmt->removeType) {
case OBJECT_TABLE: {
/* disable drop table when transfer */
/* 在传输时禁用drop表 */
ListCell* cell = NULL;
foreach (cell, stmt->objects) {
RangeVar* rel = makeRangeVarFromNameList((List*)lfirst(cell));
@ -871,7 +871,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
break;
}
case OBJECT_SCHEMA: {
/* disable drop schema when transfer */
/* 传输时禁用删除模式 */
ListCell* cell = NULL;
foreach (cell, stmt->objects) {
List* objname = (List*)lfirst(cell);
@ -887,7 +887,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
} break;
case T_CreateStmt: {
/* disable create table when transfer */
/* 禁止传输时创建表 */
CreateStmt* stmt = (CreateStmt*)parsetree;
if (stmt->relation != NULL) {
Oid nsOid = RangeVarGetCreationNamespace(stmt->relation);
@ -916,14 +916,14 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
/*
* - Brief: For online expanions, the shippable function is evaluated here, the module
* will be invoked in optimizer when do FQS evaluation, we have to define function
* as STABLE
* - Parameter:
* @funcid: oid of user defined function which is createed/dropped in scope of gs_redis
* - Return:
* @true: shippable
* @false: unshippable
* - :线
* FQS评估时
* as STABLE
* - :
* @funcid: gs_redis范围内创建/Oid
* - :
* @true:
* @false:
*/
bool redis_func_shippable(Oid funcid)
{
@ -937,11 +937,11 @@ bool redis_func_shippable(Oid funcid)
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function with OID %u does not exist", funcid)));
}
/* Fetch function signatures */
/* 获取函数签名 */
rettype = get_func_signature(funcid, &argstype, &nargs);
if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) {
/* tupleid retrive functions is shippable to datanodes */
/* Tupleid检索函数可以发布到数据节点 */
result = true;
} else if (redis_offset_retrive_function(func_name, rettype, argstype, nargs)) {
result = true;
@ -961,11 +961,11 @@ bool redis_func_shippable(Oid funcid)
}
/*
* - Brief: determine if given funcid reflects a dn-stable function
* - Parameter:
* @funcid: function oid that to evaluate
* - Return:
* @result: true:dnstable false: not-dnstable function
* - :
* - :
* @funcid: oid
* - :
* @result: true: false:
*/
bool redis_func_dnstable(Oid funcid)
{
@ -981,11 +981,11 @@ bool redis_func_dnstable(Oid funcid)
errmsg("function with OID %u does not exist when checking function dnstable", funcid)));
}
/* Fetch function signatures */
/* 获取函数签名 */
rettype = get_func_signature(funcid, &argstype, &nargs);
if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) {
/* tupleid retrive functions is dnstable */
/* 管状反射函数是不稳定的 */
result = true;
}
@ -993,23 +993,23 @@ bool redis_func_dnstable(Oid funcid)
}
/*
* - Brief: evaluate ctid functions into a const value to avoid per-scanning
* tuple invokation in seqscan.
* - Parameter:
* @rel: the rel being redistributing
* @original_quals: the original quals possible contains ctid_funcs
* @isRangeScanInRedis: if is a redis range scan
* - Return:
* @new_quals: quals which func call be replaced by a const
* - :ctid函数求值为const值以避免每次扫描
* seqscan中调用元组
* - :
* @rel:
* @original_quals: quals可能包含ctid_funcs
* @isRangeScanInRedis: redis范围扫描
* - :
* @new_quals: Quals将被const替换
*/
List* eval_ctid_funcs(Relation rel, List* original_quals, RangeScanInRedis *rangeScanInRedis)
{
StringInfo qual_str = makeStringInfo();
/*
* we have to make a copy of the original quals, since the eval_dnstable_func_mutator
* will modify the it. the original qual will be needed again and again in later
* to be re-eval in partition table scans.
* eval_dnstable_func_mutator的存在quals进行复制
*
*
*/
List* new_quals = (List*)copyObject((const void*)(original_quals));
@ -1033,16 +1033,16 @@ static int32 get_expr_const_val(Node *val){
}
/*
* - Brief: working house for eval_dnstable_func() to evaluate dn stable function into a const
* value to avoid per-scanning tuple invocation in seqscan
* - Parameter:
* @rel: the rel being redistributing
* @node: expression node
* @qual_str: predicate pattern
* @isRangeScanInRedis: output to indicate if the predicate pattern is range scan in redis
* @isRoot: we want to compare the predicate pattern only once at root level
* - Return:
* @result: expression tree with dn stable function const-evaluated
* - :eval_dnstable_func()const
* seqscan中调用每次扫描的元组
* - :
* @rel:
* @node:
* @qual_str:
* @isRangeScanInRedis: redis中的范围扫描
* @isRoot:
* - :
* @result: dn稳定函数const评估
*/
static Node* eval_dnstable_func_mutator(
Relation rel, Node* node, StringInfo qual_str, RangeScanInRedis *rangeScanInRedis, bool isRoot)
@ -1057,7 +1057,7 @@ static Node* eval_dnstable_func_mutator(
case T_FuncExpr: {
FuncExpr* expr = (FuncExpr*)node;
/* flatten dn stable function into const value */
/* 将一个稳定函数扁平化为const值 */
if (redis_func_dnstable(expr->funcid)) {
Node* new_const = NULL;
char* funcname = get_func_name(expr->funcid);
@ -1093,8 +1093,8 @@ static Node* eval_dnstable_func_mutator(
Node* new_expr = eval_dnstable_func_mutator(rel, expr, qual_str, rangeScanInRedis, false);
/*
* If a FuncExpr node is evalated into a T_Const value, we are hitting
* the point so replace it in qual list.
* FuncExpr节点求值为T_Const值
*
*/
if (expr && IsA(expr, FuncExpr) && new_expr && IsA(new_expr, Const)) {
l = list_delete_ptr(l, expr);
@ -1103,8 +1103,8 @@ static Node* eval_dnstable_func_mutator(
}
/*
* If the predicate at root is something like "where ctid between pg_get_redis_rel_start_ctid('xx')
* and pg_get_redis_rel_end_ctid('xx')" on DN, we will pushdown the predicate at scan node.
* where ctid between pg_get_redis_rel_start_ctid('xx')
* pg_get_redis_rel_end_ctid('xx')"在DN上我们将在扫描节点下推谓词。
*/
if (isRoot && pg_strcasecmp(qual_str->data, RANGE_SCAN_IN_REDIS) == 0) {
rangeScanInRedis->isRangeScanInRedis = true;
@ -1129,7 +1129,7 @@ static Node* eval_dnstable_func_mutator(
}
case T_Var: {
Var* var = (Var*)node;
/* we only expect tid column in the predicate */
/* 我们只期望谓词中有tid列 */
if (var->vartype == TIDOID) {
appendStringInfoString(qual_str, "tid");
appendStringInfoString(qual_str, "+");
@ -1147,10 +1147,10 @@ static Node* eval_dnstable_func_mutator(
}
/*
* - Brief: get and open new_table rel
* - Parameter:
* @rel: target relation of TRUNCATE operation
* - Return:
* - :new_table rel
* - :
* @rel: TRUNCATE操作的目标关系
* - :
* new_table rel
*/
Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode)
@ -1168,7 +1168,7 @@ Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode)
data_redis_namespace = get_namespace_oid("data_redis", false);
newtable_relid = get_relname_relid(new_tablename, data_redis_namespace);
if (!OidIsValid(newtable_relid)) {
/* ERROR case, should never come here */
/* 错误情况下,不应该出现在这里 */
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("new table %s is not found when do cluster resizing table \"%s\"",
@ -1185,18 +1185,18 @@ Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode)
}
/*
* - Brief: get the name of new table
* - Parameter:
* @relname: name of target table
* @newtable_name: output value for new table name
* - Return:
* no return value
* - :
* - :
* @relname:
* @newtable_name:
* - :
*
*/
void RelationGetNewTableName(Relation rel, char* newtable_name)
{
int rc = 0;
/* Check if output parameter it not palloc()-ed from caller side */
/* 检查输出参数是否没有从调用方palloc()-ed */
if (newtable_name == NULL || rel == NULL) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -1204,8 +1204,8 @@ void RelationGetNewTableName(Relation rel, char* newtable_name)
}
/*
* Look up relaion's reloptions to get table's cnoid to
* form the name of new table
*
*
*/
if (!IsInitdb) {
Oid rel_cn_oid = RelationGetRelCnOid(rel);
@ -1216,19 +1216,19 @@ void RelationGetNewTableName(Relation rel, char* newtable_name)
rc = snprintf_s(
newtable_name, NAMEDATALEN, NAMEDATALEN - 1, "data_redis_tmp_%s", RelationGetRelationName(rel));
}
/* check the return value of security function */
/* 检查安全函数的返回值 */
securec_check_ss(rc, "\0", "\0");
}
return;
}
/*
* - Brief: Determine if the relation is under cluster resizing write error mode
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing write error mode
* @FALSE: relation is not under cluster resizing write error mode
* - :
* - :
* @rel:
* - :
* @TRUE:
* @FALSE:
*/
bool RelationInClusterResizingWriteErrorMode(const Relation rel)
{

View File

@ -1,13 +1,13 @@
/* -------------------------------------------------------------------------
*
* execCurrent.c
* executor support for WHERE CURRENT OF cursor
* WHERE CURRENT OF游标执行程序支持WHERE CURRENT OF游标
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* (c) 1996-2012, PostgreSQL全球发展集团
* (c) 1994
*
* IDENTIFICATION
*
* src/backend/executor/execCurrent.c
*
* -------------------------------------------------------------------------
@ -38,14 +38,12 @@ static ScanState* search_plan_tree(PlanState *node, Oid table_oid);
/*
* execCurrentOf
*
* Given a CURRENT OF expression and the OID of a table, determine which row
* of the table is currently being scanned by the cursor named by CURRENT OF,
* and return the row's TID into *current_tid.
* CURRENT OF表达式和表的OID
* CURRENT of的游标扫描
* TID为*current_tid
*
* Returns TRUE if a row was identified. Returns FALSE if the cursor is valid
* for the table but is not currently scanning a row of the table (this is a
* legal situation in inheritance cases). Raises error if cursor is not a
* valid updatable scan of the specified table.
* TRUEFALSE
* ()
*/
bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relation, ItemPointer current_tid,
RelationPtr partitionOfCursor_tid)
@ -55,14 +53,14 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
QueryDesc *query_desc = NULL;
Oid table_oid = RelationGetRelid(relation);
/* Get the cursor name --- may have to look up a parameter reference */
/* 获取游标名称——可能需要查找参数引用 */
if (cexpr->cursor_name) {
cursor_name = cexpr->cursor_name;
} else {
cursor_name = fetch_cursor_param_value(econtext, cexpr->cursor_param);
}
/* Find the cursor's portal */
/* 找到游标的入口 */
portal = GetPortalByName(cursor_name);
if (!PortalIsValid(portal)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR),
@ -70,8 +68,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* We have to watch out for non-SELECT queries as well as held cursors,
* both of which may have null query_desc.
* select查询和持有的游标query_desc都可能为空
*/
if (portal->strategy != PORTAL_ONE_SELECT) {
ereport(ERROR,
@ -85,26 +82,23 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* We have two different strategies depending on whether the cursor uses
* FOR UPDATE/SHARE or not. The reason for supporting both is that the
* FOR UPDATE code is able to identify a target table in many cases where
* the other code can't, while the non-FOR-UPDATE case allows use of WHERE
* CURRENT OF with an insensitive cursor.
* 使
* /
* FOR UPDATE代码能够识别目标表FOR-UPDATE情况允许使用不敏感游标的when CURRENT of
*/
if (query_desc->estate->es_rowMarks) {
ExecRowMark *erm = NULL;
ListCell *lc = NULL;
/*
* Here, the query must have exactly one FOR UPDATE/SHARE reference to
* the target table, and we dig the ctid info out of that.
* FOR UPDATE/SHARE引用ctid信息
*/
erm = NULL;
foreach (lc, query_desc->estate->es_rowMarks) {
ExecRowMark *thiserm = (ExecRowMark *)lfirst(lc);
if (!RowMarkRequiresRowShareLock(thiserm->markType)) {
continue; /* ignore non-FOR UPDATE/SHARE items */
continue; /* 忽略非for UPDATE/SHARE项 */
}
if (RelationGetRelid(thiserm->relation) == table_oid) {
@ -124,15 +118,14 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* The cursor must have a current result row: per the SQL spec, it's
* an error if not.
* :SQL规范
*/
if (portal->atStart || portal->atEnd) {
ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE),
errmsg("cursor \"%s\" is not positioned on a row when the cursor uses for UPDATE/SHARE", cursor_name)));
}
/* Return the currently scanned TID, if there is one */
/* 返回当前扫描的TID(如果有) */
if (ItemPointerIsValid(&(erm->curCtid))) {
*current_tid = erm->curCtid;
@ -144,9 +137,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* This table didn't produce the cursor's current row; some other
* inheritance child of the same parent must have. Signal caller to
* do nothing on this table.
* ;
*/
return false;
} else {
@ -156,9 +147,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
ItemPointer tuple_tid;
/*
* Without FOR UPDATE, we dig through the cursor's plan to find the
* scan node. Fail if it's not there or buried underneath
* aggregation.
* FOR UPDATE
*/
scanstate = search_plan_tree(query_desc->planstate, table_oid);
if (scanstate == NULL) {
@ -168,23 +157,21 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* The cursor must have a current result row: per the SQL spec, it's
* an error if not. We test this at the top level, rather than at the
* scan node level, because in inheritance cases any one table scan
* could easily not be on a row. We want to return false, not raise
* error, if the passed-in table OID is for one of the inactive scans.
* :SQL规范
*
* OID是用于非活动扫描的false
*/
if (portal->atStart || portal->atEnd) {
ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE), errmsg(
"cursor \"%s\" is not positioned on a row when the cursor doesn't use for UPDATE/SHARE", cursor_name)));
}
/* Now OK to return false if we found an inactive scan */
/* 现在OK返回false如果我们发现一个非活动扫描 */
if (TupIsNull(scanstate->ss_ScanTupleSlot)) {
return false;
}
/* Use slot_getattr to catch any possible mistakes */
/* 使用slot_getattr捕获任何可能的错误 */
tuple_tableoid = DatumGetObjectId(tableam_tslot_getattr(scanstate->ss_ScanTupleSlot, TableOidAttributeNumber, &lisnull));
Assert(!lisnull);
tuple_tid = (ItemPointer)DatumGetPointer(
@ -206,7 +193,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
/*
* fetch_cursor_param_value
*
* Fetch the string value of a param, verifying it is of type REFCURSOR.
* REFCURSOR类型.
*/
static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
{
@ -215,20 +202,20 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
if (paramInfo && paramId > 0 && paramId <= paramInfo->numParams) {
ParamExternData *prm = &paramInfo->params[paramId - 1];
/* give hook a chance in case parameter is dynamic */
/* 如果参数是动态的,给钩子一个机会 */
if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) {
(*paramInfo->paramFetch)(paramInfo, paramId);
}
if (OidIsValid(prm->ptype) && !prm->isnull) {
/* safety check in case hook did something unexpected */
/* 安全检查,以防钩子发生意外 */
if (prm->ptype != REFCURSOROID) {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", paramId,
format_type_be(prm->ptype), format_type_be(REFCURSOROID))));
}
/* We know that refcursor uses text's I/O routines */
/* 我们知道refcursor使用text的I/O例程 */
return TextDatumGetCString(prm->value);
}
}
@ -240,8 +227,8 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
/*
* search_plan_tree
*
* Search through a PlanState tree for a scan node on the specified table.
* Return NULL if not found or multiple candidates.
* PlanState树中搜索指定表上的扫描节点
* NULL
*/
#ifdef PGXC
ScanState* search_plan_tree(PlanState* node, Oid table_oid)
@ -262,7 +249,7 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
}
#endif
/*
* scan nodes can all be treated alike
*
*/
case T_SeqScanState:
case T_IndexScanState:
@ -284,8 +271,7 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
return result;
}
/*
* For Append, we must look through the members; watch out for
* multiple matches (possible if it was from UNION ALL)
* Append;(UNION ALL)
*/
case T_AppendState: {
AppendState *astate = (AppendState *)node;
@ -297,14 +283,14 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
if (elem == NULL)
continue;
if (result != NULL)
return NULL; /* multiple matches */
return NULL; /* 多个匹配 */
result = elem;
}
return result;
}
/*
* Similarly for MergeAppend
* MergeAppend
*/
case T_MergeAppendState: {
MergeAppendState *mstate = (MergeAppendState *)node;
@ -318,15 +304,14 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
continue;
}
if (result != NULL) {
return NULL; /* multiple matches */
return NULL; /* 多个匹配 */
}
result = elem;
}
return result;
}
/*
* Result and Limit can be descended through (these are safe
* because they always return their input's current row)
* Result和Limit可以依次下降()
*/
#ifdef PGXC
case T_MaterialState:
@ -337,13 +322,13 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
return search_plan_tree(node->lefttree, table_oid);
/*
* SubqueryScan too, but it keeps the child in a different place
* SubqueryScan也可以
*/
case T_SubqueryScanState:
return search_plan_tree(((SubqueryScanState *)node)->subplan, table_oid);
default:
/* Otherwise, assume we can't descend through it */
/* 否则,假设我们不能从里面下去 */
break;
}
return NULL;

View File

@ -1,15 +1,13 @@
/* -------------------------------------------------------------------------
*
* execGrouping.cpp
* executor utility routines for grouping, hashing, and aggregation
*
*
* Note: we currently assume that equality and hashing functions are not
* collation-sensitive, so the code in this file has no support for passing
* collation settings through from callers. That may have to change someday.
* :
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* (c) 1996-2012, PostgreSQL全球发展集团
* (c) 1994
*
*
* IDENTIFICATION
@ -31,22 +29,21 @@ static uint32 TupleHashTableHash(const void* key, Size keysize);
static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize);
/*****************************************************************************
* Utility routines for grouping tuples together
*
*****************************************************************************/
/*
* execTuplesMatch
* Return true if two tuples match in all the indicated fields.
* true
*
* This actually implements SQL's notion of "not distinct". Two nulls
* match, a null and a not-null don't match.
* SQL的
*
* slot1, slot2: the tuples to compare (must have same columns!)
* numCols: the number of attributes to be examined
* matchColIdx: array of attribute column numbers
* eqFunctions: array of fmgr lookup info for the equality functions to use
* evalContext: short-term memory context for executing the functions
* slot1, slot2:(!)
* numCols:
* matchColIdx:
* eqFunctions:使fmgr查找信息的数组
* evalContext:
*
* NB: evalContext is reset each time!
* NB: evalContext !
*/
bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols, AttrNumber* matchColIdx,
FmgrInfo* eqfunctions, MemoryContext evalContext)
@ -55,15 +52,15 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols,
bool result = false;
int i;
/* Reset and switch into the temp context. */
/* 重置并切换到temp上下文。 */
MemoryContextReset(evalContext);
oldContext = MemoryContextSwitchTo(evalContext);
/*
* We cannot report a match without checking all the fields, but we can
* report a non-match as soon as we find unequal fields. So, start
* comparing at the last field (least significant sort key). That's the
* most likely to be different if we are dealing with sorted input.
*
*
* ()
*
*/
result = true;
@ -78,17 +75,17 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols,
attr2 = tableam_tslot_getattr(slot2, att, &isNull2);
if (isNull1 != isNull2) {
result = false; /* one null and one not; they aren't equal */
result = false; /* 一个null一个not;它们是不相等的 */
break;
}
if (isNull1) {
continue; /* both are null, treat as equal */
continue; /* 两者都为空,同等对待 */
}
/* Apply the type-specific equality function */
/* 应用特定于类型的相等函数 */
if (!DatumGetBool(FunctionCall2(&eqfunctions[i], attr1, attr2))) {
result = false; /* they aren't equal */
result = false; /* 它们是不相等的 */
break;
}
}
@ -100,13 +97,11 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols,
/*
* execTuplesUnequal
* Return true if two tuples are definitely unequal in the indicated
* fields.
* true
*
* Nulls are neither equal nor unequal to anything else. A true result
* is obtained only if there are non-null fields that compare not-equal.
* null既不等于也不等于其他任何东西not-equal的非空字段时
*
* Parameters are identical to execTuplesMatch.
* execTuplesMatch相同
*/
bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols, AttrNumber* matchColIdx,
FmgrInfo* eqfunctions, MemoryContext evalContext)
@ -117,15 +112,14 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols
Assert(slot1->tts_tupleDescriptor->tdTableAmType == slot2->tts_tupleDescriptor->tdTableAmType);
/* Reset and switch into the temp context. */
/* 重置并切换到temp上下文 */
MemoryContextReset(evalContext);
oldContext = MemoryContextSwitchTo(evalContext);
/*
* We cannot report a match without checking all the fields, but we can
* report a non-match as soon as we find unequal fields. So, start
* comparing at the last field (least significant sort key). That's the
* most likely to be different if we are dealing with sorted input.
*
* ()
*
*/
result = false;
@ -138,18 +132,18 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols
attr1 = tableam_tslot_getattr(slot1, att, &isNull1);
if (isNull1) {
continue; /* can't prove anything here */
continue; /* 不能证明什么 */
}
attr2 = tableam_tslot_getattr(slot2, att, &isNull2);
if (isNull2) {
continue; /* can't prove anything here */
continue; /* 不能证明什么 */
}
/* Apply the type-specific equality function */
/* 应用特定于类型的相等函数 */
if (!DatumGetBool(FunctionCall2(&eqfunctions[i], attr1, attr2))) {
result = true; /* they are unequal */
result = true; /* 它们是不相等的 */
break;
}
}
@ -161,10 +155,9 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols
/*
* execTuplesMatchPrepare
* Look up the equality functions needed for execTuplesMatch or
* execTuplesUnequal, given an array of equality operator OIDs.
* oid数组execTuplesMatch或exectuplesinequality所需的相等函数
*
* The result is a palloc'd array.
* lolod数组
*/
FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators)
{
@ -184,13 +177,12 @@ FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators)
/*
* execTuplesHashPrepare
* Look up the equality and hashing functions needed for a TupleHashTable.
* TupleHashTable所需的相等和散列函数
*
* This is similar to execTuplesMatchPrepare, but we also need to find the
* hash functions associated with the equality operators. *eqFunctions and
* *hashFunctions receive the palloc'd result arrays.
* execTuplesMatchPrepare
* *eqFunctions和*hashFunctions接收palloc结果数组
*
* Note: we expect that the given operators are not cross-type comparisons.
* :
*/
void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions, FmgrInfo** hashFunctions)
{
@ -216,7 +208,7 @@ void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions
i,
numCols)));
/* We're not supporting cross-type cases here */
/* 我们不支持交叉类型的情况 */
Assert(left_hash_function == right_hash_function);
fmgr_info(eq_function, &(*eqFunctions)[i]);
fmgr_info(right_hash_function, &(*hashFunctions)[i]);
@ -224,29 +216,23 @@ void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions
}
/*****************************************************************************
* Utility routines for all-in-memory hash tables
*
*
* These routines build hash tables for grouping tuples together (eg, for
* hash aggregation). There is one entry for each not-distinct set of tuples
* presented.
* ()
*
*****************************************************************************/
/*
* Construct an empty TupleHashTable
* TupleHashTable
*
* numCols, keyColIdx: identify the tuple fields to use as lookup key
* eqfunctions: equality comparison functions to use
* hashfunctions: datatype-specific hashing functions to use
* nbuckets: initial estimate of hashtable size
* entrysize: size of each entry (at least sizeof(TupleHashEntryData))
* tablecxt: memory context in which to store table and table entries
* tempcxt: short-lived context for evaluation hash and comparison functions
* numCols, keyColIdx:使eqfunctions:
* 使hashfunctions:使nbuckets:
* entrysize:(sizeof(TupleHashEntryData))
* tablext:tempcxt:
*
* The function arrays may be made with execTuplesHashPrepare(). Note they
* are not cross-type functions, but expect to see the table datatype(s)
* on both sides.
* execTuplesHashPrepare()
*
*
* Note that keyColIdx, eqfunctions, and hashfunctions must be allocated in
* storage that will live as long as the hashtable does.
* keyColIdxeqfunctions和hashfunctions必须分配到与散列表存在时间一样长的存储中
*/
TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo* eqfunctions, FmgrInfo* hashfunctions,
long nbuckets, Size entrysize, MemoryContext tablecxt, MemoryContext tempcxt, int workMem)
@ -257,7 +243,7 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo*
Assert(nbuckets > 0);
Assert(entrysize >= sizeof(TupleHashEntryData));
/* Limit initial table size request to not more than work_mem */
/* 限制初始表大小请求不超过work_mem */
nbuckets = Min(nbuckets, (long)((workMem * 1024L) / entrysize));
if (u_sess->attr.attr_sql.hashagg_table_size != 0)
nbuckets = Min(nbuckets, u_sess->attr.attr_sql.hashagg_table_size);
@ -271,7 +257,7 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo*
hashtable->tablecxt = tablecxt;
hashtable->tempcxt = tempcxt;
hashtable->entrysize = entrysize;
hashtable->tableslot = NULL; /* will be made on first lookup */
hashtable->tableslot = NULL; /* 将在第一次查找时进行 */
hashtable->inputslot = NULL;
hashtable->in_hash_funcs = NULL;
hashtable->cur_eq_funcs = NULL;
@ -293,20 +279,16 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo*
}
/*
* Find or create a hashtable entry for the tuple group containing the
* given tuple. The tuple must be the same type as the hashtable entries.
*
*
* If isnew is NULL, we do not create new entries; we return NULL if no
* match is found.
* isnew为NULL;NULL
*
* If isnew isn't NULL, then a new entry is created if no existing entry
* matches. On return, *isnew is true if the entry is newly created,
* false if it existed already. Any extra space in a new entry has been
* zeroed.
* isnew不为NULL
* *isnew为true
* false
*
* If isinserthashtbl is false, the para of hash search is HASH_FIND
* instead of HASH_ENTER. This slot will be insert into temp file instead of
* hash table if it is new
* isinserthashtbl为falseHASH_FINDHASH_ENTER
*
*
*/
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* slot, bool* isnew, bool isinserthashtbl)
@ -317,29 +299,27 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl
TupleHashEntryData dummy;
bool found = false;
/* If first time through, clone the input slot to make table slot */
/* 如果第一次通过,克隆输入槽来制作表槽 */
if (hashtable->tableslot == NULL) {
TupleDesc tupdesc;
oldContext = MemoryContextSwitchTo(hashtable->tablecxt);
/*
* We copy the input tuple descriptor just for safety --- we assume
* all input tuples will have equivalent descriptors.
*
*/
tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
hashtable->tableslot = MakeSingleTupleTableSlot(tupdesc);
MemoryContextSwitchTo(oldContext);
}
/* Need to run the hash functions in short-lived context */
/* 需要在短期上下文中运行哈希函数 */
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
/*
* Set up data needed by hash and match functions
*
*
* We save and restore u_sess->exec_cxt.cur_tuple_hash_table just in case someone manages to
* invoke this code re-entrantly.
* u_sess-> exec_extCur_tuple_hash_table
*/
hashtable->inputslot = slot;
hashtable->in_hash_funcs = hashtable->tab_hash_funcs;
@ -348,34 +328,33 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl
saveCurHT = u_sess->exec_cxt.cur_tuple_hash_table;
u_sess->exec_cxt.cur_tuple_hash_table = hashtable;
/* Search the hash table */
dummy.firstTuple = NULL; /* flag to reference inputslot */
/* 搜索哈希表 */
dummy.firstTuple = NULL; /* 引用输入槽的标志 */
if (isinserthashtbl) {
entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, isnew ? HASH_ENTER : HASH_FIND, &found);
} else {
/* this slot will be insert into temp file instead of hash table if it is not found in hash table */
/* 如果在哈希表中没有找到该槽位,则将其插入临时文件而不是哈希表中 */
entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, HASH_FIND, &found);
}
if (isnew != NULL) {
if (found) {
/* found pre-existing entry */
/* 发现已有条目 */
*isnew = false;
} else {
if (entry) {
Assert(isinserthashtbl);
/*
* created new entry
*
*
* Zero any caller-requested space in the entry. (This zaps the
* "key data" dynahash.c copied into the new entry, but we don't
* care since we're about to overwrite it anyway.)
*
* (dynahash.c复制到新条目中)
*/
errno_t errorno = memset_s(entry, hashtable->entrysize, 0, hashtable->entrysize);
securec_check(errorno, "\0", "\0");
/* Copy the first tuple into the table context */
/* 将第一个元组复制到表上下文中 */
MemoryContextSwitchTo(hashtable->tablecxt);
entry->firstTuple = ExecCopySlotMinimalTuple(slot);
if (hashtable->add_width)
@ -394,13 +373,9 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl
}
/*
* Search for a hashtable entry matching the given tuple. No entry is
* created if there's not a match. This is similar to the non-creating
* case of LookupTupleHashEntry, except that it supports cross-type
* comparisons, in which the given tuple is not of the same type as the
* table entries. The caller must provide the hash functions to use for
* the input tuple, as well as the equality functions, since these may be
* different from the table's internal functions.
*
* LookupTupleHashEntry的非创建情况
*
*/
TupleHashEntry FindTupleHashEntry(
TupleHashTable hashtable, TupleTableSlot* slot, FmgrInfo* eqfunctions, FmgrInfo* hashfunctions)
@ -410,14 +385,13 @@ TupleHashEntry FindTupleHashEntry(
TupleHashTable saveCurHT;
TupleHashEntryData dummy;
/* Need to run the hash functions in short-lived context */
/* 需要在短期上下文中运行哈希函数 */
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
/*
* Set up data needed by hash and match functions
*
*
* We save and restore u_sess->exec_cxt.cur_tuple_hash_table just in case someone manages to
* invoke this code re-entrantly.
* u_sess-> exec_extCur_tuple_hash_table
*/
hashtable->inputslot = slot;
hashtable->in_hash_funcs = hashfunctions;
@ -426,8 +400,8 @@ TupleHashEntry FindTupleHashEntry(
saveCurHT = u_sess->exec_cxt.cur_tuple_hash_table;
u_sess->exec_cxt.cur_tuple_hash_table = hashtable;
/* Search the hash table */
dummy.firstTuple = NULL; /* flag to reference inputslot */
/* 搜索哈希表 */
dummy.firstTuple = NULL; /* 引用输入槽的标志 */
entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, HASH_FIND, NULL);
u_sess->exec_cxt.cur_tuple_hash_table = saveCurHT;
@ -438,20 +412,19 @@ TupleHashEntry FindTupleHashEntry(
}
/*
* Compute the hash value for a tuple
*
*
* The passed-in key is a pointer to TupleHashEntryData. In an actual hash
* table entry, the firstTuple field points to a tuple (in MinimalTuple
* format). LookupTupleHashEntry sets up a dummy TupleHashEntryData with a
* NULL firstTuple field --- that cues us to look at the inputslot instead.
* This convention avoids the need to materialize virtual input tuples unless
* they actually need to get copied into the table.
* TupleHashEntryData的指针
* tuple字段指向一个元组(MinimalTuple格式中)
* LookupTupleHashEntry用一个NULL firstTuple字段
*
*
*
* u_sess->exec_cxt.cur_tuple_hash_table must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise.
* u_sess - > exec_cxtcur_tuple_hash_table必须在调用它之前设置
* dynahash.c没有提供任何让我们以其他方式获取哈希表的API
*
* Also, the caller must select an appropriate memory context for running
* the hash functions. (dynahash.c doesn't change CurrentMemoryContext.)
*
* (dynahash.c不会改变CurrentMemoryContext)
*/
static uint32 TupleHashTableHash(const void* key, Size keysize)
{
@ -465,28 +438,28 @@ static uint32 TupleHashTableHash(const void* key, Size keysize)
int i;
if (tuple == NULL) {
/* Process the current input tuple for the table */
/* 处理表的当前输入元组 */
slot = hashtable->inputslot;
hashfunctions = hashtable->in_hash_funcs;
} else {
/* Process a tuple already stored in the table */
/* (this case never actually occurs in current dynahash.c code) */
/* 处理已经存储在表中的元组 */
/* (这种情况在当前的dynahash.c代码中从未发生过) */
slot = hashtable->tableslot;
ExecStoreMinimalTuple(tuple, slot, false);
hashfunctions = hashtable->tab_hash_funcs;
}
/* Get the Table Accessor Method*/
/* 获取表访问器方法*/
for (i = 0; i < numCols; i++) {
AttrNumber att = keyColIdx[i];
Datum attr;
bool isNull = false;
/* rotate hashkey left 1 bit at each step */
/* 每一步将哈希键向左旋转1位 */
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
attr = tableam_tslot_getattr(slot, att, &isNull);
/* treat nulls as having hash key 0 */
/* 将空值视为哈希键为0 */
if (!isNull) {
uint32 hkey;
hkey = DatumGetUInt32(FunctionCall1(&hashfunctions[i], attr));
@ -500,15 +473,13 @@ static uint32 TupleHashTableHash(const void* key, Size keysize)
}
/*
* See whether two tuples (presumably of the same hash value) match
* ()
*
* As above, the passed pointers are pointers to TupleHashEntryData.
* TupleHashEntryData的指针
*
* u_sess->exec_cxt.cur_tuple_hash_table must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise.
* u_sess - > exec_cxtcur_tuple_hash_table必须在调用它之前设置dynahash.c没有提供任何让我们以其他方式获取哈希表的API
*
* Also, the caller must select an appropriate memory context for running
* the compare functions. (dynahash.c doesn't change CurrentMemoryContext.)
* (dynahash.c不会改变CurrentMemoryContext)
*/
static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
{
@ -522,10 +493,9 @@ static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
TupleHashTable hashtable = u_sess->exec_cxt.cur_tuple_hash_table;
/*
* We assume that dynahash.c will only ever call us with the first
* argument being an actual table entry, and the second argument being
* LookupTupleHashEntry's dummy TupleHashEntryData. The other direction
* could be supported too, but is not currently used by dynahash.c.
* dynahash.c调用我们时
* LookupTupleHashEntry的假TupleHashEntryData
* dynahash.c使用
*/
Assert(tuple1 != NULL);
slot1 = hashtable->tableslot;
@ -533,7 +503,7 @@ static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
Assert(tuple2 == NULL);
slot2 = hashtable->inputslot;
/* For crosstype comparisons, the inputslot must be first */
/* 对于交叉类型比较,输入槽必须是第一个 */
if (execTuplesMatch(
slot2, slot1, hashtable->numCols, hashtable->keyColIdx, hashtable->cur_eq_funcs, hashtable->tempcxt))
return 0;

View File

@ -1,14 +1,12 @@
/* -------------------------------------------------------------------------
* execJunk.cpp
* ...
*
* execJunk.cpp
* Junk attribute support stuff....
* (c) 2020
* (c) 1996-2012PostgreSQL全球开发团队
* (c) 1994
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
*
* src/gausskernel/runtime/executor/execJunk.cpp
*
* -------------------------------------------------------------------------
@ -21,84 +19,54 @@
#include "pgxc/pgxc.h"
/* -------------------------------------------------------------------------
* XXX this stuff should be rewritten to take advantage
* of ExecProject() and the ProjectionInfo node.
* -cim 6/3/91
*
* An attribute of a tuple living inside the executor, can be
* either a normal attribute or a "junk" attribute. "junk" attributes
* never make it out of the executor, i.e. they are never printed,
* returned or stored on disk. Their only purpose in life is to
* store some information useful only to the executor, mainly the values
* of system attributes like "ctid", or sort key columns that are not to
* be output.
*
* The general idea is the following: A target list consists of a list of
* TargetEntry nodes containing expressions. Each TargetEntry has a field
* called 'resjunk'. If the value of this field is true then the
* corresponding attribute is a "junk" attribute.
*
* When we initialize a plan we call ExecInitJunkFilter to create a filter.
*
* We then execute the plan, treating the resjunk attributes like any others.
*
* Finally, when at the top level we get back a tuple, we can call
* ExecFindJunkAttribute/ExecGetJunkAttribute to retrieve the values of the
* junk attributes we are interested in, and ExecFilterJunk to remove all the
* junk attributes from a tuple. This new "clean" tuple is then printed,
* inserted, or updated.
* XXX ExecProject() ProjectionInfo
* -cim 6/3/91
* "垃圾" "垃圾"
* "ctid"
* TargetEntry
* TargetEntry 'resjunk' true "垃圾"
* ExecInitJunkFilter
* resjunk
* ExecFindJunkAttribute/ExecGetJunkAttribute来检索我们感兴趣的垃圾属性的值 ExecFilterJunk
* "干净"
*
* -------------------------------------------------------------------------
*/
/*
* ExecInitJunkFilter
*
* Initialize the Junk filter.
*
* The source targetlist is passed in. The output tuple descriptor is
* built from the non-junk tlist entries, plus the passed specification
* of whether to include room for an OID or not.
* An optional resultSlot can be passed as well.
* OID
* tlist
* OID
* resultSlot
*/
JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* slot, TableAmType tam)
{
JunkFilter* junkfilter = NULL;
TupleDesc cleanTupType;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
AttrNumber cleanResno;
JunkFilter* junkfilter = NULL;
TupleDesc cleanTupType;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
AttrNumber cleanResno;
/*
* Compute the tuple descriptor for the cleaned tuple.
*/
cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, tam);
// 计算清理后的元组描述符
cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, tam);
/*
* Use the given slot, or make a new slot if we weren't given one.
*/
// 设置槽的描述符,如果给定了槽,则使用给定的槽,否则创建一个新槽
if (slot != NULL)
ExecSetSlotDescriptor(slot, cleanTupType);
else
slot = MakeSingleTupleTableSlot(cleanTupType);
/*
* Now calculate the mapping between the original tuple's attributes and
* the "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e. one
* entry for every attribute of the "clean" tuple. The value of this entry
* is the attribute number of the corresponding attribute of the
* "original" tuple. (Zero indicates a NULL output attribute, but we do
* not use that feature in this routine.)
*/
cleanLength = cleanTupType->natts;
cleanLength = cleanTupType->natts;
if (cleanLength > 0) {
cleanMap = (AttrNumber*)palloc(cleanLength * sizeof(AttrNumber));
cleanMap = (AttrNumber*)palloc(cleanLength * sizeof(AttrNumber));
cleanResno = 1;
foreach (t, targetList) {
TargetEntry* tle = (TargetEntry*)lfirst(t);
// 如果不是 "junk" 属性,则建立属性映射关系
if (!tle->resjunk) {
cleanMap[cleanResno - 1] = tle->resno;
cleanResno++;
@ -108,11 +76,10 @@ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* sl
cleanMap = NULL;
}
/*
* Finally create and initialize the JunkFilter struct.
*/
// 创建并初始化 JunkFilter 结构
junkfilter = makeNode(JunkFilter);
// 填充 JunkFilter 结构的字段
junkfilter->jf_targetList = targetList;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
@ -124,78 +91,64 @@ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* sl
/*
* ExecInitJunkFilterConversion
*
* Initialize a JunkFilter for rowtype conversions.
*
* Here, we are given the target "clean" tuple descriptor rather than
* inferring it from the targetlist. The target descriptor can contain
* deleted columns. It is assumed that the caller has checked that the
* non-deleted columns match up with the non-junk columns of the targetlist.
*
*
*
*/
JunkFilter* ExecInitJunkFilterConversion(List* targetList, TupleDesc cleanTupType, TupleTableSlot* slot)
{
JunkFilter* junkfilter = NULL;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
int i;
JunkFilter* junkfilter = NULL;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
int i;
/*
* Use the given slot, or make a new slot if we weren't given one.
*/
// 检查是否给定了槽,如果给定则使用,否则创建一个新的槽
if (slot != NULL)
ExecSetSlotDescriptor(slot, cleanTupType);
ExecSetSlotDescriptor(slot, cleanTupType);
else
slot = MakeSingleTupleTableSlot(cleanTupType);
slot = MakeSingleTupleTableSlot(cleanTupType);
/*
* Calculate the mapping between the original tuple's attributes and the
* "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e. one
* entry for every attribute of the "clean" tuple. The value of this entry
* is the attribute number of the corresponding attribute of the
* "original" tuple. We store zero for any deleted attributes, marking
* that a NULL is needed in the output tuple.
*/
cleanLength = cleanTupType->natts;
cleanLength = cleanTupType->natts;
// 为属性映射数组分配内存,并初始化为 0
if (cleanLength > 0) {
cleanMap = (AttrNumber*)palloc0(cleanLength * sizeof(AttrNumber));
t = list_head(targetList);
for (i = 0; i < cleanLength; i++) {
cleanMap = (AttrNumber*)palloc0(cleanLength * sizeof(AttrNumber));
t = list_head(targetList);
for (i = 0; i < cleanLength; i++) {
if (cleanTupType->attrs[i]->attisdropped)
continue; /* map entry is already zero */
continue; // 跳过已删除的属性
for (;;) {
TargetEntry* tle = (TargetEntry*)lfirst(t);
TargetEntry* tle = (TargetEntry*)lfirst(t);
t = lnext(t);
t = lnext(t);
if (!tle->resjunk) {
cleanMap[i] = tle->resno;
cleanMap[i] = tle->resno;
break;
}
}
}
} else {
cleanMap = NULL;
cleanMap = NULL;
}
/*
* Finally create and initialize the JunkFilter struct.
*/
junkfilter = makeNode(JunkFilter);
// 创建并初始化 JunkFilter 结构
junkfilter = makeNode(JunkFilter);
junkfilter->jf_targetList = targetList;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
// 填充 JunkFilter 结构的各个字段
junkfilter->jf_targetList = targetList;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
return junkfilter;
return junkfilter;
}
/*
* ExecFindJunkAttribute
*
* Locate the specified junk attribute in the junk filter's targetlist,
* and return its resno. Returns InvalidAttrNumber if not found.
* resno
* InvalidAttrNumber
*/
AttrNumber ExecFindJunkAttribute(JunkFilter* junkfilter, const char* attrName)
{
@ -205,8 +158,7 @@ AttrNumber ExecFindJunkAttribute(JunkFilter* junkfilter, const char* attrName)
/*
* ExecFindJunkPrimaryKeys
*
* Locate the specified junk attribute in the junk filter's targetlist.
* Returns NIL if not found.
* xc_primary_key
*/
List* ExecFindJunkPrimaryKeys(List* targetlist)
{
@ -228,8 +180,7 @@ List* ExecFindJunkPrimaryKeys(List* targetlist)
/*
* ExecFindJunkAttributeInTlist
*
* Find a junk attribute given a subplan's targetlist (not necessarily
* part of a JunkFilter).
*
*/
AttrNumber ExecFindJunkAttributeInTlist(List* targetlist, const char* attrName)
{
@ -250,9 +201,7 @@ AttrNumber ExecFindJunkAttributeInTlist(List* targetlist, const char* attrName)
/*
* ExecGetJunkAttribute
*
* Given a junk filter's input tuple (slot) and a junk attribute's number
* previously found by ExecFindJunkAttribute, extract & return the value and
* isNull flag of the attribute.
*
*/
Datum ExecGetJunkAttribute(TupleTableSlot* slot, AttrNumber attno, bool* isNull)
{
@ -265,7 +214,7 @@ Datum ExecGetJunkAttribute(TupleTableSlot* slot, AttrNumber attno, bool* isNull)
/*
* ExecFilterJunk
*
* Construct and return a slot with all the junk attributes removed.
*
*/
TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot)
{
@ -279,34 +228,24 @@ TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot)
Datum* old_values = NULL;
bool* old_isnull = NULL;
/*
* Extract all the values of the old tuple.
*/
/* Get the Table Accessor Method*/
// 从原始元组中提取所有属性值
Assert(slot != NULL && slot->tts_tupleDescriptor != NULL);
tableam_tslot_getallattrs(slot);
old_values = slot->tts_values;
old_isnull = slot->tts_isnull;
/*
* get info from the junk filter
*/
// 获取 JunkFilter 中的信息
cleanTupType = junkfilter->jf_cleanTupType;
cleanLength = cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
resultSlot = junkfilter->jf_resultSlot;
/*
* Prepare to build a virtual result tuple.
*/
// 准备构建虚拟结果元组
(void)ExecClearTuple(resultSlot);
values = resultSlot->tts_values;
isnull = resultSlot->tts_isnull;
/*
* Transpose data into proper fields of the new tuple.
*/
// 转置数据到新元组的适当字段中
for (i = 0; i < cleanLength; i++) {
int j = cleanMap[i];
@ -319,95 +258,102 @@ TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot)
}
}
/*
* And return the virtual tuple.
*/
// 返回过滤后的虚拟元组
return ExecStoreVirtualTuple(resultSlot);
}
/*
* BatchExecFilterJunk
*
* Construct and return a vector batch with all the junk attributes removed.
*
*/
VectorBatch* BatchExecFilterJunk(_in_ JunkFilter* junkfilter, __inout VectorBatch* batch)
{
AttrNumber* cleanMap = NULL;
TupleDesc cleanTupType;
int cleanLength;
int i;
ScalarVector* columns = NULL;
AttrNumber* cleanMap = NULL; // 属性映射数组,将清理后的属性编号映射到原始属性编号
TupleDesc cleanTupType; // 清理后元组的描述符
int cleanLength; // 清理后元组的属性数量
int i; // 循环计数变量
ScalarVector* columns = NULL; // 存储列向量的数组
// Get info from the junk filter
//
// 获取 JunkFilter 中的信息
cleanTupType = junkfilter->jf_cleanTupType;
cleanLength = cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
columns = batch->m_arr;
columns = batch->m_arr; // 获取列向量的数组
// Transpose data into proper fields of the new tuple.
//
// 转置数据到新元组的适当字段中
for (i = 0; i < cleanLength; i++) {
int j = cleanMap[i];
if (j == 0) {
for (int k = 0; k < columns[i].m_rows; k++) {
columns[i].SetNull(k);
columns[i].SetNull(k); // 将该列向量的元素设置为 NULL
}
} else {
columns[i] = columns[j - 1];
columns[i] = columns[j - 1]; // 将原始属性的列向量复制到新属性列向量
}
}
// Return the modified batch without changing the column count
// as the column count is early decided at compile time.
//
// 返回修改后的批处理数据,列数不变
return batch;
}
/*
*ExecSetjunkFilteDescriptor
*
*
* TupleDesc JunkFilter 便使
*/
void ExecSetjunkFilteDescriptor(JunkFilter* junkfilter, TupleDesc tupdesc)
{
TupleDesc resultslotTupType;
AttrNumber* cleanMap = NULL;
int cleanLength;
int i;
TupleDesc resultslotTupType; // 结果槽的元组描述符
AttrNumber* cleanMap = NULL; // 属性映射数组,将清理后的属性编号映射到原始属性编号
int cleanLength; // 清理后元组的属性数量
int i; // 循环计数变量
cleanLength = junkfilter->jf_cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
cleanLength = junkfilter->jf_cleanTupType->natts; // 获取清理后元组的属性数量
cleanMap = junkfilter->jf_cleanMap; // 获取属性映射数组
resultslotTupType = junkfilter->jf_resultSlot->tts_tupleDescriptor;
resultslotTupType = junkfilter->jf_resultSlot->tts_tupleDescriptor; // 获取结果槽的元组描述符
/*
* Transpose tupdesc into proper fields of the new tupdesc.
* tupdesc
*/
for (i = 0; i < cleanLength; i++) {
int j = cleanMap[i];
// 如果属性映射不为 0则将 tupdesc 的属性类型赋值给结果槽的元组描述符
if (j > 0)
resultslotTupType->attrs[i]->atttypid = tupdesc->attrs[j - 1]->atttypid;
}
}
/*
* @Description: Check if junk attribute xc_node_id is the same as current node identifier
*
* @param[IN] junkfilter: junk attributes
* @param[IN] batch: vector batch
* @return: void
/*BatchCheckNodeIdentifier
*
* `xc_node_id`
*/
void BatchCheckNodeIdentifier(JunkFilter* junkfilter, VectorBatch* batch)
{
ScalarVector* xc_node_id_col = NULL;
uint32 xc_node_id = 0;
int counter = 0;
ScalarVector* xc_node_id_col = NULL; // 用于存储 xc_node_id 的列向量
uint32 xc_node_id = 0; // 存储当前 xc_node_id
int counter = 0; // 循环计数变量
// 如果 xc_node_id 无效,则直接返回
if (InvalidAttrNumber == junkfilter->jf_xc_node_id) {
return;
}
// 获取 xc_node_id 列向量
xc_node_id_col = &(batch->m_arr[junkfilter->jf_xc_node_id - 1]);
// 遍历 xc_node_id 列向量中的值
for (counter = 0; counter < xc_node_id_col->m_rows; counter++) {
// 获取当前 xc_node_id 的值
xc_node_id = DatumGetUInt32(xc_node_id_col->m_vals[counter]);
// 检查当前 xc_node_id 是否与当前节点的标识不匹配,如果不匹配则抛出错误
if (u_sess->pgxc_cxt.PGXCNodeIdentifier != xc_node_id) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@ -418,3 +364,4 @@ void BatchCheckNodeIdentifier(JunkFilter* junkfilter, VectorBatch* batch)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,21 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* (c) 2020
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* openGauss Mulan PSL v2
* Mulan PSL v2 使
* Mulan PSL v2
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* "原样"
*
* Mulan PSL v2
* -------------------------------------------------------------------------
*
* execMerge.cpp
* routines to handle Merge nodes relating to the MERGE command
* MERGE Merge
*
* IDENTIFICATION
*
* src/gausskernel/runtime/executor/execMerge.cpp
*
* -------------------------------------------------------------------------
@ -39,11 +38,13 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot, JunkFilter* junkfilter,
ItemPointer tupleid, HeapTupleHeader oldtuple, Oid oldPartitionOid, int2 bucketid);
/*
* Perform MERGE.
* MERGE
*/
void ExecMerge(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot, JunkFilter* junkfilter,
ResultRelInfo* resultRelInfo)
{
// 获取执行上下文
ExprContext* econtext = mtstate->ps.ps_ExprContext;
ItemPointer tupleid;
ItemPointerData tuple_ctid;
@ -56,34 +57,26 @@ void ExecMerge(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot,
AttrNumber bucketIdNum;
int2 bucketid = InvalidBktId;
// 检查结果关系类型和垃圾过滤器
Assert(resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION ||
resultRelInfo->ri_RelationDesc->rd_rel->relkind == PARTTYPE_PARTITIONED_RELATION ||
junkfilter != NULL);
resultRelInfo->ri_RelationDesc->rd_rel->relkind == PARTTYPE_PARTITIONED_RELATION ||
junkfilter != NULL);
/*
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous cycle.
*
*/
ResetExprContext(econtext);
/*
* We run a JOIN between the target relation and the source relation to
* find a set of candidate source rows that has matching row in the target
* table and a set of candidate source rows that does not have matching
* row in the target table. If the join returns us a tuple with target
* relation's tid set, that implies that the join found a matching row for
* the given source tuple. This case triggers the WHEN MATCHED clause of
* the MERGE. Whereas a NULL in the target relation's ctid column
* indicates a NOT MATCHED case.
*/
// 从槽中提取关于匹配情况的信息
datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo, &isNull);
if (!isNull) {
matched = true;
tupleid = (ItemPointer)DatumGetPointer(datum);
tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
tuple_ctid = *tupleid;/* 确保我们不释放 ctid */
tupleid = &tuple_ctid;
// 处理分区表和分桶表的情况
if (RELATION_IS_PARTITIONED(resultRelInfo->ri_RelationDesc) ||
RelationIsCUFormat(resultRelInfo->ri_RelationDesc)) {
Datum tableOiddatum;
@ -114,67 +107,54 @@ void ExecMerge(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot,
bucketid = DatumGetObjectId(bucketIddatum);
}
} else {
matched = false;
tupleid = NULL; /* we don't need it for INSERT actions */
}
else {
matched = false;
tupleid = NULL; /* 对于 INSERT 操作,不需要这个信息 */
}
/*
* If we are dealing with a WHEN MATCHED case, we execute the first action
* for which the additional WHEN MATCHED AND quals pass. If an action
* without quals is found, that action is executed.
*
* Similarly, if we are dealing with WHEN NOT MATCHED case, we look at the
* given WHEN NOT MATCHED actions in sequence until one passes.
*
* Things get interesting in case of concurrent update/delete of the
* target tuple. Such concurrent update/delete is detected while we are
* executing a WHEN MATCHED action.
*
* A concurrent update can:
*
* 1. modify the target tuple so that it no longer satisfies the
* additional quals attached to the current WHEN MATCHED action OR
*
* In this case, we are still dealing with a WHEN MATCHED case, but
* we should recheck the list of WHEN MATCHED actions and choose the first
* one that satisfies the new target tuple.
*
* 2. modify the target tuple so that the join quals no longer pass and
* hence the source tuple no longer has a match.
*
* In the second case, the source tuple no longer matches the target tuple,
* so we now instead find a qualifying WHEN NOT MATCHED action to execute.
*
* A concurrent delete, changes a WHEN MATCHED case to WHEN NOT MATCHED.
*
* ExecMergeMatched takes care of following the update chain and
* re-finding the qualifying WHEN MATCHED action, as long as the updated
* target tuple still satisfies the join quals i.e. it still remains a
* WHEN MATCHED case. If the tuple gets deleted or the join quals fail, it
* returns and we try ExecMergeNotMatched. Given that ExecMergeMatched
* always make progress by following the update chain and we never switch
* from ExecMergeNotMatched to ExecMergeMatched, there is no risk of a
* livelock.
*/
* WHEN MATCHED WHEN MATCHED AND
*
*
* WHEN NOT MATCHED WHEN NOT MATCHED
*
* WHEN MATCHED /
*
*
*
* 1. 使 WHEN MATCHED
*
* WHEN MATCHED WHEN MATCHED
*
* 2. 使
*
* WHEN NOT MATCHED
*
* WHEN MATCHED WHEN NOT MATCHED
*
* ExecMergeMatched WHEN MATCHED WHEN MATCHED
* ExecMergeNotMatched ExecMergeMatched
* ExecMergeNotMatched ExecMergeMatched
*/
// 根据匹配情况执行相应的动作
if (matched)
matched = ExecMergeMatched(mtstate, estate, slot, junkfilter, tupleid, oldtuple, oldPartitionOid, bucketid);
/*
* Either we were dealing with a NOT MATCHED tuple or ExecMergeNotMatched()
* returned "false", indicating the previously MATCHED tuple is no longer a
* matching tuple.
*/
// 如果没有匹配的情况,执行相应的 NOT MATCHED 操作
if (!matched)
ExecMergeNotMatched(mtstate, estate, slot);
}
/*
* Extract tuple for checking constraints from plan slot
*
*/
static TupleTableSlot* ExtractConstraintTuple(
ModifyTableState* mtstate, CmdType commandType, TupleTableSlot* slot, TupleDesc tupDesc)
{
// 获取执行上下文
ExprContext* econtext = mtstate->ps.ps_ExprContext;
AutoContextSwitch memContext(econtext->ecxt_per_tuple_memory);
HeapTuple tempTuple = NULL;
@ -185,40 +165,47 @@ static TupleTableSlot* ExtractConstraintTuple(
int index = 0;
int i = 0;
// 根据命令类型提取约束元组的槽
switch (commandType) {
case CMD_UPDATE:
constrSlot = mtstate->mt_update_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
if (strstr(originTupleDesc->attrs[i]->attname.data, "action UPDATE target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
}
case CMD_UPDATE:
constrSlot = mtstate->mt_update_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
// 查找符合条件的属性并复制值和空标志
if (strstr(originTupleDesc->attrs[i]->attname.data, "action UPDATE target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
}
break;
case CMD_INSERT:
constrSlot = mtstate->mt_insert_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
if (strstr(originTupleDesc->attrs[i]->attname.data, "action INSERT target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
}
}
break;
case CMD_INSERT:
constrSlot = mtstate->mt_insert_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
// 查找符合条件的属性并复制值和空标志
if (strstr(originTupleDesc->attrs[i]->attname.data, "action INSERT target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
}
break;
default:
Assert(0);
}
break;
default:
Assert(0);
}
// 确保约束槽的表访问方法类型与原始元组描述一致
Assert(constrSlot->tts_tupleDescriptor->tdTableAmType == originTupleDesc->tdTableAmType);
// 使用 values 和 isnull 数组创建临时 HeapTuple并将其存储到约束槽中
tempTuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull, HEAP_TUPLE);
(void)ExecStoreTuple(tempTuple, constrSlot, InvalidBuffer, false);
return constrSlot;
}
/*
* Extract scan tuple for target table from plan slot
*
*/
TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot, TupleDesc tupDesc)
{
@ -234,18 +221,18 @@ TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot
int index = 0;
/*
* Find the right start index for target table. We should skip the sourceTargetList.
* First count the number of source targetlist. We add new columns to sourceTargetList
* but the resno is not continuous, so find the max continuous number to be the original
* length of sourceTargetList.
* sourceTargetList
* sourceTargetList sourceTargetList
* resno sourceTargetList
*/
foreach (lc, sourceTargetList) {
foreach(lc, sourceTargetList) {
TargetEntry* tle = (TargetEntry*)lfirst(lc);
if (tle->resno != startIdx + 1)
break;
startIdx++;
}
// 从原始槽中提取值和空标志,并构建一个临时 HeapTuple
for (index = 0; index < tupDesc->natts; index++) {
if (tupDesc->attrs[index]->attisdropped == true) {
isnull[index] = true;
@ -257,6 +244,7 @@ TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot
startIdx++;
}
// 使用 values 和 isnull 数组创建临时 HeapTuple并将其存储到扫描槽中
tempTuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull, HEAP_TUPLE);
(void)ExecStoreTuple(tempTuple, scanSlot, InvalidBuffer, false);
@ -264,15 +252,15 @@ TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot
}
/*
* Description: projects and evaluates qual condition for update action.
* Parameters:
* @in mtstate: modifytable state.
* @in mergeMatchedActionStates: update action states.
* @in econtext: expression context.
* @in originSlot: slot to be projected.
* @in result_slot: slot to be returned.
* @in estate: working state for executor.
* Return: slot has been projected..
*
*
* @in mtstatemodifytable
* @in mergeMatchedActionStates
* @in econtext
* @in originSlot
* @in result_slot
* @in estate
*
*/
TupleTableSlot* ExecMergeProjQual(ModifyTableState* mtstate, List* mergeMatchedActionStates, ExprContext* econtext,
TupleTableSlot* originSlot, TupleTableSlot* result_slot, EState* estate)
@ -285,55 +273,51 @@ TupleTableSlot* ExecMergeProjQual(ModifyTableState* mtstate, List* mergeMatchedA
Assert(CMD_UPDATE == action->commandType);
/*
* get information on the (current) result relation
*/
*
*/
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
* Make tuple and any needed join variables available to ExecQual and
* ExecProject. The target's existing tuple is installed in the scantuple.
* Again, this target relation's slot is required only in the case of a
* MATCHED tuple and UPDATE/DELETE actions.
*/
* 使 ExecQual ExecProject
* scantuple
* UPDATE/DELETE
*/
if (estate->es_result_update_remoterel == NULL) {
econtext->ecxt_scantuple = ExtractScanTuple(mtstate, originSlot, action->tupDesc);
econtext->ecxt_innertuple = originSlot;
econtext->ecxt_outertuple = NULL;
} else {
}
else {
econtext->ecxt_scantuple = originSlot;
econtext->ecxt_innertuple = NULL;
econtext->ecxt_outertuple = NULL;
}
/*
* Test condition, if any
*
* In the absence of a condition we perform the action unconditionally
* (no need to check separately since ExecQual() will return true if
* there are no conditions to evaluate).
*/
*
*
*
* ExecQual() true
*/
if (ExecQual((List*)action->whenqual, econtext, false)) {
if (estate->es_result_update_remoterel == NULL) {
/*
* We set up the projection earlier, so all we do here is
* Project, no need for any other tasks prior to the
* ExecUpdate.
*/
* ExecUpdate
*/
result_slot = ExecProject(action->proj, NULL);
} else {
/* we don't do projection in remote query */
}
else {
/* 在远程查询中我们不进行投影操作 */
}
/*
* We don't call ExecFilterJunk() because the projected tuple
* using the UPDATE action's targetlist doesn't have a junk
* attribute.
* ExecFilterJunk()使 UPDATE
*/
if (estate->es_result_update_remoterel) {
estate->es_result_remoterel = estate->es_result_update_remoterel;
/* Check if has constraints */
/* 检查是否有约束条件 */
if (resultRelationDesc->rd_att->constr) {
mtstate->mt_update_constr_slot =
ExtractConstraintTuple(mtstate, CMD_UPDATE, result_slot, action->tupDesc);
@ -347,25 +331,20 @@ TupleTableSlot* ExecMergeProjQual(ModifyTableState* mtstate, List* mergeMatchedA
}
/*
* Check and execute the first qualifying MATCHED action. The current target
* tuple is identified by tupleid.
* MATCHED tupleid
*
* We start from the first WHEN MATCHED action and check if the WHEN AND quals
* pass, if any. If the WHEN AND quals for the first action do not pass, we
* check the second, then the third and so on. If we reach to the end, no
* action is taken and we return true, indicating that no further action is
* required for this tuple.
* WHEN MATCHED WHEN AND
* WHEN AND
* true
*
* If we do find a qualifying action, then we attempt to execute the action.
*
*
* If the tuple is concurrently updated, EvalPlanQual is run with the updated
* tuple to recheck the join quals. Note that the additional quals associated
* with individual actions are evaluated separately by the MERGE code, while
* EvalPlanQual checks for the join quals. If EvalPlanQual tells us that the
* updated tuple still passes the join quals, then we restart from the first
* action to look for a qualifying action. Otherwise, we return false meaning
* that a NOT MATCHED action must now be executed for the current source tuple.
* 使 EvalPlanQual
* MERGE EvalPlanQual
* EvalPlanQual
* false NOT MATCHED
*/
static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot, JunkFilter* junkfilter,
ItemPointer tupleid, HeapTupleHeader oldtuple, Oid oldPartitionOid, int2 bucketid)
{
@ -378,13 +357,13 @@ static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTab
bool partKeyUpdated = ((ModifyTable*)mtstate->ps.plan)->partKeyUpdated;
/*
* Save the current information and work with the correct result relation.
*
*/
saved_resultRelInfo = resultRelInfo;
estate->es_result_relation_info = resultRelInfo;
/*
* And get the correct action lists.
*
*/
mergeMatchedActionStates = resultRelInfo->ri_mergeState->matchedActionStates;
@ -395,34 +374,33 @@ static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTab
if (slot != NULL) {
(void)ExecUpdate(tupleid,
oldPartitionOid,
bucketid,
oldtuple,
slot,
saved_slot,
epqstate,
mtstate,
mtstate->canSetTag,
partKeyUpdated);
oldPartitionOid,
bucketid,
oldtuple,
slot,
saved_slot,
epqstate,
mtstate,
mtstate->canSetTag,
partKeyUpdated);
}
if (action->commandType == CMD_UPDATE /* && tuple_updated*/)
InstrCountFiltered2(&mtstate->ps, 1);
/*
* We've activated one of the WHEN clauses, so we don't search
* further. This is required behaviour, not an optimization.
*/
* WHEN
*/
estate->es_result_relation_info = saved_resultRelInfo;
}
/*
* Successfully executed an action or no qualifying action was found.
*
*/
return true;
}
/*
* Execute the first qualifying NOT MATCHED action.
* NOT MATCHED
*/
static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot)
{
@ -433,31 +411,26 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
const int hi_options = 0;
/*
* We are dealing with NOT MATCHED tuple. Since for MERGE, the partition
* tree is not expanded for the result relation, we continue to work with
* the currently active result relation, which corresponds to the root
* of the partition tree.
* NOT MATCHED MERGE使
*
*/
resultRelInfo = mtstate->resultRelInfo;
/*
* For INSERT actions, root relation's merge action is OK since the
* INSERT's targetlist and the WHEN conditions can only refer to the
* source relation and hence it does not matter which result relation we
* work with.
* INSERT INSERT WHEN
* 使
*/
mergeNotMatchedActionStates = resultRelInfo->ri_mergeState->notMatchedActionStates;
/*
* Make source tuple available to ExecQual and ExecProject. We don't need
* the target tuple since the WHEN quals and the targetlist can't refer to
* the target columns.
* 使 ExecQual ExecProject WHEN
*/
if (estate->es_result_insert_remoterel == NULL) {
econtext->ecxt_scantuple = slot;
econtext->ecxt_innertuple = slot;
econtext->ecxt_outertuple = NULL;
} else {
}
else {
econtext->ecxt_scantuple = slot;
econtext->ecxt_innertuple = NULL;
econtext->ecxt_outertuple = NULL;
@ -471,36 +444,33 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
Assert(CMD_INSERT == action->commandType);
/*
* get information on the (current) result relation
*
*/
resultRelationInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
/*
* Test condition, if any
*
* In the absence of a condition we perform the action unconditionally
* (no need to check separately since ExecQual() will return true if
* there are no conditions to evaluate).
*/
*
*
*
* ExecQual() true
*/
if (ExecQual((List*)action->whenqual, econtext, false)) {
/*
* We set up the projection earlier, so all we do here is
* Project, no need for any other tasks prior to the
* ExecInsert.
* ExecInsert
*/
if (estate->es_result_insert_remoterel == NULL) {
ExecProject(action->proj, NULL);
/*
* ExecPrepareTupleRouting may modify the passed-in slot. Hence
* pass a local reference so that action->slot is not modified.
* ExecPrepareTupleRouting action->slot
*/
myslot = mtstate->mt_mergeproj;
} else {
/* in pgxc we do projection in the remote query*/
}
else {
/* 在 pgxc 中,我们在远程查询中进行投影操作 */
myslot = slot;
/* Check if has constraints */
/* 检查是否有约束条件 */
if (resultRelationDesc->rd_att->constr) {
mtstate->mt_insert_constr_slot = ExtractConstraintTuple(mtstate, CMD_INSERT, slot, action->tupDesc);
}
@ -516,7 +486,7 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
}
/*
* Creates the run-time state information for the Merge node
* Merge
*/
void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* resultRelInfo)
{
@ -527,32 +497,33 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res
TupleDesc relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
ModifyTable* node = (ModifyTable*)mtstate->ps.plan;
// 如果 mergeActionList 为空,则直接返回
if (node->mergeActionList == NIL)
return;
mtstate->mt_merge_subcommands = 0;
// 分配表达式上下文,如果不存在的话
if (mtstate->ps.ps_ExprContext == NULL)
ExecAssignExprContext(estate, &mtstate->ps);
econtext = mtstate->ps.ps_ExprContext;
/* initialize scan slot and constraint slot */
// 初始化扫描槽和约束槽
mtstate->mt_scan_slot = NULL;
mtstate->mt_update_constr_slot = NULL;
mtstate->mt_insert_constr_slot = NULL;
/* initialize slot for merge actions */
// 初始化用于合并操作的投影槽
Assert(mtstate->mt_mergeproj == NULL);
mtstate->mt_mergeproj = ExecInitExtraTupleSlot(mtstate->ps.state);
ExecSetSlotDescriptor(mtstate->mt_mergeproj, relationDesc);
/*
* Create a MergeActionState for each action on the mergeActionList
* and add it to either a list of matched actions or not-matched
* actions.
* mergeActionList MergeActionState
*
*/
foreach (l, node->mergeActionList) {
foreach(l, node->mergeActionList) {
MergeAction* action = (MergeAction*)lfirst(l);
MergeActionState* action_state = makeNode(MergeActionState);
TupleDesc tupDesc;
@ -562,10 +533,11 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res
action_state->commandType = action->commandType;
action_state->whenqual = ExecInitExpr((Expr*)action->qual, &mtstate->ps);
/* create target slot for this action's projection */
/* 为此动作的投影创建目标槽 */
tupDesc = ExecTypeFromTL((List*)action->targetList, false, true, relationDesc->tdTableAmType);
action_state->tupDesc = tupDesc;
// 在特定情况下创建扫描槽和约束槽
if (IS_PGXC_DATANODE && CMD_UPDATE == action->commandType) {
mtstate->mt_scan_slot = MakeSingleTupleTableSlot(tupDesc);
}
@ -578,34 +550,35 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res
mtstate->mt_insert_constr_slot = MakeSingleTupleTableSlot(tupDesc);
}
/* build action projection state */
/* 构建动作投影状态 */
targetList = (List*)ExecInitExpr((Expr*)action->targetList, &mtstate->ps);
action_state->proj = ExecBuildProjectionInfo(targetList, econtext, mtstate->mt_mergeproj, relationDesc);
/*
* We create two lists - one for WHEN MATCHED actions and one
* for WHEN NOT MATCHED actions - and stick the
* MergeActionState into the appropriate list.
* - WHEN MATCHED WHEN NOT MATCHED -
* MergeActionState
*/
if (action_state->matched)
mergeMatchedActionStates = lappend(mergeMatchedActionStates, action_state);
else
mergeNotMatchedActionStates = lappend(mergeNotMatchedActionStates, action_state);
// 根据不同的操作类型设置子命令标志
switch (action->commandType) {
case CMD_INSERT:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_INSERT;
break;
case CMD_UPDATE:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_UPDATE;
break;
default:
Assert(0);
break;
case CMD_INSERT:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_INSERT;
break;
case CMD_UPDATE:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_UPDATE;
break;
default:
Assert(0);
break;
}
// 设置匹配和不匹配动作的状态列表
resultRelInfo->ri_mergeState->matchedActionStates = mergeMatchedActionStates;
resultRelInfo->ri_mergeState->notMatchedActionStates = mergeNotMatchedActionStates;
}

View File

@ -1,80 +1,59 @@
/* -------------------------------------------------------------------------
*
* execProcnode.cpp
* contains dispatch functions which call the appropriate "initialize",
* "get a tuple", and "cleanup" routines for the given node type.
* If the node has children, then it will presumably call ExecInitNode,
* ExecProcNode, or ExecEndNode on its subnodes and do the appropriate
* processing.
* "初始化""获取元组" "清理"
* ExecInitNodeExecProcNode ExecEndNode
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 2021, openGauss Contributors
* (c) 2020
* (c) 1996-2012 PostgreSQL
* (c) 1994
* (c) 2021 openGauss
*
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/execProcnode.cpp
*
* src/gausskernel/runtime/executor/execProcnode.cpp
*
* -------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecInitNode - initialize a plan node and its subplans
* ExecProcNode - get a tuple by executing the plan node
* ExecEndNode - shut down a plan node and its subplans
*
* NOTES
* This used to be three files. It is now all combined into
* one file so that it is easier to keep ExecInitNode, ExecProcNode,
* and ExecEndNode in sync when new nodes are added.
*
* EXAMPLE
* Suppose we want the age of the manager of the shoe department and
* the number of employees in that department. So we have the query:
*
* select DEPT.no_emps, EMP.age
* where EMP.name = DEPT.mgr and
* DEPT.name = "shoe"
*
* Suppose the planner gives us the following plan:
*
* Nest Loop (DEPT.mgr = EMP.name)
* / \
* / \
* Seq Scan Seq Scan
* DEPT EMP
* (name = "shoe")
*
* ExecutorStart() is called first.
* It calls InitPlan() which calls ExecInitNode() on
* the root of the plan -- the nest loop node.
*
* * ExecInitNode() notices that it is looking at a nest loop and
* as the code below demonstrates, it calls ExecInitNestLoop().
* Eventually this calls ExecInitNode() on the right and left subplans
* and so forth until the entire plan is initialized. The result
* of ExecInitNode() is a plan state tree built with the same structure
* as the underlying plan tree.
*
* * Then when ExecutorRun() is called, it calls ExecutePlan() which calls
* ExecProcNode() repeatedly on the top node of the plan state tree.
* Each time this happens, ExecProcNode() will end up calling
* ExecNestLoop(), which calls ExecProcNode() on its subplans.
* Each of these subplans is a sequential scan so ExecSeqScan() is
* called. The slots returned by ExecSeqScan() may contain
* tuples which contain the attributes ExecNestLoop() uses to
* form the tuples it returns.
*
* * Eventually ExecSeqScan() stops returning tuples and the nest
* loop join ends. Lastly, ExecutorEnd() calls ExecEndNode() which
* calls ExecEndNestLoop() which in turn calls ExecEndNode() on
* its subplans which result in ExecEndSeqScan().
*
* This should show how the executor works by having
* ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
* their work to the appopriate node support routines which may
* in turn call these routines themselves on their subplans.
*
* ExecInitNode -
* ExecProcNode -
* ExecEndNode -
* 便ExecInitNodeExecProcNode和ExecEndNode的同步
```
select DEPT.no_emps, EMP.age
where EMP.name = DEPT.mgr and
DEPT.name = "shoe"
```
```
Nest Loop (DEPT.mgr = EMP.name)
/ \
/ \
Seq Scan Seq Scan
DEPT EMP
(name = "shoe")
```
ExecutorStart()InitPlan()ExecInitNode()
* ExecInitNode() ExecInitNestLoop()ExecInitNode()ExecInitNode()
* ExecutorRun()ExecutePlan()ExecProcNode()ExecProcNode() ExecNestLoop()ExecProcNode()ExecSeqScan()ExecSeqScan() ExecNestLoop()
* ExecSeqScan() ExecutorEnd() ExecEndNode()ExecEndNestLoop()ExecEndNode()ExecEndSeqScan()
ExecInitNode()ExecProcNode() ExecEndNode()
*/
#include "postgres.h"
#include "knl/knl_variable.h"
@ -167,30 +146,31 @@
#define NODENAMELEN 64
/*
* Function to determine a plannode should be processed in stub-routine when exec_nodes
* does not match current DN.
/*NeedStubExecution
* PlanStub Execution
*
*
* The term of "processed in stub" means we need let ExecNodeInit() bypass the actual
* initilaization work like open scanrel, instead allow NodeInit work to continue on its
* lefttree/righttree
* "在存根中处理" ExecNodeInit()
* NodeInit工作在其lefttree/righttree上继续进行
*/
bool NeedStubExecution(Plan* plan)
{
#ifndef ENABLE_MULTIPLE_NODES
return false;
return false; // 如果不支持多节点模式,则直接返回不需要存根执行
#endif
/* If a plan node is under recursive union, we don't consider stub execution */
// 如果计划节点位于递归联合操作之下,我们不考虑存根执行
if (EXEC_IN_RECURSIVE_MODE(plan)) {
return false;
}
/* First, determine if this plan step needs excution on current dn */
// 首先确定此计划步骤是否需要在当前数据库节点DN上执行
if (NeedExecute(plan)) {
return false;
}
/* Second, determine if this plan step need stub processing */
// 其次,确定此计划步骤是否需要进行存根处理
switch (nodeTag(plan)) {
case T_ModifyTable:
case T_VecModifyTable:
@ -210,15 +190,18 @@ bool NeedStubExecution(Plan* plan)
case T_CStoreIndexHeapScan:
case T_SubqueryScan:
case T_FunctionScan:
return true;
return true; // 需要进行存根处理
default:
return false;
return false; // 其他情况不需要存根处理
}
}
/*
* not need execute active sql if the datanode don't run in multi-nodegroup.
* NeedExecuteActiveSql
* SQL计划SQL
*/
static bool NeedExecuteActiveSql(Plan* plan)
{
if ((!IS_PGXC_COORDINATOR) && (!IS_SINGLE_NODE) && false == NeedExecute(plan)) {
@ -228,31 +211,50 @@ static bool NeedExecuteActiveSql(Plan* plan)
return true;
}
/*
*
*/
static inline bool SeqScanNodeIsStub(SeqScanState* seq_scan)
{
return seq_scan->ss_currentScanDesc == NULL;
}
/*
*/
static inline bool IdxScanNodeIsStub(IndexScanState* index_scan)
{
return index_scan->iss_ScanDesc == NULL;
}
/*
*/
static inline bool IdxOnlyScanNodeIsStub(IndexOnlyScanState* index_only_scan)
{
return index_only_scan->ioss_ScanDesc == NULL;
}
/*
*/
static inline bool BmIdxOnlyScanNodeIsStub(BitmapIndexScanState* bm_index_scan)
{
return bm_index_scan->biss_ScanDesc == NULL;
}
/*
*/
static inline bool BmHeapScanNodeIsStub(BitmapHeapScanState* bm_heap_scan)
{
return bm_heap_scan->ss.ss_currentScanDesc == NULL;
}
/*
*/
PlanState* ExecInitNodeByType(Plan* node, EState* estate, int eflags)
{
switch (nodeTag(node)) {
@ -406,6 +408,9 @@ PlanState* ExecInitNodeByType(Plan* node, EState* estate, int eflags)
}
}
/*
PlanSubPlan,便
*/
void ExecInitNodeSubPlan(Plan* node, EState* estate, PlanState* result)
{
List* sub_ps = NIL;
@ -432,17 +437,21 @@ void ExecInitNodeSubPlan(Plan* node, EState* estate, PlanState* result)
/* ------------------------------------------------------------------------
* ExecInitNode
*
* Recursively initializes all the nodes in the plan tree rooted
* at 'node'.
* 'node'
*
* Inputs:
* 'node' is the current node of the plan produced by the query planner
* 'estate' is the shared execution state for the plan tree
* 'eflags' is a bitwise OR of flag bits described in executor.h
* :
* 'node'
* 'estate'
* 'eflags'executor.h中描述的标志位的按位或
*
* Returns a PlanState node corresponding to the given Plan node.
* Plan节点相对应的PlanState节点
* ------------------------------------------------------------------------
*/
/*
*/
PlanState* ExecInitNode(Plan* node, EState* estate, int e_flags)
{
PlanState* result = NULL;
@ -453,14 +462,17 @@ PlanState* ExecInitNode(Plan* node, EState* estate, int e_flags)
int rc = 0;
/*
* do nothing when we get to the end of a leaf on tree.
*
*/
if (node == NULL) {
return NULL;
}
// 进入性能跟踪
gstrace_entry(GS_TRC_ID_ExecInitNode);
// 根据节点类型和执行环境生成上下文名
if (!StreamTopConsumerAmI())
rc = snprintf_s(context_name,
NODENAMELEN,
@ -478,135 +490,57 @@ PlanState* ExecInitNode(Plan* node, EState* estate, int e_flags)
node->plan_node_id);
securec_check_ss(rc, "", "");
/*
* Create working memory for expression evaluation in this context.
*/
// 在此上下文中为表达式评估创建工作内存。
node_context = AllocSetContextCreate(estate->es_const_query_cxt,
context_name,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
// 保存旧的查询上下文,并切换到新的节点上下文
query_context = estate->es_query_cxt;
// reassign the node context as we must run under this context.
estate->es_query_cxt = node_context;
/* Switch to Node Level Memory Context */
// 切换到节点级内存上下文
old_context = MemoryContextSwitchTo(node_context);
/*
* Check whether this 'plan node' needs be processed in current DN exec_nodes,
* skip real initialization if it is not in exec-nodes
*
* Note: We only have to do such kind of specialy pocessing in some plan nodes
*/
// 检查是否需要进行存根执行
if (unlikely(IS_PGXC_DATANODE && NeedStubExecution(node))) {
result = (PlanState*)ExecInitNodeStubNorm(node, estate, e_flags);
} else {
result = ExecInitNodeByType(node, estate, e_flags);
}
/* Set the nodeContext */
// 设置节点上下文
result->nodeContext = node_context;
/*
* Initialize any initPlans present in this node. The planner put them in
* a separate list for us.
*/
/*
* We initialize subplan node on coordinator (for explain) or one dn thread
* that executes the subplan
*/
// 初始化节点中的子计划
ExecInitNodeSubPlan(node, estate, result);
/* Set up instrumentation for this node if requested */
// 如果需要,为节点设置仪器(性能跟踪)
if (estate->es_instrument != INSTRUMENT_NONE) {
#ifdef ENABLE_MULTIPLE_NODES
/*
* "plan_node_id == 0" is special case, "with recursive + hdfs foreign table"
* will lead to plan_node_id of all plan node in subplan are zero.
* u_sess->instr_cxt.thread_instr->allocInstrSlot only return the instrArray->instr->instrPlanData
* which has allocated in threadinstrumentation.
*/
if (u_sess->instr_cxt.global_instr != NULL && u_sess->instr_cxt.thread_instr && node->plan_node_id > 0 &&
IS_PGXC_COORDINATOR && StreamTopConsumerAmI()) {
/* on compute pool */
result->instrument = u_sess->instr_cxt.thread_instr->allocInstrSlot(
node->plan_node_id, node->parent_node_id, result->plan, estate);
} else if (u_sess->instr_cxt.global_instr != NULL && u_sess->instr_cxt.thread_instr && node->plan_node_id > 0 &&
(IS_PGXC_DATANODE || (IS_PGXC_COORDINATOR && node->exec_type == EXEC_ON_COORDS))) {
/* plannode(exec on cn)or dn */
result->instrument = u_sess->instr_cxt.thread_instr->allocInstrSlot(
node->plan_node_id, node->parent_node_id, result->plan, estate);
} else {
/* on MPPDB CN */
result->instrument = InstrAlloc(1, estate->es_instrument);
}
// 为执行节点分配仪器槽位
// 注意:根据不同情况分配仪器槽位
#else
if (u_sess->instr_cxt.global_instr != NULL && u_sess->instr_cxt.thread_instr && node->plan_node_id > 0 &&
(!StreamTopConsumerAmI() ||
u_sess->instr_cxt.global_instr->get_planIdOffsetArray()[node->plan_node_id - 1] == 0)) {
result->instrument = u_sess->instr_cxt.thread_instr->allocInstrSlot(
node->plan_node_id, node->parent_node_id, result->plan, estate);
} else {
result->instrument = InstrAlloc(1, estate->es_instrument);
}
// 在非分布式环境下为执行节点分配仪器槽位
#endif
if (result->instrument) {
result->instrument->memoryinfo.nodeContext = node_context;
if (u_sess->attr.attr_resource.use_workload_manager &&
u_sess->attr.attr_resource.resource_track_level == RESOURCE_TRACK_OPERATOR &&
estate->es_can_realtime_statistics && u_sess->exec_cxt.need_track_resource &&
NeedExecuteActiveSql(node)) {
Qpid qid;
qid.plannodeid = node->plan_node_id;
qid.procId = u_sess->instr_cxt.gs_query_id->procId;
qid.queryId = u_sess->instr_cxt.gs_query_id->queryId;
int plan_dop = node->parallel_enabled ? u_sess->opt_cxt.query_dop : 1;
result->instrument->dop = plan_dop;
int64 plan_rows = e_rows_convert_to_int64(node->plan_rows);
if (nodeTag(node) == T_VecAgg &&
((Agg*)node)->aggstrategy == AGG_HASHED && ((VecAgg*)node)->is_sonichash) {
ExplainCreateDNodeInfoOnDN(&qid,
result->instrument,
node->exec_type == EXEC_ON_DATANODES,
"VectorSonicHashAgg",
plan_dop,
plan_rows);
} else if (nodeTag(node) == T_VecHashJoin && ((HashJoin*)node)->isSonicHash) {
ExplainCreateDNodeInfoOnDN(&qid,
result->instrument,
node->exec_type == EXEC_ON_DATANODES,
"VectorSonicHashJoin",
plan_dop,
plan_rows);
} else {
ExplainCreateDNodeInfoOnDN(&qid,
result->instrument,
node->exec_type == EXEC_ON_DATANODES,
nodeTagToString(nodeTag(node)),
plan_dop,
plan_rows);
}
}
}
// 记录节点上下文以及其他性能统计信息
}
/* Switch to OldContext */
// 切换回旧的内存上下文,恢复查询上下文
MemoryContextSwitchTo(old_context);
/* restore the per query context */
estate->es_query_cxt = query_context;
result->ps_rownum = 0;
// 退出性能跟踪
gstrace_exit(GS_TRC_ID_ExecInitNode);
return result;
}
/*
PlanState TupleTableSlot
*/
TupleTableSlot* ExecProcNodeByType(PlanState* node)
{
TupleTableSlot* result = NULL;
@ -659,12 +593,12 @@ TupleTableSlot* ExecProcNodeByType(PlanState* node)
return ExecHashJoin((HashJoinState*)node);
/*
* partition iterator node
*
*/
case T_PartIteratorState:
return ExecPartIterator((PartIteratorState*)node);
/*
* materialization nodes
/*
*
*/
case T_MaterialState:
return ExecMaterial((MaterialState*)node);
@ -710,39 +644,35 @@ TupleTableSlot* ExecProcNodeByType(PlanState* node)
return NULL;
}
}
/*
*/
void ExecProcNodeInstr(PlanState* node, TupleTableSlot* result)
{
switch (nodeTag(node)) {
case T_ModifyTableState:
case T_DistInsertSelectState:
// 计算第一个元组的处理时间
instr_time first_tuple;
INSTR_TIME_SET_ZERO(first_tuple);
INSTR_TIME_ACCUM_DIFF(
first_tuple, ((ModifyTableState*)node)->first_tuple_modified, node->instrument->starttime);
/*
* If the value of es_last_processed is zero means the value of es_processed
* just come from current operator. If not means the value of es_processed
* come from current operator and other operator, es_processed minus
* es_last_processed is tuples processed of curent operator when modify
* the hdfs table, which may include modify the main table and modify the
* detla table, in this case, the value of es_processed will be set twice,
* resulting in error row value for modify operator in explain command.
*/
// 根据 es_last_processed 更新性能计数
if (node->state->es_last_processed == 0) {
InstrStopNode(node->instrument, node->state->es_processed);
} else {
InstrStopNode(node->instrument, node->state->es_processed - node->state->es_last_processed);
}
// 更新 es_last_processed 并记录第一个元组的处理时间
node->state->es_last_processed = node->state->es_processed;
node->instrument->firsttuple = INSTR_TIME_GET_DOUBLE(first_tuple);
break;
case T_SeqScanState:
if (((SeqScanState*) node)->scanBatchMode) {
if (!TupIsNull(result)) {
/* Batch mode does not collect memory info as it takes too much CPU resources. */
// 在批处理模式下,根据处理的批次行数进行性能计数
InstrStopNode(node->instrument, ((SeqScanState*)node)->scanBatchState->scanBatch.rows, false);
} else {
InstrStopNode(node->instrument, 0.0);
@ -750,17 +680,25 @@ void ExecProcNodeInstr(PlanState* node, TupleTableSlot* result)
break;
}
default:
// 对于其他节点类型,根据是否返回了元组进行性能计数
InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
break;
}
// 更新节点的内存信息
node->instrument->memoryinfo.operatorMemory = SET_NODEMEM(node->plan->operatorMemKB[0], node->plan->dop);
// 如果未返回元组,将节点状态标记为 true表示节点执行完成
if (TupIsNull(result))
node->instrument->status = true;
}
typedef TupleTableSlot* (*ExecProcFuncType)(PlanState* node);
/*
*/
static inline TupleTableSlot *DefaultExecProc(PlanState *node)
{
ereport(ERROR,
@ -999,9 +937,10 @@ ExecProcFuncType g_execProcFuncTable[] = {
/* ----------------------------------------------------------------
* ExecProcNode
*
* Execute the given node to return a(nother) tuple.
*
* ----------------------------------------------------------------
*/
TupleTableSlot* ExecProcNode(PlanState* node)
{
TupleTableSlot* result = NULL;
@ -1009,14 +948,14 @@ TupleTableSlot* ExecProcNode(PlanState* node)
CHECK_FOR_INTERRUPTS();
MemoryContext old_context;
/* Response to stop or cancel signal. */
/* 响应停止或取消信号。 */
#ifdef ENABLE_MULTIPLE_NODES
if (unlikely(executorEarlyStop())) {
return NULL;
}
#endif
/* Switch to Node Level Memory Context */
/* 切换到节点级内存上下文 */
old_context = MemoryContextSwitchTo(node->nodeContext);
if (node->chgParam != NULL) { /* something changed */
@ -1052,16 +991,15 @@ TupleTableSlot* ExecProcNode(PlanState* node)
/* ----------------------------------------------------------------
* MultiExecProcNode
*
* Execute a node that doesn't return individual tuples
* (it might return a hashtable, bitmap, etc). Caller should
* check it got back the expected kind of Node.
*
*
*
* This has essentially the same responsibilities as ExecProcNode,
* but it does not do InstrStartNode/InstrStopNode (mainly because
* it can't tell how many returned tuples to count). Each per-node
* function must provide its own instrumentation support.
* ExecProcNode
* InstrStartNode/InstrStopNode
*
* ----------------------------------------------------------------
*/
Node* MultiExecProcNode(PlanState* node)
{
Node* result = NULL;
@ -1069,7 +1007,7 @@ Node* MultiExecProcNode(PlanState* node)
CHECK_FOR_INTERRUPTS();
/* Switch to Node Level Memory Context */
/* 切换到节点级内存上下文 */
old_context = MemoryContextSwitchTo(node->nodeContext);
if (node->chgParam != NULL) { /* something changed */
@ -1077,8 +1015,8 @@ Node* MultiExecProcNode(PlanState* node)
}
switch (nodeTag(node)) {
/*
* Only node types that actually support multiexec will be listed
/*
*
*/
case T_HashState:
result = MultiExecHash((HashState*)node);
@ -1105,7 +1043,7 @@ Node* MultiExecProcNode(PlanState* node)
break;
}
/* Print Operator Memory for Hash operator */
/* 打印哈希运算符的操作内存 */
if (node->instrument) {
node->instrument->memoryinfo.operatorMemory = node->plan->operatorMemKB[0];
}
@ -1115,29 +1053,38 @@ Node* MultiExecProcNode(PlanState* node)
return result;
}
/*
*/
void ExplainNodePending(PlanState* result_plan)
{
// 检查是否启用了工作负载管理以及资源跟踪级别是否是操作员级别,或者结果计划为 NULL。
if (!u_sess->attr.attr_resource.use_workload_manager ||
u_sess->attr.attr_resource.resource_track_level != RESOURCE_TRACK_OPERATOR || result_plan == NULL) {
return;
}
// 如果不是协调器或来自协调器的连接,并且不是单节点模式,直接返回。
if ((!IS_PGXC_COORDINATOR || IsConnFromCoord()) && !IS_SINGLE_NODE) {
return;
}
// 定义变量以存储查询标识符Qpid和返回值。
bool has_found = false;
Qpid qid;
int rc = 0;
// 从上下文中获取进程和查询标识符,并设置计划节点标识符。
qid.procId = u_sess->instr_cxt.gs_query_id->procId;
qid.queryId = u_sess->instr_cxt.gs_query_id->queryId;
qid.plannodeid = result_plan->plan->plan_node_id;
// 如果查询标识符无效,则直接返回。
if (IsQpidInvalid(&qid)) {
return;
}
uint32 hash_code = GetHashPlanCode(&qid, sizeof(Qpid));
LockOperHistHashPartition(hash_code, LW_EXCLUSIVE);
@ -1162,37 +1109,49 @@ void ExplainNodePending(PlanState* result_plan)
UnLockOperHistHashPartition(hash_code);
}
/*
*/
void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz current_time, bool is_pending)
{
// 检查是否启用了工作负载管理资源跟踪级别是否为操作员级别结果计划是否存在以及是否需要执行活动SQL。
if (!u_sess->attr.attr_resource.use_workload_manager ||
u_sess->attr.attr_resource.resource_track_level != RESOURCE_TRACK_OPERATOR || result_plan == NULL ||
!NeedExecuteActiveSql(result_plan->plan)) {
return;
}
// 如果结果计划的仪器信息不为空并且支持历史统计信息,则获取计划的并行度。
if (result_plan->instrument != NULL && result_plan->state->es_can_history_statistics) {
int plan_dop = result_plan->instrument->dop;
// 根据节点类型设置计划名称。
char *plan_name = NULL;
Plan* node = result_plan->plan;
if (nodeTag(node) == T_VecAgg && ((Agg*)node)->aggstrategy == AGG_HASHED && ((VecAgg*)node)->is_sonichash) {
plan_name = "VectorSonicHashAgg";
} else if (nodeTag(node) == T_VecHashJoin && ((HashJoin*)node)->isSonicHash) {
plan_name = "VectorSonicHashJoin";
} else {
plan_name = nodeTagToString(nodeTag(node));
}
// 如果不是挂起状态,则记录计划信息。
if (is_pending) {
ExplainNodePending(result_plan);
} else {
int64 plan_rows = e_rows_convert_to_int64(result_plan->plan->plan_rows);
Plan* node = result_plan->plan;
char *plan_name = NULL;
if (nodeTag(node) == T_VecAgg && ((Agg*)node)->aggstrategy == AGG_HASHED && ((VecAgg*)node)->is_sonichash) {
plan_name = "VectorSonicHashAgg";
} else if (nodeTag(node) == T_VecHashJoin && ((HashJoin*)node)->isSonicHash) {
plan_name = "VectorSonicHashJoin";
} else {
plan_name = nodeTagToString(nodeTag(node));
}
OperatorPlanInfo* opt_plan_info = NULL;
#ifndef ENABLE_MULTIPLE_NODES
// 提取操作员计划信息,如果是单节点模式。
if (pstmt != NULL)
opt_plan_info = ExtractOperatorPlanInfo(result_plan, pstmt);
#endif /* ENABLE_MULTIPLE_NODES */
// 设置计划的会话信息。
ExplainSetSessionInfo(result_plan->plan->plan_node_id,
result_plan->instrument,
result_plan->plan->exec_type == EXEC_ON_DATANODES,
@ -1204,9 +1163,11 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
}
}
// 根据节点类型执行递归操作。
switch (nodeTag(result_plan->plan)) {
case T_MergeAppend:
case T_VecMergeAppend: {
// 对于 MergeAppend 节点,递归调用 ExplainNodeFinish 函数。
MergeAppendState* ma = (MergeAppendState*)result_plan;
for (int i = 0; i < ma->ms_nplans; i++) {
PlanState* plan = ma->mergeplans[i];
@ -1215,42 +1176,14 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
} break;
case T_Append:
case T_VecAppend: {
// 对于 Append 节点,递归调用 ExplainNodeFinish 函数。
AppendState* append = (AppendState*)result_plan;
for (int i = 0; i < append->as_nplans; i++) {
PlanState* plan = append->appendplans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
case T_ModifyTable:
case T_VecModifyTable: {
ModifyTableState* mt = (ModifyTableState*)result_plan;
for (int i = 0; i < mt->mt_nplans; i++) {
PlanState* plan = mt->mt_plans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
case T_SubqueryScan:
case T_VecSubqueryScan: {
SubqueryScanState* ss = (SubqueryScanState*)result_plan;
if (ss->subplan)
ExplainNodeFinish(ss->subplan, pstmt, current_time, is_pending);
} break;
case T_BitmapAnd:
case T_CStoreIndexAnd: {
BitmapAndState* ba = (BitmapAndState*)result_plan;
for (int i = 0; i < ba->nplans; i++) {
PlanState* plan = ba->bitmapplans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
case T_BitmapOr:
case T_CStoreIndexOr: {
BitmapOrState* bo = (BitmapOrState*)result_plan;
for (int i = 0; i < bo->nplans; i++) {
PlanState* plan = bo->bitmapplans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
// 其他节点类型的类似递归调用,如 ModifyTable、SubqueryScan、BitmapAnd、BitmapOr 等。
default:
if (result_plan->lefttree)
ExplainNodeFinish(result_plan->lefttree, pstmt, current_time, is_pending);
@ -1259,6 +1192,7 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
break;
}
// 遍历 initPlan 和 subPlan 列表,递归调用 ExplainNodeFinish 函数。
ListCell* lst = NULL;
foreach (lst, result_plan->initPlan) {
SubPlanState* sps = (SubPlanState*)lfirst(lst);
@ -1279,35 +1213,42 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
}
}
/*
* Target : clean up sensitive information used in encryption or decryption.
* Input : NA
* Output : NA
*/
*/
void cleanup_sensitive_information()
{
/* used derive_keys and user_key in decryption. */
// 外部变量声明:用于记录加密和解密操作的状态以及使用的向量和输入数据
extern THR_LOCAL bool decryption_function_call;
extern THR_LOCAL unsigned char derive_vector_used[NUMBER_OF_SAVED_DERIVEKEYS][RANDOM_LEN];
extern THR_LOCAL unsigned char mac_vector_used[NUMBER_OF_SAVED_DERIVEKEYS][RANDOM_LEN];
extern THR_LOCAL unsigned char user_input_used[NUMBER_OF_SAVED_DERIVEKEYS][RANDOM_LEN];
/* used derive_keys and user_key in encryption. */
extern THR_LOCAL bool encryption_function_call;
extern THR_LOCAL unsigned char derive_vector_saved[RANDOM_LEN];
extern THR_LOCAL unsigned char mac_vector_saved[RANDOM_LEN];
extern THR_LOCAL unsigned char input_saved[RANDOM_LEN];
errno_t errorno = EOK;
// 清空加密信息
if (encryption_function_call == true) {
// 将保存的派生向量、输入数据和 MAC 向量的内容全部置为零
errorno = memset_s(derive_vector_saved, RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
errorno = memset_s(input_saved, RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
errorno = memset_s(mac_vector_saved, RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
// 标记加密操作已完成
encryption_function_call = false;
}
// 清空解密信息
if (decryption_function_call == true) {
// 使用循环将每个保存的派生向量、用户输入数据和 MAC 向量的内容全部置为零
for (int i = 0; i < NUMBER_OF_SAVED_DERIVEKEYS; ++i) {
errorno = memset_s(derive_vector_used[i], RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
@ -1316,6 +1257,8 @@ void cleanup_sensitive_information()
errorno = memset_s(mac_vector_used[i], RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
}
// 标记解密操作已完成
decryption_function_call = false;
}
}
@ -1323,26 +1266,27 @@ void cleanup_sensitive_information()
/* ----------------------------------------------------------------
* ExecEndNodeByType
*
* Recursively cleans up all the nodes in the plan rooted
* at 'node'.
* 'node'
*
* After this operation, the query plan will not be able to be
* processed any further. This should be called only after
* the query plan has been fully executed.
*
*
* ----------------------------------------------------------------
*/
static void ExecEndNodeByType(PlanState* node)
{
/*
* do nothing when we get to the end of a leaf on tree.
/*
*
*/
/* clean up sensitive information used in encryption or decryption */
/* As for data node, we should end instrument in this function,
* but in coordinator do in the explain function.
/* 清除在加密或解密中使用的敏感信息 */
/* 对于数据节点,我们应该在此函数中结束仪器,
*
*/
/* on the CN of the compute pool */
/* 在计算池的协调器上 */
switch (nodeTag(node)) {
/*
* control nodes
@ -1380,9 +1324,10 @@ static void ExecEndNodeByType(PlanState* node)
ExecEndBitmapOr((BitmapOrState*)node);
break;
/*
* scan nodes
*/
/*
*
*/
case T_SeqScanState:
ExecEndSeqScan((SeqScanState*)node);
break;
@ -1473,9 +1418,10 @@ static void ExecEndNodeByType(PlanState* node)
ExecEndHashJoin((HashJoinState*)node);
break;
/*
* materialization nodes
*/
/*
*
*/
case T_MaterialState:
ExecEndMaterial((MaterialState*)node);
break;
@ -1630,30 +1576,51 @@ static void ExecEndNodeByType(PlanState* node)
break;
}
}
/*
*/
void ExecEndNode(PlanState* node)
{
// 如果节点为空,直接返回
if (node == NULL) {
return;
}
// 清理敏感信息
cleanup_sensitive_information();
// 释放变更参数集合
if (node->chgParam != NULL) {
bms_free_ext(node->chgParam);
node->chgParam = NULL;
}
// 结束仪器的测量循环
if (node->instrument != NULL) {
// 如果是分布式数据节点,结束测量循环
if (IS_PGXC_DATANODE) {
InstrEndLoop(node->instrument);
}
// 如果需要执行活动SQL操作移除相应的解释信息
if (NeedExecuteActiveSql(node->plan)) {
removeExplainInfo(node->plan->plan_node_id);
}
}
// 在协调器上执行的且是最终消费者的情况下,结束测量循环
if (node->instrument != NULL && IS_PGXC_COORDINATOR && StreamTopConsumerAmI()) {
InstrEndLoop(node->instrument);
}
// 如果需要对节点进行存根处理,执行相应的存根处理并返回
if (planstate_need_stub(node)) {
ExecEndNodeStub(node);
return;
}
// 执行特定类型节点的结束处理
ExecEndNodeByType(node);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
/* -------------------------------------------------------------------------
*
* execReplication.cpp
* miscellaneous executor routines for logical replication
*
*
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* (c) 1996-2021, PostgreSQL全球发展集团
* (c) 1994
*
*
* IDENTIFICATION
@ -45,13 +45,12 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart);
/*
* Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that
* is setup to match 'rel' (*NOT* idxrel!).
* relkeyScanKey
'rel'(** idxrel!)
*
* Returns whether any column contains NULLs.
* null
*
* This is not generic routine, it expects the idxrel to be replication
* identity of a rel and meet all limitations associated with that.
* idxrel是一个rel的复制标识
*/
static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot)
{
@ -66,7 +65,7 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
Assert(!isnull);
opclass = (oidvector *)DatumGetPointer(indclassDatum);
/* Build scankey for every attribute in the index. */
/*为索引中的每个属性构建scankey。 */
for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(idxrel); attoff++) {
Oid op;
Oid opfamily;
@ -79,10 +78,17 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
errmsg("index key attribute number %d exceeds number of columns %d",
mainattno, searchslot->tts_tupleDescriptor->natts)));
}
/* 这段代码片段是一个循环,它遍历索引的键属性。下面是它的功能细分:
/*
* Load the operator info. We need this to get the equality operator
* function for the scan key.
1. attoff = 0IndexRelationGetNumberOfKeyAttributes(idxrel)
2. :
-
-
* /
/*
*
*/
opfamily = get_opclass_family(opclass->values[attoff]);
@ -91,12 +97,18 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", BTEqualStrategyNumber, optype, optype, opfamily);
regop = get_opcode(op);
/*在给定的代码片段中,在错误检查之后执行以下步骤:
/* Initialize the scankey. */
1. 使' get_opclass_family '' opclass->values[attoff] '
2. 使' get_opfamily_member '' opfamily ' ' optype ' ' optype '' BTEqualStrategyNumber '
3.使' OidIsValid '使' elog '
4. 使' get_opcode '' op '*/
/* 初始化扫描键。 */
ScanKeyInit(&skey[attoff], pkattno, BTEqualStrategyNumber, regop, searchslot->tts_values[mainattno - 1]);
skey[attoff].sk_collation = idxrel->rd_indcollation[attoff];
/* Check for null value. */
/* 检查是否为空值。 */
if (searchslot->tts_isnull[mainattno - 1]) {
hasnulls = true;
skey[attoff].sk_flags |= SK_ISNULL;
@ -105,15 +117,23 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
return hasnulls;
}
/*在给定的代码片段中,以下是代码的执行流程:
/* Check tableam_tuple_lock result, and return if need to retry */
1. `searchslot->tts_isnull[mainattno - 1]`NULL
2. NULL`hasnulls``true`NULL值
3. `skey[attoff].sk_flags``SK_ISNULL`1NULL
4. `hasnulls`NULL值
NULL值 */
/* 检查tableam_tuple_lock结果如果需要重试则返回 */
static bool inline CheckTupleLockRes(TM_Result res)
{
switch (res) {
case TM_Ok:
break;
case TM_Updated:
/* XXX: Improve handling here */
/* XXX:改进这里的操作 */
ereport(LOG, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("concurrent update, retrying")));
return true;
case TM_Invisible:
@ -125,13 +145,21 @@ static bool inline CheckTupleLockRes(TM_Result res)
}
return false;
}
/*这段代码定义了一个名为CheckTupleLockRes的静态内联函数其作用是检查给定的TM_Result结果并根据不同的结果进行相应的处理。以下是代码的执行流程
/* Check heap modify result */
res结果进行switch语句的判断
TM_Ok
TM_Updatedtrue
TM_Invisible
heap_lock_tuple状态
false*/
/* 检查堆修改结果 */
static void inline CheckTupleModifyRes(TM_Result res)
{
switch (res) {
case TM_SelfModified:
/* Tuple was already updated in current command? */
/* 元组已在当前命令中更新? */
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("tuple already updated by self")));
break;
case TM_Ok:
@ -154,21 +182,32 @@ static inline List* GetPartitionList(Relation rel, LOCKMODE lockmode)
return relationGetPartitionList(rel, lockmode);
}
}
/*CheckTupleModifyRes函数是一个静态内联函数用于检查给定的TM_Result结果并根据不同的结果输出相应的错误信息。执行流程如下
TM_SelfModified
TM_Ok
TM_Updated或TM_Deleted
GetPartitionList函数是一个内联函数
subpartitionedRelationGetSubPartitionList函数获取子分区列表
relationGetPartitionList函数获取分区列表
*/
static bool PartitionFindReplTupleByIndex(EState *estate, Relation rel, Relation idxrel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
{
/* must be non-GPI index */
/* 必须是非GPI指数 */
Assert(!RelationIsGlobalIndex(idxrel));
fakeRelInfo->partList = GetPartitionList(rel, RowExclusiveLock);
/* search the tuple in partition list one by one */
/* 在分区列表中逐个搜索元组 */
ListCell *cell = NULL;
foreach (cell, fakeRelInfo->partList) {
Partition heapPart = (Partition)lfirst(cell);
Relation partionRel = RelationIsSubPartitioned(rel) ? SubPartitionGetRelation(rel, heapPart, NoLock) :
partitionGetRelation(rel, heapPart);
/* Get index partition of this heap partition */
/* 获取此堆分区的索引分区 */
Oid idxPartOid = getPartitionIndexOid(RelationGetRelid(idxrel), heapPart->pd_id);
Partition idxPart = partitionOpen(idxrel, idxPartOid, RowExclusiveLock);
Relation idxPartRel = RelationIsSubPartitioned(rel) ? SubPartitionGetRelation(idxrel, idxPart, NoLock) :
@ -179,24 +218,28 @@ static bool PartitionFindReplTupleByIndex(EState *estate, Relation rel, Relation
fakeRelInfo->partOid = heapPart->pd_id;
if (RelationFindReplTupleByIndex(estate, rel, idxPartRel, lockmode, searchslot, outslot, fakeRelInfo)) {
/* Hit, release index resource, heap partition need to be used later, so don't release it */
/* 命中,释放索引资源,堆分区需要以后使用,所以不要释放它 */
partitionClose(idxrel, idxPart, NoLock);
releaseDummyRelation(&idxPartRel);
/* caller shoud release partRel */
/* 调用方应释放部件Rel */
fakeRelInfo->needRleaseDummyRel = true;
return true;
}
/* didn't find tuple in current partition, release dummy relation and switch to next partition */
/* 在当前分区中没有找到元组,释放虚拟关系并切换到下一个分区 */
releaseDummyRelation(&fakeRelInfo->partRel);
partitionClose(idxrel, idxPart, NoLock);
releaseDummyRelation(&idxPartRel);
}
/* do not find tuple in any patition, close and return */
/* 没有找到元组在任何分区,关闭和返回 */
releasePartitionList(rel, &fakeRelInfo->partList, NoLock);
return false;
}
/* 这段代码是在分区表中根据索引查找元组的函数。首先它断言索引不是全局分区索引。然后它获取分区列表并使用foreach循环遍历每个分区。
ID获取索引分区的OIDfakeRelInfo结构中
RelationFindReplTupleByIndex函数来在当前分区的索引中查找匹配的元组
fakeRelInfo->needRleaseDummyRel设置为truetrue表示找到了匹配的元组*/
static bool PartitionFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
@ -213,25 +256,29 @@ static bool PartitionFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
fakeRelInfo->partOid = heapPart->pd_id;
if (RelationFindReplTupleSeq(rel, lockmode, searchslot, outslot, fakeRelInfo)) {
/* caller shoud release partRel */
/* 调用方应释放部件Rel */
fakeRelInfo->needRleaseDummyRel = true;
return true;
}
releaseDummyRelation(&fakeRelInfo->partRel);
}
/* do not find tuple in any patition, close and return */
/* 没有找到元组在任何分区,关闭和返回 */
releasePartitionList(rel, &fakeRelInfo->partList, NoLock);
return false;
}
/* 这段代码是在分区表中按顺序查找元组的函数。它首先获取分区列表并使用foreach循环遍历每个分区。
fakeRelInfo结构中
RelationFindReplTupleSeq函数来在当前分区中按顺序查找匹配的元组fakeRelInfo->needRleaseDummyRel设置为truetrue表示找到了匹配的元组
false表示没有找到匹配的元组*/
/*
* Search the relation 'rel' for tuple using the index or seq scan.
* 使'rel'
*
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
* lockmode锁定它truefalse
*
* Caller should check and release fakeRelInfo->partList and fakeRelInfo->partRel
* fakeRelInfo->partList和fakeRelInfo-> parttrel
*/
bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
@ -240,7 +287,7 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
bool found = false;
Relation idxrel = NULL;
/* clear fake rel info */
/* 清除假rel信息 */
rc = memset_s(fakeRelInfo, sizeof(FakeRelationPartition), 0, sizeof(FakeRelationPartition));
securec_check(rc, "", "");
@ -248,7 +295,7 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
idxrel = index_open(idxoid, RowExclusiveLock);
}
/* for non partitioned table, or partitioned table with GPI, use parent heap and index to do the scan */
/*对于非分区表或带有GPI的分区表使用父堆和索引进行扫描 */
if (RelationIsNonpartitioned(rel) || (idxrel != NULL && RelationIsGlobalIndex(idxrel))) {
if (idxrel != NULL) {
found = RelationFindReplTupleByIndex(estate, rel, idxrel, lockmode, searchslot, outslot, fakeRelInfo);
@ -259,7 +306,7 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
}
}
/* scan with partition */
/* 分区扫描 */
if (idxrel != NULL) {
found = PartitionFindReplTupleByIndex(estate, rel, idxrel, lockmode, searchslot, outslot, fakeRelInfo);
index_close(idxrel, NoLock);
@ -270,10 +317,9 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
}
/*
* Search the relation 'rel' for tuple using the index.
* 使'rel'
*
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
* lockmode锁定它truefalse
*/
static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation idxrel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart)
@ -288,8 +334,7 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
int rc;
bool isGpi = RelationIsGlobalIndex(idxrel);
/*
* For GPI and non-partition table, use parent heap relation to search the tuple,
* otherwise use partition relation
* GPI和非分区表使使
*/
if (isGpi || RelationIsNonpartitioned(rel)) {
targetRel = rel;
@ -297,21 +342,21 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
targetRel = fakeRelPart->partRel;
}
Assert(targetRel != NULL);
/* Start an index scan. */
/* 启动索引扫描。 */
InitDirtySnapshot(snap);
scan = scan_handler_idx_beginscan(targetRel, idxrel, &snap,
IndexRelationGetNumberOfKeyAttributes(idxrel), 0);
/* refer to check_violation, we need to set isUpsert if we want to use dirty snapshot in UStore */
/* 参考check_violation如果我们想在UStore中使用脏快照我们需要设置isUpsert */
scan->isUpsert = true;
/* Build scan key. */
/* 构建扫描键。 */
build_replindex_scan_key(skey, targetRel, idxrel, searchslot);
while (true) {
found = false;
scan_handler_idx_rescan(scan, skey, IndexRelationGetNumberOfKeyAttributes(idxrel), NULL, 0);
/* Try to find the tuple */
/* 试着找到这个元组 */
if (RelationIsUstoreFormat(targetRel)) {
found = IndexGetnextSlot(scan, ForwardScanDirection, outslot);
} else {
@ -321,11 +366,10 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
}
}
if (found) {
/* Found tuple, try to lock it in the lockmode. */
/* 找到元组,尝试在锁定模式下锁定它。 */
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
/*
* If the tuple is locked, wait for locking transaction to finish
* and retry.
*
*/
if (TransactionIdIsValid(xwait)) {
XactLockTableWait(xwait);
@ -345,7 +389,7 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
ItemPointer tid = tableam_tops_get_t_self(targetRel, outslot->tts_tuple);
if (RelationIsUstoreFormat(targetRel)) {
/* materialize the slot, so we can visit it after the scan is end */
/* 将插槽物化,这样扫描结束后我们就可以访问它了 */
outslot->tts_tuple = UHeapMaterialize(outslot);
ItemPointerCopy(tid, &UHeaplocktup.ctid);
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
@ -353,13 +397,13 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
UHeaplocktup.disk_tuple = &tbuf.hdr;
locktup = &UHeaplocktup;
} else {
/* materialize the slot, so we can visit it after the scan is end */
/* 将插槽物化,这样扫描结束后我们就可以访问它了 */
outslot->tts_tuple = ExecMaterializeSlot(outslot);
ItemPointerCopy(tid, &heaplocktup.t_self);
locktup = &heaplocktup;
}
/* Get the target tuple's partition for GPI */
/* 获取目标元组的GPI分区 */
if (isGpi) {
GetFakeRelAndPart(estate, rel, outslot, fakeRelPart);
targetRel = fakeRelPart->partRel;
@ -368,20 +412,20 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
PushActiveSnapshot(GetLatestSnapshot());
res = tableam_tuple_lock(targetRel,
locktup, &buf, GetCurrentCommandId(false), lockmode, false, &hufd,
false, false, /* don't follow updates */
false, /* eval */
GetLatestSnapshot(), tid, /* ItemPointer */
false); /* is select for update */
/* the tuple slot already has the buffer pinned */
false, false, /* 不要关注更新 */
false, /* 评估 */
GetLatestSnapshot(), tid, /* 项目指针 */
false); /* 选择进行更新 */
/* 元组槽已固定缓冲区 */
ReleaseBuffer(buf);
PopActiveSnapshot();
if (CheckTupleLockRes(res)) {
/* lock tuple failed, try again */
/* 锁定元组失败,请重试 */
continue;
}
}
/* we are done */
/* 我们结束了 */
break;
}
@ -390,7 +434,7 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
}
/*
* Compare the tuple and slot and check if they have equal values.
*
*/
static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *slot, TypeCacheEntry **eq)
{
@ -401,22 +445,21 @@ static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *s
tableam_tops_deform_tuple(tup, desc, values, isnull);
/* Check equality of the attributes. */
/* 检查属性的相等性。 */
for (attrnum = 0; attrnum < desc->natts; attrnum++) {
TypeCacheEntry *typentry;
/* skip generate column */
/* 跳过生成列跳过生成列 */
if (GetGeneratedCol(desc, attrnum)) {
continue;
}
/*
* If one value is NULL and other is not, then they are certainly not
* equal
* NULLNULL
*/
if (isnull[attrnum] != slot->tts_isnull[attrnum])
return false;
/*
* If both are NULL, they can be considered equal.
* NULL
*/
if (isnull[attrnum])
continue;
@ -442,14 +485,13 @@ static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *s
}
/*
* Search the relation 'rel' for tuple using the sequential scan.
* 使rel
*
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
* 使lockmode将其锁定truefalse
*
* Note that this stops on the first matching tuple.
*
*
* This can obviously be quite slow on tables that have more than few rows.
*
*/
static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot,
TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart)
@ -468,7 +510,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
eq = (TypeCacheEntry **)palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
/* Start a heap scan. */
/* 启动堆扫描。 */
InitDirtySnapshot(snap);
scan = scan_handler_tbl_beginscan(targetRel, &snap, 0, NULL, NULL);
@ -477,7 +519,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
found = false;
scan_handler_tbl_rescan(scan, NULL, targetRel);
/* Try to find the tuple */
/* 尝试查找元组 */
while ((scantuple = scan_handler_tbl_getnext(scan, ForwardScanDirection, targetRel)) != NULL) {
if (!tuple_equals_slot(desc, scantuple, searchslot, eq)) {
continue;
@ -488,8 +530,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
/*
* If the tuple is locked, wait for locking transaction to finish
* and retry.
*
*/
if (TransactionIdIsValid(xwait)) {
/* retry */
@ -503,7 +544,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
continue;
}
if (found) {
/* Found tuple, try to lock it in the lockmode. */
/* 找到元组,请尝试在锁定模式下锁定它。 */
Buffer buf;
TM_FailureData hufd;
TM_Result res;
@ -517,7 +558,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
ItemPointer tid = tableam_tops_get_t_self(rel, outslot->tts_tuple);
if (RelationIsUstoreFormat(targetRel)) {
/* materialize the slot, so we can visit it after the scan is end */
/* 具体化插槽,这样我们就可以在扫描结束后访问它 */
outslot->tts_tuple = UHeapMaterialize(outslot);
ItemPointerCopy(tid, &UHeaplocktup.ctid);
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
@ -525,7 +566,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
UHeaplocktup.disk_tuple = &tbuf.hdr;
locktup = &UHeaplocktup;
} else {
/* materialize the slot, so we can visit it after the scan is end */
/* 具体化插槽,这样我们就可以在扫描结束后访问它 */
outslot->tts_tuple = ExecMaterializeSlot(outslot);
ItemPointerCopy(tid, &heaplocktup.t_self);
locktup = &heaplocktup;
@ -534,21 +575,21 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
PushActiveSnapshot(GetLatestSnapshot());
res = tableam_tuple_lock(targetRel, locktup, &buf, GetCurrentCommandId(false),
lockmode, false, &hufd, false,
false, /* don't follow updates */
false, /* eval */
GetLatestSnapshot(), tid, /* ItemPointer */
false); /* is select for update */
false, /* 不关注更新 */
false, /* 评估 */
GetLatestSnapshot(), tid, /* 项目指针 */
false); /* 选择进行更新 */
/* the tuple slot already has the buffer pinned */
/* 元组槽已固定缓冲区 */
ReleaseBuffer(buf);
PopActiveSnapshot();
if (CheckTupleLockRes(res)) {
/* lock tuple failed, try again */
/* 锁定元组失败,请重试 */
continue;
}
}
/* we are done */
/* 我们结束了 */
break;
}
@ -558,10 +599,9 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
}
/*
* Insert tuple represented in the slot to the relation, update the indexes,
* and execute any constraints and per-row triggers.
*
*
* Caller is responsible for opening the indexes.
*
*/
void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot, FakeRelationPartition *relAndPart)
{
@ -570,12 +610,12 @@ void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot, FakeRelation
Relation rel = resultRelInfo->ri_RelationDesc;
Relation targetRel = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
/* For now we support only tables. */
/* 目前,我们只支持表格。 */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_INSERT);
/* BEFORE ROW INSERT Triggers */
/* 在行之前插入触发器 */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_insert_before_row) {
slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
if (slot == NULL) {
@ -583,40 +623,43 @@ void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot, FakeRelation
return;
}
}
List *recheckIndexes = NIL;
/* Materialize slot into a tuple that we can scribble upon. */
/*这段代码是用于执行简单的关系表插入操作。它首先获取要插入的元组和目标关系表,
ExecBRInsertTriggers函数来执行这些触发器
*/
List *recheckIndexes = NIL;
/* 将槽具体化为一个元组,我们可以在上面乱涂乱画。 */
tuple = tableam_tslot_get_tuple_from_slot(rel, slot);
tableam_tops_update_tuple_with_oid(targetRel, tuple, slot);
/* Compute stored generated columns */
/* 计算存储的生成列 */
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_INSERT);
tuple = slot->tts_tuple;
}
/* Check the constraints of the tuple */
/* 检查元组的约束 */
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
/* OK, store the tuple and create index entries for it */
/* 好的,存储元组并为其创建索引项 */
(void)tableam_tuple_insert(targetRel, tuple, GetCurrentCommandId(true), 0, NULL);
if (resultRelInfo->ri_NumIndices > 0) {
ItemPointer pTSelf = tableam_tops_get_t_self(rel, tuple);
recheckIndexes =
ExecInsertIndexTuples(slot, pTSelf, estate, targetRel, relAndPart->part, InvalidBktId, NULL, NULL);
}
/* AFTER ROW INSERT Triggers */
/* 在行后插入触发器 */
ExecARInsertTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, (HeapTuple)tuple, recheckIndexes);
list_free_ext(recheckIndexes);
}
/*
* Find the searchslot tuple and update it with data in the slot,
* update the indexes, and execute any constraints and per-row triggers.
* searchslot元组使slot中的数据对其进行更新
*
* Caller is responsible for opening the indexes.
*
*/
void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot, TupleTableSlot *slot,
FakeRelationPartition *relAndPart)
@ -627,7 +670,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer searchSlotTid = tableam_tops_get_t_self(rel, searchslot->tts_tuple);
/* For now we support only tables. */
/* 目前,我们只支持表格。 */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_UPDATE);
@ -637,7 +680,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
allowInplaceUpdate = false;
}
/* BEFORE ROW UPDATE Triggers */
/* 排前更新触发器 */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_update_before_row) {
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL,
searchSlotTid, slot);
@ -646,8 +689,13 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
return;
}
}
/* Materialize slot into a tuple that we can scribble upon. */
/* 这段代码是用于执行简单的关系表更新操作。它首先获取要更新的元组和目标关系表,然后检查关系表的复制标识以确保更新操作是合法的。
ExecBRUpdateTriggers函数来执行这些触发器
*/
/* 将槽具体化为一个元组,我们可以在上面乱涂乱画。 */
tuple = tableam_tslot_get_tuple_from_slot(rel, slot);
List *recheckIndexes = NIL;
Bitmapset *modifiedIdxAttrs = NULL;
@ -660,17 +708,17 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
Relation targetRelation = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
Relation parentRelation = relAndPart->partRel == NULL ? NULL : rel;
/* Compute stored generated columns */
/* 计算存储的生成列 */
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_UPDATE);
}
/* Check the constraints of the tuple */
/* 检查元组的约束 */
if (rel->rd_att->constr) {
ExecConstraints(resultRelInfo, slot, estate);
}
/* check whether there is a row movement for partition table */
/* 检查分区表是否有行移动 */
GetFakeRelAndPart(estate, rel, slot, &newTupleInfo);
if (newTupleInfo.partOid != InvalidOid && newTupleInfo.partOid != relAndPart->partOid) {
if (!rel->rd_rel->relrowmovement) {
@ -680,10 +728,15 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
}
rowMovement = true;
}
/* 这段代码是用于获取虚拟关系表和分区信息。
GetRelationPartitionOid函数来获取元组所属的分区OID
FakeRelationPartition信息
*/
tuple = slot->tts_tuple;
CommandId cid = GetCurrentCommandId(true);
/* OK, update the tuple and index entries for it */
/* 好的,更新它的元组和索引项 */
if (!rowMovement) {
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, tuple, cid,
InvalidSnapshot, estate->es_snapshot, true, &oldslot, &tmfd, &updateIndexes, &modifiedIdxAttrs,
@ -700,7 +753,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
searchSlotTid, exec_index_tuples_state, InvalidBktId, modifiedIdxAttrs);
}
} else {
/* rowMovement, delete origin tuple and insert new */
/* rowMovement,删除原始元组并插入新元组 */
Assert(relAndPart->partRel != NULL);
Assert(newTupleInfo.partRel != NULL);
res = tableam_tuple_delete(relAndPart->partRel, searchSlotTid, cid, InvalidSnapshot,
@ -715,7 +768,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
tableam_tops_exec_delete_index_tuples(oldslot, relAndPart->partRel, NULL, searchSlotTid,
exec_index_tuples_state, modifiedIdxAttrs);
/* Insert new tuple */
/* 插入新元组 */
(void)tableam_tuple_insert(newTupleInfo.partRel, tuple, cid, 0, NULL);
if (resultRelInfo->ri_NumIndices > 0) {
ItemPointer pTSelf = tableam_tops_get_t_self(rel, tuple);
@ -727,8 +780,12 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
if (oldslot) {
ExecDropSingleTupleTableSlot(oldslot);
}
/* AFTER ROW UPDATE Triggers */
/* 这段代码是用于向关系表中插入新的元组。
tableam_tuple_insert函数将元组插入到目标关系表中
ExecInsertIndexTuples函数来为插入的元组创建索引
*/
/* 排后更新触发器 */
ExecARUpdateTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, relAndPart->partOid,
searchSlotTid, (HeapTuple)tuple, NULL, recheckIndexes);
@ -736,10 +793,9 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
}
/*
* Find the searchslot tuple and delete it, and execute any constraints
* and per-row triggers.
* .searchslot元组并将其删除
*
* Caller is responsible for opening the indexes.
*
*/
void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot,
FakeRelationPartition *relAndPart)
@ -749,12 +805,12 @@ void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = tableam_tops_get_t_self(rel, searchslot->tts_tuple);
/* For now we support only tables. */
/* 目前,我们只支持表格。 */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_DELETE);
/* BEFORE ROW INSERT Triggers */
/* 在行之前插入触发器 */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_delete_before_row) {
skip_tuple =
!ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL, tid);
@ -767,7 +823,7 @@ void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot
Relation targetRel = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
TM_FailureData tmfd;
/* OK, delete the tuple */
/* 好,删除元组 */
TM_Result res = tableam_tuple_delete(targetRel, tid, GetCurrentCommandId(true), InvalidSnapshot,
estate->es_snapshot, true, &oldslot, &tmfd);
CheckTupleModifyRes(res);
@ -783,29 +839,29 @@ void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot
ExecDropSingleTupleTableSlot(oldslot);
}
/* AFTER ROW DELETE Triggers */
/* 行删除触发器之后 */
ExecARDeleteTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL, tid);
}
/*
* Check if command can be executed with current replica identity.
* 使
*/
void CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
{
PublicationActions *pubactions;
/* We only need to do checks for UPDATE and DELETE. */
/* 我们只需要检查UPDATE和DELETE。 */
if (cmd != CMD_UPDATE && cmd != CMD_DELETE)
return;
/* If relation has replica identity we are always good. */
/* 若关系具有复制身份,我们总是好的。 */
if (RelationGetRelReplident(rel) == REPLICA_IDENTITY_FULL || OidIsValid(RelationGetReplicaIndex(rel)))
return;
/*
* This is either UPDATE OR DELETE and there is no replica identity.
* UPDATE或DELETE
*
* Check if the table publishes UPDATES or DELETES.
* UPDATES或DELETES
*/
pubactions = GetRelationPublicationActions(rel);
if (cmd == CMD_UPDATE && pubactions->pubupdate) {
@ -830,8 +886,13 @@ void GetFakeRelAndPart(EState *estate, Relation rel, TupleTableSlot *slot, FakeR
if (RelationIsNonpartitioned(rel)) {
return;
}
}
/* 此代码片段定义了一个名为GetFakeRelAndPart的函数
EState对象Relation对象TupleTableSlot对象和FakeRelationPartition对象作为输入参数
FakeRelationPartition对象的partRelpart和partOid属性分别初始化为NULL和InvalidOid
Relation对象是非分区的
*/
Relation partRelation = NULL;
Partition partition = NULL;
Oid partitionOid;
@ -871,3 +932,13 @@ void GetFakeRelAndPart(EState *estate, Relation rel, TupleTableSlot *slot, FakeR
break;
}
}
/* 此代码段继续实现“GetFakeElAndPart”函数。
partRelationpartitionpartitionOid
使tableam_tslot_get_tuple_from_slotTupleTableSlot
switch语句Relationparttype
switch语句
使heapTupleGetPartitionIdpartitionOid
searchFakeRetreationForPartitionOidpartitionOid
partRelationpartitionFakeRelationPartitionpartRelpartpartOid
使partitionOid使subPartOid
Relationparttype使ereport */

View File

@ -1,15 +1,13 @@
/* -------------------------------------------------------------------------
*
* execScan.cpp
* This code provides support for generalized relation scans. ExecScan
* is passed a node and a pointer to a function to "do the right thing"
* and return a tuple from the relation. ExecScan then does the tedious
* stuff - checking the qualification and projecting the tuple
* appropriately.
* 广ExecScan被传递一个节点和一个指向函数的指针
*
* ExecScan然后做一些乏味的工作
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* c2020
* c1996-2012PostgreSQL
* c1994
*
*
* IDENTIFICATION
@ -25,21 +23,30 @@
#include "utils/memutils.h"
/*
* ExecScanFetch -- fetch next potential tuple
* ExecScanFetch --
*
* This routine is concerned with substituting a test tuple if we are
* inside an EvalPlanQual recheck. If we aren't, just execute
* the access method's next-tuple routine.
* EvalPlanQual复查中
* 访
*/
static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_mtd, ExecScanRecheckMtd recheck_mtd)
/*
* ScanState对象
* access_mtd和recheck_mtd
* access_mtd是指向负责访问元组数据的函数的指针
* 访
* recheck_mtd是指向一个函数的指针
*
*
* TupleTableSlot对象NULL
*/
{
EState* estate = node->ps.state;
if (estate->es_epqTuple != NULL) {
/*
* We are inside an EvalPlanQual recheck. Return the test tuple if
* one is available, after rechecking any access-method-specific
* conditions.
* EvalPlanQual复查
* 访
*/
Index scan_rel_id = ((Scan*)node->ps.plan)->scanrelid;
@ -47,29 +54,29 @@ static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_m
if (estate->es_epqTupleSet[scan_rel_id - 1]) {
TupleTableSlot* slot = node->ss_ScanTupleSlot;
/* Return empty slot if we already returned a tuple */
/* 如果我们已经返回了元组,则返回空槽 */
if (estate->es_epqScanDone[scan_rel_id - 1])
return ExecClearTuple(slot);
/* Else mark to remember that we shouldn't return more */
return ExecClearTuple(slot);
/* 否则请记住,我们不应该再回来了 */
estate->es_epqScanDone[scan_rel_id - 1] = true;
/* Return empty slot if we haven't got a test tuple */
/* 如果我们没有测试元组,则返回空槽 */
if (estate->es_epqTuple[scan_rel_id - 1] == NULL)
return ExecClearTuple(slot);
/* Store test tuple in the plan node's scan slot */
/* 将测试元组存储在计划节点的扫描槽中 */
(void)ExecStoreTuple(estate->es_epqTuple[scan_rel_id - 1], slot, InvalidBuffer, false);
/* Check if it meets the access-method conditions */
/* 检查是否符合访问方法条件 */
if (!(*recheck_mtd)(node, slot))
(void)ExecClearTuple(slot); /* would not be returned by scan */
(void)ExecClearTuple(slot); /* 不会通过扫描返回 */
return slot;
}
}
/*
* Run the node-type-specific access method function to get the next tuple
* 访
*/
return (*access_mtd)(node);
}
@ -77,27 +84,28 @@ static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_m
/* ----------------------------------------------------------------
* ExecScan
*
* Scans the relation using the 'access method' indicated and
* returns the next qualifying tuple in the direction specified
* in the global variable ExecDirection.
* The access method returns the next tuple and execScan() is
* responsible for checking the tuple returned against the qual-clause.
* 使访ExecDirection中指定的方向返回下一个符合条件的元组
* access方法返回下一个元组execScanqual子句检查返回的元组
*
* A 'recheck method' must also be provided that can check an
* arbitrary tuple of the relation against any qual conditions
* that are implemented internal to the access method.
* 访qual条件检查关系的任意元组
*
* Conditions:
* -- the "cursor" maintained by the AMI is positioned at the tuple
* returned previously.
* :
* -- AMI维护的
*
* Initial States:
* -- the relation indicated is opened for scanning so that the
* "cursor" is positioned before the first qualifying tuple.
* :
* -- 便
* ----------------------------------------------------------------
*/
TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* function returning a tuple */
TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返回元组的函数 */
ExecScanRecheckMtd recheck_mtd)
/* 此函数负责扫描关系并返回下一个匹配的元组。它接受一个ScanState对象该对象包含有关扫描的信息以及两个函数指针access_mtd和recheck_mtd。
* access_mtd是指向负责访问元组数据的函数的指针
* 访
* recheck_mtd是指向一个函数的指针
*
* TupleTableSlot对象NULL
*/
{
ExprContext* econtext = NULL;
List* qual = NIL;
@ -109,15 +117,14 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
return NULL;
/*
* Fetch data from node
*
*/
qual = node->ps.qual;
proj_info = node->ps.ps_ProjInfo;
econtext = node->ps.ps_ExprContext;
/*
* If we have neither a qual to check nor a projection to do, just skip
* all the overhead and return the raw scan tuple.
* qual
*/
if (qual == NULL && proj_info == NULL) {
ResetExprContext(econtext);
@ -125,38 +132,36 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
}
/*
* Check to see if we're still projecting out tuples from a previous scan
* tuple (because there is a function-returning-set in the projection
* expressions). If so, try to project another one.
*
*
* u如果是
*/
if (node->ps.ps_TupFromTlist) {
Assert(proj_info); /* can't get here if not projecting */
Assert(proj_info); /* 如果不投影就不能到达这里 */
result_slot = ExecProject(proj_info, &is_done);
if (is_done == ExprMultipleResult)
return result_slot;
/* Done with that source tuple... */
/* 已完成该源元组... */
node->ps.ps_TupFromTlist = false;
}
/*
* @hdfs
* Optimize scan bu using informational constraint.
* if the is_scan_false is true, the iteration is over.
* 使bu
* isscanfalse为true
*/
if (node->is_scan_end) {
return NULL;
}
/*
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle. Note this can't happen
* until we're done projecting out tuples from a scan tuple.
*
*
*/
ResetExprContext(econtext);
/*
* get a tuple from the access method. Loop until we obtain a tuple that
* passes the qualification.
* access方法获取一个元组
*/
for (;;) {
TupleTableSlot* slot = NULL;
@ -164,13 +169,11 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
CHECK_FOR_INTERRUPTS();
slot = ExecScanFetch(node, access_mtd, recheck_mtd);
/* refresh qual every loop */
/* 刷新qual每个循环 */
qual = node->ps.qual;
/*
* if the slot returned by the accessMtd contains NULL, then it means
* there is nothing more to scan so we just return an empty slot,
* being careful to use the projection result slot so it has correct
* tupleDesc.
* accessMtd返回的槽包含NULL西
* 使tupleDesc
*/
if (TupIsNull(slot) || unlikely(executorEarlyStop())) {
if (proj_info != NULL)
@ -180,30 +183,28 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
}
/*
* place the current tuple into the expr context
* expr上下文
*/
econtext->ecxt_scantuple = slot;
/*
* check that the current tuple satisfies the qual-clause
* qual子句
*
* check for non-nil qual here to avoid a function call to ExecQual()
* when the qual is nil ... saves only a few cycles, but they add up
* nil qualqual为nil时调用ExecQual...
* ...
*/
if (qual == NULL || ExecQual(qual, econtext, false)) {
/*
* Found a satisfactory scan tuple.
*
*/
if (proj_info != NULL) {
/*
* Form a projection tuple, store it in the result tuple slot
* and return it --- unless we find we can project no tuples
* from this scan tuple, in which case continue scan.
*
*
*/
result_slot = ExecProject(proj_info, &is_done);
#ifdef PGXC
/* Copy the xcnodeoid if underlying scanned slot has one */
/* 复制xcnodeoid如果底层扫描的插槽有一个 */
result_slot->tts_xcnodeoid = slot->tts_xcnodeoid;
#endif /* PGXC */
if (is_done != ExprEndResult) {
@ -211,15 +212,14 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
/*
* @hdfs
* Optimize foreign scan by using informational constraint.
* 使
*/
if (IsA(node->ps.plan, ForeignScan)) {
ForeignScan* foreign_scan = (ForeignScan*)(node->ps.plan);
if (foreign_scan->scan.scan_qual_optimized) {
/*
* If we find a suitable tuple, set is_scan_end value is true.
* It means that we do not find suitable tuple in the next iteration,
* the iteration is over.
* set is_scan_end值为true
*
*/
node->is_scan_end = true;
}
@ -228,21 +228,20 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
}
} else {
/*
* Optimize foreign scan by using informational constraint.
* 使
*/
if (IsA(node->ps.plan, ForeignScan)) {
ForeignScan* foreign_scan = (ForeignScan*)(node->ps.plan);
if (foreign_scan->scan.scan_qual_optimized) {
/*
* If we find a suitable tuple, set is_scan_end value is true.
* It means that we do not find suitable tuple in the next iteration,
* the iteration is over.
* set is_scan_end值为true
*
*/
node->is_scan_end = true;
}
}
/*
* Here, we aren't projecting, so just return scan tuple.
*
*/
return slot;
}
@ -250,7 +249,7 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
InstrCountFiltered1(node, 1);
/*
* Tuple fails qual, so free per-tuple memory and try again.
* qual
*/
ResetExprContext(econtext);
}
@ -258,23 +257,21 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct
/*
* ExecAssignScanProjectionInfo
* Set up projection info for a scan node, if necessary.
*
*
* We can avoid a projection step if the requested tlist exactly matches
* the underlying tuple type. If so, we just set ps_ProjInfo to NULL.
* Note that this case occurs not only for simple "SELECT * FROM ...", but
* also in most cases where there are joins or other processing nodes above
* the scan node, because the planner will preferentially generate a matching
* tlist.
* tlist与底层元组类型完全匹配
* ps_ProjegInfo设置为NULL
* SELECT*FROM
* tlist
*
* ExecAssignScanType must have been called already.
* ExecAssignScanType
*/
void ExecAssignScanProjectionInfo(ScanState* node)
{
Scan* scan = (Scan*)node->ps.plan;
Index var_no;
/* Vars in an index-only scan's tlist should be INDEX_VAR */
/* 仅索引扫描的tlist中的变量应为index_VAR */
if (IsA(scan, IndexOnlyScan))
var_no = INDEX_VAR;
else
@ -285,15 +282,35 @@ void ExecAssignScanProjectionInfo(ScanState* node)
else
ExecAssignProjectionInfo(&node->ps, node->ss_ScanTupleSlot->tts_tupleDescriptor);
}
/* 函数ExecAssignScanProjectionInfo负责将投影信息分配给ScanState节点。让我们分解代码
ScanState指针作为输入
使Scan*node->ps.plan将ScanState强制转换为Scan节点
var_no
使IsAscanIndexOnlyScanvar_no设置为index_var
var_no设置为scan->scanrelid
使tlist_matches_tupdesc函数检查扫描的目标列表是否与扫描元组槽的元组描述符匹配node->ps.ps_ProjInfo设置为NULL
ExecAssignProjectionInfo使ScanState节点
ScanState节点
*/
/*
* ExecAssignScanProjectionInfoWithVarno
* As above, but caller can specify varno expected in Vars in the tlist.
* This function is called by ExecInitExtensiblePlan to initialize projection info.
* Usually the caller provides a targetlist describing the scan tuples, so we can
* avoid a projection step by setting ps_ProjInfo to NULL. Such as "SELECT * FROM ...".
* tlist中的Vars中指定varno
* ExecInitExtensiblePlan调用此函数来初始化投影信息
* ps_ProjegInfo设置为NULL来避免投影步骤SELECT*FROM
*/
void ExecAssignScanProjectionInfoWithVarno(ScanState* node, Index var_no)
/* 函数ExecAssignScanProjectionInfoWithVarno将ScanState对象和Index变量号作为参数。它用于为具有特定变量编号的扫描节点分配投影信息。
ScanState对象node
var_no变量号
var_no是否有效并且是否在可用变量的范围内
var_no有效
ExecAssignScanProjectionInfoWithVarno负责为具有特定变量号的扫描节点分配投影信息使
*/
{
Scan* scan = (Scan*)node->ps.plan;
@ -310,46 +327,42 @@ bool tlist_matches_tupdesc(PlanState* ps, List* tlist, Index var_no, TupleDesc t
bool has_oid = false;
ListCell* tlist_item = list_head(tlist);
/* Check the tlist attributes */
/* 检查tlist属性 */
for (attr_no = 1; attr_no <= num_attrs; attr_no++) {
Form_pg_attribute att_tup = tup_desc->attrs[attr_no - 1];
Var* var = NULL;
if (tlist_item == NULL)
return false; /* tlist too short */
return false; /* tlist太短 */
var = (Var*)((TargetEntry*)lfirst(tlist_item))->expr;
if (var == NULL || !IsA(var, Var))
return false; /* tlist item not a Var */
/* if these Asserts fail, planner messed up */
return false; /* tlist项不是Var */
/* 如果这些断言失败,计划者就会搞砸 */
Assert(var->varno == var_no);
Assert(var->varlevelsup == 0);
if (var->varattno != attr_no)
return false; /* out of order */
return false; /* 发生故障 */
if (att_tup->attisdropped)
return false; /* table contains dropped columns */
return false; /* 表包含删除的列 */
/*
* Note: usually the Var's type should match the tupdesc exactly, but
* in situations involving unions of columns that have different
* typmods, the Var may have come from above the union and hence have
* typmod -1. This is a legitimate situation since the Var still
* describes the column, just not as exactly as the tupdesc does. We
* could change the planner to prevent it, but it'd then insert
* projection steps just to convert from specific typmod to typmod -1,
* which is pretty silly.
* Var的类型应该与元组完全匹配mod的列的并集的情况下
* Var可能来自并集之上mod-1Var仍然描述列
* tudesc那样准确
* typmod转换为typmod-1
*/
if (var->vartype != att_tup->atttypid || (var->vartypmod != att_tup->atttypmod && var->vartypmod != -1))
return false; /* type mismatch */
return false; /* 类型不匹配 */
tlist_item = lnext(tlist_item);
}
if (tlist_item != NULL)
return false; /* tlist too long */
return false; /* tlist 列表太长 */
/*
* If the plan context requires a particular hasoid setting, then that has
* to match, too.
* hasoid设置
*
*/
if (ExecContextForcesOids(ps, &has_oid) && has_oid != tup_desc->tdhasoid)
return false;
@ -360,17 +373,16 @@ bool tlist_matches_tupdesc(PlanState* ps, List* tlist, Index var_no, TupleDesc t
/*
* ExecScanReScan
*
* This must be called within the ReScan function of any plan node type
* that uses ExecScan().
* 使ExecScanReScan函数中调用
*/
void ExecScanReScan(ScanState* node)
{
EState* estate = node->ps.state;
/* Stop projecting any tuples from SRFs in the targetlist */
/* 停止从目标列表中的SRF投影任何元组 */
node->ps.ps_TupFromTlist = false;
/* Rescan EvalPlanQual tuple if we're inside an EvalPlanQual recheck */
/* 如果我们在EvalPlanQual复查中则重新扫描EvalPlanQual元组 */
if (estate->es_epqScanDone != NULL) {
Index scan_rel_id = ((Scan*)node->ps.plan)->scanrelid;
@ -379,3 +391,14 @@ void ExecScanReScan(ScanState* node)
estate->es_epqScanDone[scan_rel_id - 1] = false;
}
}
/* 函数ExecScanReScan将ScanState对象作为参数用于重置扫描操作的状态以便重新扫描数据。
ScanState对象node
ExecScanReScan功能提供了一种重置扫描操作状态的机制使
使
*/

View File

@ -1,20 +1,17 @@
/* -------------------------------------------------------------------------
*
* execTuples.cpp
* Routines dealing with TupleTableSlots. These are used for resource
* management associated with tuples (eg, releasing buffer pins for
* tuples in disk buffers, or freeing the memory occupied by transient
* tuples). Slots also provide access abstraction that lets us implement
* "virtual" tuples to reduce data-copying overhead.
* TupleTableSlots的例程
*
* 访使
*
* Routines dealing with the type information for tuples. Currently,
* the type information for a tuple is an array of FormData_pg_attribute.
* This information is needed by routines manipulating tuples
* (getattribute, formtuple, etc.).
*
* FormData_pg_attribute的数组
* getattributeformtuple等
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* c2020
* c1996-2012PostgreSQL全球发展集团
* c1994
*
*
* IDENTIFICATION
@ -24,27 +21,27 @@
* INTERFACE ROUTINES
*
* SLOT CREATION/DESTRUCTION
* MakeTupleTableSlot - create an empty slot
* ExecAllocTableSlot - create a slot within a tuple table
* ExecResetTupleTable - clear and optionally delete a tuple table
* MakeSingleTupleTableSlot - make a standalone slot, set its descriptor
* ExecDropSingleTupleTableSlot - destroy a standalone slot
* MakeTupleTableSlot -
* ExecAllocTableSlot -
* ExecResetTupleTable -
* MakeSingleTupleTableSlot -
* ExecDropSingleTupleTableSlot -
*
* SLOT ACCESSORS
* ExecSetSlotDescriptor - set a slot's tuple descriptor
* ExecStoreTuple - store a physical tuple in the slot
* ExecStoreMinimalTuple - store a minimal physical tuple in the slot
* ExecClearTuple - clear contents of a slot
* ExecStoreVirtualTuple - mark slot as containing a virtual tuple
* ExecCopySlotTuple - build a physical tuple from a slot
* ExecCopySlotMinimalTuple - build a minimal physical tuple from a slot
* ExecMaterializeSlot - convert virtual to physical storage
* ExecCopySlot - copy one slot's contents to another
* ExecSetSlotDescriptor -
* ExecStoreTuple -
* ExecStoreMinimalTuple -
* ExecClearTuple -
* ExecStoreVirtualTuple - slot标记为包含虚拟元组
* ExecCopySlotTuple -
* ExecCopySlotMinimalTuple -
* ExecMaterializeSlot -
* ExecCopySlot -
*
* CONVENIENCE INITIALIZATION ROUTINES
* ExecInitResultTupleSlot \ convenience routines to initialize
* ExecInitScanTupleSlot \ the various tuple slots for nodes
* ExecInitExtraTupleSlot / which store copies of tuples.
* CONVENIENCE INITIALIZATION ROUTINES (便)
* ExecInitResultTupleSlot \ convenience routines to initialize (便)
* ExecInitScanTupleSlot \ the various tuple slots for nodes ()
* ExecInitExtraTupleSlot / which store copies of tuples. ()
* ExecInitNullTupleSlot /
*
* Routines that probably belong somewhere else:
@ -103,7 +100,7 @@
static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool skip_junk, bool mark_dropped = false, TableAmType tam = TAM_HEAP);
/* ----------------------------------------------------------------
* tuple table create/delete functions
* tuple table create/delete functions (/)
* ----------------------------------------------------------------
*/
/* --------------------------------
@ -113,6 +110,10 @@ static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool sk
* --------------------------------
*/
TupleTableSlot* MakeTupleTableSlot(bool has_tuple_mcxt, TableAmType tupslotTableAm)
/* 它创建一个新的TupleTableSlot对象并返回一个指向它的指针。
has_tuple_mcxt
tupslotTableAm访TAM_HEAP
*/
{
TupleTableSlot* slot = makeNode(TupleTableSlot);
Assert(tupslotTableAm == TAM_HEAP || tupslotTableAm == TAM_USTORE);
@ -149,7 +150,7 @@ TupleTableSlot* MakeTupleTableSlot(bool has_tuple_mcxt, TableAmType tupslotTable
/* --------------------------------
* ExecAllocTableSlot
*
* Create a tuple table slot within a tuple table (which is just a List).
* Create a tuple table slot within a tuple table (which is just a List). //在元组表(它只是一个列表)中创建一个元组表槽。
* --------------------------------
*/
TupleTableSlot* ExecAllocTableSlot(List** tuple_table, TableAmType tupslotTableAm)
@ -164,32 +165,37 @@ TupleTableSlot* ExecAllocTableSlot(List** tuple_table, TableAmType tupslotTableA
return slot;
}
/*它将指向TupleTableSlot对象列表的指针和TableAmType参数作为输入。它返回一个指向新分配的TupleTableSlot对象的指针。
MakeTupleTableSlotTupleTableSlot对象slot
使lappendtuple_table
tts_tupslotTableAmtupslotTableAm
TupleTableSlot对象的指针
*/
/* --------------------------------
* ExecResetTupleTable
*
* This releases any resources (buffer pins, tupdesc refcounts)
* held by the tuple table, and optionally releases the memory
* occupied by the tuple table data structure.
* It is expected that this routine be called by EndPlan().
*
*
* EndPlan
* --------------------------------
*/
void ExecResetTupleTable(List* tuple_table, /* tuple table */
bool should_free) /* true if we should free memory */
bool should_free) /* true ,如果我们应该释放内存 */
{
ListCell* lc = NULL;
foreach (lc, tuple_table) {
TupleTableSlot* slot = (TupleTableSlot*)lfirst(lc);
/* Always release resources and reset the slot to empty */
/* 始终释放资源并将插槽重置为空*/
(void)ExecClearTuple(slot);
if (slot->tts_tupleDescriptor) {
ReleaseTupleDesc(slot->tts_tupleDescriptor);
slot->tts_tupleDescriptor = NULL;
}
/* If shouldFree, release memory occupied by the slot itself */
/* 如果应该释放,释放插槽本身占用的内存 */
if (should_free) {
if (slot->tts_values)
pfree_ext(slot->tts_values);
@ -202,13 +208,33 @@ void ExecResetTupleTable(List* tuple_table, /* tuple table */
}
}
/* If shouldFree, release the list structure */
/* 如果应该释放,则释放列表结构 */
if (should_free) {
list_free_ext(tuple_table);
}
}
/* 这是一个用于重置元组表的函数。
* tuple_tableshould_free
* TupleTableSlot类型的指针
* ExecClearTuple函数来释放资源并将槽slot
* slottts_tupleDescriptor字段不为空ReleaseTupleDesc函数释放该字段指向的TupleDesc结构体
* should_free为真slot
* tts_valuestts_isnull和tts_lobPointers字段指向的内存tts_per_tuple_mcxt字段指向的内存上下文
* slot
* should_free为真
*/
TupleTableSlot* ExecMakeTupleSlot(Tuple tuple, TableScanDesc tableScan, TupleTableSlot* slot, TableAmType tableAm)
/* 这是一个用于创建TupleTableSlot的函数。它接受一个Tuple类型的参数tuple一个TableScanDesc类型的参数tableScan
TupleTableSlot类型的参数slotTableAmType类型的参数tableAm
TupleTableSlot
TupleTableSlot对象slot
tuple赋值给slot的tts_tuple字段
tableScan赋值给slot的tts_tableScan字段
tableAm赋值给slot的tts_tableAm字段访
TupleTableSlot对象
*/
{
if (unlikely(RELATION_CREATE_BUCKET(tableScan->rs_rd))) {
tableScan = ((HBktTblScanDesc)tableScan)->currBktScan;
@ -225,14 +251,24 @@ TupleTableSlot* ExecMakeTupleSlot(Tuple tuple, TableScanDesc tableScan, TupleTab
return ExecClearTuple(slot);
}
/* 这段代码是一个用于创建TupleTableSlot的函数。它接受一个Tuple类型的参数tuple
TableScanDesc类型的参数tableScanTupleTableSlot类型的参数slotTableAmType类型的参数tableAm
tableScan指向当前桶扫描描述符
tuple是否为NULLNULL
tableScan不为NULL
tableAm赋值给slot的tts_tupslotTableAm字段访
ExecStoreTuple函数tuple存储到slot中使tableScan->rs_cbuf指定的缓冲区
TupleTableSlot对象
tuple为NULLExecClearTuple函数slot清空TupleTableSlot对象
*/
/* --------------------------------
* MakeSingleTupleTableSlot
*
* This is a convenience routine for operations that need a
* standalone TupleTableSlot not gotten from the main executor
* tuple table. It makes a single slot and initializes it
* to use the given tuple descriptor.
* 便TupleTableSlot而不是从主执行器元组表中获得的操作
* 使
* --------------------------------
*/
TupleTableSlot* MakeSingleTupleTableSlot(TupleDesc tup_desc, bool allocSlotCxt, TableAmType tupslotTableAm)
@ -245,13 +281,13 @@ TupleTableSlot* MakeSingleTupleTableSlot(TupleDesc tup_desc, bool allocSlotCxt,
/* --------------------------------
* ExecDropSingleTupleTableSlot
*
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.
* DON'T use this on a slot that's part of a tuple table list!
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.(MakeSingleTupleTableSlot制作的TupleTableSlot)
* DON'T use this on a slot that's part of a tuple table list! (使)
* --------------------------------
*/
void ExecDropSingleTupleTableSlot(TupleTableSlot* slot)
{
/* This should match ExecResetTupleTable's processing of one slot */
/* This should match ExecResetTupleTable's processing of one slot(这应该与ExecResetTupleTable对一个插槽的处理相匹配) */
(void)ExecClearTuple(slot);
if (slot->tts_tupleDescriptor != NULL) {
ReleaseTupleDesc(slot->tts_tupleDescriptor);
@ -272,6 +308,16 @@ void ExecDropSingleTupleTableSlot(TupleTableSlot* slot)
}
pfree_ext(slot);
}
/* 这段代码是用于释放一个单独的TupleTableSlot的资源。下面是对代码的逐行解释
(void)ExecClearTuple(slot);slot中的tuple数据
if (slot->tts_tupleDescriptor != NULL) { ReleaseTupleDesc(slot->tts_tupleDescriptor); }slot中的tuple描述符
if (slot->tts_values != NULL) { pfree_ext(slot->tts_values); }slot中的tuple值数组
if (slot->tts_isnull != NULL) { pfree_ext(slot->tts_isnull); }slot中的null标志数组
pfree_ext(slot->tts_lobPointers);slot中的LOB指针
if (slot->tts_per_tuple_mcxt != NULL) { MemoryContextDelete(slot->tts_per_tuple_mcxt); }slot中的内存上下文
pfree_ext(slot);slot本身的内存
TupleTableSlot所占用的资源
*/
/* ----------------------------------------------------------------
* tuple table slot accessor functions
@ -285,23 +331,26 @@ void ExecDropSingleTupleTableSlot(TupleTableSlot* slot)
* at least equal to the slot's. If it is a reference-counted descriptor
* then the reference count is incremented for as long as the slot holds
* a reference.
* (
* 寿寿
* )
* --------------------------------
*/
void ExecSetSlotDescriptor(TupleTableSlot* slot, /* slot to change */
TupleDesc tup_desc) /* new tuple descriptor */
void ExecSetSlotDescriptor(TupleTableSlot* slot, /* 要更改的插槽 */
TupleDesc tup_desc) /* 新元组描述符 */
{
/* For safety, make sure slot is empty before changing it */
/*为了安全起见,在更换插槽之前,请确保插槽为空*/
(void)ExecClearTuple(slot);
/*
* Release any old descriptor. Also release old Datum/isnull arrays if
* present (we don't bother to check if they could be re-used).
* Datum/isull数组
*使
*/
if (slot->tts_tupleDescriptor != NULL) {
ReleaseTupleDesc(slot->tts_tupleDescriptor);
}
#ifdef PGXC
/* XXX there in no routine to release AttInMetadata instance */
/* XXX there in no routine to release AttInMetadata instance(XXX没有发布AttInMetadata实例的例程) */
if (slot->tts_attinmeta != NULL) {
slot->tts_attinmeta = NULL;
}
@ -315,35 +364,47 @@ void ExecSetSlotDescriptor(TupleTableSlot* slot, /* slot to change */
}
pfree_ext(slot->tts_lobPointers);
/*
* Install the new descriptor; if it's refcounted, bump its refcount.
*
*/
slot->tts_tupleDescriptor = tup_desc;
PinTupleDesc(tup_desc);
/*
* Allocate Datum/isnull arrays of the appropriate size. These must have
* the same lifetime as the slot, so allocate in the slot's own context.
*
*/
slot->tts_values = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));
slot->tts_isnull = (bool*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(bool));
slot->tts_lobPointers = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));
}
/*这段代码用于设置TupleTableSlot的描述符descriptor。下面是对代码的逐行解释
(void)ExecClearTuple(slot);slot中的tuple数据slot为空
if (slot->tts_tupleDescriptor != NULL) { ReleaseTupleDesc(slot->tts_tupleDescriptor); }slot中的旧的tuple描述符
#ifdef PGXC ... #endif这部分代码是针对特定的条件编译可能与特定的PostgreSQL扩展相关我们暂时不考虑它的作用。
if (slot->tts_values != NULL) { pfree_ext(slot->tts_values); }slot中的旧的tuple值数组
if (slot->tts_isnull != NULL) { pfree_ext(slot->tts_isnull); }slot中的旧的null标志数组
pfree_ext(slot->tts_lobPointers);slot中的旧的LOB指针
slot->tts_tupleDescriptor = tup_desc;tuple描述符赋值给slot
PinTupleDesc(tup_desc);tuple描述符的引用计数
slot->tts_values = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));slot的内存上下文中分配新的tuple值数组
slot->tts_isnull = (bool*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(bool));slot的内存上下文中分配新的null标志数组
slot->tts_lobPointers = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));slot的内存上下文中分配新的LOB指针数组
TupleTableSlot的描述符slot
*/
/* --------------------------------
* ExecStoreTuple
*
* This function is used to store a physical tuple into a specified
* slot in the tuple table.
* slot in the tuple table.()
*
* tuple: tuple to store
* slot: slot to store it in
* buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
* tuple: tuple to store()
* slot: slot to store it in()
* buffer: disk buffer if tuple is in a disk page, else InvalidBuffer(InvalidBuffer)
* shouldFree: true if ExecClearTuple should pfree_ext() the tuple
* when done with it
* when done with it ExecClearTuple在处理完元组后应该pfree_exttrue
*
* If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin
* on the buffer which is held until the slot is cleared, so that the tuple
* won't go away on us.
* bufferInvalidBufferpin
* pin将一直保留到插槽被清除
*
* shouldFree is normally set 'true' for tuples constructed on-the-fly.
* It must always be 'false' for tuples that are stored in disk pages,
@ -370,7 +431,7 @@ void ExecSetSlotDescriptor(TupleTableSlot* slot, /* slot to change */
TupleTableSlot* ExecStoreTuple(Tuple tuple, TupleTableSlot* slot, Buffer buffer, bool should_free)
{
/*
* sanity checks
* sanity checks ()
*/
Assert(tuple != NULL);
Assert(slot != NULL);
@ -387,49 +448,72 @@ TupleTableSlot* ExecStoreTuple(Tuple tuple, TupleTableSlot* slot, Buffer buffer,
return slot;
}
/*
Tuple存储到TupleTableSlot中
Assert(tuple != NULL);tuple不为空
Assert(slot != NULL);slot不为空
Assert(slot->tts_tupleDescriptor != NULL);slot的tuple描述符不为空
HeapTuple htup = (HeapTuple)tuple;tuple强制转换为HeapTuple类型htup
if (slot->tts_tupslotTableAm == TAM_USTORE && htup->tupTableType == HEAP_TUPLE)slot的存储类型是UStorehtup的表类型是Heap Tuple
tuple = (Tuple)HeapToUHeap(slot->tts_tupleDescriptor, (HeapTuple)tuple);Heap Tuple转换为UHeap Tuple
else if (slot->tts_tupslotTableAm == TAM_HEAP && htup->tupTableType == UHEAP_TUPLE)slot的存储类型是Heaphtup的表类型是UHeap Tuple
tuple = (Tuple)UHeapToHeap(slot->tts_tupleDescriptor, (UHeapTuple)tuple);UHeap Tuple转换为Heap Tuple
tableam_tslot_store_tuple(tuple, slot, buffer, should_free, false);tableam_tslot_store_tuple函数将tuple存储到slot中
return slot;tuple的slot
Tuple存储到TupleTableSlot中tuple的slot
*/
/* --------------------------------
* ExecStoreMinimalTuple
*
* Like ExecStoreTuple, but insert a "minimal" tuple into the slot.
* Like ExecStoreTuple, but insert a "minimal" tuple into the slot. (ExecStoreTuple类似)
*
* No 'buffer' parameter since minimal tuples are never stored in relations.
* No 'buffer' parameter since minimal tuples are never stored in relations. (buffer)
* --------------------------------
*/
TupleTableSlot* ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot* slot, bool should_free)
{
/*
* sanity checks
*
*/
Assert(mtup != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
/*
* store the minimal tuple in the slot.
*
*/
tableam_tslot_store_minimal_tuple(mtup, slot, should_free);
return slot;
}
/*
MinimalTuple存储到TupleTableSlot中
Assert(mtup != NULL);MinimalTuple不为空
Assert(slot != NULL);slot不为空
Assert(slot->tts_tupleDescriptor != NULL);slot的tuple描述符不为空
tableam_tslot_store_minimal_tuple(mtup, slot, should_free);tableam_tslot_store_minimal_tuple函数将MinimalTuple存储到slot中
return slot;MinimalTuple的slot
MinimalTuple存储到TupleTableSlot中MinimalTuple的slot
*/
/* --------------------------------
* ExecClearTuple
*
* This function is used to clear out a slot in the tuple table.
* This function is used to clear out a slot in the tuple table.()
*
* NB: only the tuple is cleared, not the tuple descriptor (if any).
* NB: only the tuple is cleared, not the tuple descriptor (if any). ()
* --------------------------------
*/
TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* return: slot passed slot in which to store tuple */
TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* returnslot-passed存储元组的slot*/
{
/*
* sanity checks
*
*/
Assert(slot != NULL);
/*
* clear the physical tuple or minimal tuple if present via TableAm.
* TableAm清除物理元组或最小元组
*/
if (slot->tts_shouldFree || slot->tts_shouldFreeMin) {
Assert(slot->tts_tupleDescriptor != NULL);
@ -437,7 +521,7 @@ TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* return: slot passed slot
}
/*
* tts_tuple may still be valid if tts_shouldFree is false, Original caller doesn't want this slot to free the tuple.
*tts_shouldFree为falsetts_tuple可能仍然有效
*/
slot->tts_tuple = NULL;
slot->tts_mintuple = NULL;
@ -455,7 +539,7 @@ TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* return: slot passed slot
#endif
/*
* Drop the pin on the referenced buffer, if there is one.
*
*/
if (BufferIsValid(slot->tts_buffer)) {
ReleaseBuffer(slot->tts_buffer);
@ -463,19 +547,42 @@ TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* return: slot passed slot
slot->tts_buffer = InvalidBuffer;
/*
* Mark it empty.
*
*/
slot->tts_isempty = true;
slot->tts_nvalid = 0;
// Row uncompression use slot->tts_per_tuple_mcxt in some case, So we need
// reset memory context. This memory context is introduced by PGXC and it only used
// in function 'slot_deform_datarow'. PGXC also do reset in function 'FetchTuple'.
// So it is safe
//
//在某些情况下行解压缩使用slot->tts_per_tuple_mcxt
//因此我们需要重置内存上下文。此内存上下文由PGXC引入
// 仅在函数“slot_form_datarow”中使用。PGXC也在函数“FetchTuple”中进行重置。
// 所以它是安全的
//
ResetSlotPerTupleContext(slot);
return slot;
}
/*
TupleTableSlot中的tuple
Assert(slot != NULL);slot不为空
if (slot->tts_shouldFree || slot->tts_shouldFreeMin)slot中的tuple需要释放
Assert(slot->tts_tupleDescriptor != NULL);slot的tuple描述符不为空
tableam_tslot_clear(slot);TableAm清除物理tuple或最小tuple
slot->tts_tuple = NULL;slot中的tuple置为NULL
slot->tts_mintuple = NULL;slot中的最小tuple置为NULL
slot->tts_shouldFree = false;slot的tts_shouldFree标志置为falsetuple
slot->tts_shouldFreeMin = false;slot的tts_shouldFreeMin标志置为falsetuple
if (slot->tts_shouldFreeRow) { pfree_ext(slot->tts_dataRow); }slot的tts_shouldFreeRow标志为trueslot中的数据行
slot->tts_shouldFreeRow = false;slot的tts_shouldFreeRow标志置为false
slot->tts_dataRow = NULL;slot中的数据行置为NULL
slot->tts_dataLen = -1;slot中的数据长度置为-1
slot->tts_xcnodeoid = 0;slot的tts_xcnodeoid置为0
if (BufferIsValid(slot->tts_buffer)) { ReleaseBuffer(slot->tts_buffer); }slot中的buffer有效buffer
slot->tts_buffer = InvalidBuffer;slot的buffer置为无效
slot->tts_isempty = true;slot的isempty标志置为trueslot为空
slot->tts_nvalid = 0;slot的nvalid置为0tuple数量为0
ResetSlotPerTupleContext(slot);slot的tts_per_tuple_mcxt内存上下文
return slot;tuple的slot
TupleTableSlot中的tupleslot重置为空tuple的slot
*/
/* --------------------------------
* ExecStoreVirtualTuple
@ -501,13 +608,22 @@ TupleTableSlot* ExecStoreVirtualTuple(TupleTableSlot* slot)
slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
if (slot->tts_tupslotTableAm != slot->tts_tupleDescriptor->tdTableAmType) {
// XXX: Should tts_tupleDescriptor be cloned before changing its contents
// as some time it can be direct reference to the rd_att in RelationData.
// XXX: 如果tts_tupleDescriptor在更改其内容之前进行克隆
//则它可以直接引用RelationData中的rd_att。
slot->tts_tupleDescriptor->tdTableAmType = slot->tts_tupslotTableAm;
}
return slot;
}
/*
ExecStoreVirtualTuple用于将虚拟元组存储在元组表槽中
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_tupleDescriptorslot中的元组的描述符NULLslot->tss_isempty是否为true
slot->tts_isempty设置为falseslot不再为空
slot->tts_nvalid设置为元组描述符slot->ttleStupleDescriptor->natts
slot->tts_tupslotTableAmslot的表访问方法slot->ttleStupleDescriptor->tdTableAmType访slot->tts_tupleDescriptor- >tdTableAmType以匹配slot->ts_tupslotTableAm
.
*/
/* --------------------------------
* ExecStoreAllNullTuple
@ -525,11 +641,11 @@ TupleTableSlot* ExecStoreAllNullTuple(TupleTableSlot* slot)
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
/* Clear any old contents */
/* 清除所有旧内容 */
(void)ExecClearTuple(slot);
/*
* Fill all the columns of the virtual tuple with nulls
* null填充虚拟元组的所有列
*/
errno_t rc = EOK;
@ -546,6 +662,16 @@ TupleTableSlot* ExecStoreAllNullTuple(TupleTableSlot* slot)
return ExecStoreVirtualTuple(slot);
}
/*
ExecStoreAllNullTuple用于将具有所有null值的虚拟元组存储在元组表槽中
TupleTableSlot指针作为输入
slot是否为NULLslot->tts_tupleDescriptorslot中的元组的描述符NULL
ExecClearTuple来清除插槽中的任何现有内容
使memset_s函数用null值填充插槽的tts_values数组tts_values数组设置为全零slot->tts_tupleDescriptor->natts*sizeofDatum
使memset_s用真值填充插槽的tts_isull数组nulltruenull
使ExecStoreVirtualTuple的结果
null值的虚拟元组
*/
/* --------------------------------
* ExecCopySlotTuple
@ -560,13 +686,21 @@ TupleTableSlot* ExecStoreAllNullTuple(TupleTableSlot* slot)
HeapTuple ExecCopySlotTuple(TupleTableSlot* slot)
{
/*
* sanity checks
* sanity checks ()
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
return tableam_tslot_copy_heap_tuple(slot);
}
/*
ExecCopySlotTuple用于创建存储在元组表槽中的元组的副本
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_copy_heap_tuple来创建存储在插槽中的元组的副本
HeapTuple对象返回
*/
/* --------------------------------
* ExecCopySlotMinimalTuple
@ -585,6 +719,13 @@ MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot* slot, bool need_transform_
return tableam_tslot_copy_minimal_tuple(slot);
}
/*
ExecCopySlotMinimalTuple用于创建存储在元组表槽中的最小元组的副本
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_copy_minimaltuple来创建存储在插槽中的最小元组的副本
MinimalTuple对象返回
*/
/* --------------------------------
* ExecFetchSlotTuple
@ -611,6 +752,13 @@ HeapTuple ExecFetchSlotTuple(TupleTableSlot* slot)
return tableam_tslot_get_heap_tuple(slot);
}
/*
ExecFetchSlotTuple用于从元组表槽中检索堆元组
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_get_heap_tuple从插槽中检索堆元组
HeapTuple对象返回
*/
/* --------------------------------
* ExecFetchSlotMinimalTuple
@ -634,7 +782,13 @@ MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot* slot)
return tableam_tslot_get_minimal_tuple(slot);
}
/*
ExecFetchSlotMinimalTuple用于从元组表槽中检索最小元组
TupleTableSlot指针作为输入
slot是否不为NULL
tableam_tslot_get_minimaltuple从插槽中检索最小元组
MinimalTuple对象返回
*/
/* --------------------------------
* ExecFetchSlotTupleDatum
* Fetch the slot's tuple as a composite-type Datum.
@ -650,9 +804,9 @@ Datum ExecFetchSlotTupleDatum(TupleTableSlot* slot)
HeapTupleHeader td;
TupleDesc tup_desc;
/* Make sure we can scribble on the slot contents ... */
/* Make sure we can scribble on the slot contents ... (确保我们可以在插槽内容上乱写) */
tup = ExecMaterializeSlot(slot);
/* ... and set up the composite-Datum header fields, in case not done */
/* ... and set up the composite-Datum header fields, in case not done(并设置复合基准标题字段,如果未完成) */
td = tup->t_data;
tup_desc = slot->tts_tupleDescriptor;
HeapTupleHeaderSetDatumLength(td, tup->t_len);
@ -660,6 +814,17 @@ Datum ExecFetchSlotTupleDatum(TupleTableSlot* slot)
HeapTupleHeaderSetTypMod(td, tup_desc->tdtypmod);
return PointerGetDatum(td);
}
/*
ExecFetchSlotTupleDatum用于从元组表槽中检索表示元组的Datum
HeapTuple的变量tupHeapStupleHeader的变量td和类型为TupleDesc的变量tup_desc
ExecMaterializeSlot来实现槽Datum将是指向元组标头的指针访
tup->t_datatd变量
slot->tts_tupleDescriptortup_desc变量
使HeapTupleHeaderSetDatumLengthHeapTuppleHeaderSetTypeId和HeapTupleHeaderSetTypMod宏
tup_descID和类型修饰符td
PointerGetDatumtdtdDatum表示
*/
/* --------------------------------
* ExecMaterializeSlot
@ -684,6 +849,14 @@ HeapTuple ExecMaterializeSlot(TupleTableSlot* slot)
return tableam_tslot_materialize(slot);
}
/*
ExecMaterializeSlot用于实体化存储在元组表槽中的元组
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_materialize来实现槽中的元组
HeapTuple对象返回
tableam_tslot_materialize使访
*/
/* --------------------------------
* ExecCopySlot
@ -701,9 +874,8 @@ TupleTableSlot* ExecCopySlot(TupleTableSlot* dst_slot, TupleTableSlot* src_slot)
MemoryContext old_context;
/*
* There might be ways to optimize this when the source is virtual, but
* for now just always build a physical copy. Make sure it is in the
* right context.
*
*
*/
old_context = MemoryContextSwitchTo(dst_slot->tts_mcxt);
new_tuple = ExecCopySlotTuple(src_slot);
@ -711,6 +883,17 @@ TupleTableSlot* ExecCopySlot(TupleTableSlot* dst_slot, TupleTableSlot* src_slot)
return ExecStoreTuple(new_tuple, dst_slot, InvalidBuffer, true);
}
/*
ExecCopySlot用于创建存储在源元组表槽中的元组的副本
TupleTableSlot指针作为输入dst_slotsrc_slot
HeapTuple的变量new_tuple和类型为MemoryContext的变量old_context
使MemoryContext SwitchTo将内存上下文切换到目标插槽的内存上下文
ExecCopySlotTuple来创建存储在源槽中的元组的副本
使MemoryContext SwitchToold_context
ExecStoreTuple将复制的元组存储在目标槽中new_tupledst_slotInvalidBuffertrue
*/
/* ----------------------------------------------------------------
* convenience initialization routines
@ -741,6 +924,17 @@ void ExecInitScanTupleSlot(EState* estate, ScanState* scan_state, TableAmType ta
{
scan_state->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, tam);
}
/*
ExecInitResultTupleSlot和ExecInitScanTupleSlot分别用于初始化用于存储结果元组和扫描元组的元组槽
ExecInitResultTupleSlot函数
estateplan_state和表示表访问方法类型的tam
使ExecAllocTableSlot函数将plan_state的结果元组槽分配给新分配的表槽使estate->es_tupleTable从estate的元组表中获得的
plan_state->ps_ResultTupleSlot
ExecInitScanTupleSlot函数
estatescan_state和表示表访问方法类型的tam
使ExecAllocTableSlot函数将scan_state的扫描元组槽分配给新分配的表槽使estate->es_tupleTable从estate的元组表中获得的
scan_state->ss_ScanTupleSlot
*/
/* ----------------
* ExecInitExtraTupleSlot
@ -767,6 +961,13 @@ TupleTableSlot* ExecInitNullTupleSlot(EState* estate, TupleDesc tup_type)
return ExecStoreAllNullTuple(slot);
}
/*
ExecInitNullTupleSlot用于使用null元组初始化元组表槽
estate和表示null元组的元组描述符的tup_type
ExecInitTextraTupleSlot来初始化一个额外的元组表槽
使ExecSetSlotDescriptor将元组描述符tup_type分配给插槽
使slot调用ExecStoreAllNullTuple的结果null来用null元组填充槽
*/
/* ----------------------------------------------------------------
* ExecTypeFromTL
@ -829,6 +1030,20 @@ static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool sk
return type_info;
}
/*
ExecCleanTypeFromTL用于从目标列表生成干净的元组描述符
target_listhas_oidoid列的布尔值tam访
使ExecTypeFromTLInternal函数以生成元组描述符target_listhas_oidtruefalsetam访
ExecTypeFromTLInternal函数初始化一些变量type_infoTupleDesc对象len
使CreateTemplateTupleDesc创建模板元组描述符lenhas_oid和tamOID标志的空元组描述符
使foreach循环迭代目标列表中的每个目标条目
skip_junk为trueresjunk为true
使TupleDescInitEntry初始化元组描述符中的一个条目cur_resnoresname
exprTypeNode*ttle->exprtypmodexprTypmodNode*ttle->expr0
mark_dropped为true..pg.dropped.attitdropped设置为true
cur_resno将递增
*/
/*
* ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
@ -858,6 +1073,18 @@ TupleDesc ExecTypeFromExprList(List* expr_list, List* names_list, TableAmType t
return type_info;
}
/*
ExecTypeFromExprList用于从表达式列表和相应的名称列表生成元组描述符
expr_listnames_listtam访
type_infoTupleDesc对象cur_resno
使Assert断言expr_list和names_list的长度相等
使CreateTemplateTupleDesc创建模板元组描述符expr_list的长度false表示元组描述符不应包括OID列tam表示表访问方法类型
使forboth循环并行迭代每个表达式和名称
使TupleDescInitEntry初始化元组描述符中的一个条目cur_resnonexprTypeetypmodexprTypmode0
使TupleDescInitEntryCollation和exprCollatione
cur_resno将递增
*/
/*
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs
@ -895,6 +1122,16 @@ TupleTableSlot* TupleDescGetSlot(TupleDesc tup_desc)
/* Return the slot */
return slot;
}
/*
BlessTupleDesc用于祝福元组描述符TupleDescGetSlot用于根据提供的元组描述符初始化元组表槽
BlessTupleDesc将TupleDesc对象tup_desc作为输入
tup_desc的tdtypeid是RECORDOIDtdtypmod小于0assign_record_type_typmod为记录类型分配一个合适的typmod
tup_desc对象便
TupleDescGetSlot将TupleDesc对象tup_desc作为输入
BlessTupleDesc来祝福元组描述符使
MakeSingleTupleTableSlot
*/
/*
* TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
@ -914,18 +1151,18 @@ AttInMetadata* TupleDescGetAttInMetadata(TupleDesc tup_desc)
att_in_meta = (AttInMetadata*)palloc(sizeof(AttInMetadata));
/* "Bless" the tupledesc so that we can make rowtype datums with it */
/* "Bless" the tupledesc so that we can make rowtype datums with it(“Bless”元组这样我们就可以用它制作行型基准) */
att_in_meta->tupdesc = BlessTupleDesc(tup_desc);
/*
* Gather info needed later to call the "in" function for each attribute
* Gather info needed later to call the "in" function for each attribute(in)
*/
att_in_func_info = (FmgrInfo*)palloc0(natts * sizeof(FmgrInfo));
att_io_params = (Oid*)palloc0(natts * sizeof(Oid));
att_typ_mods = (int32*)palloc0(natts * sizeof(int32));
for (i = 0; i < natts; i++) {
/* Ignore dropped attributes */
/* Ignore dropped attributes(忽略丢弃的属性) */
if (!tup_desc->attrs[i]->attisdropped) {
att_type_id = tup_desc->attrs[i]->atttypid;
getTypeInputInfo(att_type_id, &att_in_func_id, &att_io_params[i]);
@ -939,6 +1176,20 @@ AttInMetadata* TupleDescGetAttInMetadata(TupleDesc tup_desc)
return att_in_meta;
}
/*
TupleDescGetAttInMetadata用于收集调用元组描述符中每个属性的in
TupleDesc对象tup_desc作为输入
nattsi
使palloc为AttInMetadata对象att_in_meta分配内存
BlessTupleDesc来使
att_in_func_infoatt_io_params和att_typ_mod
使
attidrepped为false使getTypeInputInfo收集诸如属性类型IDinID和IO参数等信息
使fmgr_info用inatt_in_func_info数组
IO参数和属性类型mod存储在相应的数组中
att_in_meta对象中的相应字段
att_in_meta对象
*/
/*
* BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
@ -986,6 +1237,19 @@ HeapTuple BuildTupleFromCStrings(AttInMetadata* att_in_meta, char** values)
return tuple;
}
/*
BuildTupleFromCStrings用于从C样式字符串数组中构建HeapTuple
AttInMetadata对象att_in_meta和一个C样式字符串值数组作为输入
att_in_meta对象中提取TupleDesc对象tup_descnatt的数量
d_values和nullsnull标志
使palloc为d_values和null分配内存
使
attitdropped为false使InputFunctionCall为该属性调用in使C样式字符串值转换为基准null来设置null标志
NULLNULL标志设置为true
使tableam_tops_form_tuple形成一个HeapTuplenull标志创建一个新的HeapTuple
使pfree_ext释放为d_values和null分配的内存
HeapTuple
*/
/*
* Functions for sending tuples to the frontend (or other specified destination)
@ -1006,6 +1270,17 @@ TupOutputState* begin_tup_output_tupdesc(DestReceiver* dest, TupleDesc tup_desc)
return tstate;
}
/*
begin_tup_output_tupdesc用于初始化给定目标接收器和元组描述符的元组输出状态
desttup_desc
TupOutputState类型的变量tstate
使palloc为tstate对象分配内存
使tup_desc调用MakeSingleTupleTableSlot来初始化tstate的slot字段
dest参数指定给tstate的dest字段
使*tstate->dest->rStartuprStartup函数
rStartup函数是使用目标接收器CMD_SELECT
tstate对象
*/
/*
* write a single tuple
@ -1038,11 +1313,24 @@ void do_tup_output(TupOutputState* tstate, Datum* values, size_t values_len, con
/* clean up */
(void)ExecClearTuple(slot);
}
/*
do_tup_output用于使用提供的TupOutputState对象将元组输出到目标接收器
tstatevaluesDatum值的数组values_lenvalues数组的长度is_nullnull的布尔值的数组is_null_lenis _null数组的长度
使Assert断言值和is_null数组不为null
TupleTableSlot类型的变量slottstate对象的slot字段
tts_tupleDescriptor字段中获取属性natt的数量
使ExecClearTuple清除插槽
使memcpy_s将值数组复制到插槽的tts_values字段中
使memcpy_s将is_null数组复制到插槽的tts_isull字段中
使ExecStoreVirtualTuple将插槽标记为包含虚拟元组
receiveSlot函数slot和tstate->dest作为参数
使ExecClearTuple再次清除插槽以清除任何剩余数据
*/
/*
* write a chunk of text, breaking at newline characters
* write a chunk of text, breaking at newline characters()
*
* Should only be used with a single-TEXT-attribute tupdesc.
* Should only be used with a single-TEXT-attribute tupdesc.(TEXT属性tupdesc一起使用)
*/
int do_text_output_multiline(TupOutputState* tstate, char* text)
{
@ -1072,6 +1360,20 @@ int do_text_output_multiline(TupOutputState* tstate, char* text)
}
return tuple_count;
}
/*函数do_text_output_multiline用于使用提供的TupOutputState对象将多行文本作为元组输出到目标接收器。
tstatetext
1Datum类型的数组值
bool类型的数组is_null1falsenull
tuple_count0
使strchr搜索换行符'\n'eol指针以指向换行符之后的下一个字符eol设置为指向字符串的末尾
使cstring_to_text_with_len将文本行转换为基准[0]
do_tup_output函数来输出具有值数组is_null数组和提供的tstate的元组
tuple_count变量
使pfree释放为Datum值分配的内存
*/
void end_tup_output(TupOutputState* tstate)
{
@ -1149,3 +1451,28 @@ TupleTableSlot* ExecStoreDataRowTuple(char* msg, size_t len, Oid msgnode_oid, Tu
return slot;
}
#endif
/*
end_tup_output和ExecStoreDataRowTuple
end_tup_output
TupOutputState对象tstate作为输入
使*tstate->dest->rShutdownrShutdownfunction
使ExecDropSingleTupleTableSlot删除单元组表槽
使pfree_ext释放为tstate对象分配的内存
*/
/*
ExecStoreDataRowTuple
DataRow消息格式的缓冲区存储到元组表槽中
msgDataRow消息的缓冲区lenmsgnode_oidoidslotslotshould_free
dataRow
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff