From 1eae811aac69a482f997a1e7f5aa8c3f05f64e55 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 19:05:43 +0800 Subject: [PATCH 01/19] Enter --- .../storage/access/common/heaptuple.cpp | 655 ++++++++++-------- 1 file changed, 362 insertions(+), 293 deletions(-) diff --git a/src/gausskernel/storage/access/common/heaptuple.cpp b/src/gausskernel/storage/access/common/heaptuple.cpp index ccaa5203c..b3e6a71ae 100644 --- a/src/gausskernel/storage/access/common/heaptuple.cpp +++ b/src/gausskernel/storage/access/common/heaptuple.cpp @@ -141,112 +141,115 @@ Size heap_compute_data_size(TupleDesc tupleDesc, Datum *values, const bool *isnu void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit) { - bits8 *bitP = NULL; - uint32 bitmask; - int i; - int numberOfAttributes = tupleDesc->natts; - Form_pg_attribute *att = tupleDesc->attrs; - errno_t rc = EOK; - char *begin = data; + bits8 *bitP = NULL; // 位图字节指针 + uint32 bitmask; // 位掩码,用于表示位图中的位 + int i; // 循环计数器 + int numberOfAttributes = tupleDesc->natts; // 元组的属性数量 + Form_pg_attribute *att = tupleDesc->attrs; // 属性描述符数组 + errno_t rc = EOK; // 错误码 + char *begin = data; // 元组数据的起始指针 #ifdef USE_ASSERT_CHECKING - char *start = data; + char *start = data; // 用于断言检查的元组数据起始指针 #endif if (bit != NULL) { - bitP = &bit[-1]; - bitmask = HIGHBIT; + bitP = &bit[-1]; // 如果位图不为空,设置 bitP 指针 + bitmask = HIGHBIT; // 设置位掩码为最高位 } else { /* just to keep compiler quiet */ - bitP = NULL; - bitmask = 0; + bitP = NULL; // 否则,设置 bitP 为 NULL + bitmask = 0; // 位掩码为0 } - *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL); + *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL); // 清除部分标志位 for (i = 0; i < numberOfAttributes; i++) { - Size data_length; - Size remain_length = data_size - (size_t)(data - begin); + Size data_length; // 数据长度 + Size remain_length = data_size - (size_t)(data - begin); // 剩余空间长度 if (bit != NULL) { if (bitmask != HIGHBIT) { - bitmask <<= 1; + bitmask <<= 1; // 位掩码左移一位 } else { - bitP += 1; - *bitP = 0x0; - bitmask = 1; + bitP += 1; // 切换到下一个字节 + *bitP = 0x0; // 重置位图字节为0 + bitmask = 1; // 位掩码重置为1 } if (isnull[i]) { - *infomask |= HEAP_HASNULL; + *infomask |= HEAP_HASNULL; // 设置 HEAP_HASNULL 标志位 continue; } - *bitP |= bitmask; + *bitP |= bitmask; // ���置位图中的位 } + /* * XXX we use the att_align macros on the pointer value itself, not on * an offset. This is a bit of a hack. */ - if (att[i]->attbyval) { - /* pass-by-value */ - data = (char *)att_align_nominal(data, att[i]->attalign); - store_att_byval(data, values[i], att[i]->attlen); - data_length = att[i]->attlen; + if (att[i]->attbyval) { + /* pass-by-value 类型的属性 */ + data = (char *)att_align_nominal(data, att[i]->attalign); // 对齐属性 + store_att_byval(data, values[i], att[i]->attlen); // 存储 pass-by-value 数据 + data_length = att[i]->attlen; // 设置数据长度 } else if (att[i]->attlen == -1) { - /* varlena */ - Pointer val = DatumGetPointer(values[i]); + /* varlena 类型的属性(变长类型) */ + Pointer val = DatumGetPointer(values[i]); // 获取 varlena 数据 + + *infomask |= HEAP_HASVARWIDTH; // 设置 HEAP_HASVARWIDTH 标志位 - *infomask |= HEAP_HASVARWIDTH; if (VARATT_IS_EXTERNAL(val)) { - *infomask |= HEAP_HASEXTERNAL; - /* no alignment, since it's short by definition */ - data_length = VARSIZE_EXTERNAL(val); - rc = memcpy_s(data, remain_length, val, data_length); + *infomask |= HEAP_HASEXTERNAL; // 设置 HEAP_HASEXTERNAL 标志位 + /* 由于是外部引用,无需对齐 */ + data_length = VARSIZE_EXTERNAL(val); // 获取外部引用 varlena 数据长度 + rc = memcpy_s(data, remain_length, val, data_length); // 复制数据 securec_check(rc, "\0", "\0"); } else if (VARATT_IS_SHORT(val)) { - /* no alignment for short varlenas */ - data_length = VARSIZE_SHORT(val); - rc = memcpy_s(data, remain_length, val, data_length); + /* 短 varlena 类型,无需对齐 */ + data_length = VARSIZE_SHORT(val); // 获取短 varlena 数据长度 + rc = memcpy_s(data, remain_length, val, data_length); // 复制数据 securec_check(rc, "\0", "\0"); } else if (VARLENA_ATT_IS_PACKABLE(att[i]) && VARATT_CAN_MAKE_SHORT(val)) { - /* convert to short varlena -- no alignment */ - data_length = VARATT_CONVERTED_SHORT_SIZE(val); - SET_VARSIZE_SHORT(data, data_length); + /* 转换为短 varlena 类型,无需对齐 */ + data_length = VARATT_CONVERTED_SHORT_SIZE(val); // 获取转换后的短 varlena 数据长度 + SET_VARSIZE_SHORT(data, data_length); // 设置短 varlena 数据的长度 if (data_length > 1) { - rc = memcpy_s(data + 1, remain_length - 1, VARDATA(val), data_length - 1); + rc = memcpy_s(data + 1, remain_length - 1, VARDATA(val), data_length - 1); // 复制数据 securec_check(rc, "\0", "\0"); } } else { - /* full 4-byte header varlena */ - data = (char *)att_align_nominal(data, att[i]->attalign); - data_length = VARSIZE(val); - rc = memcpy_s(data, remain_length, val, data_length); + /* 完整的 4 字节头部 varlena 类型,需要对齐 */ + data = (char *)att_align_nominal(data, att[i]->attalign); // 对齐属性 + data_length = VARSIZE(val); // 获取完整 varlena 数据的长度 + rc = memcpy_s(data, remain_length, val, data_length); // 复制数据 securec_check(rc, "\0", "\0"); } } else if (att[i]->attlen == -2) { - /* cstring ... never needs alignment */ - *infomask |= HEAP_HASVARWIDTH; - Assert(att[i]->attalign == 'c'); - data_length = strlen(DatumGetCString(values[i])) + 1; - rc = memcpy_s(data, remain_length, DatumGetPointer(values[i]), data_length); + /* cstring 类型的属性(以空字符结尾的字符串),不需要对齐 */ + *infomask |= HEAP_HASVARWIDTH; // 设置 HEAP_HASVARWIDTH 标志位 + Assert(att[i]->attalign == 'c'); // 断言字符串类型的对齐方式为 'c' + data_length = strlen(DatumGetCString(values[i])) + 1; // 获取字符串数据的长度(包括空字符) + rc = memcpy_s(data, remain_length, DatumGetPointer(values[i]), data_length); // 复制数据 securec_check(rc, "\0", "\0"); } else { - /* fixed-length pass-by-reference */ - data = (char *)att_align_nominal(data, att[i]->attalign); - Assert(att[i]->attlen > 0); - data_length = att[i]->attlen; - rc = memcpy_s(data, remain_length, DatumGetPointer(values[i]), data_length); + /* 固定长度的 pass-by-reference 属性,需要对齐 */ + data = (char *)att_align_nominal(data, att[i]->attalign); // 对齐属性 + Assert(att[i]->attlen > 0); // 断言属性长度为正数 + data_length = att[i]->attlen; // 获取属性数据的长度 + rc = memcpy_s(data, remain_length, DatumGetPointer(values[i]), data_length); // 复制数据 securec_check(rc, "\0", "\0"); } - data += data_length; + data += data_length; // 更新数据指针,指向下一个属性的存储位置 } - Assert((size_t)(data - start) == data_size); + Assert((size_t)(data - start) == data_size); // 断言已经正确填充了指定的数据大小 } + /* ---------------------------------------------------------------- * heap tuple interface * ---------------------------------------------------------------- @@ -257,17 +260,20 @@ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, cha */ bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupDesc) { + // 检查属性编号是否大于元组中的属性数量 if (attnum > (int)HeapTupleHeaderGetNatts(tup->t_data, tupDesc)) { - return true; + return true; // 属性编号大于属性数量,表示属性为空值 } if (attnum > 0) { + // 如果属性编号大于 0,则检查是否存在 NULL 值 if (HeapTupleNoNulls(tup)) { - return false; + return false; // 不存在 NULL 值 } - return att_isnull(((uint)(attnum - 1)), tup->t_data->t_bits); + return att_isnull(((uint)(attnum - 1)), tup->t_data->t_bits); // 通过位图检查是否为 NULL 值 } + // 处理特殊属性编号的情况 switch (attnum) { case TableOidAttributeNumber: case SelfItemPointerAttributeNumber: @@ -281,31 +287,34 @@ bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupDesc) case BucketIdAttributeNumber: case UidAttributeNumber: #endif - /* these are never null */ + /* 这些属性永远不会为空 */ break; default: + // 对于非法的属性编号,报错 ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("invalid attnum: %d", attnum))); } - return false; + return false; // 默认情况下,属性不为空 } + /* get init default value from tupleDesc. * attrinitdefvals of tupleDesc come from the attrinitdefval of pg_attribute */ Datum heapGetInitDefVal(int attNum, TupleDesc tupleDesc, bool *isNull) { - *isNull = true; + *isNull = true; // 默认设置属性为 NULL if (tupleDesc->initdefvals != NULL) { - *isNull = tupleDesc->initdefvals[attNum - 1].isNull; + *isNull = tupleDesc->initdefvals[attNum - 1].isNull; // 获取属性是否为 NULL 值 if (!(*isNull)) { + // 如果属性不为 NULL,则返回默认值 return fetchatt(tupleDesc->attrs[attNum - 1], tupleDesc->initdefvals[attNum - 1].datum); } } - return (Datum)0; + return (Datum)0; // 默认情况下返回 0 } /* Another version of heap_attisnull. Think about attinitdefval of pg_attribute. @@ -314,9 +323,13 @@ Datum heapGetInitDefVal(int attNum, TupleDesc tupleDesc, bool *isNull) */ bool relationAttIsNull(HeapTuple tup, int attNum, TupleDesc tupleDesc) { + // 检查属性编号是否大于元组中的属性数量 if (attNum > (int)HeapTupleHeaderGetNatts(tup->t_data, tupleDesc)) { + // 如果属性编号大于属性数量,检查是否存在默认值 return (tupleDesc->initdefvals == NULL) ? true : tupleDesc->initdefvals[attNum - 1].isNull; } + + // 调用 heap_attisnull 函数检查属性是否为 NULL 值 return heap_attisnull(tup, attNum, tupleDesc); } @@ -547,24 +560,24 @@ Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnu { Datum result; - Assert(tup); + Assert(tup); // 断言元组不为空 - /* Currently, no sys attribute ever reads as NULL. */ - *isnull = false; + /* 当前,没有系统属性会读取为 NULL。 */ + *isnull = false; // 默认设置属性不为空 switch (attnum) { case SelfItemPointerAttributeNumber: - /* pass-by-reference datatype */ - result = PointerGetDatum(&(tup->t_self)); + /* 以引用传递的数据类型 */ + result = PointerGetDatum(&(tup->t_self)); // 返回指向自身的指针 break; case ObjectIdAttributeNumber: - result = ObjectIdGetDatum(HeapTupleGetOid(tup)); + result = ObjectIdGetDatum(HeapTupleGetOid(tup)); // 获取堆元组的 OID break; case MinTransactionIdAttributeNumber: - result = TransactionIdGetDatum(HeapTupleGetRawXmin(tup)); + result = TransactionIdGetDatum(HeapTupleGetRawXmin(tup)); // 获取堆元组的最小事务 ID break; case MaxTransactionIdAttributeNumber: - result = TransactionIdGetDatum(HeapTupleGetRawXmax(tup)); + result = TransactionIdGetDatum(HeapTupleGetRawXmax(tup)); // 获取堆元组的最大事务 ID break; case MinCommandIdAttributeNumber: case MaxCommandIdAttributeNumber: @@ -575,30 +588,29 @@ Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnu * return the "real" cmin or cmax if possible, that is if we are * inside the originating transaction? */ - result = CommandIdGetDatum(HeapTupleHeaderGetRawCommandId(tup->t_data)); + result = CommandIdGetDatum(HeapTupleHeaderGetRawCommandId(tup->t_data)); // 获取堆元组的最小/最大命令 ID break; case TableOidAttributeNumber: - result = ObjectIdGetDatum(tup->t_tableOid); + result = ObjectIdGetDatum(tup->t_tableOid); // 获取堆元组所属表的 OID break; #ifdef PGXC case BucketIdAttributeNumber: - result = ObjectIdGetDatum((uint2)tup->t_bucketId); + result = ObjectIdGetDatum((uint2)tup->t_bucketId); // 获取堆元组的桶 ID break; case XC_NodeIdAttributeNumber: - result = UInt32GetDatum(tup->t_xc_node_id); + result = UInt32GetDatum(tup->t_xc_node_id); // 获取分布式节点 ID break; case UidAttributeNumber: - result = UInt64GetDatum(HeapTupleGetUid(tup)); + result = UInt64GetDatum(HeapTupleGetUid(tup)); // 获取堆元组的 UID break; #endif default: ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("invalid attnum: %d", attnum))); - result = 0; /* keep compiler quiet */ + result = 0; /* 保持编译器静默 */ break; } - return result; + return result; // 返回属性的值 } - /* ---------------- * heap_copytuple && heapCopyCompressedTuple * @@ -622,18 +634,18 @@ HeapTuple heap_copytuple(HeapTuple tuple) errno_t rc = EOK; if (!HeapTupleIsValid(tuple) || tuple->t_data == NULL) { - return NULL; + return NULL; // 如果输入的堆元组无效或为空,则返回空指针 } - Assert(!HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)); + Assert(!HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)); // 断言堆元组没有被压缩 - newTuple = (HeapTuple)palloc(HEAPTUPLESIZE + tuple->t_len); + newTuple = (HeapTuple)palloc(HEAPTUPLESIZE + tuple->t_len); // 分配新的堆元组空间 newTuple->tupTableType = HEAP_TUPLE; newTuple->t_len = tuple->t_len; newTuple->t_self = tuple->t_self; newTuple->t_tableOid = tuple->t_tableOid; newTuple->t_bucketId = tuple->t_bucketId; - HeapTupleCopyBase(newTuple, tuple); + HeapTupleCopyBase(newTuple, tuple); // 复制元组的基本信息 #ifdef PGXC newTuple->t_xc_node_id = tuple->t_xc_node_id; @@ -641,9 +653,10 @@ HeapTuple heap_copytuple(HeapTuple tuple) newTuple->t_data = (HeapTupleHeader)((char *)newTuple + HEAPTUPLESIZE); rc = memcpy_s((char *)newTuple->t_data, tuple->t_len, (char *)tuple->t_data, tuple->t_len); securec_check(rc, "\0", "\0"); - return newTuple; + return newTuple; // 返回复制后的堆元组 } + /* ---------------- * heap_copytuple_with_tuple && heapCopyTupleWithCompressedTuple * @@ -670,12 +683,12 @@ void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest) } /* case 2: copy the normal tuple without compressing */ - Assert(!HEAP_TUPLE_IS_COMPRESSED(src->t_data)); + Assert(!HEAP_TUPLE_IS_COMPRESSED(src->t_data)); // 断言原始堆元组没有被压缩 dest->t_len = src->t_len; dest->t_self = src->t_self; dest->t_tableOid = src->t_tableOid; dest->t_bucketId = src->t_bucketId; - HeapTupleCopyBase(dest, src); + HeapTupleCopyBase(dest, src); // 复制元组的基本信息 #ifdef PGXC dest->t_xc_node_id = src->t_xc_node_id; #endif @@ -684,6 +697,7 @@ void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest) securec_check(rc, "\0", "\0"); } + /* * heap_form_tuple * construct a tuple from the given values[] and isnull[] arrays, @@ -693,16 +707,17 @@ void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest) */ HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull) { - HeapTuple tuple; /* return tuple */ - HeapTupleHeader td; /* tuple data */ + HeapTuple tuple; /* 返回的元组 */ + HeapTupleHeader td; /* 元组数据 */ Size len, data_len; int hoff; - bool hasnull = false; - Form_pg_attribute *att = tupleDescriptor->attrs; - int numberOfAttributes = tupleDescriptor->natts; + bool hasnull = false; // 是否有 NULL 值 + Form_pg_attribute *att = tupleDescriptor->attrs; // 元组描述符的属性数组 + int numberOfAttributes = tupleDescriptor->natts; // 属性的数量 int i; if (numberOfAttributes > MaxTupleAttributeNumber) { + // 如果属性数量超过了最大限制,则报错 ereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("number of columns (%d) exceeds limit (%d)", numberOfAttributes, MaxTupleAttributeNumber))); } @@ -718,19 +733,31 @@ HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull * if an attribute is already toasted, it must have been sent to disk * already and so cannot contain toasted attributes. */ - for (i = 0; i < numberOfAttributes; i++) { - if (isnull[i]) { - hasnull = true; - } else if (att[i]->attlen == -1 && att[i]->attalign == 'd' && att[i]->attndims == 0 && - !VARATT_IS_EXTENDED(DatumGetPointer(values[i]))) { - values[i] = toast_flatten_tuple_attribute(values[i], att[i]->atttypid, att[i]->atttypmod); - } else if (att[i]->attlen == -1 && att[i]->attalign == 'i' && - VARATT_IS_HUGE_TOAST_POINTER(DatumGetPointer(values[i])) && - !(att[i]->atttypid == CLOBOID || att[i]->atttypid == BLOBOID)) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("only suport type(clob/blob) for more than 1G toast"))); - } + // 循环开始,初始化循环变量 i,从 0 开始,逐个处理属性 +for (i = 0; i < numberOfAttributes; i++) { + + // 如果属性 i 是空的(isnull[i] 为真),将 hasnull 标志设置为 true + if (isnull[i]) { + hasnull = true; + } + // 如果属性 i 满足一系列条件:长度为 -1、对齐方式为 'd'、维度为 0, + // 且值不是扩展类型,进行下面的操作 + else if (att[i]->attlen == -1 && att[i]->attalign == 'd' && att[i]->attndims == 0 && + !VARATT_IS_EXTENDED(DatumGetPointer(values[i]))) { + // 使用 toast_flatten_tuple_attribute 函数处理属性值,将其转换为扁平格式 + values[i] = toast_flatten_tuple_attribute(values[i], att[i]->atttypid, att[i]->atttypmod); + } + // 如果属性 i 满足一系列条件:长度为 -1、对齐方式为 'i'、值是巨大的 TOAST 指针, + // 且不是 CLOBOID 或 BLOBOID 类型,抛出错误 + else if (att[i]->attlen == -1 && att[i]->attalign == 'i' && + VARATT_IS_HUGE_TOAST_POINTER(DatumGetPointer(values[i])) && + !(att[i]->atttypid == CLOBOID || att[i]->atttypid == BLOBOID)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("only support type (clob/blob) for more than 1G toast"))); } +} + +// 循环结束 /* * Determine total space needed @@ -766,33 +793,49 @@ HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull * And fill in the information. Note we fill the Datum fields even though * this tuple may never become a Datum. */ - tuple->t_len = len; - ItemPointerSetInvalid(&(tuple->t_self)); - tuple->t_tableOid = InvalidOid; - tuple->t_bucketId = InvalidBktId; - HeapTupleSetZeroBase(tuple); + // 设置 HeapTuple 结构体中的 t_len 字段为给定的 len +tuple->t_len = len; + +// 将 HeapTuple 结构体中的 t_self 字段设置为无效值 +ItemPointerSetInvalid(&(tuple->t_self)); + +// 将 HeapTuple 结构体中的 t_tableOid 和 t_bucketId 字段设置为无效值 +tuple->t_tableOid = InvalidOid; +tuple->t_bucketId = InvalidBktId; + +// 调用 HeapTupleSetZeroBase 函数,将 HeapTuple 结构体中的其他字段初始化为零 +HeapTupleSetZeroBase(tuple); + #ifdef PGXC - tuple->t_xc_node_id = 0; +// 如果定义了 PGXC,将 HeapTuple 结构体中的 t_xc_node_id 字段设置为 0 +tuple->t_xc_node_id = 0; #endif - HeapTupleHeaderSetDatumLength(td, len); - HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid); - HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod); +// 调用 HeapTupleHeaderSetDatumLength、HeapTupleHeaderSetTypeId、HeapTupleHeaderSetTypMod +// 和 HeapTupleHeaderSetNatts 函数,设置 HeapTupleData 结构体中的相关字段 +HeapTupleHeaderSetDatumLength(td, len); +HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid); +HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod); +HeapTupleHeaderSetNatts(td, numberOfAttributes); - HeapTupleHeaderSetNatts(td, numberOfAttributes); - td->t_hoff = hoff; +// 将 HeapTupleData 结构体中的 t_hoff 字段设置为给定的 hoff +td->t_hoff = hoff; - /* else leave infomask = 0 */ - if (tupleDescriptor->tdhasoid) { - td->t_infomask = HEAP_HASOID; - } +// 如果 tupleDescriptor 表示的表有 oid 列,设置 t_infomask 字段的 HEAP_HASOID 位 +if (tupleDescriptor->tdhasoid) { + td->t_infomask = HEAP_HASOID; +} - td->t_infomask &= ~HEAP_UID_MASK; +// 清除 t_infomask 字段的 HEAP_UID_MASK 位 +td->t_infomask &= ~HEAP_UID_MASK; - heap_fill_tuple(tupleDescriptor, values, isnull, (char *)td + hoff, data_len, &td->t_infomask, - (hasnull ? td->t_bits : NULL)); +// 调用 heap_fill_tuple 函数,将属性值、是否为 null、数据存储位置等信息填充到 tuple 中 +heap_fill_tuple(tupleDescriptor, values, isnull, (char *)td + hoff, data_len, &td->t_infomask, + (hasnull ? td->t_bits : NULL)); + +// 返回填充后的 HeapTuple 结构体 +return tuple; - return tuple; } /* @@ -906,28 +949,34 @@ HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replVal * This is deprecated and should not be used in new code, but we keep it * around for use by old add-on modules. */ +// 创建一个函数 heap_modifytuple,它用于修改堆元组(HeapTuple) HeapTuple heap_modifytuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, const char *replNulls, const char *replActions) { - HeapTuple result; - int numberOfAttributes = tupleDesc->natts; - bool *boolNulls = (bool *)palloc(numberOfAttributes * sizeof(bool)); - bool *boolActions = (bool *)palloc(numberOfAttributes * sizeof(bool)); - int attnum; + HeapTuple result; // 存储修改后的结果 + int numberOfAttributes = tupleDesc->natts; // 获取属性的数量 + bool *boolNulls = (bool *)palloc(numberOfAttributes * sizeof(bool)); // 分配用于存储是否为 NULL 的布尔数组 + bool *boolActions = (bool *)palloc(numberOfAttributes * sizeof(bool)); // 分配用于存储替换操作的布尔数组 + int attnum; // 用于循环的属性号 + // 遍历每个属性 for (attnum = 0; attnum < numberOfAttributes; attnum++) { - boolNulls[attnum] = (replNulls[attnum] == 'n'); - boolActions[attnum] = (replActions[attnum] == 'r'); + boolNulls[attnum] = (replNulls[attnum] == 'n'); // 如果 replNulls 中对应位置为 'n',则设置为 true,表示为 NULL + boolActions[attnum] = (replActions[attnum] == 'r'); // 如果 replActions 中对应位置为 'r',则设置为 true,表示进行替换 } + // 调用函数 heap_modify_tuple 来修改元组 result = heap_modify_tuple(tuple, tupleDesc, replValues, boolNulls, boolActions); + // 释放分配的布尔数组内存 pfree(boolNulls); pfree(boolActions); + // 返回修改后的结果元组 return result; } + /* * heap_deform_tuple * Given a tuple, extract data into values/isnull arrays; this is @@ -1071,50 +1120,55 @@ void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool * This is deprecated and should not be used in new code, but we keep it * around for use by old add-on modules. */ +// 定义函数 heap_deformtuple,用于将堆元组解构为属性值和 NULL 标志 void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, char *nulls) { - int natts = tupleDesc->natts; - bool *boolNulls = (bool *)palloc(natts * sizeof(bool)); + int natts = tupleDesc->natts; // 获取属性数量 + bool *boolNulls = (bool *)palloc(natts * sizeof(bool)); // 分配用于存储是否为 NULL 的布尔数组 int attnum; + // 调用 heap_deform_tuple 函数,将堆元组解构为属性值和 NULL 标志 heap_deform_tuple(tuple, tupleDesc, values, boolNulls); + // 遍历每个属性 for (attnum = 0; attnum < natts; attnum++) { + // 根据布尔数组中的值,将 NULL 标志设置为 'n'(表示为 NULL)或 ' '(表示非 NULL) nulls[attnum] = (boolNulls[attnum] ? 'n' : ' '); } + // 释放分配的布尔数组内存 pfree(boolNulls); } +// 定义函数 slot_deform_cmprs_tuple,该函数未在提供的代码中实现 static void slot_deform_cmprs_tuple(TupleTableSlot *slot, uint32 natts); +// 定义函数 deform_next_attribute,该函数似乎用于处理下一个属性的偏移 static void deform_next_attribute(bool& slow, long& off, Form_pg_attribute thisatt, char* tp) { + // 如果不是 slow 模式且该属性有缓存偏移值,则使用缓存偏移值 if (!slow && thisatt->attcacheoff >= 0) { off = thisatt->attcacheoff; - } else if (thisatt->attlen == -1) { - /* - * We can only cache the offset for a varlena attribute if the - * offset is already suitably aligned, so that there would be no - * pad bytes in any case: then the offset will be valid for either - * an aligned or unaligned value. - */ + } + // 如果属性长度为 -1(VARLENA 类型) + else if (thisatt->attlen == -1) { + // 如果不是 slow 模式且当前偏移值是对齐的,设置缓存偏移值;否则,进入 slow 模式 if (!slow && (uintptr_t)(off) == att_align_nominal(off, thisatt->attalign)) { thisatt->attcacheoff = off; } else { off = att_align_pointer(off, thisatt->attalign, -1, tp + off); slow = true; } - } else { - /* not varlena, so safe to use att_align_nominal */ + } + // 对于其他情况,根据属性的对齐方式进行处理 + else { off = att_align_nominal(off, thisatt->attalign); - + // 如果不是 slow 模式,设置缓存偏移值 if (!slow) { thisatt->attcacheoff = off; } } } - /* * slot_deform_tuple * Given a TupleTableSlot, extract data from the slot's physical tuple @@ -1162,33 +1216,35 @@ static void slot_deform_tuple(TupleTableSlot *slot, uint32 natts) * Ustore has different alignment rules so we force slow = true here. * See the comments in heap_deform_tuple() for more information. */ - slow = heapToUHeap ? true : slow; + // 这部分代码中的变量和逻辑是与元组的解构有关的 - tp = (char *)tup + tup->t_hoff; +slow = heapToUHeap ? true : slow; // 如果 heapToUHeap 为真,则将 slow 设置为 true,否则保持 slow 的值不变 - for (; attnum < natts; attnum++) { - Form_pg_attribute thisatt = att[attnum]; +tp = (char *)tup + tup->t_hoff; // 计算指向元组数据的指针,考虑到元组的偏移量 - if (hasnulls && att_isnull(attnum, bp)) { - values[attnum] = (Datum)0; - isnull[attnum] = true; - slow = true; /* can't use attcacheoff anymore */ - continue; - } +for (; attnum < natts; attnum++) { // 遍历属性 + Form_pg_attribute thisatt = att[attnum]; // 获取当前属性的描述信息 - isnull[attnum] = false; - - deform_next_attribute(slow, off, thisatt, tp); - - values[attnum] = fetchatt(thisatt, tp + off); - - off = att_addlength_pointer(off, thisatt->attlen, tp + off); - - if (thisatt->attlen <= 0) { - slow = true; /* can't use attcacheoff anymore */ - } + if (hasnulls && att_isnull(attnum, bp)) { // 如果元组具有 NULL 值,并且当前属性为 NULL + values[attnum] = (Datum)0; // 设置属性值为 0(这通常用于表示 NULL) + isnull[attnum] = true; // 将属性的 NULL 标志设置为 true + slow = true; // 进入 slow 模式 + continue; // 继续下一个属性的处理 } + isnull[attnum] = false; // 将属性的 NULL 标志设置为 false + + deform_next_attribute(slow, off, thisatt, tp); // 调用 deform_next_attribute 函数处理下一个属性的偏移 + + values[attnum] = fetchatt(thisatt, tp + off); // 获取属性值 + + off = att_addlength_pointer(off, thisatt->attlen, tp + off); // 更新偏移以指向下一个属性 + + if (thisatt->attlen <= 0) { // 如果属性长度小于等于 0,则进入 slow 模式 + slow = true; + } +} + /* * Save state for next execution */ @@ -1275,47 +1331,53 @@ static void slot_deform_batch(TupleTableSlot *slot, VectorBatch* batch, int cur_ * buff is the input message buff. * len is length of anyarray in buff. */ -static void slot_extract_anyarray_from_buff(TupleTableSlot *slot, int index, const StringInfo buffer, int len, - bool need_transform_anyarray) -{ - char *pstr = NULL; - Datum array_datum; - Size data_length; - errno_t rc = EOK; - Form_pg_attribute *att = slot->tts_tupleDescriptor->attrs; - int attnum = slot->tts_tupleDescriptor->natts; +// 定义函数 slot_extract_anyarray_from_buff,用于从缓冲区中提取数据并处理为数组 - if (index >= attnum) { - ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("index is not correct"))); - } +char *pstr = NULL; // 创建一个字符指针 pstr,用于存储提取的字符串数据 +Datum array_datum; // 创建一个变量 array_datum,用于存储数组的 Datum 数据类型 +Size data_length; // 用于存储数据长度 +errno_t rc = EOK; // 用于存储函数调用的错误码 +Form_pg_attribute *att = slot->tts_tupleDescriptor->attrs; // 获取 TupleTableSlot 中的属性描述符数组 +int attnum = slot->tts_tupleDescriptor->natts; // 获取属性数量 - /* If from remote cn, the datatype in buffer is string, not varattrib, so we should not use it as varattrib */ - if ((att[index]->attlen == -1) && (!need_transform_anyarray)) { - data_length = VARSIZE_ANY(buffer->data); - if (data_length <= (Size)((uint32)len + 1)) { - data_length = len + 1; - } - } else { +if (index >= attnum) { + ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("index is not correct"))); +} +// 如果提供的索引大于或等于属性数量,抛出错误 + +if ((att[index]->attlen == -1) && (!need_transform_anyarray)) { + // 如果属性是 VARLENA(长度为 -1)并且不需要转换为任意数组 + data_length = VARSIZE_ANY(buffer->data); // 获取缓冲区中数据的长度 + if (data_length <= (Size)((uint32)len + 1)) { data_length = len + 1; } +} else { + data_length = len + 1; +} +// 根据属性的特性计算数据长度 - if (data_length > MaxAllocSize) { - ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("data length is not correct"))); - } - pstr = (char *)palloc0(data_length); - rc = memcpy_s(pstr, len + 1, buffer->data, len); - securec_check(rc, "", ""); - pstr[len] = '\0'; - array_datum = (Datum)pstr; - if (need_transform_anyarray) { - array_datum = OidFunctionCall3Coll(ANYARRAYINFUNCOID, InvalidOid, CStringGetDatum(pstr), - UInt32GetDatum(CSTRINGOID), - Int32GetDatum(slot->tts_tupleDescriptor->tdtypmod)); - pfree_ext(pstr); - } - slot->tts_values[index] = array_datum; +if (data_length > MaxAllocSize) { + ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("data length is not correct"))); +} +// 如果数据长度超过最大可分配大小,抛出错误 - return; +pstr = (char *)palloc0(data_length); // 分配内存以存储提取的数据 +rc = memcpy_s(pstr, len + 1, buffer->data, len); // 复制数据到 pstr +securec_check(rc, "", ""); +pstr[len] = '\0'; // 在数据末尾添加终止符 +array_datum = (Datum)pstr; // 将 pstr 转换为 Datum 数据类型 + +if (need_transform_anyarray) { + // 如果需要将数据转换为任意数组 + array_datum = OidFunctionCall3Coll(ANYARRAYINFUNCOID, InvalidOid, CStringGetDatum(pstr), + UInt32GetDatum(CSTRINGOID), + Int32GetDatum(slot->tts_tupleDescriptor->tdtypmod)); + pfree_ext(pstr); // 释放 pstr 的内存 +} +// 将转换后的数组存储在 TupleTableSlot 的指定索引位置 +slot->tts_values[index] = array_datum; + +return; } /* @@ -1324,96 +1386,98 @@ static void slot_extract_anyarray_from_buff(TupleTableSlot *slot, int index, con * We always extract all atributes, as specified in tts_tupleDescriptor, * because there is no easy way to find random attribute in the DataRow. */ -static void slot_deform_datarow(TupleTableSlot *slot, bool need_transform_anyarray) -{ - int attnum; - int i; - int col_count; - char *cur = slot->tts_dataRow; - StringInfo buffer; - uint16 n16; - uint32 n32; - MemoryContext oldcontext; - errno_t rc = EOK; +// 定义函数 slot_deform_datarow,用于解析数据行并填充 TupleTableSlot - if (slot->tts_tupleDescriptor == NULL || slot->tts_dataRow == NULL) { - return; - } +int attnum; // 属性数量 +int i; +int col_count; +char *cur = slot->tts_dataRow; // 指向数据行的当前位置 +StringInfo buffer; // 用于存储数据的 StringInfo 结构 +uint16 n16; +uint32 n32; +MemoryContext oldcontext; +errno_t rc = EOK; - Form_pg_attribute *att = slot->tts_tupleDescriptor->attrs; - attnum = slot->tts_tupleDescriptor->natts; +// 检查输入的 TupleTableSlot 是否为空或数据行是否为空,如果为空则返回 +if (slot->tts_tupleDescriptor == NULL || slot->tts_dataRow == NULL) { + return; +} - /* fastpath: exit if values already extracted */ - if (slot->tts_nvalid == attnum) { - return; - } +Form_pg_attribute *att = slot->tts_tupleDescriptor->attrs; // 获取属性描述符数组 +attnum = slot->tts_tupleDescriptor->natts; // 获取属性数量 - Assert(slot->tts_dataRow); +// 如果已经解析的属性数量等于总属性数量,表示数据已经解析完毕,直接返回 +if (slot->tts_nvalid == attnum) { + return; +} - rc = memcpy_s(&n16, sizeof(uint16), cur, 2); +Assert(slot->tts_dataRow); // 断言数据行不为空 + +rc = memcpy_s(&n16, sizeof(uint16), cur, 2); // 从数据行中复制一个 16 位整数(网络字节序)到 n16 +securec_check(rc, "\0", "\0"); +cur += 2; +col_count = ntohs(n16); // 将网络字节序的整数转换为主机字节序 +if (col_count != attnum) { + ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("Tuple does not match the descriptor"))); +} +// 检查列数是否与属性数量匹配,如果不匹配,抛出错误 + +oldcontext = MemoryContextSwitchTo(slot->tts_mcxt); + +// 获取属性元数据,用于数据转换 +if (slot->tts_attinmeta == NULL) { + slot->tts_attinmeta = TupleDescGetAttInMetadata(slot->tts_tupleDescriptor); +} + +// 创建用于存储每个元组的内存上下文 +if (slot->tts_per_tuple_mcxt == NULL) { + slot->tts_per_tuple_mcxt = AllocSetContextCreate(slot->tts_mcxt, "SlotPerTupleMcxt", ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); +} + +MemoryContextSwitchTo(slot->tts_per_tuple_mcxt); +buffer = makeStringInfo(); // 创建 StringInfo 结构,用于存储数据 + +for (i = 0; i < attnum; i++) { + int len; + + rc = memcpy_s(&n32, sizeof(uint32), cur, 4); // 从数据行中复制一个 32 位整数(网络字节序)到 n32 securec_check(rc, "\0", "\0"); - cur += 2; - col_count = ntohs(n16); - if (col_count != attnum) { - ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("Tuple does not match the descriptor"))); - } + cur += 4; + len = ntohl(n32); // 将网络字节序的整数转换为主机字节序 - /* - * Ensure info about input functions is available as long as slot lives - * as well as deformed values - */ - oldcontext = MemoryContextSwitchTo(slot->tts_mcxt); + if (len == -1) { // 如果长度为 -1,表示属性为 NULL + slot->tts_values[i] = (Datum)0; + slot->tts_isnull[i] = true; + } else { + appendBinaryStringInfo(buffer, cur, len); // 将数据添加到 buffer 中 + cur += len; - if (slot->tts_attinmeta == NULL) { - slot->tts_attinmeta = TupleDescGetAttInMetadata(slot->tts_tupleDescriptor); - } - - if (slot->tts_per_tuple_mcxt == NULL) { - slot->tts_per_tuple_mcxt = AllocSetContextCreate(slot->tts_mcxt, "SlotPerTupleMcxt", ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - } - - MemoryContextSwitchTo(slot->tts_per_tuple_mcxt); - buffer = makeStringInfo(); - for (i = 0; i < attnum; i++) { - int len; - - /* get size */ - rc = memcpy_s(&n32, sizeof(uint32), cur, 4); - securec_check(rc, "\0", "\0"); - cur += 4; - len = ntohl(n32); - /* get data */ - if (len == -1) { - slot->tts_values[i] = (Datum)0; - slot->tts_isnull[i] = true; + if (att[i]->atttypid == ANYARRAYOID) { + // 如果属性类型为 ANYARRAY,调用 slot_extract_anyarray_from_buff 函数进行解析 + slot_extract_anyarray_from_buff(slot, i, buffer, len, need_transform_anyarray); } else { - appendBinaryStringInfo(buffer, cur, len); - cur += len; - if (att[i]->atttypid == ANYARRAYOID) { - /* For anyarray, it need more information to handle it, so leave it, just copy */ - slot_extract_anyarray_from_buff(slot, i, buffer, len, need_transform_anyarray); - } else { - /* for abstimein, transfer str to time in select has some problem, so distinguish - * insert and select for ABSTIMEIN to avoid problem */ - t_thrd.time_cxt.is_abstimeout_in = true; - - slot->tts_values[i] = InputFunctionCall(slot->tts_attinmeta->attinfuncs + i, buffer->data, - slot->tts_attinmeta->attioparams[i], - slot->tts_attinmeta->atttypmods[i]); - t_thrd.time_cxt.is_abstimeout_in = false; - } - slot->tts_isnull[i] = false; - - resetStringInfo(buffer); + // 否则,调用相应的输入函数进行数据转换 + t_thrd.time_cxt.is_abstimeout_in = true; + slot->tts_values[i] = InputFunctionCall(slot->tts_attinmeta->attinfuncs + i, buffer->data, + slot->tts_attinmeta->attioparams[i], + slot->tts_attinmeta->atttypmods[i]); + t_thrd.time_cxt.is_abstimeout_in = false; } + slot->tts_isnull[i] = false; // 设置属性的 NULL 标志为 false + + resetStringInfo(buffer); // 重置 buffer } - pfree(buffer->data); - pfree(buffer); +} - slot->tts_nvalid = attnum; +// 释放 buffer 相关的内存 +pfree(buffer->data); +pfree(buffer); - MemoryContextSwitchTo(oldcontext); +// 设置已解析的属性数量 +slot->tts_nvalid = attnum; + +MemoryContextSwitchTo(oldcontext); } #endif @@ -1615,24 +1679,27 @@ void heap_slot_getallattrs(TupleTableSlot *slot, bool need_transform_anyarray) slot->tts_nvalid = tdesc_natts; } +// 定义函数 GetAttrNumber,用于获取指定属性的属性编号 static inline int GetAttrNumber(TupleTableSlot* slot, int attnum) { /* Check for caller error */ + // 检查调用者是否出错 if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("invalid attribute number %d", attnum))); } /* internal error */ + // 内部错误,如果 TupleTableSlot 中的元组为空 if (slot->tts_tuple == NULL) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot extract attribute from empty tuple slot"))); } - HeapTuple tuple = (HeapTuple)slot->tts_tuple; - int attno = HeapTupleHeaderGetNatts(tuple->t_data, slot->tts_tupleDescriptor); - attno = Min(attno, attnum); + HeapTuple tuple = (HeapTuple)slot->tts_tuple; // 将 TupleTableSlot 中的元组转换为 HeapTuple + int attno = HeapTupleHeaderGetNatts(tuple->t_data, slot->tts_tupleDescriptor); // 获取元组中的属性数量 + attno = Min(attno, attnum); // 取较小的值作为属性编号 - return attno; + return attno; // 返回属性编号 } void heap_slot_formbatch(TupleTableSlot* slot, VectorBatch* batch, int cur_rows, int attnum) @@ -1933,28 +2000,30 @@ MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup) * The HeapTuple struct, tuple header, and tuple data are all allocated * as a single palloc() block. */ +// 定义函数 heap_tuple_from_minimal_tuple,用于从 MinimalTuple 创建一个 HeapTuple HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup) { - HeapTuple result; - uint32 len = mtup->t_len + MINIMAL_TUPLE_OFFSET; + HeapTuple result; // 用于存储创建的 HeapTuple + uint32 len = mtup->t_len + MINIMAL_TUPLE_OFFSET; // 计算 HeapTuple 的长度 errno_t rc = EOK; - result = (HeapTuple)heaptup_alloc(HEAPTUPLESIZE + len); - result->t_len = len; - ItemPointerSetInvalid(&(result->t_self)); - result->t_tableOid = InvalidOid; - result->t_bucketId = InvalidBktId; - HeapTupleSetZeroBase(result); + result = (HeapTuple)heaptup_alloc(HEAPTUPLESIZE + len); // 分配内存用于 HeapTuple + result->t_len = len; // 设置 HeapTuple 的长度 + ItemPointerSetInvalid(&(result->t_self)); // 设置 HeapTuple 的 t_self 为无效值 + result->t_tableOid = InvalidOid; // 设置 HeapTuple 的表OID为无效值 + result->t_bucketId = InvalidBktId; // 设置 HeapTuple 的桶ID为无效值 + HeapTupleSetZeroBase(result); // 设置 HeapTuple 的基数为零 #ifdef PGXC - result->t_xc_node_id = 0; + result->t_xc_node_id = 0; // 设置 HeapTuple 的节点ID为零(如果支持 PGXC) #endif - result->t_data = (HeapTupleHeader)((char *)result + HEAPTUPLESIZE); - rc = memcpy_s((char *)result->t_data + MINIMAL_TUPLE_OFFSET, mtup->t_len, mtup, mtup->t_len); + result->t_data = (HeapTupleHeader)((char *)result + HEAPTUPLESIZE); // 设置 HeapTuple 的数据指针 + rc = memcpy_s((char *)result->t_data + MINIMAL_TUPLE_OFFSET, mtup->t_len, mtup, mtup->t_len); // 复制数据到 HeapTuple securec_check(rc, "\0", "\0"); rc = memset_s(result->t_data, offsetof(HeapTupleHeaderData, t_infomask2), 0, - offsetof(HeapTupleHeaderData, t_infomask2)); + offsetof(HeapTupleHeaderData, t_infomask2)); // 初始化 HeapTuple 的一部分数据 securec_check(rc, "\0", "\0"); - return result; + + return result; // 返回创建的 HeapTuple } /* -- 2.34.1 From 37ed778a4031b3c89d62e65dee61bdc3f4d7f609 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 21:01:17 +0800 Subject: [PATCH 02/19] Enter --- .../storage/access/common/heaptuple.cpp | 499 +++++++++--------- 1 file changed, 243 insertions(+), 256 deletions(-) diff --git a/src/gausskernel/storage/access/common/heaptuple.cpp b/src/gausskernel/storage/access/common/heaptuple.cpp index b3e6a71ae..be50eec79 100644 --- a/src/gausskernel/storage/access/common/heaptuple.cpp +++ b/src/gausskernel/storage/access/common/heaptuple.cpp @@ -182,7 +182,7 @@ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, cha continue; } - *bitP |= bitmask; // ���置位图中的位 + *bitP |= bitmask; // �����置位图中的位 } @@ -2150,202 +2150,198 @@ static void heap_fill_bitmap(char *buf, const bool *flags, int nflag) static void heap_fill_cmprs_tuple(TupleDesc tupleDesc, FormCmprTupleData *cmprsInfo, char *data, Size data_size, uint16 *infomask, bits8 *bit) { - bits8 *bitP = NULL; - uint32 bitmask; - int i; - int numberOfAttributes = tupleDesc->natts; - errno_t retno = EOK; - Form_pg_attribute *att = tupleDesc->attrs; - char *start = data; + bits8 *bitP = NULL; // 用于处理位图(bitmap)的指针 + uint32 bitmask; // 用于位操作的掩码 + int i; // 循环计数器 + int numberOfAttributes = tupleDesc->natts; // 元组中属性的数量 + errno_t retno = EOK; // 用于错误检查的返回值 + Form_pg_attribute *att = tupleDesc->attrs; // 属性描述数组的指针 + char *start = data; // 数据的起始位置 /* compression-bitmap MUST be put firstly. * refer to heap_compute_cmprs_data_size() */ + // 压缩位图必须首先放置在数据中,这是元组压缩的一部分 heap_fill_bitmap(data, cmprsInfo->compressed, numberOfAttributes); - data += (int16)BITMAPLEN(numberOfAttributes); + data += (int16)BITMAPLEN(numberOfAttributes); // 更新数据指针以跳过位图数据 if (bit != NULL) { - bitP = &bit[-1]; - bitmask = HIGHBIT; + bitP = &bit[-1]; // 用于位操作的指针,初始化为bit的前一个位置 + bitmask = HIGHBIT; // 位掩码初始化为最高位 } else { /* just to keep compiler quiet */ + // 仅用于使编译器不报错,不实际使用 bitP = NULL; bitmask = 0; } - *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL | HEAP_COMPRESSED); + *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL | HEAP_COMPRESSED); // 初始化infomask标志 for (i = 0; i < numberOfAttributes; i++) { Size data_length; - Size remian_length = data_size - (size_t)(data - start); + Size remain_length = data_size - (size_t)(data - start); // 剩余可用空间大小 /* NULLs first: no compression, no storage. */ + // 首先处理NULL值:不进行压缩和存储 if (bit != NULL) { if (bitmask != HIGHBIT) { - bitmask <<= 1; + bitmask <<= 1; // 移动位掩码到下一位 } else { - bitP += 1; - *bitP = 0x0; - bitmask = 1; + bitP += 1; // 移动到下一个字节 + *bitP = 0x0; // 重置位掩码为0 + bitmask = 1; // 位掩码重新初始化为1 } if (cmprsInfo->isnulls[i]) { - *infomask |= HEAP_HASNULL; - continue; + *infomask |= HEAP_HASNULL; // 设置相应的标志位表示有NULL值 + continue; // 继续下一轮循环 } - *bitP |= bitmask; + *bitP |= bitmask; // 设置位图中的相应位表示没有NULL值 } - /* compression second: specail value, special size, and special alligned. */ if (cmprsInfo->compressed[i]) { - /* Important: compressed value is passed by pointer (char*) */ - Assert(!cmprsInfo->isnulls[i]); + // 处理压缩的属性 + Assert(!cmprsInfo->isnulls[i]); // 断言没有NULL值 if (cmprsInfo->valsize[i] != 0) { - Assert(cmprsInfo->valsize[i] > 0); - Pointer val = DatumGetPointer(cmprsInfo->values[i]); - retno = memcpy_s(data, remian_length, val, cmprsInfo->valsize[i]); - securec_check(retno, "\0", "\0"); - data += cmprsInfo->valsize[i]; + Assert(cmprsInfo->valsize[i] > 0); // 断言值大小大于0 + Pointer val = DatumGetPointer(cmprsInfo->values[i]); // 获取值的指针 + retno = memcpy_s(data, remain_length, val, cmprsInfo->valsize[i]); // 复制数据到元组中 + securec_check(retno, "\0", "\0"); // 安全检查 + data += cmprsInfo->valsize[i]; // 更新数据指针 } - *infomask |= HEAP_COMPRESSED; + *infomask |= HEAP_COMPRESSED; // 设置相应的标志位表示属性是压缩的 - /* Important: we must keep setting flag */ if (att[i]->attlen < 0) { - Assert((-1 == att[i]->attlen) || (-2 == att[i]->attlen)); - *infomask |= HEAP_HASVARWIDTH; + Assert((-1 == att[i]->attlen) || (-2 == att[i]->attlen)); // 断言属性长度是-1或-2 + *infomask |= HEAP_HASVARWIDTH; // 设置相应的标志位表示属性是变宽度的 } - continue; + continue; // 继续下一轮循环 } - /* - * XXX we use the att_align macros on the pointer value itself, not on - * an offset. This is a bit of a hack. - */ if (att[i]->attbyval) { - /* pass-by-value */ - data = (char *)att_align_nominal(data, att[i]->attalign); - store_att_byval(data, cmprsInfo->values[i], att[i]->attlen); - data_length = att[i]->attlen; + // 处理传值属性 + data = (char *)att_align_nominal(data, att[i]->attalign); // 对齐数据 + store_att_byval(data, cmprsInfo->values[i], att[i]->attlen); // 存储传值属性 + data_length = att[i]->attlen; // 数据长度等于属性长度 } else if (att[i]->attlen == -1) { - /* varlena */ - Pointer val = DatumGetPointer(cmprsInfo->values[i]); + // 处理变宽度属性 + Pointer val = DatumGetPointer(cmprsInfo->values[i]); // 获取值的指针 - *infomask |= HEAP_HASVARWIDTH; + *infomask |= HEAP_HASVARWIDTH; // 设置相应的标志位表示属性是变宽度的 if (VARATT_IS_EXTERNAL(val)) { - *infomask |= HEAP_HASEXTERNAL; - /* no alignment, since it's short by definition */ - data_length = VARSIZE_EXTERNAL(val); - retno = memcpy_s(data, remian_length, val, data_length); - securec_check(retno, "\0", "\0"); + *infomask |= HEAP_HASEXTERNAL; // 设置相应的标志位表示属性是外部存储的 + data_length = VARSIZE_EXTERNAL(val); // 获取外部存储数据的长度 + retno = memcpy_s(data, remain_length, val, data_length); // 复制数据到元组中 + securec_check(retno, "\0", "\0"); // 安全检查 } else if (VARATT_IS_SHORT(val)) { - /* no alignment for short varlenas */ - data_length = VARSIZE_SHORT(val); - retno = memcpy_s(data, remian_length, val, data_length); - securec_check(retno, "\0", "\0"); + data_length = VARSIZE_SHORT(val); // 获取短数据的长度 + retno = memcpy_s(data, remain_length, val, data_length); // 复制数据到元组中 + securec_check(retno, "\0", "\0"); // 安全检查 } else if (VARLENA_ATT_IS_PACKABLE(att[i]) && VARATT_CAN_MAKE_SHORT(val)) { - /* convert to short varlena -- no alignment */ - data_length = VARATT_CONVERTED_SHORT_SIZE(val); - SET_VARSIZE_SHORT(data, data_length); - retno = memcpy_s(data + 1, remian_length - 1, VARDATA(val), data_length - 1); - securec_check(retno, "\0", "\0"); + // 处理可以打包为短数据的变宽度属性 + data_length = VARATT_CONVERTED_SHORT_SIZE(val); // 获取打包后的短数据的长度 + SET_VARSIZE_SHORT(data, data_length); // 设置短数据的长度 + retno = memcpy_s(data + 1, remain_length - 1, VARDATA(val), data_length - 1); // 复制数据到元组中 + securec_check(retno, "\0", "\0"); // 安全检查 } else { - /* full 4-byte header varlena */ - data = (char *)att_align_nominal(data, att[i]->attalign); - data_length = VARSIZE(val); - retno = memcpy_s(data, remian_length, val, data_length); - securec_check(retno, "\0", "\0"); + data = (char *)att_align_nominal(data, att[i]->attalign); // 对齐数据 + data_length = VARSIZE(val); // 获取变宽度数据的长度 + retno = memcpy_s(data, remain_length, val, data_length); // 复制数据到元组中 + securec_check(retno, "\0", "\0"); // 安全检查 } } else if (att[i]->attlen == -2) { - /* cstring ... never needs alignment */ - *infomask |= HEAP_HASVARWIDTH; - Assert(att[i]->attalign == 'c'); - data_length = strlen(DatumGetCString(cmprsInfo->values[i])) + 1; - retno = memcpy_s(data, remian_length, DatumGetPointer(cmprsInfo->values[i]), data_length); - securec_check(retno, "\0", "\0"); + // 处理C字符串属性 + *infomask |= HEAP_HASVARWIDTH; // 设置相应的标志位表示属性是变宽度的 + Assert(att[i]->attalign == 'c'); // 断言属性对齐方式为'C' + data_length = strlen(DatumGetCString(cmprsInfo->values[i])) + 1; // 获取C字符串的长度 + retno = memcpy_s(data, remain_length, DatumGetPointer(cmprsInfo->values[i]), data_length); // 复制数据到元组中 + securec_check(retno, "\0", "\0"); // 安全检查 } else { - /* fixed-length pass-by-reference */ - data = (char *)att_align_nominal(data, att[i]->attalign); - Assert(att[i]->attlen > 0); - data_length = att[i]->attlen; - retno = memcpy_s(data, remian_length, DatumGetPointer(cmprsInfo->values[i]), data_length); - securec_check(retno, "\0", "\0"); + // 处理定长属性 + data = (char *)att_align_nominal(data, att[i]->attalign); // 对齐数据 + Assert(att[i]->attlen > 0); // 断言属性长度大于0 + data_length = att[i]->attlen; // 数据长度等于属性长度 + retno = memcpy_s(data, remain_length, DatumGetPointer(cmprsInfo->values[i]), data_length); // 复制数据到元组中 + securec_check(retno, "\0", "\0"); // 安全检查 } - data += data_length; + data += data_length; // 更新数据指针 } - Assert((size_t)(data - start) == data_size); + Assert((size_t)(data - start) == data_size); // 断言填充后的数据长度等于预期的数据长度 } Datum nocache_cmprs_get_attr(HeapTuple tuple, unsigned int attnum, TupleDesc tupleDesc, char *cmprsInfo) { - HeapTupleHeader tup = tuple->t_data; - Form_pg_attribute *att = tupleDesc->attrs; - char *tp = NULL; /* ptr to data part of tuple */ - bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ - bits8 *cmprsBitmap = NULL; - int off = 0; /* current offset within data */ - uint32 i = 0; + HeapTupleHeader tup = tuple->t_data; // 获取堆元组的头部信息 + Form_pg_attribute *att = tupleDesc->attrs; // 获取元组描述中的属性信息数组 + char *tp = NULL; // 用于指向元组数据的指针 + bits8 *bp = tup->t_bits; // 用于处理位图的指针,表示哪些属性为NULL + bits8 *cmprsBitmap = NULL; // 用于处理压缩位图的指针,表示哪些属性被压缩 + int off = 0; // 偏移量,用于定位属性数据的位置 + uint32 i = 0; // 循环计数器 - Assert(HEAP_TUPLE_IS_COMPRESSED(tup) && (cmprsInfo != NULL)); - Assert(attnum <= HeapTupleHeaderGetNatts(tup, tupleDesc)); - Assert(tupleDesc->natts >= (int)HeapTupleHeaderGetNatts(tup, tupleDesc)); + Assert(HEAP_TUPLE_IS_COMPRESSED(tup) && (cmprsInfo != NULL)); // 断言元组是压缩的且cmprsInfo不为空 + Assert(attnum <= HeapTupleHeaderGetNatts(tup, tupleDesc)); // 断言要获取的属性序号不超过元组中的属性数量 + Assert(tupleDesc->natts >= (int)HeapTupleHeaderGetNatts(tup, tupleDesc)); // 断言元组描述中的属性数量不小于实际元组中的属性数量 - attnum--; - tp = (char *)tup + tup->t_hoff; - cmprsBitmap = (bits8 *)tp; - off = BITMAPLEN(HeapTupleHeaderGetNatts(tup, tupleDesc)); + attnum--; // 转换属性序号为0-based - int cmprsOff = 0; /* pointer to the start of compression meta */ - void *metaInfo = NULL; - char mode = 0; + tp = (char *)tup + tup->t_hoff; // 初始化指向元组数据的指针 + cmprsBitmap = (bits8 *)tp; // 初始化指向压缩位图的指针 + off = BITMAPLEN(HeapTupleHeaderGetNatts(tup, tupleDesc)); // 计算位图占用的字节数 - for (i = 0;; i++) { /* loop exit is at "break" */ - Assert(i < HeapTupleHeaderGetNatts(tup, tupleDesc)); + int cmprsOff = 0; // 压缩信息的偏移量 + void *metaInfo = NULL; // 压缩元数据的指针 + char mode = 0; // 压缩模式 + + for (i = 0;; i++) { // 开始循环,遍历元组中的属性 + + Assert(i < HeapTupleHeaderGetNatts(tup, tupleDesc)); // 断言属性序号不超过元组中的属性数量 - /* first parse compression metaInfo data of this attr */ int metaSize = 0; metaInfo = PageCompress::FetchAttrCmprMeta(cmprsInfo + cmprsOff, att[i]->attlen, &metaSize, &mode); - cmprsOff += metaSize; + cmprsOff += metaSize; // 获取压缩元数据信息,更新偏移量 if (HeapTupleHasNulls(tuple) && att_isnull(i, bp)) { - continue; /* this cannot be the target att */ + continue; // 如果元组中有NULL,并且当前属性为NULL,继续下一个属性 } if (isAttrCompressed(i, cmprsBitmap)) { if (attnum != i) { off += PageCompress::GetAttrCmprValSize(mode, att[i]->attlen, metaInfo, tp + off); - continue; + continue; // 如果当前属性被压缩,但不是目标属性,则跳过 } - - break; + break; // 如果找到目标属性,跳出循环 } if (att[i]->attlen == -1) { - off = att_align_pointer((uint32)off, att[i]->attalign, -1, tp + off); + off = att_align_pointer((uint32)off, att[i]->attalign, -1, tp + off); // 处理变宽度属性的对齐 } else { - off = att_align_nominal((uint32)off, att[i]->attalign); + off = att_align_nominal((uint32)off, att[i]->attalign); // 处理非变宽度属性的对齐 } if (i == attnum) { - break; + break; // 如果找到目标属性,跳出循环 } - off = att_addlength_pointer(off, att[i]->attlen, tp + off); + off = att_addlength_pointer(off, att[i]->attlen, tp + off); // 计算下一个属性的偏移量 } - Assert(attnum == i); + Assert(attnum == i); // 断言已找到目标属性 if (isAttrCompressed(attnum, cmprsBitmap)) { int attsize = 0; - Datum attr_val = PageCompress::UncompressOneAttr(mode, metaInfo, att[i]->atttypid, att[i]->attlen, tp + off, - &attsize); - return attr_val; + Datum attr_val = PageCompress::UncompressOneAttr(mode, metaInfo, att[i]->atttypid, att[i]->attlen, tp + off, &attsize); // 解压缩属性值 + return attr_val; // 返回解压缩后的属性值 } - return fetchatt(att[attnum], tp + off); + return fetchatt(att[attnum], tp + off); // 如果属性没有被压缩,则从元组中获取属性值并返回 } + + + /* * HeapUncompressTup * Uncompress tuple into destTup @@ -2354,193 +2350,184 @@ Datum nocache_cmprs_get_attr(HeapTuple tuple, unsigned int attnum, TupleDesc tup template static HeapTuple HeapUncompressTup(HeapTuple srcTuple, TupleDesc tupleDesc, char *cmprsInfo, HeapTuple destTuple) { - Assert(srcTuple && tupleDesc && cmprsInfo); + Assert(srcTuple && tupleDesc && cmprsInfo); // 断言输入参数不为空 - HeapTupleHeader srcTup = srcTuple->t_data; - Form_pg_attribute *att = tupleDesc->attrs; - uint32 tdesc_natts = tupleDesc->natts; - uint32 natts; /* number of atts to extract */ - uint32 attrIdx; - char *srcTupData = NULL; /* ptr to srcTuple data */ - long srcOff; /* offset in srcTuple data */ - bits8 *srcNullBits = srcTup->t_bits; /* ptr to null bitmap in srcTuple */ - bits8 *cmprsBitmap = NULL; /* pointer to compression bitmap in srcTuple */ + HeapTupleHeader srcTup = srcTuple->t_data; // 获取源元组的头部信息 + Form_pg_attribute *att = tupleDesc->attrs; // 获取元组描述中的属性信息数组 + uint32 tdesc_natts = tupleDesc->natts; // 元组描述中的属性数量 + uint32 natts; /* number of atts to extract */ // 需要提取的属性数量 + uint32 attrIdx; // 属性索引 + char *srcTupData = NULL; // 指向源元组数据的指针 + long srcOff; // 源数据偏移量 + bits8 *srcNullBits = srcTup->t_bits; // 源元组的NULL位图 + bits8 *cmprsBitmap = NULL; // 压缩位图 #ifdef USE_ASSERT_CHECKING - uint16 testInfomask = tupleDesc->tdhasoid ? HEAP_HASOID : 0; + uint16 testInfomask = tupleDesc->tdhasoid ? HEAP_HASOID : 0; // 用于断言检查的infomask #endif - Assert(HEAP_TUPLE_IS_COMPRESSED(srcTuple->t_data) && (cmprsInfo != NULL)); + Assert(HEAP_TUPLE_IS_COMPRESSED(srcTuple->t_data) && (cmprsInfo != NULL)); // 断言源元组是压缩的且cmprsInfo不为空 - /* - * In inheritance situations, it is possible that the given srcTuple actually - * has more fields than the caller is expecting. Don't run srcOff the end of - * the caller's arrays. - */ - Assert(tdesc_natts >= HeapTupleHeaderGetNatts(srcTup, tupleDesc)); - natts = Min(HeapTupleHeaderGetNatts(srcTup, tupleDesc), tdesc_natts); - srcTupData = (char *)srcTup + srcTup->t_hoff; - cmprsBitmap = (bits8 *)srcTupData; - srcOff = BITMAPLEN(natts); + Assert(tdesc_natts >= HeapTupleHeaderGetNatts(srcTup, tupleDesc)); // 断言元组描述的属性数量不小于源元组的属性数量 + natts = Min(HeapTupleHeaderGetNatts(srcTup, tupleDesc), tdesc_natts); // 计算需要提取的属性数量,取较小者 + srcTupData = (char *)srcTup + srcTup->t_hoff; // 初始化源元组数据的指针 + cmprsBitmap = (bits8 *)srcTupData; // 初始化压缩位图的指针 + srcOff = BITMAPLEN(natts); // 计算位图所占字节数 errno_t retno = EOK; - int cmprsOff = 0; /* pointer to the start of compression meta */ - void *metaInfo = NULL; - char mode = 0; + int cmprsOff = 0; // 压缩信息的偏移量 + void *metaInfo = NULL; // 压缩元数据的指针 + char mode = 0; // 压缩模式 - /* Dest srcTuple header size */ - int hoff = srcTup->t_hoff; - int destDataLength = 0; + int hoff = srcTup->t_hoff; // 源元组数据的头部偏移量 + int destDataLength = 0; // 目标元组数据的长度 if (destTuple == NULL) { - destTuple = (HeapTuple)heaptup_alloc(MaxHeapTupleSize + HEAPTUPLESIZE); - destTuple->t_data = (HeapTupleHeader)((char *)destTuple + HEAPTUPLESIZE); + destTuple = (HeapTuple)heaptup_alloc(MaxHeapTupleSize + HEAPTUPLESIZE); // 为目标元组分配内存 + destTuple->t_data = (HeapTupleHeader)((char *)destTuple + HEAPTUPLESIZE); // 初始化目标元组头部信息的指针 } - Assert(destTuple->t_data != NULL); - HeapTupleHeader destTup = destTuple->t_data; - Assert(((size_t)MAXALIGN(destTup)) == (size_t)destTup); + Assert(destTuple->t_data != NULL); // 断言目标元组的头部信息不为空 + HeapTupleHeader destTup = destTuple->t_data; // 获取目标元组的头部信息 + Assert(((size_t)MAXALIGN(destTup)) == (size_t)destTup); // 断言目标元组头部信息已对齐 - bits8 *destNullBits = destTup->t_bits; - char *destTupData = (char *)destTup + hoff; - Datum val = 0; + bits8 *destNullBits = destTup->t_bits; // 目标元组的NULL位图 + char *destTupData = (char *)destTup + hoff; // 指向目标元组数据的指针 + Datum val = 0; // 用于存储属性值 - /* Copy null bitmap */ if (hasnulls) { - retno = memcpy_s(destNullBits, BITMAPLEN(natts), srcNullBits, BITMAPLEN(natts)); + retno = memcpy_s(destNullBits, BITMAPLEN(natts), srcNullBits, BITMAPLEN(natts)); // 复制源元组的NULL位图到目标元组 securec_check(retno, "\0", "\0"); #ifdef USE_ASSERT_CHECKING - testInfomask |= HEAP_HASNULL; -#endif - } - - for (attrIdx = 0; attrIdx < natts; ++attrIdx) { - Form_pg_attribute thisatt = att[attrIdx]; - /* parse compression metaInfo data of this attr */ - int metaSize = 0; - metaInfo = PageCompress::FetchAttrCmprMeta(cmprsInfo + cmprsOff, thisatt->attlen, &metaSize, &mode); - cmprsOff += metaSize; - - /* IMPORTANT: NULLs first, row-compression second, and the normal fields the last; */ - if (hasnulls && att_isnull(attrIdx, srcNullBits)) { - continue; - } - - if (isAttrCompressed(attrIdx, cmprsBitmap)) { - int attsize = 0; - - if (PageCompress::NeedExternalBuf(mode)) { -#ifdef USE_ASSERT_CHECKING - testInfomask |= HEAP_HASVARWIDTH; + testInfomask |= HEAP_HASNULL; // 设置断言检查的NULL标志 #endif - destDataLength = PageCompress::UncompressOneAttr(mode, thisatt->attalign, thisatt->attlen, metaInfo, - srcTupData + srcOff, &attsize, destTupData); - /* attsize is the size of compressed value. */ - srcOff = srcOff + attsize; - /* both datum size and padding size are included in destDataLength. */ - Assert(destDataLength > 0); - destTupData += destDataLength; - continue; - } else { - val = PageCompress::UncompressOneAttr(mode, metaInfo, thisatt->atttypid, thisatt->attlen, - srcTupData + srcOff, &attsize); - srcOff = srcOff + attsize; /* attsize is the size of compressed value. */ - } - } else { - if (thisatt->attlen == -1) { - srcOff = att_align_pointer(srcOff, thisatt->attalign, -1, srcTupData + srcOff); - } else { - /* not varlena, so safe to use att_align_nominal */ - srcOff = att_align_nominal(srcOff, thisatt->attalign); + for (attrIdx = 0; attrIdx < natts; ++attrIdx) { + Form_pg_attribute thisatt = att[attrIdx]; // 获取当前属性的描述信息 + /* parse compression metaInfo data of this attr */ + int metaSize = 0; + metaInfo = PageCompress::FetchAttrCmprMeta(cmprsInfo + cmprsOff, thisatt->attlen, &metaSize, &mode); // 解析属性的压缩元数据 + cmprsOff += metaSize; // 更新压缩信息偏移量 + + /* IMPORTANT: NULLs first, row-compression second, and the normal fields the last; */ + if (hasnulls && att_isnull(attrIdx, srcNullBits)) { + continue; // 如果属性为NULL,跳过 } - val = fetchatt(thisatt, srcTupData + srcOff); - srcOff = att_addlength_pointer(srcOff, thisatt->attlen, srcTupData + srcOff); - } + if (isAttrCompressed(attrIdx, cmprsBitmap)) { + int attsize = 0; - /* Now we fill dest srcTuple with the val */ - if (thisatt->attbyval) { - /* pass-by-value */ - destTupData = (char *)att_align_nominal(destTupData, thisatt->attalign); - store_att_byval(destTupData, val, thisatt->attlen); - destDataLength = thisatt->attlen; - } else if (thisatt->attlen == -1) { - /* varlena */ - Pointer tmpVal = DatumGetPointer(val); + if (PageCompress::NeedExternalBuf(mode)) { #ifdef USE_ASSERT_CHECKING - testInfomask |= HEAP_HASVARWIDTH; + testInfomask |= HEAP_HASVARWIDTH; // 设置断言检查的变宽度标志 #endif - if (VARATT_IS_EXTERNAL(tmpVal)) { - /* no alignment, since it's short by definition */ - destDataLength = VARSIZE_EXTERNAL(tmpVal); - retno = memcpy_s(destTupData, destDataLength, tmpVal, destDataLength); - securec_check(retno, "\0", "\0"); - } else if (VARATT_IS_SHORT(tmpVal)) { - /* no alignment for short varlenas */ - destDataLength = VARSIZE_SHORT(tmpVal); - retno = memcpy_s(destTupData, destDataLength, tmpVal, destDataLength); - securec_check(retno, "\0", "\0"); - } else if (VARLENA_ATT_IS_PACKABLE(thisatt) && VARATT_CAN_MAKE_SHORT(tmpVal)) { - /* convert to short varlena -- no alignment */ - destDataLength = VARATT_CONVERTED_SHORT_SIZE(tmpVal); - SET_VARSIZE_SHORT(destTupData, destDataLength); - retno = memcpy_s(destTupData + 1, destDataLength - 1, VARDATA(tmpVal), destDataLength - 1); - securec_check(retno, "\0", "\0"); + destDataLength = PageCompress::UncompressOneAttr(mode, thisatt->attalign, thisatt->attlen, metaInfo, + srcTupData + srcOff, &attsize, destTupData); // 解压缩属性值 + /* attsize is the size of compressed value. */ + srcOff = srcOff + attsize; // 更新源数据偏移量 + /* both datum size and padding size are included in destDataLength. */ + Assert(destDataLength > 0); + destTupData += destDataLength; // 更新目标元组数据指针 + continue; + } else { + val = PageCompress::UncompressOneAttr(mode, metaInfo, thisatt->atttypid, thisatt->attlen, + srcTupData + srcOff, &attsize); + srcOff = srcOff + attsize; /* attsize is the size of compressed value. */ + } } else { - /* - * Memset padding bytes, because att_align_pointer will judge - * padding bytes whether zero. Please refer to att_align_pointer - */ - *destTupData = 0; + if (thisatt->attlen == -1) { + srcOff = att_align_pointer(srcOff, thisatt->attalign, -1, srcTupData + srcOff); + } else { + /* not varlena, so safe to use att_align_nominal */ + srcOff = att_align_nominal(srcOff, thisatt->attalign); + } - /* full 4-byte header varlena */ + val = fetchatt(thisatt, srcTupData + srcOff); + srcOff = att_addlength_pointer(srcOff, thisatt->attlen, srcTupData + srcOff); + } + + /* Now we fill dest srcTuple with the val */ + if (thisatt->attbyval) { + /* pass-by-value */ destTupData = (char *)att_align_nominal(destTupData, thisatt->attalign); - destDataLength = VARSIZE(tmpVal); - retno = memcpy_s(destTupData, destDataLength, tmpVal, destDataLength); + store_att_byval(destTupData, val, thisatt->attlen); + destDataLength = thisatt->attlen; + } else if (thisatt->attlen == -1) { + /* varlena */ + Pointer tmpVal = DatumGetPointer(val); +#ifdef USE_ASSERT_CHECKING + testInfomask |= HEAP_HASVARWIDTH; // 设置断言检查的变宽度标志 +#endif + + if (VARATT_IS_EXTERNAL(tmpVal)) { + /* no alignment, since it's short by definition */ + destDataLength = VARSIZE_EXTERNAL(tmpVal); + retno = memcpy_s(destTupData, destDataLength, tmpVal, destDataLength); + securec_check(retno, "\0", "\0"); + } else if (VARATT_IS_SHORT(tmpVal)) { + /* no alignment for short varlenas */ + destDataLength = VARSIZE_SHORT(tmpVal); + retno = memcpy_s(destTupData, destDataLength, tmpVal, destDataLength); + securec_check(retno, "\0", "\0"); + } else if (VARLENA_ATT_IS_PACKABLE(thisatt) && VARATT_CAN_MAKE_SHORT(tmpVal)) { + /* convert to short varlena -- no alignment */ + destDataLength = VARATT_CONVERTED_SHORT_SIZE(tmpVal); + SET_VARSIZE_SHORT(destTupData, destDataLength); + retno = memcpy_s(destTupData + 1, destDataLength - 1, VARDATA(tmpVal), destDataLength - 1); + securec_check(retno, "\0", "\0"); + } else { + /* + * Memset padding bytes, because att_align_pointer will judge + * padding bytes whether zero. Please refer to att_align_pointer + */ + *destTupData = 0; + + /* full 4-byte header varlena */ + destTupData = (char *)att_align_nominal(destTupData, thisatt->attalign); + destDataLength = VARSIZE(tmpVal); + retno = memcpy_s(destTupData, destDataLength, tmpVal, destDataLength); + securec_check(retno, "\0", "\0"); + } + } else if (thisatt->attlen == -2) { +#ifdef USE_ASSERT_CHECKING + testInfomask |= HEAP_HASVARWIDTH; // 设置断言检查的变宽度标志 +#endif + Assert(thisatt->attalign == 'c'); + destDataLength = strlen(DatumGetCString(val)) + 1; + retno = memcpy_s(destTupData, destDataLength, DatumGetPointer(val), destDataLength); + securec_check(retno, "\0", "\0"); + } else { + destTupData = (char *)att_align_nominal(destTupData, thisatt->attalign); + Assert(thisatt->attlen > 0); + destDataLength = thisatt->attlen; + retno = memcpy_s(destTupData, destDataLength, DatumGetPointer(val), destDataLength); securec_check(retno, "\0", "\0"); } - } else if (thisatt->attlen == -2) { -#ifdef USE_ASSERT_CHECKING - testInfomask |= HEAP_HASVARWIDTH; -#endif - Assert(thisatt->attalign == 'c'); - destDataLength = strlen(DatumGetCString(val)) + 1; - retno = memcpy_s(destTupData, destDataLength, DatumGetPointer(val), destDataLength); - securec_check(retno, "\0", "\0"); - } else { - /* fixed-length pass-by-reference */ - destTupData = (char *)att_align_nominal(destTupData, thisatt->attalign); - Assert(thisatt->attlen > 0); - destDataLength = thisatt->attlen; - retno = memcpy_s(destTupData, destDataLength, DatumGetPointer(val), destDataLength); - securec_check(retno, "\0", "\0"); + destTupData += destDataLength; // 更新目标元组数据指针 } - destTupData += destDataLength; } - /* complete destTuple other info excluding t_data */ - destTuple->t_len = (uint32)(destTupData - (char *)destTup); - destTuple->t_self = srcTuple->t_self; - destTuple->t_tableOid = srcTuple->t_tableOid; - destTuple->t_bucketId = srcTuple->t_bucketId; + destTuple->t_len = (uint32)(destTupData - (char *)destTup); // 设置目标元组的长度 + destTuple->t_self = srcTuple->t_self; // 设置目标元组的位置信息 + destTuple->t_tableOid = srcTuple->t_tableOid; // 设置目标元组的表OID + destTuple->t_bucketId = srcTuple->t_bucketId; // 设置目标元组的桶ID #ifdef PGXC - destTuple->t_xc_node_id = srcTuple->t_xc_node_id; + destTuple->t_xc_node_id = srcTuple->t_xc_node_id; // 设置目标元组的PGXC节点ID #endif - /* complete destTup header info excluding data part */ - COPY_TUPLE_HEADERINFO(destTup, srcTup); - HEAP_TUPLE_CLEAR_COMPRESSED(destTuple->t_data); - Assert(testInfomask == (destTup->t_infomask & 0x0F)); + COPY_TUPLE_HEADERINFO(destTup, srcTup); // 复制源元组的头部信息到目标元组 + HEAP_TUPLE_CLEAR_COMPRESSED(destTuple->t_data); // 清除目标元组的压缩标志 + Assert(testInfomask == (destTup->t_infomask & 0x0F)); // 断言检查infomask是否正确 - destTup->t_hoff = hoff; + destTup->t_hoff = hoff; // 设置目标元组数据的头部偏移量 if (tupleDesc->tdhasoid) { - HeapTupleHeaderSetOid(destTup, HeapTupleGetOid(srcTuple)); + HeapTupleHeaderSetOid(destTup, HeapTupleGetOid(srcTuple)); // 设置目标元组的OID } - return destTuple; + return destTuple; // 返回目标元组 } + /* the same to HeapUncompressTup() function which is a faster and * lighter-weight implement. * we highly recommand that you would not using HeapUncompressTup2() -- 2.34.1 From 93ac2d993cb1afc6de2f4b479aa7c9330cb1ea40 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 21:03:34 +0800 Subject: [PATCH 03/19] Enter --- .../storage/access/common/heaptuple.cpp | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/gausskernel/storage/access/common/heaptuple.cpp b/src/gausskernel/storage/access/common/heaptuple.cpp index be50eec79..6140e0e78 100644 --- a/src/gausskernel/storage/access/common/heaptuple.cpp +++ b/src/gausskernel/storage/access/common/heaptuple.cpp @@ -182,7 +182,7 @@ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, cha continue; } - *bitP |= bitmask; // �����置位图中的位 + *bitP |= bitmask; // �������置位图中的位 } @@ -2535,16 +2535,21 @@ static HeapTuple HeapUncompressTup(HeapTuple srcTuple, TupleDesc tupleDesc, char */ static HeapTuple HeapUncompressTup2(HeapTuple tuple, TupleDesc tupleDesc, Page dictPage) { - Assert(HeapTupleIsValid(tuple) && (tuple->t_data != NULL)); - Assert(HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)); - Assert((tupleDesc != NULL) && (dictPage != NULL)); + Assert(HeapTupleIsValid(tuple) && (tuple->t_data != NULL)); // 断言输入的堆元组有效且包含数据 + Assert(HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)); // 断言输入的堆元组是压缩的 + Assert((tupleDesc != NULL) && (dictPage != NULL)); // 断言元组描述和字典页面不为空 + // 为解压后的属性值和NULL标志数组分配内存 Datum *values = (Datum *)palloc(sizeof(Datum) * tupleDesc->natts); bool *isnulls = (bool *)palloc(sizeof(bool) * tupleDesc->natts); + // 使用heap_deform_cmprs_tuple函数解压缩堆元组的数据 heap_deform_cmprs_tuple(tuple, tupleDesc, values, isnulls, dictPage); + + // 使用解压后的属性值和NULL标志创建一个新的堆元组 HeapTuple newTuple = heap_form_tuple(tupleDesc, values, isnulls); + // 复制一些元数据信息,如位置、表OID、桶ID等 /* don't copy tuple->t_len, that has been set in heap_form_tuple */ newTuple->t_self = tuple->t_self; newTuple->t_tableOid = tuple->t_tableOid; @@ -2552,18 +2557,27 @@ static HeapTuple HeapUncompressTup2(HeapTuple tuple, TupleDesc tupleDesc, Page d #ifdef PGXC newTuple->t_xc_node_id = tuple->t_xc_node_id; #endif + + // 如果元组描述中包含OID字段,将新元组的OID设置为与原始元组相同 if (tupleDesc->tdhasoid) { HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); } + // 断言新元组不是压缩的 Assert(!HEAP_TUPLE_IS_COMPRESSED(newTuple->t_data)); + + // 复制事务信息和其他元数据 COPY_TUPLE_HEADER_XACT_INFO(newTuple, tuple); + // 释放分配的内存 pfree_ext(isnulls); pfree_ext(values); + + // 返回解压后的新元组 return newTuple; } + HeapTuple test_HeapUncompressTup2(HeapTuple tuple, TupleDesc tupleDesc, Page dictPage) { return HeapUncompressTup2(tuple, tupleDesc, dictPage); -- 2.34.1 From 56caf895651892d81fdacb4a22a6b692cc504e7e Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 21:05:16 +0800 Subject: [PATCH 04/19] Enter --- .../storage/access/common/heaptuple.cpp | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/gausskernel/storage/access/common/heaptuple.cpp b/src/gausskernel/storage/access/common/heaptuple.cpp index 6140e0e78..dd338defc 100644 --- a/src/gausskernel/storage/access/common/heaptuple.cpp +++ b/src/gausskernel/storage/access/common/heaptuple.cpp @@ -182,7 +182,7 @@ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, cha continue; } - *bitP |= bitmask; // �������置位图中的位 + *bitP |= bitmask; // ���������置位图中的位 } @@ -2805,15 +2805,17 @@ void heap_deform_cmprs_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values void heap_deform_tuple2(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull, Buffer buffer) { - Assert((tuple != NULL) && (tuple->t_data != NULL)); - if (!HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)) { - heap_deform_tuple(tuple, tupleDesc, values, isnull); + Assert((tuple != NULL) && (tuple->t_data != NULL)); // 断言输入的堆元组有效且包含数据 + if (!HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)) { // 如果堆元组不是压缩的 + heap_deform_tuple(tuple, tupleDesc, values, isnull); // 使用普通的解析函数解析 return; } - Assert(BufferIsValid(buffer)); - Page page = BufferGetPage(buffer); - Assert((page != NULL) && (PageIsCompressed(page))); + Assert(BufferIsValid(buffer)); // 断言缓冲区有效 + Page page = BufferGetPage(buffer); // 获取缓冲区对应的页 + Assert((page != NULL) && (PageIsCompressed(page))); // 断言页存在且是压缩的 + + // 使用压缩元组解析函数解析堆元组,传递页中的字典信息 heap_deform_cmprs_tuple(tuple, tupleDesc, values, isnull, (char *)getPageDict(page)); } @@ -2823,13 +2825,15 @@ void heap_deform_tuple2(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, boo */ void heap_deform_tuple3(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull, Page page) { - Assert((tuple != NULL) && (tuple->t_data != NULL)); - if (!HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)) { - heap_deform_tuple(tuple, tupleDesc, values, isnull); + Assert((tuple != NULL) && (tuple->t_data != NULL)); // 断言输入的堆元组有效且包含数据 + if (!HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)) { // 如果堆元组不是压缩的 + heap_deform_tuple(tuple, tupleDesc, values, isnull); // 使用普通的解析函数解析 return; } - Assert((page != NULL) && (PageIsCompressed(page))); + Assert((page != NULL) && (PageIsCompressed(page))); // 断言页存在且是压缩的 + + // 使用压缩元组解析函数解析堆元组,传递页中的字典信息 heap_deform_cmprs_tuple(tuple, tupleDesc, values, isnull, (char *)getPageDict(page)); } -- 2.34.1 From 5544037dafb5627a38899028306ca25264631114 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 21:31:35 +0800 Subject: [PATCH 05/19] Enter --- .../storage/access/common/heaptuple.cpp | 156 +++++++++++------- 1 file changed, 94 insertions(+), 62 deletions(-) diff --git a/src/gausskernel/storage/access/common/heaptuple.cpp b/src/gausskernel/storage/access/common/heaptuple.cpp index dd338defc..9e290911c 100644 --- a/src/gausskernel/storage/access/common/heaptuple.cpp +++ b/src/gausskernel/storage/access/common/heaptuple.cpp @@ -182,7 +182,7 @@ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, cha continue; } - *bitP |= bitmask; // ���������置位图中的位 + *bitP |= bitmask; // �����������置位图中的位 } @@ -2840,9 +2840,15 @@ void heap_deform_tuple3(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, boo /* decompress one tuple and return a copy of uncompressed tuple */ HeapTuple heapCopyCompressedTuple(HeapTuple tuple, TupleDesc tupleDesc, Page page, HeapTuple destTup) { - HeapTuple newTuple = NULL; + HeapTuple newTuple = NULL; // 用于存储新的堆元组 + + // 断言输入的堆元组有效且包含数据 Assert(HeapTupleIsValid(tuple) && (tuple->t_data != NULL)); + + // 断言输入的堆元组是压缩的 Assert(HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)); + + // 断言元组描述和页不为空,并且页不是加密的 Assert((tupleDesc != NULL) && (page != NULL)); Assert(!PageIsEncrypt(page)); @@ -2850,34 +2856,45 @@ HeapTuple heapCopyCompressedTuple(HeapTuple tuple, TupleDesc tupleDesc, Page pag * HeapUncompressTup() don't think about that case * and now is difficult to handle this problem. */ + // 如果元组描述包含默认值,并且属性数大于元组数据中的属性数 if (tupleDesc->initdefvals && tupleDesc->natts > (int)HeapTupleHeaderGetNatts(tuple->t_data, tupleDesc)) { + // 调用 HeapUncompressTup2 函数将压缩的堆元组解压缩,并传递页中的字典信息 newTuple = HeapUncompressTup2(tuple, tupleDesc, (Page)getPageDict(page)); + + // 如果提供了目标元组,则将新元组的数据复制到目标元组中 if (destTup) { errno_t retno = EOK; + + // 断言新元组的长度不超过最大堆元组大小 Assert(MAXALIGN(newTuple->t_len) <= MaxHeapTupleSize); - /* copy the new tuple into existing space. */ + // 复制元数据信息到目标元组 destTup->t_len = newTuple->t_len; destTup->t_self = newTuple->t_self; destTup->t_tableOid = newTuple->t_tableOid; destTup->t_bucketId = newTuple->t_bucketId; destTup->t_xc_node_id = newTuple->t_xc_node_id; + + // 复制新元组的数据到目标元组 retno = memcpy_s(destTup->t_data, destTup->t_len, newTuple->t_data, newTuple->t_len); securec_check(retno, "\0", "\0"); - /* release unused space and make *newTuple* point to *destTup*. */ + // 释放新元组的内存,并将新元组指针指向目标元组 heap_freetuple(newTuple); newTuple = destTup; } } else { + // 如果堆元组没有NULL值 if (!HeapTupleHasNulls(tuple)) { + // 调用 HeapUncompressTup 函数将压缩的堆元组解压缩,传递页中的字典信息,并且不包含NULL值 newTuple = HeapUncompressTup(tuple, tupleDesc, (char *)getPageDict(page), destTup); } else { + // 调用 HeapUncompressTup 函数将压缩的堆元组解压缩,传递页中的字典信息,并且包含NULL值 newTuple = HeapUncompressTup(tuple, tupleDesc, (char *)getPageDict(page), destTup); } } - return newTuple; + return newTuple; // 返回新的堆元组 } /* copy new tuple from given tuple, and fill the @@ -2903,22 +2920,27 @@ static HeapTuple HeapCopyInitdefvalTup(HeapTuple tuple, TupleDesc tupDesc) * * --------------------------------------------------------------------- */ +// 定义一个名为FORCE_INLINE的宏,用于请求编译器将该函数内联(如果可能) FORCE_INLINE HeapTuple heapCopyTuple(HeapTuple tuple, TupleDesc tupDesc, Page page) { + // 检查输入的堆元组是否有效,如果无效则发出警告并返回NULL if (!HeapTupleIsValid(tuple) || tuple->t_data == NULL) { ereport(WARNING, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errmsg("tuple copy failed, because tuple is invalid or tuple data is null ")))); return NULL; } + // 如果堆元组的数据已经被压缩,则调用另一个函数来复制压缩的元组 if (HEAP_TUPLE_IS_COMPRESSED(tuple->t_data)) { return heapCopyCompressedTuple(tuple, tupDesc, page); } + // 如果元组的属性数量小于元组描述符中的属性数量,并且元组描述符包含初始化默认值的信息,则调用HeapCopyInitdefvalTup函数来进行复制 if (unlikely(tupDesc->initdefvals && tupDesc->natts > (int)HeapTupleHeaderGetNatts(tuple->t_data, tupDesc))) { return HeapCopyInitdefvalTup(tuple, tupDesc); } + // 如果以上条件都不满足,则调用heap_copytuple函数来复制元组 return heap_copytuple(tuple); } @@ -2958,8 +2980,10 @@ MinimalTuple heapFormMinimalTuple(HeapTuple tuple, TupleDesc tupleDesc, Page pag } /* a copy of slot_deform_tuple for compressied tuple */ +// 从输入的槽(slot)中获取所需的数据 static void slot_deform_cmprs_tuple(TupleTableSlot *slot, uint32 natts) { + // 获取槽中的堆元组、元组描述符、属性值、是否为NULL的标志等信息 HeapTuple tuple = (HeapTuple)slot->tts_tuple; TupleDesc tupleDesc = slot->tts_tupleDescriptor; Datum *values = slot->tts_values; @@ -2968,68 +2992,73 @@ static void slot_deform_cmprs_tuple(TupleTableSlot *slot, uint32 natts) bool hasnulls = HeapTupleHasNulls(tuple); Form_pg_attribute *att = tupleDesc->attrs; uint32 attnum; - char *tp = NULL; /* ptr to tuple data */ - long off; /* offset in tuple data */ - bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ - - bits8 *cmprsBitmap = NULL; - char *cmprsInfo = NULL; - void *metaInfo = NULL; - int cmprsOff = 0; - char mode = 0; + char *tp = NULL; // 用于指向元组数据的指针 + long off; // 偏移量 + bits8 *bp = tup->t_bits; // 用于指向元组的位图数据 + bits8 *cmprsBitmap = NULL; // 用于指向压缩位图的数据 + char *cmprsInfo = NULL; // 用于指向压缩信息的数据 + void *metaInfo = NULL; // 用于指向元数据信息的数据 + int cmprsOff = 0; // 压缩信息的偏移量 + char mode = 0; // 压缩模式,默认为0 + // 断言堆元组是压缩的,确保槽中的缓冲区有效 Assert(HEAP_TUPLE_IS_COMPRESSED(tup)); Assert(BufferIsValid(slot->tts_buffer)); + // 如果槽中的元组上下文为空,则创建一个上下文用于分配内存 if (slot->tts_per_tuple_mcxt == NULL) { slot->tts_per_tuple_mcxt = AllocSetContextCreate(slot->tts_mcxt, "SlotPerTupleMcxt", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } + // 在分配内存前,切换到新的上下文 AutoContextSwitch memContextGuard(slot->tts_per_tuple_mcxt); +} /* * Check whether the first call for this tuple, and initialize or restore * loop state. */ - attnum = slot->tts_nvalid; - if (attnum == 0) { - /* Start from the first attribute */ - Assert(tupleDesc->natts >= (int)HeapTupleHeaderGetNatts(tup, tupleDesc)); - off = BITMAPLEN(HeapTupleHeaderGetNatts(tup, tupleDesc)); - cmprsOff = 0; - } else { + attnum = slot->tts_nvalid; // 获取已经解析的属性数量 + +if (attnum == 0) { + /* 如果没有已解析的属性,则从第一个属性开始解析 */ + Assert(tupleDesc->natts >= (int)HeapTupleHeaderGetNatts(tup, tupleDesc)); // 断言元组描述符中的属性数量不小于堆元组中的属性数量 + off = BITMAPLEN(HeapTupleHeaderGetNatts(tup, tupleDesc)); // 计算位图长度的偏移量 + cmprsOff = 0; // 压缩信息的偏移量 +} else { /* Restore state from previous execution */ - off = slot->tts_off; - cmprsOff = slot->tts_meta_off; + off = slot->tts_off; // 恢复上一次解析的偏移量 + cmprsOff = slot->tts_meta_off; // 恢复上一次解析的压缩信息偏移量 +} + +tp = (char *)tup + tup->t_hoff; // 计算指向元组数据的指针,跳过元组头部 +cmprsBitmap = (bits8 *)tp; // 压缩位图数据 + +Page page = BufferGetPage(slot->tts_buffer); // 获取槽所在的页面 +Assert((page != NULL) && PageIsCompressed(page)); // 断言页面不为空且为压缩格式 +cmprsInfo = (char *)getPageDict(page); // 获取页面的压缩信息数据 + /* parse compression metaInfo data of this attr */ + int metaSize = 0; +for (; attnum < natts; attnum++) { + metaInfo = PageCompress::FetchAttrCmprMeta(cmprsInfo + cmprsOff, att[attnum]->attlen, &metaSize, &mode); // 从压缩信息中获取属性的压缩元信息 + cmprsOff += metaSize; // 更新压缩信息的偏移量 + + if (hasnulls && att_isnull(attnum, bp)) { + // 如果属性为NULL,则设置对应的值和NULL标志 + values[attnum] = (Datum)0; + isnull[attnum] = true; + continue; } - tp = (char *)tup + tup->t_hoff; - cmprsBitmap = (bits8 *)tp; + isnull[attnum] = false; - Page page = BufferGetPage(slot->tts_buffer); - Assert((page != NULL) && PageIsCompressed(page)); - cmprsInfo = (char *)getPageDict(page); - - /* parse compression metaInfo data of this attr */ - int metaSize = 0; - for (; attnum < natts; attnum++) { - metaInfo = PageCompress::FetchAttrCmprMeta(cmprsInfo + cmprsOff, att[attnum]->attlen, &metaSize, &mode); - cmprsOff += metaSize; - - if (hasnulls && att_isnull(attnum, bp)) { - values[attnum] = (Datum)0; - isnull[attnum] = true; - continue; - } - - isnull[attnum] = false; - - if (isAttrCompressed(attnum, cmprsBitmap)) { - int attsize = 0; - values[attnum] = PageCompress::UncompressOneAttr(mode, metaInfo, att[attnum]->atttypid, att[attnum]->attlen, - tp + off, &attsize); - off = off + attsize; /* attsize is the size of compressed value. */ + if (isAttrCompressed(attnum, cmprsBitmap)) { + // 如果属性被压缩,则解压缩属性值 + int attsize = 0; + values[attnum] = PageCompress::UncompressOneAttr(mode, metaInfo, att[attnum]->atttypid, att[attnum]->attlen, + tp + off, &attsize); + off = off + attsize; /* attsize is the size of compressed value. */ continue; } @@ -3041,7 +3070,7 @@ static void slot_deform_cmprs_tuple(TupleTableSlot *slot, uint32 natts) /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, thisatt->attalign); } - +// 解析非压缩属性值 values[attnum] = fetchatt(thisatt, tp + off); off = att_addlength_pointer(off, thisatt->attlen, tp + off); } @@ -3049,10 +3078,10 @@ static void slot_deform_cmprs_tuple(TupleTableSlot *slot, uint32 natts) /* * Save state for next execution */ - slot->tts_nvalid = attnum; - slot->tts_off = off; - slot->tts_meta_off = cmprsOff; - slot->tts_slow = true; + slot->tts_nvalid = attnum; // 更新已解析的属性数量 +slot->tts_off = off; // 更新偏移量 +slot->tts_meta_off = cmprsOff; // 更新压缩信息偏移量 +slot->tts_slow = true; // 设置槽为"慢"模式,表示它包含复杂的数据 } /* @@ -3069,16 +3098,19 @@ void heap_slot_clear(TupleTableSlot *slot) /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree) { - heap_freetuple((HeapTuple)slot->tts_tuple); - slot->tts_tuple = NULL; - slot->tts_shouldFree = false; - } + // 如果槽标记为需要释放(tts_shouldFree为true),则释放槽中的堆元组 +if (slot->tts_shouldFree) { + heap_freetuple((HeapTuple)slot->tts_tuple); // 释放堆元组的内存 + slot->tts_tuple = NULL; // 将槽中的堆元组指针置为空 + slot->tts_shouldFree = false; // 将槽的释放标志设置为false,表示不再需要释放 +} + +// 如果槽标记为需要释放最小元组(tts_shouldFreeMin为true),则释放槽中的最小元组 +if (slot->tts_shouldFreeMin) { + heap_free_minimal_tuple(slot->tts_mintuple); // 释放最小元组的内存 + slot->tts_shouldFreeMin = false; // 将槽的最小元组释放标志设置为false,表示不再需要释放 +} - if (slot->tts_shouldFreeMin) { - heap_free_minimal_tuple(slot->tts_mintuple); - slot->tts_shouldFreeMin = false; - } } /* -- 2.34.1 From 9d39bc7b8193aa3675affb9b1f2389762cdca199 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 21:42:33 +0800 Subject: [PATCH 06/19] Enter --- .../storage/access/common/heaptuple.cpp | 193 ++++++++++++------ 1 file changed, 127 insertions(+), 66 deletions(-) diff --git a/src/gausskernel/storage/access/common/heaptuple.cpp b/src/gausskernel/storage/access/common/heaptuple.cpp index 9e290911c..3c70dca01 100644 --- a/src/gausskernel/storage/access/common/heaptuple.cpp +++ b/src/gausskernel/storage/access/common/heaptuple.cpp @@ -182,7 +182,7 @@ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, cha continue; } - *bitP |= bitmask; // �����������置位图中的位 + *bitP |= bitmask; // �������������置位图中的位 } @@ -3171,17 +3171,20 @@ void heap_slot_materialize(TupleTableSlot *slot) * storage, we must not pfree it now, since callers might have already * fetched datum pointers referencing it.) */ - if (!slot->tts_shouldFreeMin) { - slot->tts_mintuple = NULL; - } -#ifdef PGXC - if (!slot->tts_shouldFreeRow) { - slot->tts_dataRow = NULL; - slot->tts_dataLen = -1; - } -#endif + // 如果槽的最小元组不需要释放(tts_shouldFreeMin为false),则将最小元组指针置为空 +if (!slot->tts_shouldFreeMin) { + slot->tts_mintuple = NULL; // 清空最小元组指针 } +#ifdef PGXC +// 如果在PGXC环境下,且槽的数据行不需要释放(tts_shouldFreeRow为false) +if (!slot->tts_shouldFreeRow) { + slot->tts_dataRow = NULL; // 清空数据行指针 + slot->tts_dataLen = -1; // 将数据行长度设置为-1 +} +#endif + + /* * Return a minimal tuple "owned" by the slot. It is slot's responsibility * to free the memory consumed by the minimal tuple. If the slot can not @@ -3252,15 +3255,20 @@ MinimalTuple heap_slot_copy_minimal_tuple(TupleTableSlot *slot) * If we have a physical tuple then just copy it. Prefer to copy * tts_mintuple since that's a tad cheaper. */ - if (slot->tts_mintuple) { - return heap_copy_minimal_tuple(slot->tts_mintuple); - } else if (slot->tts_tuple != NULL ) { - return heapFormMinimalTuple((HeapTuple)slot->tts_tuple, - slot->tts_tupleDescriptor, - (BufferIsValid(slot->tts_buffer) ? BufferGetPage(slot->tts_buffer) : NULL)); - } + // 如果槽中存在最小元组(tts_mintuple不为空),则复制并返回该最小元组 +if (slot->tts_mintuple) { + return heap_copy_minimal_tuple(slot->tts_mintuple); +} +// 否则,如果槽中存在堆元组(tts_tuple不为空),则创建并返回对应的最小元组 +else if (slot->tts_tuple != NULL) { + return heapFormMinimalTuple((HeapTuple)slot->tts_tuple, + slot->tts_tupleDescriptor, + (BufferIsValid(slot->tts_buffer) ? BufferGetPage(slot->tts_buffer) : NULL)); +} #ifdef PGXC +// 在PGXC环境下的条件处理(省略) + /* * Ensure values are extracted from data row to the Datum array @@ -3290,31 +3298,36 @@ void heap_slot_store_minimal_tuple(MinimalTuple mtup, TupleTableSlot *slot, bool /* * sanity checks */ - Assert(mtup != NULL); - Assert(slot != NULL); - Assert(slot->tts_tupleDescriptor != NULL); - Assert(slot->tts_tupslotTableAm == TAM_HEAP); + // 执行一些合理性检查 +Assert(mtup != NULL); // 确保输入的最小元组不为空 +Assert(slot != NULL); // 确保槽不为空 +Assert(slot->tts_tupleDescriptor != NULL); // 确保槽的元组描述符不为空 +Assert(slot->tts_tupslotTableAm == TAM_HEAP); // 确保槽的表存储访问方法为TAM_HEAP(堆表) /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree && (HeapTuple)slot->tts_tuple != NULL) { - heap_freetuple((HeapTuple)slot->tts_tuple); - slot->tts_tuple = NULL; - } - if (slot->tts_shouldFreeMin) { - heap_free_minimal_tuple(slot->tts_mintuple); - } + // 释放槽中可能存在的旧的物理元组 +if (slot->tts_shouldFree && (HeapTuple)slot->tts_tuple != NULL) { + heap_freetuple((HeapTuple)slot->tts_tuple); // 释放槽中的物理元组的内存 + slot->tts_tuple = NULL; // 将槽中的物理元组指针置为空 +} +if (slot->tts_shouldFreeMin) { + heap_free_minimal_tuple(slot->tts_mintuple); // 释放槽中的最小元组的内存 +} #ifdef PGXC +// 在PGXC环境下,处理槽中的数据行内存的释放 if (slot->tts_shouldFreeRow) { - pfree_ext(slot->tts_dataRow); + pfree_ext(slot->tts_dataRow); // 释放数据行的内存 } -slot->tts_shouldFreeRow = false; -slot->tts_dataRow = NULL; -slot->tts_dataLen = -1; +slot->tts_shouldFreeRow = false; // 将数据行的释放标志设置为false +slot->tts_dataRow = NULL; // 将数据行指针置为空 +slot->tts_dataLen = -1; // 将数据行长度设置为-1 #endif + + /* * Drop the pin on the referenced buffer, if there is one. */ @@ -3326,15 +3339,18 @@ slot->tts_buffer = InvalidBuffer; /* * Store the new tuple into the specified slot. */ - slot->tts_isempty = false; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = shouldFree; - slot->tts_tuple = &slot->tts_minhdr; - slot->tts_mintuple = mtup; + slot->tts_isempty = false; // 将槽的标志设置为非空 +slot->tts_shouldFree = false; // 将槽的释放标志设置为false,表示不需要释放物理元组内存 +slot->tts_shouldFreeMin = shouldFree; // 将槽的最小元组释放标志设置为传入的shouldFree参数的值 +slot->tts_tuple = &slot->tts_minhdr; // 将槽的元组指针指向槽的最小元组头部 + +slot->tts_mintuple = mtup; // 将槽的最小元组指针指向传入的最小元组 + +// 设置槽的最小元组头部的元组表类型、长度和数据指针 +slot->tts_minhdr.tupTableType = HEAP_TUPLE; // 设置元组表类型为HEAP_TUPLE(堆表) +slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET; // 设置元组长度为最小元组长度加上偏移量 +slot->tts_minhdr.t_data = (HeapTupleHeader)((char*)mtup - MINIMAL_TUPLE_OFFSET); // 设置元组数据指针为最小元组数据减去偏移量后的位置 - slot->tts_minhdr.tupTableType = HEAP_TUPLE; - slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET; - slot->tts_minhdr.t_data = (HeapTupleHeader)((char*)mtup - MINIMAL_TUPLE_OFFSET); /* no need to set t_self or t_tableOid since we won't allow access */ /* Mark extracted state invalid */ @@ -3440,21 +3456,26 @@ void heap_slot_store_heap_tuple(HeapTuple tuple, TupleTableSlot* slot, Buffer bu /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree && (HeapTuple)slot->tts_tuple != NULL) { - heap_freetuple((HeapTuple)slot->tts_tuple); - slot->tts_tuple = NULL; - } - if (slot->tts_shouldFreeMin) { - heap_free_minimal_tuple(slot->tts_mintuple); - } + // 如果槽标记为需要释放(tts_shouldFree为true),且槽中的物理元组不为空,则释放槽中的物理元组内存 +if (slot->tts_shouldFree && (HeapTuple)slot->tts_tuple != NULL) { + heap_freetuple((HeapTuple)slot->tts_tuple); // 释放槽中的物理元组内存 + slot->tts_tuple = NULL; // 将槽中的物理元组指针置为空 +} + +// 如果槽标记为需要释放最小元组(tts_shouldFreeMin为true),则释放槽中的最小元组内存 +if (slot->tts_shouldFreeMin) { + heap_free_minimal_tuple(slot->tts_mintuple); // 释放槽中的最小元组内存 +} + #ifdef ENABLE_MULTIPLE_NODES #ifdef PGXC - if (slot->tts_shouldFreeRow) { - pfree_ext(slot->tts_dataRow); - } - slot->tts_shouldFreeRow = false; - slot->tts_dataRow = NULL; - slot->tts_dataLen = -1; +// 在多节点PostgreSQL(PGXC)环境下,处理槽中的数据行内存的释放 +if (slot->tts_shouldFreeRow) { + pfree_ext(slot->tts_dataRow); // 释放数据行的内存 +} +slot->tts_shouldFreeRow = false; // 将数据行的释放标志设置为false +slot->tts_dataRow = NULL; // 将数据行指针置为空 +slot->tts_dataLen = -1; // 将数据行长度设置为-1 /* Batch Mode only first tuple need reset context */ if (!batchMode) { @@ -3471,11 +3492,11 @@ void heap_slot_store_heap_tuple(HeapTuple tuple, TupleTableSlot* slot, Buffer bu /* * Store the new tuple into the specified slot. */ - slot->tts_isempty = false; - slot->tts_shouldFree = should_free; - slot->tts_shouldFreeMin = false; - slot->tts_tuple = tuple; - slot->tts_mintuple = NULL; + slot->tts_isempty = false; // 将槽的标志设置为非空 +slot->tts_shouldFree = should_free; // 将槽的释放标志设置为传入的should_free参数的值 +slot->tts_shouldFreeMin = false; // 将槽的最小元组释放标志设置为false,表示不需要释放最小元组内存 +slot->tts_tuple = tuple; // 将槽的物理元组指针指向传入的物理元组 +slot->tts_mintuple = NULL; // 将槽的最小元组指针置为空 /* Mark extracted state invalid */ slot->tts_nvalid = 0; @@ -3491,13 +3512,17 @@ void heap_slot_store_heap_tuple(HeapTuple tuple, TupleTableSlot* slot, Buffer bu * * Batch Mode only first tuple need do buffer reference. */ - if (!batchMode && slot->tts_buffer != buffer) { - if (BufferIsValid(slot->tts_buffer)) { - ReleaseBuffer(slot->tts_buffer); - } - slot->tts_buffer = buffer; - if (BufferIsValid(buffer)) { - IncrBufferRefCount(buffer); + // 如果不在批处理模式下且槽的缓冲区与传入的缓冲区不同 +if (!batchMode && slot->tts_buffer != buffer) { + // 如果槽的缓冲区有效,则释放它的引用计数 + if (BufferIsValid(slot->tts_buffer)) { + ReleaseBuffer(slot->tts_buffer); + } + // 将槽的缓冲区指针指向传入的缓冲区 + slot->tts_buffer = buffer; + // 如果传入的缓冲区有效,则增加它的引用计数 + if (BufferIsValid(buffer)) { + IncrBufferRefCount(buffer); } } } @@ -3507,27 +3532,36 @@ void heap_slot_store_heap_tuple(HeapTuple tuple, TupleTableSlot* slot, Buffer bu * * Note: Only the dead tuple of pg_partition needs to be verified in the current code. */ +// 函数用于检查是否应该保留不可见元组(invisible tuple) bool HeapKeepInvisibleTuple(HeapTuple tuple, TupleDesc tupleDesc, KeepInvisbleTupleFunc checkKeepFunc) { + // 静态数组定义,包含了要保留不可见元组的条件 static KeepInvisbleOpt keepInvisibleArray[] = { {PartitionRelationId, Anum_pg_partition_parttype, PartitionLocalIndexSkipping}, {PartitionRelationId, Anum_pg_partition_reloptions, PartitionInvisibleMetadataKeep}, - {PartitionRelationId, Anum_pg_partition_parentid, PartitionParentOidIsLive}}; + {PartitionRelationId, Anum_pg_partition_parentid, PartitionParentOidIsLive} + }; + // 初始化返回值为true bool ret = true; + + // 遍历keepInvisibleArray数组 for (int i = 0; i < (int)lengthof(keepInvisibleArray); i++) { bool isNull = false; KeepInvisbleOpt keepOpt = keepInvisibleArray[i]; + // 检查表OID是否匹配并且ret为true,否则返回false if (keepOpt.tableOid != tuple->t_tableOid || !ret) { return false; } + // 获取指定列的值并检查是否为NULL,是则返回false Datum checkDatum = fastgetattr(tuple, keepOpt.checkAttnum, tupleDesc, &isNull); if (isNull) { return false; } + // 如果提供了自定义的检查函数(checkKeepFunc),则使用它进行检查,否则使用keepOpt中的检查函数 if (checkKeepFunc != NULL) { ret &= checkKeepFunc(checkDatum); } else if (keepOpt.checkKeepFunc != NULL) { @@ -3537,17 +3571,22 @@ bool HeapKeepInvisibleTuple(HeapTuple tuple, TupleDesc tupleDesc, KeepInvisbleTu } } + // 返回最终的结果 return ret; } +// 函数用于复制堆元组的内容到另一个堆元组,不分配新内存 void HeapCopyTupleNoAlloc(HeapTuple dest, HeapTuple src) { + // 检查源堆元组是否有效且具有数据 if (!HeapTupleIsValid(src) || src->t_data == NULL) { return; } + // 检查源堆元组是否未被压缩 Assert(!HEAP_TUPLE_IS_COMPRESSED(src->t_data)); + // 复制元组的各个字段和头信息 Assert(dest && dest->t_data); dest->t_len = src->t_len; dest->t_self = src->t_self; @@ -3556,30 +3595,52 @@ void HeapCopyTupleNoAlloc(HeapTuple dest, HeapTuple src) dest->t_xc_node_id = src->t_xc_node_id; HeapTupleCopyBase(dest, src); + // 使用内存拷贝函数(memcpy_s)复制元组数据 errno_t errorNo = memcpy_s((char *) dest->t_data, src->t_len, (char *) src->t_data, src->t_len); securec_check(errorNo, "\0", "\0"); } +// 函数用于从堆元组中获取UID(unsigned 64位整数) uint64 HeapTupleGetUid(HeapTuple tup) { + // 获取堆元组头部信息 HeapTupleHeader tupHeader = tup->t_data; + + // 如果堆元组头部不包含UID信息,返回0 if (!HeapTupleHeaderHasUid(tupHeader)) { return 0; } + + // 从堆元组头部数据中获取UID return *((uint64*)((char*)(tupHeader) + tupHeader->t_hoff - sizeof(uint64))); } + +// 函数用于设置堆元组的UID void HeapTupleSetUid(HeapTuple tup, uint64 uid, int nattrs) { /* catalog table not supportted uids */ + // 断言,目录表不支持UID Assert(!(tup->t_data->t_infomask & HEAP_HASOID)); + + // 计算UID的字节长度 int uidLen = GetUidByteLen(uid); + + // 声明一些变量 errno_t rc = 0; Size len = offsetof(HeapTupleHeaderData, t_bits); Size data_len = tup->t_len - tup->t_data->t_hoff; + + // 增加长度以适应NULL位图(如果存在) len += HeapTupleHasNulls(tup) ? BITMAPLEN(nattrs) : 0; + + // 计算新的偏移量 int hoff = MAXALIGN(len + uidLen); + + // 移动数据,以便为UID腾出空间 rc = memmove_s((char*)tup->t_data + hoff, data_len, (char*)tup->t_data + tup->t_data->t_hoff, data_len); securec_check(rc, "", ""); + + // 更新堆元组头信息 tup->t_data->t_hoff = hoff; tup->t_data->t_infomask |= GetUidByteLenInfomask(uid); tup->t_len = hoff + data_len; -- 2.34.1 From 5cd8ca2016fe59d8fbb9b00b59947250723e3c7e Mon Sep 17 00:00:00 2001 From: Cachuela Date: Thu, 5 Oct 2023 22:10:55 +0800 Subject: [PATCH 07/19] enter --- src/gausskernel/storage/buffer/buf_init.cpp | 66 +++--- src/gausskernel/storage/buffer/buf_table.cpp | 26 ++- src/gausskernel/storage/buffer/bufmgr.cpp | 220 +++++++++++-------- 3 files changed, 187 insertions(+), 125 deletions(-) diff --git a/src/gausskernel/storage/buffer/buf_init.cpp b/src/gausskernel/storage/buffer/buf_init.cpp index 6956b35d7..33be7b737 100644 --- a/src/gausskernel/storage/buffer/buf_init.cpp +++ b/src/gausskernel/storage/buffer/buf_init.cpp @@ -73,40 +73,38 @@ void InitBufferPool(void) bool found_buf_ckpt = false; uint64 buffer_size; + // 初始化一个存储buffer描述符的共享内存区域 t_thrd.storage_cxt.BufferDescriptors = (BufferDescPadded *)CACHELINEALIGN( ShmemInitStruct("Buffer Descriptors", TOTAL_BUFFER_NUM * sizeof(BufferDescPadded) + PG_CACHE_LINE_SIZE, &found_descs)); - /* Init candidate buffer list and candidate buffer free map */ + // 初始化候选buffer列表和候选buffer的空闲映射 candidate_buf_init(); #ifdef __aarch64__ + // 针对 ARM 架构,计算buffer大小并初始化buffer区块的共享内存区域 buffer_size = TOTAL_BUFFER_NUM * (Size)BLCKSZ + PG_CACHE_LINE_SIZE; t_thrd.storage_cxt.BufferBlocks = (char *)CACHELINEALIGN(ShmemInitStruct("Buffer Blocks", buffer_size, &found_bufs)); #else + // 针对其他架构,计算buffer大小并初始化buffer区块的共享内存区域 buffer_size = TOTAL_BUFFER_NUM * (Size)BLCKSZ; t_thrd.storage_cxt.BufferBlocks = (char *)ShmemInitStruct("Buffer Blocks", buffer_size, &found_bufs); #endif if (BBOX_BLACKLIST_SHARE_BUFFER) { - /* Segment Buffer is exclued from the black list, as it contains many critical information for debug */ + // 如果设置了将共享buffer段添加到黑名单中,执行该操作 bbox_blacklist_add(SHARED_BUFFER, t_thrd.storage_cxt.BufferBlocks, NORMAL_SHARED_BUFFER_NUM * (Size)BLCKSZ); } - /* - * The array used to sort to-be-checkpointed buffer ids is located in - * shared memory, to avoid having to allocate significant amounts of - * memory at runtime. As that'd be in the middle of a checkpoint, or when - * the checkpointer is restarted, memory allocation failures would be - * painful. - */ + // 初始化一个用于排序即将被检查点(checkpoint)操作处理的buffer ID的数组 g_instance.ckpt_cxt_ctl->CkptBufferIds = (CkptSortItem *)ShmemInitStruct("Checkpoint BufferIds", TOTAL_BUFFER_NUM * sizeof(CkptSortItem), &found_buf_ckpt); if (ENABLE_INCRE_CKPT && g_instance.ckpt_cxt_ctl->dirty_page_queue == NULL) { + // 如果启用了增量检查点,并且脏页队列为空,则初始化该队列 g_instance.ckpt_cxt_ctl->dirty_page_queue_size = TOTAL_BUFFER_NUM * PAGE_QUEUE_SLOT_MULTI_NBUFFERS; MemoryContext oldcontext = MemoryContextSwitchTo(g_instance.increCheckPoint_context); @@ -115,30 +113,29 @@ void InitBufferPool(void) g_instance.ckpt_cxt_ctl->dirty_page_queue = (DirtyPageQueueSlot *)palloc_huge(CurrentMemoryContext, queue_mem_size); - /* The memory of the memset sometimes exceeds 2 GB. so, memset_s cannot be used. */ MemSet((char*)g_instance.ckpt_cxt_ctl->dirty_page_queue, 0, queue_mem_size); (void)MemoryContextSwitchTo(oldcontext); } if (g_instance.bgwriter_cxt.unlink_rel_hashtbl == NULL) { + // 如果未链接关系哈希表为空,则创建它 g_instance.bgwriter_cxt.unlink_rel_hashtbl = relfilenode_hashtbl_create("unlink_rel_hashtbl", true); } if (g_instance.bgwriter_cxt.unlink_rel_fork_hashtbl == NULL) { + // 如果未链接关系分支哈希表为空,则创建它 g_instance.bgwriter_cxt.unlink_rel_fork_hashtbl = relfilenode_fork_hashtbl_create("unlink_rel_one_fork_hashtbl", true); } if (found_descs || found_bufs || found_buf_ckpt) { - /* both should be present or neither */ + // 确保三者都存在或都不存在 Assert(found_descs && found_bufs && found_buf_ckpt); - /* note: this path is only taken in EXEC_BACKEND case */ + // 注意:只有在 EXEC_BACKEND 情况下才走这条路径 } else { int i; - /* - * Initialize all the buffer headers. - */ + // 初始化所有buffer头部信息 for (i = 0; i < TOTAL_BUFFER_NUM; i++) { BufferDesc *buf = GetBufferDescriptor(i); CLEAR_BUFFERTAG(buf->tag); @@ -153,23 +150,25 @@ void InitBufferPool(void) buf->dirty_queue_loc = PG_UINT64_MAX; buf->encrypt = false; } + // 分配解锁关系表的锁 g_instance.bgwriter_cxt.rel_hashtbl_lock = LWLockAssign(LWTRANCHE_UNLINK_REL_TBL); g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock = LWLockAssign(LWTRANCHE_UNLINK_REL_FORK_TBL); } - /* Init other shared buffer-management stuff */ + // 初始化其他的共享buffer管理相关的内容 StrategyInitialize(!found_descs); - /* Init Vector Buffer management stuff */ + // 初始化向量Buffer管理相关的内容 DataCacheMgr::NewSingletonInstance(); - /* Init Meta data cache management stuff */ + // 初始化元数据缓存管理相关的内容 MetaCacheMgr::NewSingletonInstance(); - /* Initialize per-backend file flush context */ + // 初始化每个后端文件刷新上下文 WritebackContextInit(t_thrd.storage_cxt.BackendWritebackContext, &u_sess->attr.attr_common.backend_flush_after); } + /* * BufferShmemSize * @@ -178,29 +177,38 @@ void InitBufferPool(void) */ Size BufferShmemSize(void) { - Size size = 0; + Size size = 0; // 初始化一个大小为0的变量用于存储总共需要的共享内存大小 /* size of buffer descriptors */ - size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(BufferDescPadded))); - size = add_size(size, PG_CACHE_LINE_SIZE); + size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(BufferDescPadded))); + // 计算缓冲区描述符的大小,TOTAL_BUFFER_NUM是缓冲区的总数,sizeof(BufferDescPadded)是每个描述符的大小 + size = add_size(size, PG_CACHE_LINE_SIZE); + // 添加一个缓存行的大小(通常用于内存对齐) /* size of data pages */ - size = add_size(size, mul_size(TOTAL_BUFFER_NUM, BLCKSZ)); + size = add_size(size, mul_size(TOTAL_BUFFER_NUM, BLCKSZ)); + // 计算数据页的大小,TOTAL_BUFFER_NUM是数据页的总数,BLCKSZ是每个数据页的大小 #ifdef __aarch64__ - size = add_size(size, PG_CACHE_LINE_SIZE); + size = add_size(size, PG_CACHE_LINE_SIZE); + // 如果是在aarch64架构下,再添加一个缓存行的大小 #endif + /* size of stuff controlled by freelist.c */ - size = add_size(size, StrategyShmemSize()); + size = add_size(size, StrategyShmemSize()); + // 计算由freelist.c控制的内容的大小 /* size of checkpoint sort array in bufmgr.c */ - size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(CkptSortItem))); + size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(CkptSortItem))); + // 计算bufmgr.c中的检查点排序数组的大小 /* size of candidate buffers */ - size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(Buffer))); + size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(Buffer))); + // 计算候选缓冲区的大小 /* size of candidate free map */ - size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(bool))); + size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(bool))); + // 计算候选空闲映射的大小 - return size; + return size; // 返回计算得到的总共需要的共享内存大小 } diff --git a/src/gausskernel/storage/buffer/buf_table.cpp b/src/gausskernel/storage/buffer/buf_table.cpp index 2d69f0fd0..ad5ad3591 100644 --- a/src/gausskernel/storage/buffer/buf_table.cpp +++ b/src/gausskernel/storage/buffer/buf_table.cpp @@ -88,17 +88,20 @@ uint32 BufTableHashCode(BufferTag *tagPtr) */ int BufTableLookup(BufferTag *tag, uint32 hashcode) { - BufferLookupEnt *result = NULL; + BufferLookupEnt *result = NULL; // 初始化一个指向BufferLookupEnt结构的指针,用于存储查找结果 result = (BufferLookupEnt *)buf_hash_operate(t_thrd.storage_cxt.SharedBufHash, tag, hashcode, NULL); + // 调用buf_hash_operate函数进行查找操作,t_thrd.storage_cxt.SharedBufHash是缓冲区的哈希表,tag是要查找的缓冲区标签,hashcode是哈希码 if (SECUREC_UNLIKELY(result == NULL)) { + // 如果查找结果为空(未找到对应的缓冲区),则返回-1表示未找到 return -1; } - return result->id; + return result->id; // 如果找到了对应的缓冲区,返回该缓冲区的id } + /* * BufTableInsert * Insert a hashtable entry for given tag and buffer ID, @@ -111,23 +114,26 @@ int BufTableLookup(BufferTag *tag, uint32 hashcode) */ int BufTableInsert(BufferTag *tag, uint32 hashcode, int buf_id) { - BufferLookupEnt *result = NULL; - bool found = false; + BufferLookupEnt *result = NULL; // 初始化一个指向BufferLookupEnt结构的指针,用于存储查找结果 + bool found = false; // 初始化一个布尔变量,用于标识是否找到了相应的缓冲区 - Assert(buf_id >= 0); /* -1 is reserved for not-in-table */ - Assert(tag->blockNum != P_NEW); /* invalid tag */ + Assert(buf_id >= 0); // 确保buf_id不小于0,因为-1通常用于表示不在表中的情况 + Assert(tag->blockNum != P_NEW); // 确保标签中的块号不是P_NEW,因为这是无效的标签 result = (BufferLookupEnt *)buf_hash_operate(t_thrd.storage_cxt.SharedBufHash, tag, hashcode, &found); + // 调用buf_hash_operate函数进行插入操作,t_thrd.storage_cxt.SharedBufHash是缓冲区的哈希表,tag是要插入的缓冲区标签,hashcode是哈希码,&found是一个标志位,用于表示是否找到相应的缓冲区 if (found) { /* found something already in the table */ + // 如果在表中找到了相应的缓冲区,则返回该缓冲区的id return result->id; } - result->id = buf_id; + result->id = buf_id; // 如果没有找到相应的缓冲区,则将缓冲区的id设置为传入的buf_id - return -1; + return -1; // 返回-1表示插入成功 } + /* * BufTableDelete * Delete the hashtable entry for given tag (which must exist) @@ -136,11 +142,13 @@ int BufTableInsert(BufferTag *tag, uint32 hashcode, int buf_id) */ void BufTableDelete(BufferTag *tag, uint32 hashcode) { - BufferLookupEnt *result = NULL; + BufferLookupEnt *result = NULL; // 初始化一个指向BufferLookupEnt结构的指针,用于存储查找结果 result = (BufferLookupEnt *)buf_hash_operate(t_thrd.storage_cxt.SharedBufHash, tag, hashcode, NULL); + // 调用buf_hash_operate函数进行删除操作,t_thrd.storage_cxt.SharedBufHash是缓冲区的哈希表,tag是要删除的缓冲区标签,hashcode是哈希码,NULL表示不需要返回结果 if (result == NULL) { /* shouldn't happen */ + // 如果删除操作未找到相应的缓冲区,通常表示哈希表已经损坏或发生了异常情况,应该抛出错误 ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("shared buffer hash table corrupted.")))); } } diff --git a/src/gausskernel/storage/buffer/bufmgr.cpp b/src/gausskernel/storage/buffer/bufmgr.cpp index 1f384f92b..e26276adc 100644 --- a/src/gausskernel/storage/buffer/bufmgr.cpp +++ b/src/gausskernel/storage/buffer/bufmgr.cpp @@ -137,33 +137,40 @@ static Buffer ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumb * * Only works for shared buffers. */ + //GetPrivateRefCountEntryFast 函数用于快速获取给定缓冲区(buffer)的私有引用计数条目(Private Refcount Entry)的指针。 + //私有引用计数主要用于跟踪某个后端(即数据库的一个客户端连接进程)对某个缓冲区的引用计数。 static PrivateRefCountEntry* GetPrivateRefCountEntryFast(Buffer buffer, PrivateRefCountEntry* &free_entry) { - PrivateRefCountEntry* res = NULL; + PrivateRefCountEntry* res = NULL; // 用于存储找到的引用计数条目的指针 int i; + // 确保输入的buffer是有效的,并且不是本地的 Assert(BufferIsValid(buffer)); Assert(!BufferIsLocal(buffer)); /* - * First search for references in the array, that'll be sufficient in the - * majority of cases. + * 首先在数组中搜索引用,大多数情况下,这应该足够了。 */ for (i = 0; i < REFCOUNT_ARRAY_ENTRIES; i++) { + // 获取当前遍历到的引用计数条目的指针 res = &t_thrd.storage_cxt.PrivateRefCountArray[i]; + // 如果找到了与输入buffer匹配的条目,则返回它的指针 if (res->buffer == buffer) { return res; } - /* Remember where to put a new refcount, should it become necessary. */ + /* 如果尚未找到空闲的引用计数条目且当前条目是无效的,则记下它的位置,可能稍后需要在这里放置一个新的引用计数 */ if (free_entry == NULL && res->buffer == InvalidBuffer) { free_entry = res; } } + + // 如果在数组中没有找到引用计数条目,则返回NULL return NULL; } + /* * Return the PrivateRefCount entry for the passed buffer. * @@ -185,20 +192,18 @@ static PrivateRefCountEntry* GetPrivateRefCountEntryFast(Buffer buffer, PrivateR static PrivateRefCountEntry* GetPrivateRefCountEntrySlow(Buffer buffer, bool create, bool do_move, PrivateRefCountEntry* free_entry) { - Assert(!create || do_move); - Assert(BufferIsValid(buffer)); - Assert(!BufferIsLocal(buffer)); + Assert(!create || do_move); // 确保如果指定创建新条目,则必须进行移动 + Assert(BufferIsValid(buffer)); // 确保缓冲区是有效的 + Assert(!BufferIsLocal(buffer)); // 确保缓冲区不是本地的 /* - * By here we know that the buffer, if already pinned, isn't residing in - * the array. + * 到达这一点,我们知道如果缓冲区已经被固定(pinned),它不在数组中。 */ PrivateRefCountEntry* res = NULL; bool found = false; /* - * Look up the buffer in the hashtable if we've previously overflowed into - * it. + * 如果我们之前已经溢出到哈希表中,则在哈希表中查找缓冲区。 */ if (t_thrd.storage_cxt.PrivateRefCountOverflowed > 0) { res = (PrivateRefCountEntry *)hash_search(t_thrd.storage_cxt.PrivateRefCountHash, (void *)&buffer, HASH_FIND, @@ -207,34 +212,33 @@ static PrivateRefCountEntry* GetPrivateRefCountEntrySlow(Buffer buffer, if (!found) { if (!create) { - /* Neither array nor hash have an entry and no new entry is needed */ + // 数组和哈希表都没有条目,并且不需要新条目 return NULL; } else if (free_entry != NULL) { - /* add entry into the free array slot */ + // 将条目添加到空闲的数组插槽中 free_entry->buffer = buffer; free_entry->refcount = 0; return free_entry; } else { /* - * Move entry from the current clock position in the array into the - * hashtable. Use that slot. + * 将数组中当前时钟位置的条目移动到哈希表中。使用该插槽。 */ PrivateRefCountEntry *array_ent = NULL; PrivateRefCountEntry *hash_ent = NULL; - /* select victim slot */ + // 选择受害者插槽 array_ent = &t_thrd.storage_cxt .PrivateRefCountArray[t_thrd.storage_cxt.PrivateRefCountClock++ % REFCOUNT_ARRAY_ENTRIES]; Assert(array_ent->buffer != InvalidBuffer); - /* enter victim array entry into hashtable */ + // 将受害者数组条目输入到哈希表中 hash_ent = (PrivateRefCountEntry *)hash_search(t_thrd.storage_cxt.PrivateRefCountHash, (void *)&array_ent->buffer, HASH_ENTER, &found); Assert(!found); hash_ent->refcount = array_ent->refcount; - /* fill the now free array slot */ + // 填充现在空闲的数组插槽 array_ent->buffer = buffer; array_ent->refcount = 0; @@ -246,14 +250,14 @@ static PrivateRefCountEntry* GetPrivateRefCountEntrySlow(Buffer buffer, if (!do_move) { return res; } else if (found && free_entry != NULL) { - /* move buffer from hashtable into the free array slot + /* 从哈希表中移动缓冲区到空闲的数组插槽 * - * fill array slot + * 填充数组插槽 */ free_entry->buffer = buffer; free_entry->refcount = res->refcount; - /* delete from hashtable */ + // 从哈希表中删除 (void)hash_search(t_thrd.storage_cxt.PrivateRefCountHash, (void *)&buffer, HASH_REMOVE, &found); Assert(found); Assert(t_thrd.storage_cxt.PrivateRefCountOverflowed > 0); @@ -262,32 +266,31 @@ static PrivateRefCountEntry* GetPrivateRefCountEntrySlow(Buffer buffer, return free_entry; } else { /* - * Swap the entry in the hash table with the one in the array at the - * current clock position. + * 将哈希表中的条目与数组中当前时钟位置的条目交换。 */ PrivateRefCountEntry *array_ent = NULL; PrivateRefCountEntry *hash_ent = NULL; - /* select victim slot */ + // 选择受害者插槽 array_ent = &t_thrd.storage_cxt .PrivateRefCountArray[t_thrd.storage_cxt.PrivateRefCountClock++ % REFCOUNT_ARRAY_ENTRIES]; Assert(array_ent->buffer != InvalidBuffer); - /* enter victim entry into the hashtable */ + // 将受害者条目输入到哈希表中 hash_ent = (PrivateRefCountEntry *)hash_search(t_thrd.storage_cxt.PrivateRefCountHash, (void *)&array_ent->buffer, HASH_ENTER, &found); Assert(!found); hash_ent->refcount = array_ent->refcount; - /* fill now free array entry with previously searched entry */ + // 用之前搜索到的条目填充现在空闲的数组条目 array_ent->buffer = res->buffer; array_ent->refcount = res->refcount; - /* and remove the old entry */ + // 并删除旧条目 (void)hash_search(t_thrd.storage_cxt.PrivateRefCountHash, (void *)&array_ent->buffer, HASH_REMOVE, &found); Assert(found); - /* PrivateRefCountOverflowed stays the same -1 + +1 = 0 */ + // PrivateRefCountOverflowed保持不变 -1 + +1 = 0 return array_ent; } } @@ -295,64 +298,96 @@ static PrivateRefCountEntry* GetPrivateRefCountEntrySlow(Buffer buffer, return NULL; } + /* A combination of GetPrivateRefCountEntryFast & GetPrivateRefCountEntrySlow. */ +//这个函数的主要作用是获取缓冲区的私有引用计数项。它首先尝试从快速路径(GetPrivateRefCountEntryFast)获取引用计数项, +//如果失败则进入慢速路径(GetPrivateRefCountEntrySlow)来获取。 +//这通常用于管理缓冲区的引用计数,以确保正确地释放和管理缓冲区的使用。 PrivateRefCountEntry *GetPrivateRefCountEntry(Buffer buffer, bool create, bool do_move) { - PrivateRefCountEntry *free_entry = NULL; - PrivateRefCountEntry *ref = NULL; + PrivateRefCountEntry *free_entry = NULL; // 初始化一个指向PrivateRefCountEntry的指针,用于存储可用的空闲项 + PrivateRefCountEntry *ref = NULL; // 初始化一个指向PrivateRefCountEntry的指针,用于存储最终的引用计数项 + // 尝试从快速路径获取引用计数项 ref = GetPrivateRefCountEntryFast(buffer, free_entry); + + // 如果快速路径获取失败,进入慢速路径 if (ref == NULL) { ref = GetPrivateRefCountEntrySlow(buffer, create, do_move, free_entry); } - return ref; + return ref; // 返回获取到的引用计数项 } + /* * Returns how many times the passed buffer is pinned by this backend. * * Only works for shared memory buffers! */ + //这个函数的主要作用是获取缓冲区的私有引用计数。 + //首先,它会验证缓冲区的有效性和是否为本地缓冲区。 + //然后,它尝试从快速路径或慢速路径获取引用计数项,如果无法获取,则返回0表示引用计数为0。 + //如果成功获取引用计数项,它将返回引用计数项中的引用计数值。 + //私有引用计数通常用于跟踪缓冲区的引用情况,以确保正确释放和管理缓冲区的使用。 static int32 GetPrivateRefCount(Buffer buffer) { - PrivateRefCountEntry *ref = NULL; + PrivateRefCountEntry *ref = NULL; // 初始化一个指向PrivateRefCountEntry的指针,用于存储引用计数项 - Assert(BufferIsValid(buffer)); - Assert(!BufferIsLocal(buffer)); + Assert(BufferIsValid(buffer)); // 断言缓冲区有效性 + Assert(!BufferIsLocal(buffer)); // 断言缓冲区不是本地缓冲区 - PrivateRefCountEntry *free_entry = NULL; - ref = GetPrivateRefCountEntryFast(buffer, free_entry); + PrivateRefCountEntry *free_entry = NULL; // 初始化一个指向PrivateRefCountEntry的指针,用于存储可用的空闲项 + ref = GetPrivateRefCountEntryFast(buffer, free_entry); // 尝试从快速路径获取引用计数项 + + // 如果快速路径获取失败,进入慢速路径 if (ref == NULL) { ref = GetPrivateRefCountEntrySlow(buffer, false, false, free_entry); + // 第一个false表示不要创建新的引用计数项,第二个false表示不要移动引用计数项 } + if (ref == NULL) { - return 0; + return 0; // 如果无法获取引用计数项,返回0表示引用计数为0 } - return ref->refcount; + + return ref->refcount; // 返回引用计数项中的引用计数值 } + /* * Release resources used to track the reference count of a buffer which we no * longer have pinned and don't want to pin again immediately. */ + //这个函数的主要作用是忘记(清除)私有引用计数项,通常是在引用计数为0时进行。根据引用计数项的存储位置,它有两种不同的处理方式: + +//如果引用计数项在PrivateRefCountArray数组内部,它将引用计数项的缓冲区字段设置为InvalidBuffer,表示忘记了该引用计数项。 + +//如果引用计数项不在PrivateRefCountArray数组内部,它将从PrivateRefCountHash哈希表中删除引用计数项的记录,并更新溢出计数器以表示删除了一个溢出的引用计数项。 + +//这个函数通常用于管理私有引用计数项的生命周期,以确保正确地释放和管理内存。 void ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref) { - Assert(ref->refcount == 0); + Assert(ref->refcount == 0); // 断言引用计数项的引用计数为0 if (ref >= &t_thrd.storage_cxt.PrivateRefCountArray[0] && ref < &t_thrd.storage_cxt.PrivateRefCountArray[REFCOUNT_ARRAY_ENTRIES]) { - ref->buffer = InvalidBuffer; + // 如果引用计数项在PrivateRefCountArray数组内部 + ref->buffer = InvalidBuffer; // 将引用计数项的缓冲区字段设置为InvalidBuffer,表示忘记引用计数项 } else { + // 如果引用计数项不在PrivateRefCountArray数组内部 bool found = false; Buffer buffer = ref->buffer; (void)hash_search(t_thrd.storage_cxt.PrivateRefCountHash, (void *)&buffer, HASH_REMOVE, &found); - Assert(found); + // 从PrivateRefCountHash哈希表中删除引用计数项的记录 + + Assert(found); // 确保找到了要删除的引用计数项 Assert(t_thrd.storage_cxt.PrivateRefCountOverflowed > 0); t_thrd.storage_cxt.PrivateRefCountOverflowed--; + // 更新溢出计数器,表示删除了一个溢出的引用计数项 } } + static void BufferSync(int flags); static uint32 WaitBufHdrUnlocked(BufferDesc* buf); static void WaitIO(BufferDesc* buf); @@ -383,59 +418,60 @@ static bool ConditionalStartBufferIO(BufferDesc* buf, bool forInput); * block will not be delayed by the I/O. Prefetching is optional. * No-op if prefetching isn't compiled in. */ + //这段代码首先检查是否已定义了USE_PREFETCH和USE_POSIX_FADVISE, + //如果定义了这两个宏,才会执行预取操作。然后,它会验证关系的有效性和块号的有效性, + //打开关系的存储管理器(smgr),并在需要的情况下执行预取操作。 void PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum) { #if defined(USE_PREFETCH) && defined(USE_POSIX_FADVISE) - Assert(RelationIsValid(reln)); - Assert(BlockNumberIsValid(blockNum)); + Assert(RelationIsValid(reln)); // 断言关系有效 + Assert(BlockNumberIsValid(blockNum)); // 断言块号有效 - /* Open it at the smgr level if not already done */ + /* 在smgr级别打开关系,如果尚未打开 */ RelationOpenSmgr(reln); if (RelationUsesLocalBuffers(reln)) { - /* see comments in ReadBufferExtended */ + /* 请参阅ReadBufferExtended中的注释 */ if (RELATION_IS_OTHER_TEMP(reln)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); } } - BufferTag new_tag; /* identity of requested block */ - uint32 new_hash; /* hash value for newTag */ - LWLock *new_partition_lock; /* buffer partition lock for it */ + BufferTag new_tag; /* 请求块的标识 */ + uint32 new_hash; /* newTag的哈希值 */ + LWLock *new_partition_lock; /* 用于新标签的缓冲区分区锁 */ int buf_id; - /* create a tag so we can lookup the buffer */ + /* 创建一个标签以便我们可以查找缓冲区 */ INIT_BUFFERTAG(new_tag, reln->rd_smgr->smgr_rnode.node, forkNum, blockNum); - /* determine its hash code and partition lock ID */ + /* 确定其哈希代码和分区锁ID */ new_hash = BufTableHashCode(&new_tag); new_partition_lock = BufMappingPartitionLock(new_hash); - /* see if the block is in the buffer pool already */ + /* 查看块是否已经在缓冲池中 */ (void)LWLockAcquire(new_partition_lock, LW_SHARED); buf_id = BufTableLookup(&new_tag, new_hash); LWLockRelease(new_partition_lock); - /* If not in buffers, initiate prefetch */ + /* 如果不在缓冲区中,则启动预取 */ if (buf_id < 0) { smgrprefetch(reln->rd_smgr, forkNum, blockNum); } /* - * If the block *is* in buffers, we do nothing. This is not really - * ideal: the block might be just about to be evicted, which would be - * stupid since we know we are going to need it soon. But the only - * easy answer is to bump the usage_count, which does not seem like a - * great solution: when the caller does ultimately touch the block, - * usage_count would get bumped again, resulting in too much - * favoritism for blocks that are involved in a prefetch sequence. A - * real fix would involve some additional per-buffer state, and it's - * not clear that there's enough of a problem to justify that. + * 如果块已经在缓冲区中,我们什么也不做。这不是理想的情况:块可能 + * 刚好要被逐出,这会很愚蠢,因为我们知道我们很快就会需要它。但 + * 唯一容易的答案是增加usage_count,这不是一个很好的解决方案:当 + * 调用者最终触摸块时,usage_count会再次增加,导致对于参与预取 + * 序列的块有太多的偏向性。一个真正的修复需要一些额外的每个缓冲 + * 区的状态,并且目前尚不清楚是否有足够的问题来证明这是合理的。 */ #endif /* USE_PREFETCH && USE_POSIX_FADVISE */ } + /* * @Description: ConditionalStartBufferIO: conditionally begin and Asynchronous Prefetch or * WriteBack I/O on this buffer. @@ -459,22 +495,21 @@ void PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum) * @Return: true -- lock sucess; false-- lock failed * @See also: */ -static bool ConditionalStartBufferIO(BufferDesc *buf, bool for_input) +static bool ConditionalStartBufferIO(BufferDesc *buf, bool for_input)//这个函数的目的是协调并控制缓冲区的I/O操作,以避免多个线程同时对同一个缓冲区进行读取或写入操作。 { uint32 buf_state; /* - * Grab the io_in_progress lock so that other processes can wait for - * me to finish the I/O. If we cannot acquire the lock it means - * I/O we want to do is in progress on this buffer. + * 获取io_in_progress_lock锁,以便其他进程可以等待我完成I/O操作。 + * 如果无法获取锁,说明要进行的I/O操作已经在该缓冲区上进行中。 */ if (LWLockConditionalAcquire(buf->io_in_progress_lock, LW_EXCLUSIVE) == true) { - /* Got the lock */ + /* 获取了锁 */ buf_state = LockBufHdr(buf); /* - * If BM_IO_IN_PROGRESS and the lock isn't held, - * it means the i/o was in progress and an error occured, - * and some other thread should take care of this. + * 如果BM_IO_IN_PROGRESS标志被设置,并且锁没有被持有, + * 这意味着I/O操作正在进行中,并且发生了错误,其他线程 + * 应该处理这个错误。 */ if (buf_state & BM_IO_IN_PROGRESS) { UnlockBufHdr(buf, buf_state); @@ -483,20 +518,20 @@ static bool ConditionalStartBufferIO(BufferDesc *buf, bool for_input) } } else { /* - * Could not get the lock... - * Another thread is currently attempting to read - * or write this buffer. We don't need to do our I/O. + * 无法获取锁... + * 另一个线程当前正在尝试读取或写入这个缓冲区。 + * 我们不需要执行我们的I/O操作。 */ return false; } /* - * At this point, there is no I/O active on this buffer - * We are holding the BufHdr lock and the io_in_progress_lock. + * 此时,这个缓冲区上没有进行I/O操作。 + * 我们持有BufHdr锁和io_in_progress_lock锁。 */ buf_state = pg_atomic_read_u32(&buf->state); if (for_input ? (buf_state & BM_VALID) : !(buf_state & BM_DIRTY)) { - /* Another thread already did the I/O */ + /* 另一个线程已经完成了I/O操作 */ UnlockBufHdr(buf, buf_state); LWLockRelease(buf->io_in_progress_lock); return false; @@ -507,12 +542,13 @@ static bool ConditionalStartBufferIO(BufferDesc *buf, bool for_input) UnlockBufHdr(buf, buf_state); /* - * Return holding the io_in_progress_lock, - * The caller is expected to perform the actual I/O. + * 返回时持有io_in_progress_lock锁, + * 调用者应该执行实际的I/O操作。 */ return true; } + /* * @Description: PageListBufferAlloc-- * @@ -788,36 +824,46 @@ static volatile BufferDesc *PageListBufferAlloc(SMgrRelation smgr, char relpersi * @Param[IN] reln: relation * @See also: */ -void PageRangePrefetch(Relation reln, ForkNumber fork_num, BlockNumber block_num, int32 n, uint32 flags = 0, - uint32 col = 0) + //这个函数的目的是预取一定范围内的多个块,它采取以下步骤: + +//分配一个块号列表(block_list),用于存储要预取的块号。这个列表只在函数的上下文中需要,因此使用palloc动态分配内存来存储块号列表。 + +//使用循环填充块号列表,从block_num开始,预取n个连续的块。这些块号被添加到block_list中。 + +//调用PageListPrefetch函数来处理块号列表,该函数将根据列表中的块号执行实际的预取操作。 + +//最后,释放分配的块号列表内存,以避免内存泄漏。 + +//这个函数通常用于优化数据访问性能,以减少等待时间,并提高从磁盘读取数据的效率。 +void PageRangePrefetch(Relation reln, ForkNumber fork_num, BlockNumber block_num, int32 n, uint32 flags = 0, uint32 col = 0) { - BlockNumber *block_list = NULL; - BlockNumber *block_ptr = NULL; + BlockNumber *block_list = NULL; // 用于存储块号列表的指针 + BlockNumber *block_ptr = NULL; // 指向块号列表的当前位置 BlockNumber block, end; /* - * Allocate the block list. It is only required - * within the context of this function. + * 分配块号列表。它只在此函数的上下文中需要。 */ block_list = (BlockNumber *)palloc(sizeof(BlockNumber) * n); /* - * Fill the blockList and call PageListPrefetch to process it. + * 填充块列表并调用PageListPrefetch来处理它。 */ for (block = block_num, end = block_num + n, block_ptr = block_list; block < end; block++) { - *(block_ptr++) = block; + *(block_ptr++) = block; // 将块号添加到块列表中 } /* - * Call PageListPrefetch to process the list + * 调用PageListPrefetch来处理列表 */ PageListPrefetch(reln, fork_num, block_list, n, flags, col); - pfree(block_list); + pfree(block_list); // 释放分配的块列表内存 return; } + /* * @Description: PageListPrefetch * The dispatch list of AioDispatchDesc_t structures is released -- 2.34.1 From 66d405f769dbd183bbe7e428e3e427e4527f4f24 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 22:14:19 +0800 Subject: [PATCH 08/19] Enter --- .../storage/access/common/indextuple.cpp | 318 ++++++++++-------- 1 file changed, 178 insertions(+), 140 deletions(-) diff --git a/src/gausskernel/storage/access/common/indextuple.cpp b/src/gausskernel/storage/access/common/indextuple.cpp index e3f4cf93d..0846debd0 100644 --- a/src/gausskernel/storage/access/common/indextuple.cpp +++ b/src/gausskernel/storage/access/common/indextuple.cpp @@ -30,47 +30,56 @@ * index_ tuple interface routines * ---------------------------------------------------------------- */ +// 函数用于在索引元组中查找指定属性号(attrno)的属性值 static inline bool index_findattr(Relation irel, IndexTuple itup, AttrNumber attrno, Datum *value) { - bool isnull = false; + bool isnull = false; // 初始化是否为NULL标志 - TupleDesc tupdesc = RelationGetDescr(irel); - int nattrs = IndexRelationGetNumberOfAttributes(irel); + TupleDesc tupdesc = RelationGetDescr(irel); // 获取索引关系的元组描述符 + int nattrs = IndexRelationGetNumberOfAttributes(irel); // 获取索引关系的属性数目 for (int i = 0; i < nattrs; i++) { + // 检查索引属性的键(indkey)中是否包含要查找的属性号 if (irel->rd_index->indkey.values[i] == attrno) { + // 如果匹配,使用index_getattr函数获取属性值 *value = index_getattr(itup, i + 1, tupdesc, &isnull); break; } } - return !isnull; + return !isnull; // 返回是否找到非NULL的属性值 } + +// 函数用于获取索引元组的表OID Oid index_getattr_tableoid(Relation irel, IndexTuple itup) { - Datum val = 0; - Oid tableoid = InvalidOid; + Datum val = 0; // 初始化属性值 + Oid tableoid = InvalidOid; // 初始化表OID为无效OID - Assert(RelationIsIndex(irel)); + Assert(RelationIsIndex(irel)); // 断言,确保传入的关系是索引关系 + // 调用index_findattr函数查找表OID属性值 if (index_findattr(irel, itup, TableOidAttributeNumber, &val)) { - tableoid = DatumGetUInt32(val); + tableoid = DatumGetUInt32(val); // 将属性值转换为Oid类型 } - return tableoid; + return tableoid; // 返回表OID } + +// 函数用于获取索引元组的桶ID int2 index_getattr_bucketid(Relation irel, IndexTuple itup) { - Datum val = 0; - int2 bucketid = InvalidBktId; + Datum val = 0; // 初始化属性值 + int2 bucketid = InvalidBktId; // 初始化桶ID为无效桶ID - Assert(RelationIsIndex(irel)); + Assert(RelationIsIndex(irel)); // 断言,确保传入的关系是索引关系 + // 调用index_findattr函数查找桶ID属性值 if (index_findattr(irel, itup, BucketIdAttributeNumber, &val)) { - bucketid = DatumGetInt16(val); + bucketid = DatumGetInt16(val); // 将属性值转换为int2类型 } - return bucketid; + return bucketid; // 返回桶ID } /* ---------------- @@ -84,39 +93,43 @@ IndexTuple index_form_tuple(TupleDesc tuple_descriptor, Datum* values, const boo { char *tp = NULL; /* tuple pointer */ IndexTuple tuple = NULL; /* return tuple */ - Size size, data_size, hoff; - int i; - unsigned short infomask = 0; - bool hasnull = false; - uint16 tupmask = 0; - int attributeNum = tuple_descriptor->natts; + Size size, data_size, hoff; // 声明一些用于计算大小和偏移量的变量 +int i; +unsigned short infomask = 0; // 初始化元组信息掩码 +bool hasnull = false; // 初始化是否包含NULL值的标志 +uint16 tupmask = 0; // 初始化元组掩码 +int attributeNum = tuple_descriptor->natts; // 获取属性数目 - Size (*computedatasize_tuple)(TupleDesc tuple_desc, Datum* values, const bool* isnull); - void (*filltuple)(TupleDesc tuple_desc, Datum* values, const bool* isnull, char* data, Size data_size, uint16* infomask, bits8* bit); +// 声明函数指针,用于计算数据大小和填充元组数据 +Size (*computedatasize_tuple)(TupleDesc tuple_desc, Datum* values, const bool* isnull); +void (*filltuple)(TupleDesc tuple_desc, Datum* values, const bool* isnull, char* data, Size data_size, uint16* infomask, bits8* bit); - computedatasize_tuple = &heap_compute_data_size; - filltuple = &heap_fill_tuple; +// 初始化函数指针 +computedatasize_tuple = &heap_compute_data_size; +filltuple = &heap_fill_tuple; #ifdef TOAST_INDEX_HACK - Datum untoasted_values[INDEX_MAX_KEYS]; - bool untoasted_free[INDEX_MAX_KEYS]; +Datum untoasted_values[INDEX_MAX_KEYS]; // 声明未压缩的属性值数组 +bool untoasted_free[INDEX_MAX_KEYS]; // 声明未压缩的属性值释放标志数组 #endif - if (attributeNum > INDEX_MAX_KEYS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_COLUMNS), - errmsg("number of index columns (%d) exceeds limit (%d)", attributeNum, INDEX_MAX_KEYS))); +// 检查属性数目是否超过索引键的最大限制 +if (attributeNum > INDEX_MAX_KEYS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_COLUMNS), + errmsg("number of index columns (%d) exceeds limit (%d)", attributeNum, INDEX_MAX_KEYS))); #ifdef TOAST_INDEX_HACK - uint32 toastTarget = TOAST_INDEX_TARGET; - if (tuple_descriptor->tdTableAmType == TAM_USTORE) { - toastTarget = UTOAST_INDEX_TARGET; - } - for (i = 0; i < attributeNum; i++) { - Form_pg_attribute att = tuple_descriptor->attrs[i]; +uint32 toastTarget = TOAST_INDEX_TARGET; // 初始化TOAST目标大小 +if (tuple_descriptor->tdTableAmType == TAM_USTORE) { + toastTarget = UTOAST_INDEX_TARGET; // 如果表AM类型是UTable,则使用UTable的TOAST目标大小 +} +for (i = 0; i < attributeNum; i++) { + Form_pg_attribute att = tuple_descriptor->attrs[i]; - untoasted_values[i] = values[i]; - untoasted_free[i] = false; + untoasted_values[i] = values[i]; // 复制未压缩的属性值 + untoasted_free[i] = false; // 初始化未压缩的属性值释放标志 +} /* Do nothing if value is NULL or not of varlena type */ if (isnull[i] || att->attlen != -1) @@ -137,60 +150,71 @@ IndexTuple index_form_tuple(TupleDesc tuple_descriptor, Datum* values, const boo * If value is above size target, and is of a compressible datatype, * try to compress it in-line. */ - if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) && - VARSIZE(DatumGetPointer(untoasted_values[i])) > toastTarget && - (att->attstorage == 'x' || att->attstorage == 'm')) { - Datum cvalue = toast_compress_datum(untoasted_values[i]); - if (DatumGetPointer(cvalue) != NULL) { + // 遍历所有属性值,检查是否需要进行TOAST压缩 +for (i = 0; i < attributeNum; i++) { + // 如果属性值不是扩展形式并且大于TOAST目标大小,并且属性存储类型是扩展形式或主存储形式 + if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) && + VARSIZE(DatumGetPointer(untoasted_values[i])) > toastTarget && + (att->attstorage == 'x' || att->attstorage == 'm')) { + // 对属性值进行TOAST压缩 + Datum cvalue = toast_compress_datum(untoasted_values[i]); + if (DatumGetPointer(cvalue) != NULL) { /* successful compression */ - if (untoasted_free[i]) - pfree(DatumGetPointer(untoasted_values[i])); - untoasted_values[i] = cvalue; - untoasted_free[i] = true; - } + if (untoasted_free[i]) + pfree(DatumGetPointer(untoasted_values[i])); + untoasted_values[i] = cvalue; + untoasted_free[i] = true; } } -#endif +} - for (i = 0; i < attributeNum; i++) { - if (isnull[i]) { - hasnull = true; - break; - } +// 检查是否有NULL属性值 +for (i = 0; i < attributeNum; i++) { + if (isnull[i]) { + hasnull = true; + break; } +} - if (hasnull) - infomask |= INDEX_NULL_MASK; +if (hasnull) + infomask |= INDEX_NULL_MASK; - hoff = IndexInfoFindDataOffset(infomask); +// 计算数据在元组中的偏移量 +hoff = IndexInfoFindDataOffset(infomask); + +// 计算数据大小 #ifdef TOAST_INDEX_HACK - data_size = computedatasize_tuple(tuple_descriptor, untoasted_values, isnull); +data_size = computedatasize_tuple(tuple_descriptor, untoasted_values, isnull); #else - data_size = computedatasize_tuple(tuple_descriptor, values, isnull); +data_size = computedatasize_tuple(tuple_descriptor, values, isnull); #endif - size = hoff + data_size; - size = MAXALIGN(size); /* be conservative */ - tp = (char*)palloc0(size); - tuple = (IndexTuple)tp; +size = hoff + data_size; // 计算总大小 +size = MAXALIGN(size); // 对齐到最大值 - filltuple(tuple_descriptor, +// 分配元组数据的内存 +tp = (char*)palloc0(size); +tuple = (IndexTuple)tp; + +// 填充元组数据 +filltuple(tuple_descriptor, #ifdef TOAST_INDEX_HACK - untoasted_values, + untoasted_values, #else - values, + values, #endif - isnull, - (char*)tp + hoff, - data_size, - &tupmask, - (hasnull ? (bits8*)tp + sizeof(IndexTupleData) : NULL)); + isnull, + (char*)tp + hoff, // 数据的偏移量 + data_size, + &tupmask, + (hasnull ? (bits8*)tp + sizeof(IndexTupleData) : NULL)); // 如果包含NULL值,则设置位图 +// 如果使用了TOAST压缩,释放未压缩的属性值 #ifdef TOAST_INDEX_HACK - for (i = 0; i < attributeNum; i++) { - if (untoasted_free[i]) - pfree(DatumGetPointer(untoasted_values[i])); - } +for (i = 0; i < attributeNum; i++) { + if (untoasted_free[i]) + pfree(DatumGetPointer(untoasted_values[i])); +} #endif /* @@ -206,14 +230,17 @@ IndexTuple index_form_tuple(TupleDesc tuple_descriptor, Datum* values, const boo * Here we make sure that the size will fit in the field reserved for it * in t_info. */ - if ((size & INDEX_SIZE_MASK) != size) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("index row requires %lu bytes, maximum size is %lu", - (unsigned long)size, - (unsigned long)INDEX_SIZE_MASK))); + // 检查索引行大小是否超过了索引行大小限制(INDEX_SIZE_MASK) +if ((size & INDEX_SIZE_MASK) != size) { + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("index row requires %lu bytes, maximum size is %lu", + (unsigned long)size, + (unsigned long)INDEX_SIZE_MASK))); +} - infomask |= size; +// 将大小信息添加到infomask中 +infomask |= size; /* * initialize metadata @@ -337,25 +364,34 @@ Datum nocache_index_getattr(IndexTuple tup, uint32 attnum, TupleDesc tuple_desc) att[0]->attcacheoff = 0; /* we might have set some offsets in the slow path previously */ - while (j < natts && att[j]->attcacheoff > 0) - j++; + // 循环直到找到第一个具有正值attcacheoff的属性或达到属性数上限 +while (j < natts && att[j]->attcacheoff > 0) + j++; - off = att[j - 1]->attcacheoff + att[j - 1]->attlen; +// 计算偏移量,从上一步找到的第一个属性之后开始 +off = att[j - 1]->attcacheoff + att[j - 1]->attlen; - for (; j < natts; j++) { - if (att[j]->attlen <= 0) - break; +// 遍历属性,为每个属性计算偏移量 +for (; j < natts; j++) { + // 如果属性的长度小于等于0,退出循环 + if (att[j]->attlen <= 0) + break; - off = att_align_nominal((uint32)off, att[j]->attalign); + // 根据属性的对齐方式对偏移量进行对齐 + off = att_align_nominal((uint32)off, att[j]->attalign); - att[j]->attcacheoff = off; + // 将计算后的偏移量存储在属性描述符的attcacheoff字段中 + att[j]->attcacheoff = off; - off += att[j]->attlen; - } + // 增加偏移量,以包括当前属性 + off += att[j]->attlen; +} - Assert(j > attnum); +// 断言确保找到的属性数量(j)大于原始属性编号(attnum) +Assert(j > attnum); - off = att[attnum]->attcacheoff; +// 将指定属性(attnum)的偏移量存储在off中 +off = att[attnum]->attcacheoff; } else { bool usecache = true; uint32 i; @@ -387,27 +423,28 @@ Datum nocache_index_getattr(IndexTuple tup, uint32 attnum, TupleDesc tuple_desc) * no pad bytes in any case: then the offset will be valid for * either an aligned or unaligned value. */ - if (usecache && (uintptr_t)(off) == att_align_nominal((uint32)off, att[i]->attalign)) - att[i]->attcacheoff = off; - else { - off = att_align_pointer((uint32)off, att[i]->attalign, -1, tp + off); - usecache = false; - } + if (usecache && (uintptr_t)(off) == att_align_nominal((uint32)off, att[i]->attalign)) + att[i]->attcacheoff = off; // 如果使用缓存且与属性对齐,将缓存偏移设置为 off +else { + off = att_align_pointer((uint32)off, att[i]->attalign, -1, tp + off); // 否则,重新计算 off 以满足属性对齐,并禁用缓存 + usecache = false; +} } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal((uint32)off, att[i]->attalign); if (usecache) att[i]->attcacheoff = off; - } + }// 如果允许缓存,将缓存偏移设置为 off - if (i == attnum) - break; + if (i == attnum) + break; // 如果 i 等于目标属性编号,跳出循环 - off = att_addlength_pointer(off, att[i]->attlen, tp + off); +off = att_addlength_pointer(off, att[i]->attlen, tp + off); // 根据属性长度调整 off + +if (usecache && att[i]->attlen <= 0) + usecache = false; // 如果允许缓存且属性长度小于等于0,禁用缓存 - if (usecache && att[i]->attlen <= 0) - usecache = false; } } @@ -441,13 +478,14 @@ IndexTuple CopyIndexTuple(IndexTuple source) Size size; errno_t rc = EOK; - size = IndexTupleSize(source); - result = (IndexTuple)palloc(size); - rc = memcpy_s(result, size, source, size); - securec_check(rc, "\0", "\0"); - return result; + size = IndexTupleSize(source); // 计算源索引元组的大小 + result = (IndexTuple)palloc(size); // 为结果分配内存空间 + rc = memcpy_s(result, size, source, size); // 复制源索引元组到结果 + securec_check(rc, "\0", "\0"); // 检查复制是否成功 + return result; // 返回复制的索引元组 } + /* * Create a palloc'd copy of an index tuple with a reserved space. */ @@ -457,12 +495,12 @@ IndexTuple CopyIndexTupleAndReserveSpace(IndexTuple source, Size reserved_size) Size size; errno_t rc = EOK; - size = IndexTupleSize(source); - result = (IndexTuple)palloc0(size + reserved_size); - rc = memcpy_s(result, size, source, size); - securec_check(rc, "\0", "\0"); - IndexTupleSetSize(result, size + reserved_size); - return result; + size = IndexTupleSize(source); // 计算源索引元组的大小 + result = (IndexTuple)palloc0(size + reserved_size); // 为结果分配内存空间,并清零 + rc = memcpy_s(result, size, source, size); // 复制源索引元组到结果 + securec_check(rc, "\0", "\0"); // 检查复制是否成功 + IndexTupleSetSize(result, size + reserved_size); // 设置结果的大小 + return result; // 返回带有保留空间的复制索引元组 } /* @@ -471,25 +509,25 @@ IndexTuple CopyIndexTupleAndReserveSpace(IndexTuple source, Size reserved_size) */ IndexTuple index_truncate_tuple(TupleDesc tupleDescriptor, IndexTuple olditup, int new_indnatts) { - TupleDesc itupdesc = CreateTupleDescCopyConstr(tupleDescriptor); + TupleDesc itupdesc = CreateTupleDescCopyConstr(tupleDescriptor); // 创建源元组描述符的副本 Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; IndexTuple newitup; - Assert(tupleDescriptor->natts <= INDEX_MAX_KEYS); - Assert(new_indnatts > 0); - Assert(new_indnatts < tupleDescriptor->natts); + Assert(tupleDescriptor->natts <= INDEX_MAX_KEYS); // 断言检查索引列数不超过最大限制 + Assert(new_indnatts > 0); // 断言检查新索引列数大于0 + Assert(new_indnatts < tupleDescriptor->natts); // 断言检查新索引列数小于原索引列数 - index_deform_tuple(olditup, tupleDescriptor, values, isnull); + index_deform_tuple(olditup, tupleDescriptor, values, isnull); // 解析原索引元组 - /* form new tuple that will contain only key attributes */ - itupdesc->natts = new_indnatts; - newitup = index_form_tuple(itupdesc, values, isnull); - newitup->t_tid = olditup->t_tid; + /* form new tuple that will contain only key attributes */ + itupdesc->natts = new_indnatts; // 设置新描述符的属性数 + newitup = index_form_tuple(itupdesc, values, isnull); // 创建新索引元组 + newitup->t_tid = olditup->t_tid; // 设置新索引元组的 TID 与原索引元组相同 - FreeTupleDesc(itupdesc); - Assert(IndexTupleSize(newitup) <= IndexTupleSize(olditup)); - return newitup; + FreeTupleDesc(itupdesc); // 释放新元组描述符 + Assert(IndexTupleSize(newitup) <= IndexTupleSize(olditup)); // 断言检查新索引元组的大小不超过原索引元组 + return newitup; // 返回新的索引元组 } /* @@ -502,16 +540,16 @@ IndexTuple UBTreeIndexTruncateTuple(TupleDesc tupleDescriptor, IndexTuple olditu Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; IndexTuple newitup; - int indnatts = tupleDescriptor->natts; + int indnatts = tupleDescriptor->natts;// 创建源元组描述符的副本 - if (indnatts > INDEX_MAX_KEYS) { + if (indnatts > INDEX_MAX_KEYS) { ereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("number of index columns (%d) exceeds limit (%d)", indnatts, INDEX_MAX_KEYS))); - } + } // 如果索引列数超过了最大限制,报错 - Assert(leavenatts > 0); - Assert(leavenatts <= indnatts); + Assert(leavenatts > 0); // 断言检查要保留的新索引列数大于0 + Assert(leavenatts <= indnatts); // 断言检查要保留的新索引列数不超过原索引列数 /* Easy case: no truncation actually required */ if (leavenatts == indnatts) { @@ -535,13 +573,13 @@ IndexTuple UBTreeIndexTruncateTuple(TupleDesc tupleDescriptor, IndexTuple olditu index_deform_tuple(olditup, tupleDescriptor, values, isnull); /* form new tuple that will contain only key attributes */ - itupdesc->natts = leavenatts; - newitup = index_form_tuple(itupdesc, values, isnull); - newitup->t_tid = olditup->t_tid; - Assert(IndexTupleSize(newitup) <= IndexTupleSize(olditup)); + itupdesc->natts = leavenatts; // 设置新描述符的属性数 + newitup = index_form_tuple(itupdesc, values, isnull); // 创建新索引元组 + newitup->t_tid = olditup->t_tid; // 设置新索引元组的 TID 与原索引元组相同 + Assert(IndexTupleSize(newitup) <= IndexTupleSize(olditup)); // 断言检查新索引元组的大小不超过原索引元组 - FreeTupleDesc(itupdesc); + FreeTupleDesc(itupdesc); // 释放新元组描述符 - return newitup; + return newitup; // 返回新的索引元组 } -- 2.34.1 From e87d746b458828f00098eddd0c9789a9afe87419 Mon Sep 17 00:00:00 2001 From: Cachuela Date: Thu, 5 Oct 2023 22:20:56 +0800 Subject: [PATCH 09/19] enter --- src/gausskernel/storage/buffer/freelist.cpp | 144 +++++++++++++------- 1 file changed, 92 insertions(+), 52 deletions(-) diff --git a/src/gausskernel/storage/buffer/freelist.cpp b/src/gausskernel/storage/buffer/freelist.cpp index af45abcab..339544080 100644 --- a/src/gausskernel/storage/buffer/freelist.cpp +++ b/src/gausskernel/storage/buffer/freelist.cpp @@ -81,17 +81,24 @@ void PageListBackWrite(uint32* bufList, int32 n, int32* bufs_reusable = NULL); /* opt reusable count returned */ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, uint32* buf_state); -static void perform_delay(StrategyDelayStatus *status) +static void perform_delay(StrategyDelayStatus *status)//这个函数的目的是在满足一定条件时执行延迟操作。条件包括已经重试的次数超过最大次数(MAX_RETRY_TIMES)并且脏页的数量超过了一个阈值(g_instance.attr.attr_storage.NBuffers * NEED_DELAY_RETRY_GET_BUF)。 { if (++(status->retry_times) > MAX_RETRY_TIMES && get_dirty_page_num() > g_instance.attr.attr_storage.NBuffers * NEED_DELAY_RETRY_GET_BUF) { + // 如果已经重试了最大���数,并且脏页数量超过了阈值 + if (status->cur_delay_time == 0) { + // 如果当前延迟时间为0,则初始化为最小延迟时间 status->cur_delay_time = MIN_DELAY_RETRY; } + + // 通过睡眠来延迟操作 pg_usleep(status->cur_delay_time); - /* increase delay by a random fraction between 1X and 2X */ + /* 增加延迟时间,增加的时间在1X和2X之间的随机分数 */ status->cur_delay_time += (int)(status->cur_delay_time * ((double)random() / (double)MAX_RANDOM_VALUE) + 0.5); + + // 如果当前延迟时间超过最大延迟时间,则重新初始化为最小延迟时间 if (status->cur_delay_time > MAX_DELAY_RETRY) { status->cur_delay_time = MIN_DELAY_RETRY; } @@ -100,33 +107,38 @@ static void perform_delay(StrategyDelayStatus *status) } + /* * ClockSweepTick - Helper routine for StrategyGetBuffer() * * Move the clock hand one buffer ahead of its current position and return the * id of the buffer now under the hand. */ + //这个函数的目的是根据时钟扫描算法选择要回收的缓冲区。 + //函数首先通过原子操作增加一个称为"victim"的计数器, + //该计数器用于标识下一个要回收的缓冲区。 + //如果"victim"的值超过了可用的缓冲区数量,就需要对其进行包装, + //以确保在BufferDescriptors中查找时不会越界。 static inline uint32 ClockSweepTick(int max_nbuffer_can_use) { uint32 victim; /* - * Atomically move hand ahead one buffer - if there's several processes - * doing this, this can lead to buffers being returned slightly out of - * apparent order. + * 原子地将“手”向前移动一个缓冲区 - 如果有多个进程执行此操作, + * 这可能导致缓冲区的返回略微不按顺序。 */ victim = pg_atomic_fetch_add_u32(&t_thrd.storage_cxt.StrategyControl->nextVictimBuffer, 1); + if (victim >= (uint32)max_nbuffer_can_use) { uint32 original_victim = victim; - /* always wrap what we look up in BufferDescriptors */ + /* 始终对BufferDescriptors中查找的内容进行包装 */ victim = victim % max_nbuffer_can_use; /* - * If we're the one that just caused a wraparound, force - * completePasses to be incremented while holding the spinlock. We - * need the spinlock so StrategySyncStart() can return a consistent - * value consisting of nextVictimBuffer and completePasses. + * 如果我们是导致回绕的那个进程,就需要在持有自旋锁的情况下强制 + * 递增completePasses。我们需要自旋锁以便StrategySyncStart()可以返回 + * 一个由nextVictimBuffer和completePasses组成的一致值。 */ if (victim == 0) { uint32 expected; @@ -137,12 +149,10 @@ static inline uint32 ClockSweepTick(int max_nbuffer_can_use) while (!success) { /* - * Acquire the spinlock while increasing completePasses. That - * allows other readers to read nextVictimBuffer and - * completePasses in a consistent manner which is required for - * StrategySyncStart(). In theory delaying the increment - * could lead to a overflow of nextVictimBuffers, but that's - * highly unlikely and wouldn't be particularly harmful. + * 在增加completePasses时获取自旋锁。这允许其他读取器以一致的方式 + * 读取nextVictimBuffer和completePasses,这是StrategySyncStart()所 + * 需要的。理论上,延迟增加可能导致nextVictimBuffers溢出,但这是非常不 + * 可能的,也不会特别有害。 */ SpinLockAcquire(&t_thrd.storage_cxt.StrategyControl->buffer_strategy_lock); @@ -159,6 +169,7 @@ static inline uint32 ClockSweepTick(int max_nbuffer_can_use) return victim; } + /* * StrategyGetBuffer * @@ -177,20 +188,30 @@ static inline uint32 ClockSweepTick(int max_nbuffer_can_use) * If the fraction is too small, we will increase dynamiclly to avoid elog(ERROR) * in `Startup' process because of ERROR will promote to FATAL. */ + /* +这个函数的主要目的是从共享缓冲区池中获取一个缓冲区,以供后续的读写操作使用。函数首先尝试使用给定的策略对象(如果提供的话)获取缓冲区。 +如果策略对象无法提供缓冲区,或者没有提供策略对象,那么函数将使用时钟扫描算法选择要回收的缓冲区。 + +需要注意的是,代码中还包含了一些条件判断,用于处理不同的情况,如在热备模式下、是否启用了增量检查点、缓冲区是否被锁定等情况。 +根据这些情况,函数会采取不同的策略来获取缓冲区或等待合适的缓冲区可用。 + +总的来说,这段代码实现了一种高效的缓冲区分配策略,以确保在需要时能够获取到合适的缓冲区, +同时也考虑了各种情况下的异常处理。这是数据库管理系统中非常重要的一部分,影响了系统的性能和可用性。 + */ BufferDesc* StrategyGetBuffer(BufferAccessStrategy strategy, uint32* buf_state) { BufferDesc *buf = NULL; int bgwproc_no; int try_counter; - uint32 local_buf_state = 0; /* to avoid repeated (de-)referencing */ + uint32 local_buf_state = 0; /* 用于避免重复引用 */ int max_buffer_can_use; bool am_standby = RecoveryInProgress(); StrategyDelayStatus retry_lock_status = { 0, 0 }; StrategyDelayStatus retry_buf_status = { 0, 0 }; /* - * If given a strategy object, see whether it can select a buffer. We - * assume strategy objects don't need buffer_strategy_lock. + * 如果给定了策略对象,尝试使用策略对象来获取缓冲区。我们假设策略对象不需要 + * buffer_strategy_lock。 */ if (strategy != NULL) { buf = GetBufferFromRing(strategy, buf_state); @@ -200,38 +221,28 @@ BufferDesc* StrategyGetBuffer(BufferAccessStrategy strategy, uint32* buf_state) } /* - * If asked, we need to waken the bgwriter. Since we don't want to rely on - * a spinlock for this we force a read from shared memory once, and then - * set the latch based on that value. We need to go through that length - * because otherwise bgprocno might be reset while/after we check because - * the compiler might just reread from memory. - * - * This can possibly set the latch of the wrong process if the bgwriter - * dies in the wrong moment. But since PGPROC->procLatch is never - * deallocated the worst consequence of that is that we set the latch of - * some arbitrary process. + * 如果需要,唤醒后台写入进程(bgwriter)。 */ bgwproc_no = INT_ACCESS_ONCE(t_thrd.storage_cxt.StrategyControl->bgwprocno); if (bgwproc_no != -1) { - /* reset bgwprocno first, before setting the latch */ + /* 先重置bgwprocno,然后设置latch */ t_thrd.storage_cxt.StrategyControl->bgwprocno = -1; /* - * Not acquiring ProcArrayLock here which is slightly icky. It's - * actually fine because procLatch isn't ever freed, so we just can - * potentially set the wrong process' (or no process') latch. + * 在这里不获取ProcArrayLock,这可能有点不太好。实际上,这是可以接受的, + * 因为procLatch永远不会被释放,所以我们可能会将latch设置为错误的进程(或者 + * 没有进程的latch)。 */ SetLatch(&g_instance.proc_base_all_procs[bgwproc_no]->procLatch); } /* - * We count buffer allocation requests so that the bgwriter can estimate - * the rate of buffer consumption. Note that buffers recycled by a - * strategy object are intentionally not counted here. + * 我们计算缓冲区分配请求的数量,以便后台写入进程(bgwriter)可以估算缓冲区的使用速率。 + * 需要注意的是,由策略对象回收的缓冲区在这里不会计数。 */ (void)pg_atomic_fetch_add_u32(&t_thrd.storage_cxt.StrategyControl->numBufferAllocs, 1); - /* Check the Candidate list */ + /* 检查候选列表 */ if (ENABLE_INCRE_CKPT && pg_atomic_read_u32(&g_instance.ckpt_cxt_ctl->current_page_writer_count) > 1) { if (NEED_CONSIDER_USECOUNT) { const uint32 MAX_RETRY_SCAN_CANDIDATE_LISTS = 5; @@ -259,7 +270,7 @@ BufferDesc* StrategyGetBuffer(BufferAccessStrategy strategy, uint32* buf_state) } retry: - /* Nothing on the freelist, so run the "clock sweep" algorithm */ + /* 在自由列表上没有可用的缓冲区,因此运行“时钟扫描”算法 */ if (am_standby) max_buffer_can_use = int(NORMAL_SHARED_BUFFER_NUM * u_sess->attr.attr_storage.shared_buffers_fraction); else @@ -269,7 +280,7 @@ retry: for (;;) { buf = GetBufferDescriptor(ClockSweepTick(max_buffer_can_use)); /* - * If the buffer is pinned, we cannot use it. + * 如果缓冲区被锁定(pinned),则无法使用它。 */ if (!retryLockBufHdr(buf, &local_buf_state)) { if (--try_get_loc_times == 0) { @@ -284,7 +295,7 @@ retry: retry_lock_status.retry_times = 0; if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0 && !(local_buf_state & BM_IS_META) && (backend_can_flush_dirty_page() || !(local_buf_state & BM_DIRTY))) { - /* Found a usable buffer */ + /* 找到可用的缓冲区 */ if (strategy != NULL) AddBufferToRing(strategy, buf); *buf_state = local_buf_state; @@ -292,11 +303,9 @@ retry: return buf; } else if (--try_counter == 0) { /* - * We've scanned all the buffers without making any state changes, - * so all the buffers are pinned (or were when we looked at them). - * We could hope that someone will free one eventually, but it's - * probably better to fail than to risk getting stuck in an - * infinite loop. + * 在没有对缓冲区状态进行任何更改的情况下,我们已经扫描了所有的缓冲区, + * 因此所有的缓冲区都被锁定(或者在我们查看它们时已经被锁定)。我们可以 + * 希望有人最终会释放其中一个,但最好是失败,而不是冒险陷入无限循环。 */ UnlockBufHdr(buf, local_buf_state); @@ -322,10 +331,11 @@ retry: perform_delay(&retry_buf_status); } - /* not reached */ + /* 不会执行到这里 */ return NULL; } + /* * StrategySyncStart -- tell BufferSync where to start syncing * @@ -337,31 +347,51 @@ retry: * allocs if non-NULL pointers are passed. The alloc count is reset after * being read. */ + /* +这段代码主要用于获取缓冲区池中的相关信息, +包括下一个待回收的缓冲区的编号、 +完成的扫描轮数以及缓冲区分配请求的计数。 +这些信息可以用于监视和调优缓冲区管理策略, +以确保数据库系统的性能和稳定性。 + */ int StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc) { uint32 next_victim_buffer; int result; + /* 获取缓冲区策略控制锁 */ SpinLockAcquire(&t_thrd.storage_cxt.StrategyControl->buffer_strategy_lock); + + /* 获取下一个待回收的缓冲区的编号 */ next_victim_buffer = pg_atomic_read_u32(&t_thrd.storage_cxt.StrategyControl->nextVictimBuffer); + + /* 计算结果,用于返回 */ result = next_victim_buffer % TOTAL_BUFFER_NUM; + /* 获取完成的扫描轮数,如果complete_passes不为NULL */ if (complete_passes != NULL) { *complete_passes = t_thrd.storage_cxt.StrategyControl->completePasses; + /* - * Additionally add the number of wraparounds that happened before - * completePasses could be incremented. C.f. ClockSweepTick(). + * 此外,还需要加上在completePasses被增加之前发生的循环次数。参见 + * ClockSweepTick() 函数。 */ - *complete_passes += next_victim_buffer / (unsigned int) NORMAL_SHARED_BUFFER_NUM; + *complete_passes += next_victim_buffer / (unsigned int)NORMAL_SHARED_BUFFER_NUM; } + /* 获取缓冲区分配请求的计数,如果num_buf_alloc不为NULL */ if (num_buf_alloc != NULL) { *num_buf_alloc = pg_atomic_exchange_u32(&t_thrd.storage_cxt.StrategyControl->numBufferAllocs, 0); } + + /* 释放缓冲区策略控制锁 */ SpinLockRelease(&t_thrd.storage_cxt.StrategyControl->buffer_strategy_lock); + + /* 返回结果 */ return result; } + /* * StrategyNotifyBgWriter -- set or clear allocation notification latch * @@ -370,15 +400,25 @@ int StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc) * happens. This feature is used by the bgwriter process to wake itself up * from hibernation, and is not meant for anybody else to use. */ + /* +这段代码的主要作用是设置bgwriter进程的编号, +以便通知它有关缓冲区管理策略的信息。 +通常情况下,这个函数会在后台写入进程需要执行某些特定操作时被调用, +以确保缓冲区管理策略的协调和优化。 + */ void StrategyNotifyBgWriter(int bgwproc_no) { /* - * We acquire the BufFreelistLock just to ensure that the store appears - * atomic to StrategyGetBuffer. The bgwriter should call this rather - * infrequently, so there's no performance penalty from being safe. + * 我们获取BufFreelistLock仅仅是为了确保存储看起来是原子的,对于 + * StrategyGetBuffer来说。bgwriter应该相对不频繁地调用这个函数,因此 + * 安全性方面没有性能开销。 */ SpinLockAcquire(&t_thrd.storage_cxt.StrategyControl->buffer_strategy_lock); + + /* 设置bgwriter进程的编号,通知它有关缓冲区管理策略的信息 */ t_thrd.storage_cxt.StrategyControl->bgwprocno = bgwproc_no; + + /* 释放BufFreelistLock */ SpinLockRelease(&t_thrd.storage_cxt.StrategyControl->buffer_strategy_lock); } -- 2.34.1 From 3f585e4d3519622405ee89f651db2bd875c6e9be Mon Sep 17 00:00:00 2001 From: Cachuela Date: Thu, 5 Oct 2023 22:29:04 +0800 Subject: [PATCH 10/19] enter --- src/gausskernel/storage/buffer/freelist.cpp | 143 ++++++++++++-------- 1 file changed, 89 insertions(+), 54 deletions(-) diff --git a/src/gausskernel/storage/buffer/freelist.cpp b/src/gausskernel/storage/buffer/freelist.cpp index 339544080..2c3808e99 100644 --- a/src/gausskernel/storage/buffer/freelist.cpp +++ b/src/gausskernel/storage/buffer/freelist.cpp @@ -85,7 +85,7 @@ static void perform_delay(StrategyDelayStatus *status)//这个函数的目的是 { if (++(status->retry_times) > MAX_RETRY_TIMES && get_dirty_page_num() > g_instance.attr.attr_storage.NBuffers * NEED_DELAY_RETRY_GET_BUF) { - // 如果已经重试了最大���数,并且脏页数量超过了阈值 + // 如果已经重试了最大�����数,并且脏页数量超过了阈值 if (status->cur_delay_time == 0) { // 如果当前延迟时间为0,则初始化为最小延迟时间 @@ -430,19 +430,30 @@ void StrategyNotifyBgWriter(int bgwproc_no) * Note: for somewhat historical reasons, the buffer lookup hashtable size * is also determined here. */ -Size StrategyShmemSize(void) + /* +这段代码首先调用 BufTableShmemSize 函数计算了查找哈希表的共享内存大小, +其中 TOTAL_BUFFER_NUM 表示缓冲区的总数, +NUM_BUFFER_PARTITIONS 表示缓冲区分区的数量。 +然后,它计算了共享替换策略控制块的大小, +并使用 MAXALIGN 函数对齐到内存中的最大对齐大小。 +最后,将这两个大小相加得到了总的共享内存大小,并返回给调用者。 +这个大小通常用于初始化共享内存段,以便在多个进程之间共享缓冲区管理策略的信息。 + */ + Size StrategyShmemSize(void) { Size size = 0; - /* size of lookup hash table ... see comment in StrategyInitialize */ + /* 计算查找哈希表的共享内存大小,详情见 StrategyInitialize 函数的注释 */ size = add_size(size, BufTableShmemSize(TOTAL_BUFFER_NUM + NUM_BUFFER_PARTITIONS)); - /* size of the shared replacement strategy control block */ + /* 计算共享替换策略控制块的大小,需要对齐到 MAXALIGN 大小 */ size = add_size(size, MAXALIGN(sizeof(BufferStrategyControl))); return size; } +} + /* * StrategyInitialize -- initialize the buffer cache replacement * strategy. @@ -450,49 +461,56 @@ Size StrategyShmemSize(void) * Assumes: All of the buffers are already built into a linked list. * Only called by postmaster and only during initialization. */ + /* +这段代码首先调用 InitBufTable 函数初始化共享缓冲区查找哈希表,以用于在缓冲区的管理中查找缓冲区。 +哈希表的大小被设置为 TOTAL_BUFFER_NUM + NUM_BUFFER_PARTITIONS,以确保足够的哈希表大小来处理缓冲区的管理。 + +然后,它通过 ShmemInitStruct 函数获取或创建共享策略控制块 t_thrd.storage_cxt.StrategyControl。 +如果该控制块尚不存在,则会进行初始化,包括初始化互斥锁、时钟扫描指针等。 + +这个函数通常在 PostgreSQL 的启动阶段被调用一次,用于初始化缓冲区管理策略的共享内存数据结构和控制块。 + */ void StrategyInitialize(bool init) { bool found = false; /* - * Initialize the shared buffer lookup hashtable. + * 初始化共享缓冲区查找哈希表。 * - * Since we can't tolerate running out of lookup table entries, we must be - * sure to specify an adequate table size here. The maximum steady-state - * usage is of course NBuffers entries, but BufferAlloc() tries to insert - * a new entry before deleting the old. In principle this could be - * happening in each partition concurrently, so we could need as many as - * NBuffers + NUM_BUFFER_PARTITIONS entries. + * 由于我们不能容忍查找表条目用尽,因此必须确保在这里指定足够大的表大小。最大稳态使用的条目数量 + * 当然是 NBuffers,但 BufferAlloc() 在删除旧条目之前尝试插入新条目。从原理上讲,这可能在每个 + * 分区中同时发生,因此我们可能需要多达 NBuffers + NUM_BUFFER_PARTITIONS 个条目。 */ InitBufTable(TOTAL_BUFFER_NUM + NUM_BUFFER_PARTITIONS); /* - * Get or create the shared strategy control block + * 获取或创建共享策略控制块 */ t_thrd.storage_cxt.StrategyControl = (BufferStrategyControl *)ShmemInitStruct("Buffer Strategy Status", sizeof(BufferStrategyControl), &found); if (!found) { /* - * Only done once, usually in postmaster + * 仅在初始化时执行一次,通常在 postmaster 中执行 */ Assert(init); SpinLockInit(&t_thrd.storage_cxt.StrategyControl->buffer_strategy_lock); - /* Initialize the clock sweep pointer */ + /* 初始化时钟扫描指针 */ pg_atomic_init_u32(&t_thrd.storage_cxt.StrategyControl->nextVictimBuffer, 0); - /* Clear statistics */ + /* 清空统计信息 */ t_thrd.storage_cxt.StrategyControl->completePasses = 0; pg_atomic_init_u32(&t_thrd.storage_cxt.StrategyControl->numBufferAllocs, 0); - /* No pending notification */ + /* 没有挂起的通知 */ t_thrd.storage_cxt.StrategyControl->bgwprocno = -1; } else { Assert(!init); } } + const int MIN_REPAIR_FILE_SLOT_NUM = 32; /* ---------------------------------------------------------------- * Backend-private buffer ring management @@ -503,20 +521,26 @@ const int MIN_REPAIR_FILE_SLOT_NUM = 32; * * The object is allocated in the current memory context. */ + /* +这个函数首先根据不同的访问策略类型 btype 计算所需的环大小 ring_size。 +然后,它分配了一个 BufferAccessStrategy 对象,并根据计算的参数对其进行了初始化。 +策略对象的类型、环的大小和刷新率等属性都会根据不同的访问策略类型进行设置。 + +最后,函数返回创建的策略对象,该对象可以用于后续的缓冲区访问操作,以实现不同的访问策略。 + */ BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype) { BufferAccessStrategy strategy; int ring_size; /* - * Select ring size to use. See buffer/README for rationales. + * 选择要使用的环大小。请参阅buffer/README中的原理说明。 * - * Note: if you change the ring size for BAS_BULKREAD, see also - * SYNC_SCAN_REPORT_INTERVAL in access/heap/syncscan.c. + * 注意:如果更改了BAS_BULKREAD的环大小,请同时查看access/heap/syncscan.c中的SYNC_SCAN_REPORT_INTERVAL。 */ switch (btype) { case BAS_NORMAL: - /* if someone asks for NORMAL, just give 'em a "default" object */ + /* 如果有人要求NORMAL,只需给他们一个“默认”对象 */ return NULL; case BAS_BULKREAD: @@ -535,22 +559,22 @@ BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype) default: ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), (errmsg("unrecognized buffer access strategy: %d", (int)btype)))); - return NULL; /* keep compiler quiet */ + return NULL; /* 保持编译器安静 */ } - /* If the shared buffers is too small, make sure ring size not equal zero. */ + /* 如果共享缓冲区太小,请确保环大小不等于零。 */ ring_size = Max(ring_size, 4); - /* Make sure ring isn't an undue fraction of shared buffers */ + /* 确保环不是共享缓冲区的过大比例 */ if (btype != BAS_BULKWRITE && btype != BAS_BULKREAD) ring_size = Min(g_instance.attr.attr_storage.NBuffers / 8, ring_size); else ring_size = Min(g_instance.attr.attr_storage.NBuffers / 4, ring_size); - /* Allocate the object and initialize all elements to zeroes */ + /* 分配对象并将所有元素初始化为零 */ strategy = (BufferAccessStrategy)palloc0(offsetof(BufferAccessStrategyData, buffers) + ring_size * sizeof(Buffer)); - /* Set fields that don't start out zero */ + /* 设置初始不为零的字段 */ strategy->btype = btype; strategy->ring_size = ring_size; strategy->flush_rate = Min(u_sess->attr.attr_storage.backwrite_quantity, ring_size); @@ -558,6 +582,7 @@ BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype) return strategy; } + /* * FreeAccessStrategy -- release a BufferAccessStrategy object * @@ -566,13 +591,14 @@ BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype) */ void FreeAccessStrategy(BufferAccessStrategy strategy) { - /* don't crash if called on a "default" strategy */ + /* 不要在“默认”策略上调用时崩溃 */ if (strategy != NULL) { - pfree(strategy); - strategy = NULL; + pfree(strategy); // 释放策略对象占用的内存 + strategy = NULL; // 将策略对象指针设置为 NULL,以避免引用已释放的内存 } } + const int MAX_RETRY_RING_TIMES = 100; const float MAX_RETRY_RING_PCT = 0.1; /* @@ -581,15 +607,21 @@ const float MAX_RETRY_RING_PCT = 0.1; * * The bufhdr spin lock is held on the returned buffer. */ + /* +这段代码的主要功能是从环形缓冲区策略中获取一个缓冲区描述符, +该策略用于管理缓冲区的分配和使用。 +代码中包含了许多条件和逻辑,用于确定是否可以分配特定的缓冲区描述符, +以及何时进行异步刷新等操作。 + */ static BufferDesc *GetBufferFromRing(BufferAccessStrategy strategy, uint32 *buf_state) { - BufferDesc *buf = NULL; - Buffer buf_num; - uint32 local_buf_state; /* to avoid repeated (de-)referencing */ - uint16 retry_times = 0; + BufferDesc *buf = NULL; // 用于存储缓冲区描述符的指针 + Buffer buf_num; // 用于存储缓冲区编号的变量 + uint32 local_buf_state; // 用于存储缓冲区状态的变量,以避免重复引用 + uint16 retry_times = 0; // 用于记录重试次数的变量 RETRY: - /* Advance to next ring slot */ + /* 移动到下一个环形槽位 */ if (++strategy->current >= strategy->ring_size) strategy->current = 0; retry_times++; @@ -597,9 +629,9 @@ RETRY: ADIO_RUN() { /* - * Flush out buffers asynchronously from behind the current slot. - * This is a kludge because the PageListBackWrite() is not strictly - * asynchronous and this function really shouldn't be doing the actual I/O. + * 异步刷新位于当前槽位之后的缓冲区。 + * 这是一种权宜之计,因为 PageListBackWrite() 不是严格异步的, + * 而且这个函数实际上不应该执行实际的 I/O 操作。 */ if (AioCompltrIsReady() && ((strategy->btype == BAS_BULKWRITE) && (strategy->current % strategy->flush_rate == 0))) { @@ -625,9 +657,8 @@ RETRY: ADIO_END(); /* - * If the slot hasn't been filled yet, tell the caller to allocate a new - * buffer with the normal allocation strategy. He will then fill this - * slot by calling AddBufferToRing with the new buffer. + * 如果槽位尚未填充,则告诉调用者使用正常的分配策略来分配新的缓冲区。 + * 调用者将通过调用 AddBufferToRing 来填充这个槽位。 */ buf_num = strategy->buffers[strategy->current]; if (buf_num == InvalidBuffer) { @@ -636,13 +667,10 @@ RETRY: } /* - * If the buffer is pinned we cannot use it under any circumstances. + * 如果缓冲区被固定,无论如何都不能使用它。 * - * If usage_count is 0 or 1 then the buffer is fair game (we expect 1, - * since our own previous usage of the ring element would have left it - * there, but it might've been decremented by clock sweep since then). A - * higher usage_count indicates someone else has touched the buffer, so we - * shouldn't re-use it. + * 如果 usage_count 为 0 或 1,则可以使用缓冲区(我们期望为 1,因为我们之前使用了环形元素, + * 但可能已经被时钟扫描减少了)。更高的 usage_count 表示其他进程已经访问了缓冲区,所以我们不应该重用它。 */ buf = GetBufferDescriptor(buf_num - 1); if (pg_atomic_read_u32(&buf->state) & (BM_DIRTY | BM_IS_META)) { @@ -666,13 +694,14 @@ RETRY: UnlockBufHdr(buf, local_buf_state); /* - * Tell caller to allocate a new buffer with the normal allocation - * strategy. He'll then replace this ring element via AddBufferToRing. + * 告诉调用者使用正常的分配策略来分配新的缓冲区。 + * 他将通过 AddBufferToRing 来替换这个环形元素。 */ strategy->current_was_in_ring = false; return NULL; } + /* * AddBufferToRing -- add a buffer to the buffer ring * @@ -695,25 +724,31 @@ static void AddBufferToRing(BufferAccessStrategy strategy, volatile BufferDesc * * Returns true if buffer manager should ask for a new victim, and false * if this buffer should be written and re-used. */ + /* +这段代码的主要目的是在特定条件下拒绝缓冲区 +,通常在批量读取模式下,如果当前槽位在环中且与给定的缓冲区描述符匹配。如 +果满足这些条件,它会将当前槽位中的缓冲区标记为无效,并返回 true, +表示已经拒绝了缓冲区。否则,它返回 false,表示不拒绝缓冲区。 + */ bool StrategyRejectBuffer(BufferAccessStrategy strategy, BufferDesc *buf) { - /* We only do this in bulkread mode */ + /* 只在批量读取模式下执行此操作 */ if (strategy->btype != BAS_BULKREAD) - return false; + return false; // 如果不是批量读取模式,则不进行拒绝操作 - /* Don't muck with behavior of normal buffer-replacement strategy */ + /* 不要改变正常缓冲区替换策略的行为 */ if (!strategy->current_was_in_ring || strategy->buffers[strategy->current] != BufferDescriptorGetBuffer(buf)) - return false; + return false; // 如果当前槽位不在环中,或者环中的缓冲区与给定的缓冲区描述符不匹配,则不进行拒绝操作 /* - * Remove the dirty buffer from the ring; necessary to prevent infinite - * loop if all ring members are dirty. + * 从环中移除脏缓冲区;这是为了防止如果所有环成员都是脏的时出现无限循环。 */ strategy->buffers[strategy->current] = InvalidBuffer; - return true; + return true; // 返回true表示已经拒绝了缓冲区 } + void StrategyGetRingPrefetchQuantityAndTrigger(BufferAccessStrategy strategy, int *quantity, int *trigger) { int threshold; -- 2.34.1 From b18b170e8b8011db355260db801398dfdbf8f969 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 22:34:22 +0800 Subject: [PATCH 11/19] Enter --- .../storage/access/common/printtup.cpp | 326 ++++++++++-------- 1 file changed, 190 insertions(+), 136 deletions(-) diff --git a/src/gausskernel/storage/access/common/printtup.cpp b/src/gausskernel/storage/access/common/printtup.cpp index 1c8e61406..ae5ce8214 100644 --- a/src/gausskernel/storage/access/common/printtup.cpp +++ b/src/gausskernel/storage/access/common/printtup.cpp @@ -89,70 +89,69 @@ DestReceiver *createStreamDestReceiver(CommandDest dest) streamReceiver *self = (streamReceiver *)palloc0(sizeof(streamReceiver)); /* Assign data send function based on the stream type. */ - switch (dest) { - case DestTupleBroadCast: - self->pub.receiveSlot = printBroadCastTuple; - break; + switch (dest) { + case DestTupleBroadCast: + self->pub.receiveSlot = printBroadCastTuple; // 如果 dest 为 DestTupleBroadCast,设置 receiveSlot 为 printBroadCastTuple 函数 + break; - case DestTupleLocalBroadCast: - self->pub.receiveSlot = printLocalBroadCastTuple; - break; + case DestTupleLocalBroadCast: + self->pub.receiveSlot = printLocalBroadCastTuple; // 如果 dest 为 DestTupleLocalBroadCast,设置 receiveSlot 为 printLocalBroadCastTuple 函数 + break; - case DestTupleRedistribute: - self->pub.receiveSlot = printRedistributeTuple; - break; + case DestTupleRedistribute: + self->pub.receiveSlot = printRedistributeTuple; // 如果 dest 为 DestTupleRedistribute,设置 receiveSlot 为 printRedistributeTuple 函数 + break; - case DestTupleLocalRedistribute: - self->pub.receiveSlot = printLocalRedistributeTuple; - break; + case DestTupleLocalRedistribute: + self->pub.receiveSlot = printLocalRedistributeTuple; // 如果 dest 为 DestTupleLocalRedistribute,设置 receiveSlot 为 printLocalRedistributeTuple 函数 + break; - case DestTupleLocalRoundRobin: - self->pub.receiveSlot = printLocalRoundRobinTuple; - break; + case DestTupleLocalRoundRobin: + self->pub.receiveSlot = printLocalRoundRobinTuple; // 如果 dest 为 DestTupleLocalRoundRobin,设置 receiveSlot 为 printLocalRoundRobinTuple 函数 + break; - case DestTupleHybrid: - self->pub.receiveSlot = printHybridTuple; - break; + case DestTupleHybrid: + self->pub.receiveSlot = printHybridTuple; // 如果 dest 为 DestTupleHybrid,设置 receiveSlot 为 printHybridTuple 函数 + break; - case DestBatchBroadCast: - self->pub.sendBatch = printBroadCastBatchCompress; - break; + case DestBatchBroadCast: + self->pub.sendBatch = printBroadCastBatchCompress; // 如果 dest 为 DestBatchBroadCast,设置 sendBatch 为 printBroadCastBatchCompress 函数 + break; - case DestBatchLocalBroadCast: - self->pub.sendBatch = printLocalBroadCastBatch; - break; + case DestBatchLocalBroadCast: + self->pub.sendBatch = printLocalBroadCastBatch; // 如果 dest 为 DestBatchLocalBroadCast,设置 sendBatch 为 printLocalBroadCastBatch 函数 + break; - case DestBatchRedistribute: - self->pub.sendBatch = printRedistributeBatch; - break; + case DestBatchRedistribute: + self->pub.sendBatch = printRedistributeBatch; // 如果 dest 为 DestBatchRedistribute,设置 sendBatch 为 printRedistributeBatch 函数 + break; - case DestBatchLocalRedistribute: - self->pub.sendBatch = printLocalRedistributeBatch; - break; + case DestBatchLocalRedistribute: + self->pub.sendBatch = printLocalRedistributeBatch; // 如果 dest 为 DestBatchLocalRedistribute,设置 sendBatch 为 printLocalRedistributeBatch 函数 + break; - case DestBatchLocalRoundRobin: - self->pub.sendBatch = printLocalRoundRobinBatch; - break; + case DestBatchLocalRoundRobin: + self->pub.sendBatch = printLocalRoundRobinBatch; // 如果 dest 为 DestBatchLocalRoundRobin,设置 sendBatch 为 printLocalRoundRobinBatch 函数 + break; - case DestBatchHybrid: - self->pub.sendBatch = printHybridBatch; - break; + case DestBatchHybrid: + self->pub.sendBatch = printHybridBatch; // 如果 dest 为 DestBatchHybrid,设置 sendBatch 为 printHybridBatch 函数 + break; - default: - Assert(false); - break; - } - - self->pub.rStartup = printStreamStartup; - self->pub.rShutdown = printStreamShutdown; - self->pub.rDestroy = printtup_destroy; - self->pub.finalizeLocalStream = NULL; - self->pub.mydest = dest; - self->pub.tmpContext = NULL; - - return (DestReceiver *)self; + default: + Assert(false); // 如果 dest 未匹配到任何已知的值,断言报错 + break; } +self->pub.rStartup = printStreamStartup; // 设置 rStartup 为 printStreamStartup 函数 +self->pub.rShutdown = printStreamShutdown; // 设置 rShutdown 为 printStreamShutdown 函数 +self->pub.rDestroy = printtup_destroy; // 设置 rDestroy 为 printtup_destroy 函数 +self->pub.finalizeLocalStream = NULL; // 设置 finalizeLocalStream 为 NULL +self->pub.mydest = dest; // 设置 mydest 为 dest +self->pub.tmpContext = NULL; // 设置 tmpContext 为 NULL + +return (DestReceiver *)self; // 返回 DestReceiver 类型的指针,指向 self +} /* * @Description: Flush data in the buffer * @@ -568,16 +567,36 @@ static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *t /* Do we have a non-resjunk tlist item? */ while (tlist_item && #ifdef STREAMPLAN - StreamTopConsumerAmI() == false && StreamThreadAmI() == false && + // 如果定义了 STREAMPLAN 宏,执行以下条件判断 + StreamTopConsumerAmI() == false && StreamThreadAmI() == false && #endif - ((TargetEntry *)lfirst(tlist_item))->resjunk) - tlist_item = lnext(tlist_item); - if (tlist_item != NULL) { - TargetEntry *tle = (TargetEntry *)lfirst(tlist_item); + // 判断当前是否为流式计划中的最终消费者,如果是则跳过下面的逻辑,否则继续 + // 判断当前是否为流式计划中的线程,如果是则跳过下面的逻辑,否则继续 + // 上述两个条件都满足,才会继续执行下面的逻辑 + // 如果它们不满足,则不会执行下面的逻辑 + // 注意:这是一段条件编译,具体情况依赖于是否定义了 STREAMPLAN 宏 + + // 检查当前目标项是否为 "resjunk"(通常表示结果列是否为垃圾列) + ((TargetEntry *)lfirst(tlist_item))->resjunk) + // 如果目标项为垃圾列,则跳过当前项 + tlist_item = lnext(tlist_item); + +// 继续执行下面的逻辑,不再受到上面条件的限制 + +if (tlist_item != NULL) { + // 如果 tlist_item 不为空 + TargetEntry *tle = (TargetEntry *)lfirst(tlist_item); + + // 使用缓冲区 buf 写入 tle->resorigtbl 的值,通常是一个表的原始 ID + pq_writeint32(buf, tle->resorigtbl); + + // 使用缓冲区 buf 写入 tle->resorigcol 的值,通常是一个列的原始 ID + pq_writeint16(buf, tle->resorigcol); + + // 移动 tlist_item 到下一个目标项 + tlist_item = lnext(tlist_item); +} - pq_writeint32(buf, tle->resorigtbl); - pq_writeint16(buf, tle->resorigcol); - tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ pq_writeint32(buf, 0); @@ -615,16 +634,17 @@ static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *t */ static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats) { - Form_pg_attribute *attrs = typeinfo->attrs; - int natts = typeinfo->natts; + Form_pg_attribute *attrs = typeinfo->attrs; // 从 TupleDesc 中获取属性数组 + int natts = typeinfo->natts; // 获取属性数量 int i; + // 遍历属性列表 for (i = 0; i < natts; ++i) { - Oid atttypid = attrs[i]->atttypid; - int32 atttypmod = attrs[i]->atttypmod; + Oid atttypid = attrs[i]->atttypid; // 获取属性的数据类型 ID + int32 atttypmod = attrs[i]->atttypmod; // 获取属性的类型修饰符 writeString(buf, NameStr(attrs[i]->attname), false); - + // 使用 writeString 函数将属性的名称写入缓冲区 buf,第三个参数表示不添加引号 #ifdef PGXC /* * for analyze global stats, because DN will send sample rows to CN, @@ -722,22 +742,23 @@ static void printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int int16 *formats = myState->portal != NULL ? myState->portal->formats : myState->formats; int i; - /* get rid of any old data */ + // 清除旧数据 if (myState->myinfo != NULL) { - pfree(myState->myinfo); + pfree(myState->myinfo); // 释放之前分配的内存 } - myState->myinfo = NULL; + myState->myinfo = NULL; // 将 myinfo 设置为 NULL + + myState->attrinfo = typeinfo; // 设置 attrinfo 为给定的 TupleDesc + myState->nattrs = numAttrs; // 设置属性数量 - myState->attrinfo = typeinfo; - myState->nattrs = numAttrs; if (numAttrs <= 0) { - return; + return; // 如果属性数量小于等于0,直接返回 } - + /* get rid of any old data */ if (myState->portal != NULL && myState->portal->tupDesc != NULL) { #ifdef USE_ASSERT_CHECKING - Assert(numAttrs <= myState->portal->tupDesc->natts); + Assert(numAttrs <= myState->portal->tupDesc->natts); // 使用断言检查属性数量是否小于等于 tupDesc 中的属性数量 #else if (numAttrs > myState->portal->tupDesc->natts) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -746,12 +767,14 @@ static void printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int #endif } + // 分配并初始化 myinfo 数组 myState->myinfo = (PrinttupAttrInfo *)palloc0(numAttrs * sizeof(PrinttupAttrInfo)); for (i = 0; i < numAttrs; i++) { - PrinttupAttrInfo *thisState = myState->myinfo + i; - int16 format = (formats ? formats[i] : 0); - + PrinttupAttrInfo *thisState = myState->myinfo + i; // 获取当前属性信息的指针 + int16 format = (formats ? formats[i] : 0); // 获取格式信息,如果 formats 为空,则默认为0 + } +} /* * for analyze global stats, because DN will send sample rows to CN, * if we encounter droped columns, we should send it to CN. but atttypid of dropped column @@ -763,13 +786,20 @@ static void printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int thisState->format = format; if (format == 0) { - getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &thisState->typoutput, &thisState->typisvarlena); - fmgr_info(thisState->typoutput, &thisState->finfo); - } else if (format == 1) { - getTypeBinaryOutputInfo(typeinfo->attrs[i]->atttypid, &thisState->typsend, &thisState->typisvarlena); - fmgr_info(thisState->typsend, &thisState->finfo); - } else { - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", format))); + // 如果格式为0,表示文本格式 + getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &thisState->typoutput, &thisState->typisvarlena); + // 获取当前属性的输出函数及是否为可变长度数据类型 + fmgr_info(thisState->typoutput, &thisState->finfo); + // 获取输出函数的信息 +} else if (format == 1) { + // 如果格式为1,表示二进制格式 + getTypeBinaryOutputInfo(typeinfo->attrs[i]->atttypid, &thisState->typsend, &thisState->typisvarlena); + // 获取当前属性的二进制输出函数及是否为可变长度数据类型 + fmgr_info(thisState->typsend, &thisState->finfo); + // 获取二进制输出函数的信息 +} else { + // 如果格式不是0或1,报错 + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", format))); } } } @@ -782,48 +812,56 @@ static void printtup_prepare_info_for_stream(DR_printtup *myState, TupleDesc typ { int i; - /* get rid of any old data */ + /* get rid of any old data */ if (myState->myinfo != NULL) { - pfree(myState->myinfo); + pfree(myState->myinfo); // 释放之前分配的内存 } - myState->myinfo = NULL; + myState->myinfo = NULL; // 将 myinfo 设置为 NULL + + myState->attrinfo = typeinfo; // 设置 attrinfo 为给定的 TupleDesc + myState->nattrs = numAttrs; // 设置属性数量 - myState->attrinfo = typeinfo; - myState->nattrs = numAttrs; if (numAttrs <= 0) { - return; + return; // 如果属性数量小于等于0,直接返回 } + // 分配并初始化 myinfo 数组 myState->myinfo = (PrinttupAttrInfo *)palloc0(numAttrs * sizeof(PrinttupAttrInfo)); /* let's say for stream thread only support format = 0; */ for (i = 0; i < numAttrs; i++) { - PrinttupAttrInfo *thisState = myState->myinfo + i; - thisState->format = 0; + PrinttupAttrInfo *thisState = myState->myinfo + i; // 获取当前属性信息的指针 + thisState->format = 0; // 设置输出格式为0(文本格式) getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &thisState->typoutput, &thisState->typisvarlena); + // 获取当前属性的输出函数及是否为可变长度数据类型 fmgr_info(thisState->typoutput, &thisState->finfo); + // 获取输出函数的信息 } } + inline MemoryContext changeToTmpContext(DestReceiver *self) { - MemoryContext old_context = CurrentMemoryContext; - if (self->tmpContext != NULL) { - old_context = MemoryContextSwitchTo(self->tmpContext); + MemoryContext old_context = CurrentMemoryContext; // 保存当前内存上下文 + if (self->tmpContext != NULL) { // 如果存在临时内存上下文 + old_context = MemoryContextSwitchTo(self->tmpContext); // 切换到临时内存上下文 } - return old_context; + return old_context; // 返回切换前的内存上下文 } void assembleStreamMessage(TupleTableSlot *slot, DestReceiver *self, StringInfo buf) { - TupleDesc typeinfo = slot->tts_tupleDescriptor; - DR_printtup *myState = (DR_printtup *)self; - int natts = typeinfo->natts; + TupleDesc typeinfo = slot->tts_tupleDescriptor; // 获取槽中的元组描述符 + DR_printtup *myState = (DR_printtup *)self; // 将 DestReceiver 转换为 DR_printtup 类型 + int natts = typeinfo->natts; // 获取属性数量 int i; - StreamTimeSerilizeStart(t_thrd.pgxc_cxt.GlobalNetInstr); - if (slot->tts_dataRow) { - Assert(buf->len == 0); + StreamTimeSerilizeStart(t_thrd.pgxc_cxt.GlobalNetInstr); // 开始流式时间序列化 + + if (slot->tts_dataRow) { // 如果槽中包含数据行 + Assert(buf->len == 0); // 使用断言确保缓冲区长度为0 + // 通常,这个条件检查用于确保开始新的数据行时缓冲区为空 + /* * Prepare a DataRow message @@ -901,13 +939,13 @@ void assembleStreamMessage(TupleTableSlot *slot, DestReceiver *self, StringInfo */ void printtupStream(TupleTableSlot *slot, DestReceiver *self) { - TupleDesc typeinfo = slot->tts_tupleDescriptor; - DR_printtup *myState = (DR_printtup *)self; - StringInfo buf = &myState->buf; - int natts = typeinfo->natts; + TupleDesc typeinfo = slot->tts_tupleDescriptor; // 获取槽中的元组描述符 + DR_printtup *myState = (DR_printtup *)self; // 将 DestReceiver 转换为 DR_printtup 类型 + StringInfo buf = &myState->buf; // 获取 DR_printtup 结构中的缓冲区 + int natts = typeinfo->natts; // 获取属性数量 int i; - StreamTimeSerilizeStart(t_thrd.pgxc_cxt.GlobalNetInstr); + StreamTimeSerilizeStart(t_thrd.pgxc_cxt.GlobalNetInstr);// 开始流式时间序列化 #ifdef PGXC /* @@ -915,13 +953,14 @@ void printtupStream(TupleTableSlot *slot, DestReceiver *self) * values, just send over the DataRow message as we received it from the * Datanode */ - if (slot->tts_dataRow) { - pq_beginmessage_reuse(buf, 'D'); - appendBinaryStringInfo(buf, slot->tts_dataRow, slot->tts_dataLen); - AddCheckInfo(buf); - pq_endmessage_reuse(buf); - StreamTimeSerilizeEnd(t_thrd.pgxc_cxt.GlobalNetInstr); - return; + + if (slot->tts_dataRow) { // 如果槽中包含数据行 + pq_beginmessage_reuse(buf, 'D'); // 开始一个数据行消息 + appendBinaryStringInfo(buf, slot->tts_dataRow, slot->tts_dataLen); // 将数据行追加到消息中 + AddCheckInfo(buf); // 添加校验信息(这部分功能可能需要上下文) + pq_endmessage_reuse(buf); // 结束消息 + StreamTimeSerilizeEnd(t_thrd.pgxc_cxt.GlobalNetInstr); // 结束流式时间序列化 + return; // 返回 } #endif /* Set or update my derived attribute info, if needed */ @@ -996,26 +1035,32 @@ void printtupStream(TupleTableSlot *slot, DestReceiver *self) */ void printBatch(VectorBatch *batch, DestReceiver *self) { - DR_printtup *myState = (DR_printtup *)self; - StringInfo buf = &myState->buf; - pq_beginmessage_reuse(buf, 'B'); - batch->SerializeWithLZ4Compress(buf); - AddCheckInfo(buf); - pq_endmessage_reuse(buf); + DR_printtup *myState = (DR_printtup *)self; // 将 DestReceiver 转换为 DR_printtup 类型 + StringInfo buf = &myState->buf; // 获取 DR_printtup 结构中的缓冲区 + + pq_beginmessage_reuse(buf, 'B'); // 开始一个批处理消息,使用字符 'B' + + batch->SerializeWithLZ4Compress(buf); // 将批处理数据进行 LZ4 压缩后追加到消息中 + + AddCheckInfo(buf); // 添加校验信息(这部分功能可能需要上下文) + + pq_endmessage_reuse(buf); // 结束消息 } + /* ---------------- * printtup --- print a tuple in protocol 3.0 * ---------------- */ void printtup(TupleTableSlot *slot, DestReceiver *self) { - TupleDesc typeinfo = slot->tts_tupleDescriptor; - DR_printtup *myState = (DR_printtup *)self; - StringInfo buf = &myState->buf; - int natts = typeinfo->natts; + TupleDesc typeinfo = slot->tts_tupleDescriptor; // 获取槽中的元组描述符 + DR_printtup *myState = (DR_printtup *)self; // 将 DestReceiver 转换为 DR_printtup 类型 + StringInfo buf = &myState->buf; // 获取 DR_printtup 结构中的缓冲区 + int natts = typeinfo->natts; // 获取属性数量 int i; - bool binary = false; + bool binary = false; // 初始化 binary 标志为 false,表示输出为文本格式 + /* just as we define in backend/commands/analyze.cpp */ #define WIDTH_THRESHOLD 1024 @@ -1045,13 +1090,16 @@ void printtup(TupleTableSlot *slot, DestReceiver *self) * Datanode */ if (slot->tts_dataRow != NULL && (pg_get_client_encoding() == GetDatabaseEncoding()) && !binary) { - pq_beginmessage_reuse(buf, 'D'); - appendBinaryStringInfo(buf, slot->tts_dataRow, slot->tts_dataLen); - AddCheckInfo(buf); - pq_endmessage_reuse(buf); - StreamTimeSerilizeEnd(t_thrd.pgxc_cxt.GlobalNetInstr); - return; - } + // 如果槽中包含数据行,客户端编码与数据库编码匹配,且输出格式为文本(非二进制) + + pq_beginmessage_reuse(buf, 'D'); // 开始一个数据行消息,使用字符 'D' + appendBinaryStringInfo(buf, slot->tts_dataRow, slot->tts_dataLen); // 将数据行追加到消息中 + AddCheckInfo(buf); // 添加校验信息(这部分功能可能需要上下文) + pq_endmessage_reuse(buf); // 结束消息 + + StreamTimeSerilizeEnd(t_thrd.pgxc_cxt.GlobalNetInstr); // 结束流式时间序列化 + return; // 返回 +} #endif /* Make sure the tuple is fully deconstructed */ @@ -1104,13 +1152,19 @@ void printtup(TupleTableSlot *slot, DestReceiver *self) char *outputstr = NULL; outputstr = OutputFunctionCall(&thisState->finfo, attr); - if (thisState->typisvarlena && self->forAnalyzeSampleTuple && - (typeinfo->attrs[i]->atttypid == BYTEAOID || typeinfo->attrs[i]->atttypid == CHAROID || - typeinfo->attrs[i]->atttypid == TEXTOID || typeinfo->attrs[i]->atttypid == BLOBOID || - typeinfo->attrs[i]->atttypid == CLOBOID || typeinfo->attrs[i]->atttypid == RAWOID || - typeinfo->attrs[i]->atttypid == BPCHAROID || typeinfo->attrs[i]->atttypid == VARCHAROID || - typeinfo->attrs[i]->atttypid == NVARCHAR2OID) && - strlen(outputstr) > WIDTH_THRESHOLD * 2) { + if (thisState->typisvarlena && self->forAnalyzeSampleTuple && + (typeinfo->attrs[i]->atttypid == BYTEAOID || typeinfo->attrs[i]->atttypid == CHAROID || + typeinfo->attrs[i]->atttypid == TEXTOID || typeinfo->attrs[i]->atttypid == BLOBOID || + typeinfo->attrs[i]->atttypid == CLOBOID || typeinfo->attrs[i]->atttypid == RAWOID || + typeinfo->attrs[i]->atttypid == BPCHAROID || typeinfo->attrs[i]->atttypid == VARCHAROID || + typeinfo->attrs[i]->atttypid == NVARCHAR2OID) && + strlen(outputstr) > WIDTH_THRESHOLD * 2) +{ + // 检查当前属性是否为可变长度数据类型,并且处于分析示例元组的模式中 + // 同时检查当前属性的数据类型是否为指定的文本类型之一(BYTEA、CHAR、TEXT、BLOB、CLOB、RAW、BPCHAR、VARCHAR、NVARCHAR2) + // 最后,检查输出字符串的长度是否超过某个阈值的两倍 +} +{ /* * in compute_scalar_stats, we just skip detoast value if value size is * bigger than WIDTH_THRESHOLD to avoid consuming too much memory -- 2.34.1 From 1a3b0e6c56902c8a99e8f3fb6661c77c5617c0ac Mon Sep 17 00:00:00 2001 From: Cachuela Date: Thu, 5 Oct 2023 22:36:11 +0800 Subject: [PATCH 12/19] enter --- src/gausskernel/storage/buffer/freelist.cpp | 98 ++++++++++++++++----- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/src/gausskernel/storage/buffer/freelist.cpp b/src/gausskernel/storage/buffer/freelist.cpp index 2c3808e99..af9fa401a 100644 --- a/src/gausskernel/storage/buffer/freelist.cpp +++ b/src/gausskernel/storage/buffer/freelist.cpp @@ -85,7 +85,7 @@ static void perform_delay(StrategyDelayStatus *status)//这个函数的目的是 { if (++(status->retry_times) > MAX_RETRY_TIMES && get_dirty_page_num() > g_instance.attr.attr_storage.NBuffers * NEED_DELAY_RETRY_GET_BUF) { - // 如果已经重试了最大�����数,并且脏页数量超过了阈值 + // 如果已经重试了最大�������数,并且脏页数量超过了阈值 if (status->cur_delay_time == 0) { // 如果当前延迟时间为0,则初始化为最小延迟时间 @@ -748,87 +748,132 @@ bool StrategyRejectBuffer(BufferAccessStrategy strategy, BufferDesc *buf) return true; // 返回true表示已经拒绝了缓冲区 } - +/* +这段代码的主要功能是根据环形缓冲区的大小和全局配置中的预取参数,计算出预取的数量和触发值。 +预取是一种优化技术,用于在需要访问数据之前预先将数据加载到缓冲区,以提高访问性能。 +此函数根据策略和全局配置来确定适当的预取数量和触发值,并将其存储在传入的参数 quantity 和 trigger 中。 +*/ void StrategyGetRingPrefetchQuantityAndTrigger(BufferAccessStrategy strategy, int *quantity, int *trigger) { - int threshold; - int prefetch_trigger = u_sess->attr.attr_storage.prefetch_quantity; + int threshold; // 阈值,用于确定预取数量和触发值 + int prefetch_trigger = u_sess->attr.attr_storage.prefetch_quantity; // 预取触发值,从全局配置获取 + // 如果策略为空或者不是批量读取模式,直接返回 if (strategy == NULL || strategy->btype != BAS_BULKREAD) { return; } + + // 计算阈值为环形缓冲区大小的四分之一 threshold = strategy->ring_size / 4; + + // 如果 quantity 不为空,则将其设置为预取数量或阈值中的较小值 if (quantity != NULL) { *quantity = (threshold > u_sess->attr.attr_storage.prefetch_quantity) ? u_sess->attr.attr_storage.prefetch_quantity : threshold; } + + // 如果 trigger 不为空,则将其设置为预取触发值或阈值中的较小值 if (trigger != NULL) { *trigger = (threshold > prefetch_trigger) ? prefetch_trigger : threshold; } } +} + +/* +这段代码的主要目的是唤醒页写入线程,以便它可以执行页写入操作。 +它首先获取页写入线程的信息(通常是一个线程结构体或进程描述符), +然后检查是否有页写入线程处于等待状态。如果有等待的页写入线程, +它会通过设置线程的进程标志位(通常是一个标志或标志位), +以唤醒线程从等待状态中恢复执行。 +*/ void wakeup_pagewriter_thread() { PageWriterProc *pgwr = &g_instance.ckpt_cxt_ctl->pgwr_procs.writer_proc[0]; - /* The current candidate list is empty, wake up the buffer writer. */ + + /* 如果当前的候选列表为空,唤醒页写入线程。 */ if (pgwr->proc != NULL) { - SetLatch(&pgwr->proc->procLatch); + SetLatch(&pgwr->proc->procLatch); // 设置页写入线程的进程标志位,以唤醒线程 } + return; } + const int CANDIDATE_DIRTY_LIST_LEN = 100; const float HIGH_WATER = 0.75; + +/* +这段代码的主要功能是在候选缓冲区列表中查找一个可用的缓冲区描述符, +并根据需要将其添加到环形缓冲区或脏缓冲区列表中。 +函数首先检查当前进程的后台状态,然后从候选缓冲区列表中查找可用的缓冲区, +考虑了各种条件,如缓冲区是否可用、是否是脏缓冲区等。 +最后,函数会唤醒页写入线程(如果需要),并返回找到的缓冲区描述符或 NULL。 +这个函数的目的是优化缓冲区的管理和使用,以提高数据库性能。 +*/ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, uint32* buf_state) { - BufferDesc* buf = NULL; - uint32 local_buf_state; - int buf_id = 0; - int list_num = g_instance.ckpt_cxt_ctl->pgwr_procs.sub_num; - int list_id = 0; - volatile PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry; - Buffer *candidate_dirty_list = NULL; - int dirty_list_num = 0; - bool enable_available = false; - bool need_push_dirst_list = false; + BufferDesc* buf = NULL; // 用于存储缓冲区描述符的指针 + uint32 local_buf_state; // 用于存储缓冲区状态的变量 + int buf_id = 0; // 缓冲区的编号 + int list_num = g_instance.ckpt_cxt_ctl->pgwr_procs.sub_num; // 获取子线程数量 + int list_id = 0; // 子线程的ID + volatile PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry; // 获取当前后台进程的状态 + Buffer *candidate_dirty_list = NULL; // 用于存储候选的脏缓冲区列表 + int dirty_list_num = 0; // 脏缓冲区列表的数量 + bool enable_available = false; // 缓冲区是否可用的标志 + bool need_push_dirty_list = false; // 是否需要将缓冲区添加到脏缓冲区列表的标志 bool need_scan_dirty = (g_instance.ckpt_cxt_ctl->actual_dirty_page_num / (float)(g_instance.attr.attr_storage.NBuffers) > HIGH_WATER) - && backend_can_flush_dirty_page(); + && backend_can_flush_dirty_page(); // 是否需要扫描脏缓冲区的标志 + + // 如果需要扫描脏缓冲区,分配一个候选脏缓冲区列表 if (need_scan_dirty) { - /*Not return the dirty page when there are few dirty pages */ candidate_dirty_list = (Buffer*)palloc0(sizeof(Buffer) * CANDIDATE_DIRTY_LIST_LEN); } + // 根据当前进程的线程号或会话号计算子线程的ID list_id = beentry->st_tid > 0 ? (beentry->st_tid % list_num) : (beentry->st_sessionid % list_num); + // 遍历所有子线程的候选缓冲区列表 for (int i = 0; i < list_num; i++) { - /* the pagewriter sub thread store normal buffer pool, sub thread starts from 1 */ + // 计算子线程的实际ID(从1开始) int thread_id = (list_id + i) % list_num + 1; Assert(thread_id > 0 && thread_id <= list_num); + + // 循环弹出候选缓冲区,直到列表为空 while (candidate_buf_pop(&buf_id, thread_id)) { Assert(buf_id < SegmentBufferStartID); buf = GetBufferDescriptor(buf_id); local_buf_state = LockBufHdr(buf); + // 检查缓冲区是否可用 if (g_instance.ckpt_cxt_ctl->candidate_free_map[buf_id]) { g_instance.ckpt_cxt_ctl->candidate_free_map[buf_id] = false; enable_available = BUF_STATE_GET_REFCOUNT(local_buf_state) == 0 && !(local_buf_state & BM_IS_META); - need_push_dirst_list = need_scan_dirty && dirty_list_num < CANDIDATE_DIRTY_LIST_LEN && + need_push_dirty_list = need_scan_dirty && dirty_list_num < CANDIDATE_DIRTY_LIST_LEN && free_space_enough(buf_id); + if (enable_available) { + // 如果需要考虑 usage count,减少 usage count if (NEED_CONSIDER_USECOUNT && BUF_STATE_GET_USAGECOUNT(local_buf_state) != 0) { local_buf_state -= BUF_USAGECOUNT_ONE; - } else if (!(local_buf_state & BM_DIRTY)) { + } + // 如果缓冲区不是脏的,将其添加到环形缓冲区 + else if (!(local_buf_state & BM_DIRTY)) { if (strategy != NULL) { AddBufferToRing(strategy, buf); } *buf_state = local_buf_state; + // 释放候选脏缓冲区列表的内存并返回找到的缓冲区描述符 if (candidate_dirty_list != NULL) { pfree(candidate_dirty_list); } return buf; - } else if (need_push_dirst_list) { + } + // 如果需要将缓冲区添加到脏缓冲区列表 + else if (need_push_dirty_list) { candidate_dirty_list[dirty_list_num++] = buf_id; } } @@ -837,8 +882,10 @@ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, ui } } + // 唤醒页写入线程以处理脏缓冲区 wakeup_pagewriter_thread(); + // 遍历候选脏缓冲区列表 if (need_scan_dirty) { for (int i = 0; i < dirty_list_num; i++) { buf_id = candidate_dirty_list[i]; @@ -846,11 +893,14 @@ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, ui local_buf_state = LockBufHdr(buf); enable_available = (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0) && !(local_buf_state & BM_IS_META) && free_space_enough(buf_id); + if (enable_available) { + // 如果需要将缓冲区添加到环形缓冲区 if (strategy != NULL) { AddBufferToRing(strategy, buf); } *buf_state = local_buf_state; + // 释放候选脏缓冲区列表的内存并返回找到的缓冲区描述符 pfree(candidate_dirty_list); return buf; } @@ -858,9 +908,11 @@ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, ui } } + // 释放候选脏缓冲区列表的内存 if (candidate_dirty_list != NULL) { pfree(candidate_dirty_list); } + + // 如果未找到可用缓冲区,则返回NULL return NULL; } - -- 2.34.1 From 679db10bf3e5ccf0219e4cf7819ca1b39e4e883a Mon Sep 17 00:00:00 2001 From: Cachuela Date: Thu, 5 Oct 2023 22:40:46 +0800 Subject: [PATCH 13/19] enter --- src/gausskernel/storage/buffer/localbuf.cpp | 56 +++++++++++++++++---- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/src/gausskernel/storage/buffer/localbuf.cpp b/src/gausskernel/storage/buffer/localbuf.cpp index c14078840..24ea21955 100644 --- a/src/gausskernel/storage/buffer/localbuf.cpp +++ b/src/gausskernel/storage/buffer/localbuf.cpp @@ -47,77 +47,115 @@ static Block GetLocalBufferStorage(void); * Do PrefetchBuffer's work for temporary relations. * No-op if prefetching isn't compiled in. */ + /* +这个函数的目的是在需要访问数据块之前,尽量将其加载到缓冲区中, +以加速后续的数据访问。它在有条件的情况下执行本地预取,以提高数据库性能。 + */ void LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum) { #ifdef USE_PREFETCH - BufferTag new_tag; /* identity of requested block */ + BufferTag new_tag; /* 请求块的标识 */ LocalBufferLookupEnt *hresult = NULL; + // 初始化请求块的标识 INIT_BUFFERTAG(new_tag, smgr->smgr_rnode.node, forkNum, blockNum); - /* Initialize local buffers if first request in this session */ + // 如果在当前会话中首次请求本地缓冲区,初始化本地缓冲区 if (u_sess->storage_cxt.LocalBufHash == NULL) InitLocalBuffers(); - /* See if the desired buffer already exists */ + // 查看所需的缓冲区是否已经存在 hresult = (LocalBufferLookupEnt*)hash_search(u_sess->storage_cxt.LocalBufHash, (void*)&new_tag, HASH_FIND, NULL); if (hresult != NULL) { - /* Yes, so nothing to do */ + /* 是的,所以不需要进行预取操作 */ return; } - /* Not in buffers, so initiate prefetch */ + // 如果缓冲区中不存在所需块,启动预取操作 smgrprefetch(smgr, forkNum, blockNum); #endif /* USE_PREFETCH */ } +/* +函数执行以下步骤: +查找缓冲区对应的存储管理器关系(SMgrRelation)。 +对缓冲区中的数据进行加密(如果有加密需求的话,这部分可能是自定义函数)。 +计算并设置数据页的校验和,以确保数据的完整性。 +调用 smgrwrite 函数将数据写入磁盘,指定存储管理器关系、分支(fork)、数据块号以及要写入的数据。 +这个函数用于确保本地缓冲区中的数据在适当的时候被写回磁盘,以保持数据的一致性和持久性。根据需要,它可能还包括了数据加密和校验和计算的步骤。 +*/ void LocalBufferWrite(BufferDesc *bufHdr) { SMgrRelation oreln; Page localpage = (char *)LocalBufHdrGetBlock(bufHdr); char *bufToWrite = NULL; - /* Find smgr relation for buffer */ + /* 查找缓冲区对应的SMgrRelation */ oreln = smgropen(bufHdr->tag.rnode, BackendIdForTempRelations); - /* data encrypt */ + + /* 对缓冲区中的数据进行加密(data encrypt) */ bufToWrite = PageDataEncryptForBuffer(localpage, bufHdr); + /* 计算并设置数据页的校验和 */ PageSetChecksumInplace((Page)bufToWrite, bufHdr->tag.blockNum); - /* And write... */ + /* 将数据写入磁盘 */ smgrwrite(oreln, bufHdr->tag.forkNum, bufHdr->tag.blockNum, bufToWrite, false); } + void LocalBufferFlushForExtremRTO(BufferDesc *bufHdr) { if (dw_enabled()) { - /* double write */ + /* 如果启用了双写(double write)机制,执行相应的操作 */ + /* 双写是一种数据持久性保护机制,确保数据写入磁盘的安全性 */ + /* 在此处可能包括双写操作的代码 */ } + + /* 调用 FlushBuffer 函数将本地缓冲区刷新到磁盘 */ + /* WITH_LOCAL_CACHE 参数表示使用本地缓存 */ FlushBuffer(bufHdr, NULL, WITH_LOCAL_CACHE); } +/* +这个函数的主要功能是遍历所有本地缓冲区,检查每个缓冲区的状态。 +如果发现某个缓冲区是有效的且脏的(即需要刷新到磁盘), +则调用 LocalBufferFlushForExtremRTO 函数将该缓冲区的数据刷新到磁盘, +然后清除缓冲区的脏标志。 +最后,更新本地缓冲区写入计数。 +这个函数用于确保本地缓冲区中的脏数据在需要时能够被及时刷新到磁盘,以保证数据的持久性。 +*/ void LocalBufferFlushAllBuffer() { int i; + // 遍历所有本地缓冲区 for (i = 0; i < u_sess->storage_cxt.NLocBuffer; i++) { BufferDesc *bufHdr = &u_sess->storage_cxt.LocalBufferDescriptors[i]; uint32 buf_state; + // 读取缓冲区状态 buf_state = pg_atomic_read_u32(&bufHdr->state); + + // 确保本地引用计数为0,表示没有任何进程正在使用该缓冲区 Assert(u_sess->storage_cxt.LocalRefCount[i] == 0); + // 如果缓冲区是有效的且脏的 if ((buf_state & BM_VALID) && (buf_state & BM_DIRTY)) { + // 调用 LocalBufferFlushForExtremRTO 函数刷新缓冲区到磁盘 LocalBufferFlushForExtremRTO(bufHdr); + // 清除缓冲区的脏标志 buf_state &= ~BM_DIRTY; pg_atomic_write_u32(&bufHdr->state, buf_state); + // 更新本地缓冲区写入计数 u_sess->instr_cxt.pg_buffer_usage->local_blks_written++; } } } + static void LocalBufferSanityCheck(BufferTag tag1, BufferTag tag2) { if (!BUFFERTAGS_EQUAL(tag1, tag2)) { -- 2.34.1 From 061efc00cee2f69c2eb5e7c683b7a8152baf4d99 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 22:42:48 +0800 Subject: [PATCH 14/19] Enter --- .../storage/access/common/printtup.cpp | 124 +++++++++--------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/src/gausskernel/storage/access/common/printtup.cpp b/src/gausskernel/storage/access/common/printtup.cpp index ae5ce8214..2a7c198a6 100644 --- a/src/gausskernel/storage/access/common/printtup.cpp +++ b/src/gausskernel/storage/access/common/printtup.cpp @@ -1171,46 +1171,44 @@ void printtup(TupleTableSlot *slot, DestReceiver *self) * during analysis, so we just send as WIDTH_THRESHOLD + 4 to cn so that * it can use as little memory as we can to satisfy the threshold */ - const int length = WIDTH_THRESHOLD + 4; - text *txt = NULL; - Datum str; - text *result = NULL; + const int length = WIDTH_THRESHOLD + 4; // 设置一个常量长度 +text *txt = NULL; // 声明一个 text 类型指针,并初始化为 NULL +Datum str; // 声明一个 Datum 类型变量 +text *result = NULL; // 声明一个 text 类型指针,并初始化为 NULL - txt = cstring_to_text(outputstr); - pfree(outputstr); +txt = cstring_to_text(outputstr); // 将字符串转换为 text 类型 +pfree(outputstr); // 释放原始字符串的内存 - str = DirectFunctionCall3(substrb_with_lenth, PointerGetDatum(txt), Int32GetDatum(0), - Int32GetDatum(length)); - result = DatumGetTextP(str); - if (result != txt) - pfree(txt); +str = DirectFunctionCall3(substrb_with_lenth, PointerGetDatum(txt), Int32GetDatum(0), Int32GetDatum(length)); // 调用 substrb_with_lenth 函数 +result = DatumGetTextP(str); // 将返回的 Datum 转换为 text - outputstr = TextDatumGetCString(str); - pfree(result); +if (result != txt) + pfree(txt); // 如果结果不等于原始的 text,则释放原始 text 的内存 + +outputstr = TextDatumGetCString(str); // 将 text 转换为 C 字符串 +pfree(result); // 释放结果 text 的内存 } pq_sendcountedtext(buf, outputstr, strlen(outputstr), false); pfree(outputstr); } else { /* Binary output */ - bytea *outputbytes = NULL; + bytea *outputbytes = NULL; // 声明一个 bytea 类型指针,并初始化为 NULL - outputbytes = SendFunctionCall(&thisState->finfo, attr); - pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ); - pq_sendbytes(buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); - pfree(outputbytes); + outputbytes = SendFunctionCall(&thisState->finfo, attr); // 调用 SendFunctionCall 函数 + pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ); // 发送 bytea 数据的长度 + pq_sendbytes(buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); // 发送 bytea 数据内容 + pfree(outputbytes); // 释放 bytea 数据的内存 } /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) - pfree(DatumGetPointer(attr)); - } - } + pfree(DatumGetPointer(attr)); // 释放去掉 TOAST 的拷贝的内存 - (void)MemoryContextSwitchTo(old_context); - StreamTimeSerilizeEnd(t_thrd.pgxc_cxt.GlobalNetInstr); +(void)MemoryContextSwitchTo(old_context); // 切换回之前的内存上下文 +StreamTimeSerilizeEnd(t_thrd.pgxc_cxt.GlobalNetInstr); // 结束流式时间序列化 - AddCheckInfo(buf); - pq_endmessage_reuse(buf); +AddCheckInfo(buf); // 添加校验信息(这部分功能可能需要上下文) +pq_endmessage_reuse(buf); // 结束消息 } /* ---------------- @@ -1300,15 +1298,16 @@ static void printtup_20(TupleTableSlot *slot, DestReceiver *self) */ static void printtup_shutdown(DestReceiver *self) { - DR_printtup *myState = (DR_printtup *)self; + DR_printtup *myState = (DR_printtup *)self; // 将 DestReceiver 转换为 DR_printtup 类型 - if (myState->myinfo != NULL) - pfree(myState->myinfo); - myState->myinfo = NULL; + if (myState->myinfo != NULL) // 如果 myinfo 不为 NULL + pfree(myState->myinfo); // 释放 myinfo 的内存 + myState->myinfo = NULL; // 将 myinfo 设置为 NULL - myState->attrinfo = NULL; + myState->attrinfo = NULL; // 将 attrinfo 设置为 NULL } + /* ---------------- * printtup_destroy * ---------------- @@ -1354,37 +1353,41 @@ void debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo) */ void debugtup(TupleTableSlot *slot, DestReceiver *self) { - TupleDesc typeinfo = slot->tts_tupleDescriptor; - int natts = typeinfo->natts; + TupleDesc typeinfo = slot->tts_tupleDescriptor; // 获取槽中的元组描述符 + int natts = typeinfo->natts; // 获取属性数量 int i; Datum origattr, attr; - char *value = NULL; - bool isnull = false; + char *value = NULL; // 初始化属性值字符串为 NULL + bool isnull = false; // 初始化是否为 NULL 值的标志为 false Oid typoutput; bool typisvarlena = false; for (i = 0; i < natts; ++i) { - origattr = tableam_tslot_getattr(slot, i + 1, &isnull); + origattr = tableam_tslot_getattr(slot, i + 1, &isnull); // 获取属性的原始值和是否为 NULL if (isnull) { - continue; + continue; // 如果属性值为 NULL,跳过此次循环 } - getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &typoutput, &typisvarlena); + getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &typoutput, &typisvarlena); // 获取属性的输出信息 + /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ - if (typisvarlena) { - attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); - } else { - attr = origattr; - } + if (typisvarlena) { + // 如果属性类型为可变长度(VARLENA),解压缩属性值 + attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); +} else { + // 否则,使用原始属性值 + attr = origattr; +} - value = OidOutputFunctionCall(typoutput, attr); +value = OidOutputFunctionCall(typoutput, attr); // 将属性值转换为字符串形式 - printatt((unsigned)i + 1, typeinfo->attrs[i], value); +printatt((unsigned)i + 1, typeinfo->attrs[i], value); // 打印属性信息 + +pfree(value); // 释放属性值字符串的内存 - pfree(value); /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) { @@ -1490,43 +1493,46 @@ static void printtup_internal_20(TupleTableSlot *slot, DestReceiver *self) */ void assembleStreamBatchMessage(BatchCompressType ctype, VectorBatch *batch, StringInfo buf) { - buf->cursor = 'B'; + buf->cursor = 'B'; // 设置消息缓冲区的标记为 'B' switch (ctype) { case BCT_NOCOMP: + // 如果压缩类型为 BCT_NOCOMP,使用无压缩方式序列化向量批次数据 batch->SerializeWithoutCompress(buf); break; case BCT_LZ4: + // 如果压缩类型为 BCT_LZ4,使用 LZ4 压缩方式序列化向量批次数据 batch->SerializeWithLZ4Compress(buf); break; default: + // 如果压缩类型不被识别,报告错误 ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("unrecognized batch compress type"))); } } inline void AddCheckInfo(StringInfo buf) { - StringInfoData buf_check; - bool is_check_added = false; + StringInfoData buf_check; // 声明一个 StringInfoData 结构体 + bool is_check_added = false; // 初始化一个标志变量为 false /* add check info for datanode and coordinator */ - if (IsConnFromCoord()) { + if (IsConnFromCoord()) { // 检查是否来自协调节点的连接 #ifdef USE_ASSERT_CHECKING - initStringInfo(&buf_check); - AddCheckMessage(&buf_check, buf, false); - is_check_added = true; + initStringInfo(&buf_check); // 初始化 buf_check 字符串缓冲区 + AddCheckMessage(&buf_check, buf, false); // 添加校验信息到 buf_check + is_check_added = true; // 设置校验信息已添加的标志为 true #else if (anls_opt_is_on(ANLS_STREAM_DATA_CHECK)) { - initStringInfo(&buf_check); - AddCheckMessage(&buf_check, buf, false); - is_check_added = true; + initStringInfo(&buf_check); // 初始化 buf_check 字符串缓冲区 + AddCheckMessage(&buf_check, buf, false); // 添加校验信息到 buf_check + is_check_added = true; // 设置校验信息已添加的标志为 true } #endif if (unlikely(is_check_added)) { - pfree(buf->data); - buf->len = buf_check.len; - buf->maxlen = buf_check.maxlen; - buf->data = buf_check.data; + pfree(buf->data); // 释放原始缓冲区的数据内存 + buf->len = buf_check.len; // 更新缓冲区的长度 + buf->maxlen = buf_check.maxlen; // 更新缓冲区的最大长度 + buf->data = buf_check.data; // 更新缓冲区的数据指针 } } } -- 2.34.1 From a0d860446b093f0699b89d4ac9fb5b9b6991224a Mon Sep 17 00:00:00 2001 From: Cachuela Date: Thu, 5 Oct 2023 22:52:34 +0800 Subject: [PATCH 15/19] enter --- src/gausskernel/storage/buffer/localbuf.cpp | 201 ++++++++++++-------- 1 file changed, 125 insertions(+), 76 deletions(-) diff --git a/src/gausskernel/storage/buffer/localbuf.cpp b/src/gausskernel/storage/buffer/localbuf.cpp index 24ea21955..bfcd35464 100644 --- a/src/gausskernel/storage/buffer/localbuf.cpp +++ b/src/gausskernel/storage/buffer/localbuf.cpp @@ -173,9 +173,17 @@ static void LocalBufferSanityCheck(BufferTag tag1, BufferTag tag2) * does not get set. Lastly, we support only default access strategy * (hence, usage_count is always advanced). */ + /* +这个函数的主要功能是分配本地缓冲区, +如果请求的块已经存在于缓冲区中,则返回缓冲区描述符。 +如果请求的块不在缓冲区中,它将尝试分配一个新的缓冲区, +并将其添加到本地缓冲区哈希表中。此外,如果分配的缓冲区之前存在脏数据, +函数还会负责将脏数据写回磁盘。最后,函数会更新缓冲区的状态,并返回相应的缓冲区描述符。 +这个函数用于管理本地缓冲区的分配和使用。 + */ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, bool *foundPtr) { - BufferTag new_tag; /* identity of requested block */ + BufferTag new_tag; /* 请求块的标识 */ LocalBufferLookupEnt *hresult = NULL; BufferDesc *buf_desc = NULL; int b; @@ -183,24 +191,22 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber bool found = false; uint32 buf_state; + // 初始化请求块的标识 INIT_BUFFERTAG(new_tag, smgr->smgr_rnode.node, forkNum, blockNum); - /* Initialize local buffers if first request in this session */ + // 如果在当前会话中首次请求本地缓冲区,初始化本地缓冲区 if (u_sess->storage_cxt.LocalBufHash == NULL) InitLocalBuffers(); - /* See if the desired buffer already exists */ + // 查看所需的缓冲区是否已经存在 hresult = (LocalBufferLookupEnt*)hash_search(u_sess->storage_cxt.LocalBufHash, (void*)&new_tag, HASH_FIND, NULL); if (hresult != NULL) { b = hresult->id; buf_desc = &u_sess->storage_cxt.LocalBufferDescriptors[b]; LocalBufferSanityCheck(buf_desc->tag, new_tag); -#ifdef LBDEBUG - fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n", smgr->smgr_rnode.node.relNode, forkNum, blockNum, -b - 1); -#endif - buf_state = pg_atomic_read_u32(&buf_desc->state); - /* this part is equivalent to PinBuffer for a shared buffer */ + // 如果缓冲区已经存在,增加本地引用计数,标记缓冲区为使用中 + buf_state = pg_atomic_read_u32(&buf_desc->state); if (u_sess->storage_cxt.LocalRefCount[b] == 0) { if (BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT) { buf_state += BUF_USAGECOUNT_ONE; @@ -209,26 +215,12 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber } u_sess->storage_cxt.LocalRefCount[b]++; ResourceOwnerRememberBuffer(t_thrd.utils_cxt.CurrentResourceOwner, BufferDescriptorGetBuffer(buf_desc)); - *foundPtr = (buf_state & BM_VALID) ? TRUE : FALSE; /* If previous read attempt have failed; try again */ -#ifdef EXTREME_RTO_DEBUG - ereport(LOG, (errmsg("LocalBufferAlloc %u/%u/%u %u %u find in local buf %u/%u/%u %u %u id %d state %X, lsn %lu", - smgr->smgr_rnode.node.spcNode, smgr->smgr_rnode.node.dbNode, smgr->smgr_rnode.node.relNode, - forkNum, blockNum, hresult->key.rnode.spcNode, hresult->key.rnode.dbNode, - hresult->key.rnode.relNode, hresult->key.forkNum, hresult->key.blockNum, hresult->id, - buf_state, LocalBufGetLSN(buf_desc)))); -#endif + *foundPtr = (buf_state & BM_VALID) ? TRUE : FALSE; /* 如果以前的读取尝试失败,则再次尝试 */ + return buf_desc; } -#ifdef LBDEBUG - fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n", smgr->smgr_rnode.node.relNode, forkNum, blockNum, - -t_thrd.storage_cxt.nextFreeLocalBuf - 1); -#endif - - /* - * Need to get a new buffer. We use a clock sweep algorithm (essentially - * the same as what freelist.c does now...) - */ + // 如果缓冲区不存在,需要分配新的缓冲区 try_counter = u_sess->storage_cxt.NLocBuffer; for (;;) { b = u_sess->storage_cxt.nextFreeLocalBuf; @@ -246,19 +238,18 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber pg_atomic_write_u32(&buf_desc->state, buf_state); try_counter = u_sess->storage_cxt.NLocBuffer; } else { - /* Found a usable buffer */ + /* 找到可用的缓冲区,增加引用计数,标记为使用中 */ u_sess->storage_cxt.LocalRefCount[b]++; ResourceOwnerRememberBuffer(t_thrd.utils_cxt.CurrentResourceOwner, BufferDescriptorGetBuffer(buf_desc)); break; } - } else if (--try_counter == 0) + } else if (--try_counter == 0) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("no empty local buffer available"))); + } } - /* - * this buffer is not referenced but it might still be dirty. if that's - * the case, write it out before reusing it! - */ + // 如果缓冲区存在脏数据,需要在重用之前写回磁盘 + buf_state = pg_atomic_read_u32(&buf_desc->state); if (buf_state & BM_DIRTY) { if (AmPageRedoProcess()) { LocalBufferFlushForExtremRTO(buf_desc); @@ -266,30 +257,26 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber LocalBufferWrite(buf_desc); } - /* Mark not-dirty now in case we error out below */ + // 清除脏标志 buf_state &= ~BM_DIRTY; pg_atomic_write_u32(&buf_desc->state, buf_state); u_sess->instr_cxt.pg_buffer_usage->local_blks_written++; } - /* - * lazy memory allocation: allocate space on first use of a buffer. - */ + // 懒惰内存分配:在第一次使用缓冲区时分配空间 if (LocalBufHdrGetBlock(buf_desc) == NULL) { - /* Set pointer for use by BufferGetBlock() macro */ + /* 为 BufferGetBlock 宏设置指针 */ LocalBufHdrGetBlock(buf_desc) = GetLocalBufferStorage(); } - /* - * Update the hash table: remove old entry, if any, and make new one. - */ + // 更新哈希表:删除旧条目(如果存在)并创建新条目 if (buf_state & BM_TAG_VALID) { hresult = (LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&buf_desc->tag, HASH_REMOVE, NULL); - if (hresult == NULL) /* shouldn't happen */ + if (hresult == NULL) /* 不应该发生 */ ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash table corrupted.")))); - /* mark buffer invalid just in case hash insert fails */ + /* 清除缓冲区标签以防哈希插入失败 */ CLEAR_BUFFERTAG(buf_desc->tag); buf_state &= ~(BM_VALID | BM_TAG_VALID); pg_atomic_write_u32(&buf_desc->state, buf_state); @@ -297,15 +284,12 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber hresult = (LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&new_tag, HASH_ENTER, &found); - if (found) /* shouldn't happen */ + if (found) /* 不应该发生 */ ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash table corrupted.")))); hresult->id = b; - /* - * it's all ours now. - */ buf_desc->tag = new_tag; - buf_desc->encrypt = smgr->encrypt ? true : false; /* set tde flag */ + buf_desc->encrypt = smgr->encrypt ? true : false; /* 设置 TDE 标志 */ buf_state &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR); buf_state |= BM_TAG_VALID; buf_state &= ~BUF_USAGECOUNT_MASK; @@ -318,35 +302,52 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber return buf_desc; } + /* * MarkLocalBufferDirty - * mark a local buffer dirty */ + /* +这个函数用于标记本地缓冲区中的数据为脏数据, +表示数据已被修改并且需要写回磁盘以确保数据持久性。 +函数首先验证传入的缓冲区是否为本地缓冲区,然后计算缓冲区描述符的索引。 +接着,它使用原子操作将缓冲区的状态标记为脏数据(设置 BM_DIRTY 标志), +并增加相关的脏数据统计信息。 +这个函数通常在数据被修改后调用,以确保修改的数据会被正确写回磁盘。 + */ void MarkLocalBufferDirty(Buffer buffer) { int buf_id; BufferDesc *buf_desc = NULL; uint32 buf_state; + // 确保传入的缓冲区是本地缓冲区 Assert(BufferIsLocal(buffer)); #ifdef LBDEBUG fprintf(stderr, "LB DIRTY %d\n", buffer); #endif + // 通过缓冲区号计算缓冲区描述符的索引 buf_id = -(buffer + 1); + // 确保本地引用计数大于0,表示缓冲区正在使用中 Assert(u_sess->storage_cxt.LocalRefCount[buf_id] > 0); + // 获取缓冲区描述符 buf_desc = &u_sess->storage_cxt.LocalBufferDescriptors[buf_id]; + // 使用原子操作将缓冲区的状态标记为脏数据 buf_state = pg_atomic_fetch_or_u32(&buf_desc->state, BM_DIRTY); + + // 如果之前未标记为脏数据,增加本地缓冲区脏数据统计 if (!(buf_state & BM_DIRTY)) { u_sess->instr_cxt.pg_buffer_usage->local_blks_dirtied++; pgstatCountLocalBlocksDirtied4SessionLevel(); } } + /* * DropRelFileNodeLocalBuffers * This function removes from the buffer pool all the pages of the @@ -358,6 +359,13 @@ void MarkLocalBufferDirty(Buffer buffer) * * See DropRelFileNodeBuffers in bufmgr.c for more notes. */ + /* +这个函数用于清除与指定关系文件节点和分支号相关的本地缓冲区中的块。 +它遍历本地缓冲区的所有条目,检查每个缓冲区的标签是否匹配传入的条件。 +如果匹配成功,它会首先检查缓冲区是否仍然被引用,如果是,则报错。 +然后,它从本地缓冲区哈希表中移除对应的缓冲区条目,并标记缓冲区为无效状态, +以便稍后被重新分配。这个函数通常在需要释放特定块的缓冲区时调用。 + */ void DropRelFileNodeLocalBuffers(const RelFileNode &rnode, ForkNumber forkNum, BlockNumber firstDelBlock) { int i; @@ -367,10 +375,14 @@ void DropRelFileNodeLocalBuffers(const RelFileNode &rnode, ForkNumber forkNum, B LocalBufferLookupEnt* hresult = NULL; uint32 buf_state; + // 读取缓冲区的状态信息 buf_state = pg_atomic_read_u32(&buf_desc->state); + // 检查缓冲区的标签是否有效,且与传入的关系文件节点、分支号、以及块号条件匹配 if ((buf_state & BM_TAG_VALID) && RelFileNodeEquals(rnode, buf_desc->tag.rnode) && buf_desc->tag.forkNum == forkNum && buf_desc->tag.blockNum >= firstDelBlock) { + + // 如果缓冲区仍然被引用,报错 if (u_sess->storage_cxt.LocalRefCount[i] != 0) { ereport(ERROR, (errcode(ERRCODE_INVALID_BUFFER_REFERENCE), @@ -379,13 +391,17 @@ void DropRelFileNodeLocalBuffers(const RelFileNode &rnode, ForkNumber forkNum, B relpathbackend(buf_desc->tag.rnode, BackendIdForTempRelations, buf_desc->tag.forkNum), u_sess->storage_cxt.LocalRefCount[i])))); } - /* Remove entry from hashtable */ + + // 从哈希表中移除缓冲区的条目 hresult = (LocalBufferLookupEnt*)hash_search( u_sess->storage_cxt.LocalBufHash, (void*)&buf_desc->tag, HASH_REMOVE, NULL); - if (hresult == NULL) /* shouldn't happen */ + + if (hresult == NULL) /* 不应该发生 */ ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash table corrupted.")))); - /* Mark buffer invalid */ + + // 清除缓冲区的标签 CLEAR_BUFFERTAG(buf_desc->tag); + // 标记缓冲区为无效状态 buf_state &= ~BUF_FLAG_MASK; buf_state &= ~BUF_USAGECOUNT_MASK; pg_atomic_write_u32(&buf_desc->state, buf_state); @@ -393,6 +409,7 @@ void DropRelFileNodeLocalBuffers(const RelFileNode &rnode, ForkNumber forkNum, B } } + /* * DropRelFileNodeAllLocalBuffers * This function removes from the buffer pool all pages of all forks @@ -400,6 +417,14 @@ void DropRelFileNodeLocalBuffers(const RelFileNode &rnode, ForkNumber forkNum, B * * See DropRelFileNodeAllBuffers in bufmgr.c for more notes. */ + /* +这个函数用于清除与指定关系文件节点相关的所有本地缓冲区中的块。 +它遍历本地缓冲区的所有条目,检查每个缓冲区的标签是否匹配传入的条件。 +如果匹配成功,它会首先检查缓冲区是否仍然被引用,如果是,则报错。 +然后,它从本地缓冲区哈希表中移除对应的缓冲区条目, +并标记缓冲区为无效状态,以便稍后被重新分配。 +这个函数通常在需要释放与特定关系文件节点相关的所有缓冲区时调用。 + */ void DropRelFileNodeAllLocalBuffers(const RelFileNode &rnode) { int i; @@ -409,9 +434,13 @@ void DropRelFileNodeAllLocalBuffers(const RelFileNode &rnode) LocalBufferLookupEnt* hresult = NULL; uint32 buf_state; + // 读取缓冲区的状态信息 buf_state = pg_atomic_read_u32(&buf_desc->state); + // 检查缓冲区的标签是否有效,且与传入的关系文件节点条件匹配 if ((buf_state & BM_TAG_VALID) && RelFileNodeEquals(rnode, buf_desc->tag.rnode)) { + + // 如果缓冲区仍然被引用,报错 if (u_sess->storage_cxt.LocalRefCount[i] != 0) { if (buf_desc->tag.forkNum < 0) { ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), @@ -424,13 +453,17 @@ void DropRelFileNodeAllLocalBuffers(const RelFileNode &rnode) relpathbackend(buf_desc->tag.rnode, BackendIdForTempRelations, buf_desc->tag.forkNum), u_sess->storage_cxt.LocalRefCount[i])))); } - /* Remove entry from hashtable */ + + // 从哈希表中移除缓冲区的条目 hresult = (LocalBufferLookupEnt*)hash_search( u_sess->storage_cxt.LocalBufHash, (void*)&buf_desc->tag, HASH_REMOVE, NULL); - if (hresult == NULL) /* shouldn't happen */ + + if (hresult == NULL) /* 不应该发生 */ ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash table corrupted.")))); - /* Mark buffer invalid */ + + // 清除缓冲区的标签 CLEAR_BUFFERTAG(buf_desc->tag); + // 标记缓冲区为无效状态 buf_state &= ~BUF_FLAG_MASK; buf_state &= ~BUF_USAGECOUNT_MASK; pg_atomic_write_u32(&buf_desc->state, buf_state); @@ -438,19 +471,27 @@ void DropRelFileNodeAllLocalBuffers(const RelFileNode &rnode) } } + /* * InitLocalBuffers - * init the local buffer cache. Since most queries (esp. multi-user ones) * don't involve local buffers, we delay allocating actual memory for the * buffers until we need them; just make the buffer headers here. */ + /* +该函数的主要任务是初始化本地缓冲区。 +在初始化过程中,它分配了用于缓冲区描述符、块指针和引用计数的内存空间, +并清零这些内存区域。然后,它为每个本地缓冲区分配了一个唯一的缓冲区ID, +并创建了一个用于查找的哈希表。这个函数通常在数据库启动时被调用, +用于准备本地缓冲区以供后续使用。 + */ static void InitLocalBuffers(void) { - int nbufs = u_sess->attr.attr_storage.num_temp_buffers; + int nbufs = u_sess->attr.attr_storage.num_temp_buffers; // 获取本地缓冲区的数量 HASHCTL info; int i; - /* Allocate and zero buffer headers and auxiliary arrays */ + /* 分配并清零缓冲区头和辅助数组的内存空间 */ u_sess->storage_cxt.LocalBufferDescriptors = (BufferDesc*)MemoryContextAllocZero( SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), (unsigned int)nbufs * sizeof(BufferDesc)); u_sess->storage_cxt.LocalBufferBlockPointers = (Block*)MemoryContextAllocZero( @@ -463,20 +504,18 @@ static void InitLocalBuffers(void) u_sess->storage_cxt.nextFreeLocalBuf = 0; - /* initialize fields that need to start off nonzero */ + /* 初始化需要初始值非零的字段 */ for (i = 0; i < nbufs; i++) { BufferDesc* buf = &u_sess->storage_cxt.LocalBufferDescriptors[i]; /* - * negative to indicate local buffer. This is tricky: shared buffers - * start with 0. We have to start with -2. (Note that the routine - * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id - * is -1.) + * 使用负数表示本地缓冲区。这有点复杂:共享缓冲区从0开始,我们必须从-2开始。 + * (请注意,BufferDescriptorGetBuffer函数会将buf_id加1,因此我们的第一个缓冲区ID是-1。) */ buf->buf_id = -i - 2; } - /* Create the lookup hash table */ + /* 创建查找哈希表 */ errno_t ret = memset_s(&info, sizeof(info), 0, sizeof(info)); securec_check(ret, "\0", "\0"); info.keysize = sizeof(BufferTag); @@ -492,10 +531,11 @@ static void InitLocalBuffers(void) (errmsg("could not initialize local buffer hash table.")))); } - /* Initialization done, mark buffers allocated */ + /* 初始化完成,标记分配了缓冲区 */ u_sess->storage_cxt.NLocBuffer = nbufs; } + /* * GetLocalBufferStorage - allocate memory for a local buffer * @@ -505,6 +545,14 @@ static void InitLocalBuffers(void) * within a particular process, no point in burdening memmgr with separately * managed chunks. */ + /* +该函数用于获取本地缓冲区的内存存储空间。在初始化本地缓冲区时, + +系统分配了一块连续的内存空间来存储多个缓冲区。 +这个函数负责分配每个缓冲区所需的内存,并返回对该内存的引用。 +如果当前内存块中没有足够的空间来分配新的缓冲区, +则会请求内存管理器分配一个新的内存块,然后从新内存块中分配缓冲区。这样,可以根据需要动态增加本地缓冲区的数量,而不会预先分配所有缓冲区所需的内存 + */ static Block GetLocalBufferStorage(void) { char *this_buf = NULL; @@ -512,13 +560,13 @@ static Block GetLocalBufferStorage(void) Assert(u_sess->storage_cxt.total_bufs_allocated < u_sess->storage_cxt.NLocBuffer); if (u_sess->storage_cxt.next_buf_in_block >= u_sess->storage_cxt.num_bufs_in_block) { - /* Need to make a new request to memmgr */ + /* 需要向内存管理器请求新的内存块 */ + int num_bufs; /* - * We allocate local buffers in a context of their own, so that the - * space eaten for them is easily recognizable in MemoryContextStats - * output. Create the context on first use. + * 我们在一个单独的上下文中分配本地缓冲区,以便在 MemoryContextStats 输出中更容易识别它们所占用的空间。 + * 在首次使用时创建此上下文。 */ if (u_sess->storage_cxt.LocalBufferContext == NULL) u_sess->storage_cxt.LocalBufferContext = AllocSetContextCreate(u_sess->top_mem_cxt, @@ -527,11 +575,11 @@ static Block GetLocalBufferStorage(void) ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - /* Start with a 16-buffer request; subsequent ones double each time */ + /* 从一个16个缓冲区的请求开始;后续请求每次翻倍 */ num_bufs = Max(u_sess->storage_cxt.num_bufs_in_block * 2, 16); - /* But not more than what we need for all remaining local bufs */ + /* 但不能超过剩余本地缓冲区所需的数量 */ num_bufs = Min(num_bufs, u_sess->storage_cxt.NLocBuffer - u_sess->storage_cxt.total_bufs_allocated); - /* And don't overflow MaxAllocSize, either */ + /* 也不能超过 MaxAllocSize */ num_bufs = Min((unsigned int)(num_bufs), MaxAllocSize / BLCKSZ); u_sess->storage_cxt.cur_block = @@ -540,7 +588,7 @@ static Block GetLocalBufferStorage(void) u_sess->storage_cxt.num_bufs_in_block = num_bufs; } - /* Allocate next buffer in current memory block */ + /* 在当前内存块中分配下一个缓冲区 */ this_buf = u_sess->storage_cxt.cur_block + u_sess->storage_cxt.next_buf_in_block * BLCKSZ; u_sess->storage_cxt.next_buf_in_block++; u_sess->storage_cxt.total_bufs_allocated++; @@ -548,6 +596,7 @@ static Block GetLocalBufferStorage(void) return (Block)this_buf; } + /* * AtEOXact_LocalBuffers - clean up at end of transaction. * @@ -598,32 +647,32 @@ void AtProcExit_LocalBuffers(void) void ForgetLocalBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum) { SMgrRelation smgr = smgropen(rnode, t_thrd.proc_cxt.MyBackendId); - BufferTag tag; /* identity of target block */ + BufferTag tag; /* 目标块的标识 */ LocalBufferLookupEnt *hresult; BufferDesc *bufHdr; uint32 bufState; /* - * If somehow this is the first request in the session, there's nothing to - * do. (This probably shouldn't happen, though.) + * 如果不知何故这是会话中的第一个请求,则无需执行任何操作。 + * (尽管这可能不应该发生。) */ if (t_thrd.storage_cxt.LocalBufHash == NULL) { return; } - /* create a tag so we can lookup the buffer */ + /* 创建一个标签以便查找缓冲块 */ INIT_BUFFERTAG(tag, smgr->smgr_rnode.node, forkNum, blockNum); - /* see if the block is in the local buffer pool */ + /* 查看块是否在本地缓冲池中 */ hresult = (LocalBufferLookupEnt *) hash_search(t_thrd.storage_cxt.LocalBufHash, (void *) &tag, HASH_REMOVE, NULL); - /* didn't find it, so nothing to do */ + /* 没找到,所以无需处理 */ if (!hresult) { return; } - /* mark buffer invalid */ + /* 将缓冲块标记为无效 */ bufHdr = GetLocalBufferDescriptor(hresult->id); CLEAR_BUFFERTAG(bufHdr->tag); bufState = pg_atomic_read_u32(&bufHdr->state); -- 2.34.1 From 0de7fe183877afa5e550a95bec754e60e0d47bdd Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 22:55:53 +0800 Subject: [PATCH 16/19] Enter --- .../storage/access/common/reloptions.cpp | 422 +++++++++++------- 1 file changed, 273 insertions(+), 149 deletions(-) diff --git a/src/gausskernel/storage/access/common/reloptions.cpp b/src/gausskernel/storage/access/common/reloptions.cpp index a3fcfbfee..1bb8cfdcf 100644 --- a/src/gausskernel/storage/access/common/reloptions.cpp +++ b/src/gausskernel/storage/access/common/reloptions.cpp @@ -122,88 +122,123 @@ static relopt_bool boolRelOpts[] = { }; static relopt_int intRelOpts[] = { - {{ "fillfactor", "Packs table pages only to this percentage", RELOPT_KIND_HEAP }, - HEAP_DEFAULT_FILLFACTOR, - HEAP_MIN_FILLFACTOR, - 100 }, - {{ "fillfactor", "Packs btree index pages only to this percentage", RELOPT_KIND_BTREE }, - BTREE_DEFAULT_FILLFACTOR, - BTREE_MIN_FILLFACTOR, - 100 }, - {{ "fillfactor", "Packs hash index pages only to this percentage", RELOPT_KIND_HASH }, - HASH_DEFAULT_FILLFACTOR, - HASH_MIN_FILLFACTOR, - 100 }, - {{ "fillfactor", "Packs gist index pages only to this percentage", RELOPT_KIND_GIST }, - GIST_DEFAULT_FILLFACTOR, - GIST_MIN_FILLFACTOR, - 100 }, - {{ "fillfactor", "Packs spgist index pages only to this percentage", RELOPT_KIND_SPGIST }, - SPGIST_DEFAULT_FILLFACTOR, - SPGIST_MIN_FILLFACTOR, - 100 }, - {{ "autovacuum_vacuum_threshold", "Minimum number of tuple updates or deletes prior to vacuum", - RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, - -1, - 0, - INT_MAX }, - {{ "autovacuum_analyze_threshold", "Minimum number of tuple inserts, updates or deletes prior to analyze", - RELOPT_KIND_HEAP }, - -1, - 0, - INT_MAX }, - {{ "autovacuum_vacuum_cost_delay", "Vacuum cost delay in milliseconds, for autovacuum", - RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, - -1, - 0, - 100 }, - {{ "autovacuum_vacuum_cost_limit", "Vacuum cost amount available before napping, for autovacuum", - RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, - -1, - 1, - 10000 }, + // "fillfactor" 选项适用于 HEAP 类型的关系 + {{ "fillfactor", "仅将表页打包到此百分比", RELOPT_KIND_HEAP }, + HEAP_DEFAULT_FILLFACTOR, // HEAP 类型关系的默认填充因子 + HEAP_MIN_FILLFACTOR, // 允许的最小填充因子 + 100 }, // 允许的最大填充因子 + + // "fillfactor" 选项适用于 BTREE 类型的关系 + {{ "fillfactor", "仅将 btree 索引页打包到此百分比", RELOPT_KIND_BTREE }, + BTREE_DEFAULT_FILLFACTOR, // BTREE 类型关系的默认填充因子 + BTREE_MIN_FILLFACTOR, // 允许的最小填充因子 + 100 }, // 允许的最大填充因子 + + // "fillfactor" 选项适用于 HASH 类型的关系 + {{ "fillfactor", "仅将 hash 索引页打包到此百分比", RELOPT_KIND_HASH }, + HASH_DEFAULT_FILLFACTOR, // HASH 类型关系的默认填充因子 + HASH_MIN_FILLFACTOR, // 允许的最小填充因子 + 100 }, // 允许的最大填充因子 + + // "fillfactor" 选项适用于 GIST 类型的关系 + {{ "fillfactor", "仅将 gist 索引页打包到此百分比", RELOPT_KIND_GIST }, + GIST_DEFAULT_FILLFACTOR, // GIST 类型关系的默认填充因子 + GIST_MIN_FILLFACTOR, // 允许的最小填充因子 + 100 }, // 允许的最大填充因子 + + // "fillfactor" 选项适用于 SPGIST 类型的关系 + {{ "fillfactor", "仅将 spgist 索引页打包到此百分比", RELOPT_KIND_SPGIST }, + SPGIST_DEFAULT_FILLFACTOR, // SPGIST 类型关系的默认填充因子 + SPGIST_MIN_FILLFACTOR, // 允许的最小填充因子 + 100 }, // 允许的最大填充因子 + + // "autovacuum_vacuum_threshold" 选项适用于 HEAP 和 TOAST 类型的关系 + {{ "autovacuum_vacuum_threshold", "在执行自动清理之前更新或删除的元组数的最小值", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, + -1, // 默认的自动清理阈值 + 0, // 允许的最小自动清理阈值 + INT_MAX }, // 允许的最大自动清理阈值 + + // "autovacuum_analyze_threshold" 选项适用于 HEAP 类型的关系 + {{ "autovacuum_analyze_threshold", "在执行自动分析之前插入、更新或删除的元组数的最小值", RELOPT_KIND_HEAP }, + -1, // 默认的自动分析阈值 + 0, // 允许的最小自动分析阈值 + INT_MAX }, // 允许的最大自动分析阈值 + + // "autovacuum_vacuum_cost_delay" 选项适用于 HEAP 和 TOAST 类型的关系 + {{ "autovacuum_vacuum_cost_delay", "自动清理的延迟成本,以毫秒为单位", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, + -1, // 默认的自动清理成本延迟 + 0, // 允许的最小自动清理成本延迟 + 100 }, // 允许的最大自动清理成本延迟 + + // "autovacuum_vacuum_cost_limit" 选项适用于 HEAP 和 TOAST 类型的关系 + {{ "autovacuum_vacuum_cost_limit", "自动清理之前可用的清理成本量", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, + -1, // 默认的自动清理成本限制 + 1, // 允许的最小自动清理成本限制 + 10000 }, // 允许的最大自动清理成本限制 + #ifdef ENABLE_MULTIPLE_NODES + // "tsdb_deltamerge_interval" 选项适用于 HEAP 类型的关系 { - { "tsdb_deltamerge_interval", "job interval for tsdb delta merge", RELOPT_KIND_HEAP}, - Tsdb::DELTAMERGE_INTERVAL_DEFAULT, - Tsdb::DELTAMERGE_INTERVAL_MIN, - Tsdb::DELTAMERGE_INTERVAL_MAX + { "tsdb_deltamerge_interval", "tsdb delta 合并任务的间隔", RELOPT_KIND_HEAP }, + Tsdb::DELTAMERGE_INTERVAL_DEFAULT, // 默认的 delta 合并间隔 + Tsdb::DELTAMERGE_INTERVAL_MIN, // 允许的最小 delta 合并间隔 + Tsdb::DELTAMERGE_INTERVAL_MAX // 允许的最大 delta 合并间隔 }, + + // "tsdb_deltamerge_threshold" 选项适用于 HEAP 类型的关系 { - { "tsdb_deltamerge_threshold", "if the number of rows in a tsdb delta table is less than "\ - "tsdb_deltamerge_threshold, skip delta merge", RELOPT_KIND_HEAP}, - Tsdb::DELTAMERGE_THRESHOLD_DEFAULT, - Tsdb::DELTAMERGE_THRESHOLD_MIN, - Tsdb::DELTAMERGE_THRESHOLD_MAX + { "tsdb_deltamerge_threshold", "如果 tsdb delta 表中的行数小于此阈值,则跳过 delta 合并", RELOPT_KIND_HEAP }, + Tsdb::DELTAMERGE_THRESHOLD_DEFAULT, // 默认的 delta 合并阈值 + Tsdb::DELTAMERGE_THRESHOLD_MIN, // 允许的最小 delta 合并阈值 + Tsdb::DELTAMERGE_THRESHOLD_MAX // 允许的最大 delta 合并阈值 }, + + // "tsdb_deltainsert_threshold" 选项适用于 HEAP 类型的关系 { - { "tsdb_deltainsert_threshold", "insert data into delta table if the number of rows of one insert"\ - " operation is less than tsdb_deltainsert_threshold", RELOPT_KIND_HEAP}, - Tsdb::DELTAINSERT_THRESHOLD_DEFAULT, - Tsdb::DELTAINSERT_THRESHOLD_MIN, - Tsdb::DELTAINSERT_THRESHOLD_MAX + { "tsdb_deltainsert_threshold", "如果一个插入操作的行数小于此阈值,则插入到 delta 表,否则插入到正常表", RELOPT_KIND_HEAP }, + Tsdb::DELTAINSERT_THRESHOLD_DEFAULT, // 默认的 delta 插入阈值 + Tsdb::DELTAINSERT_THRESHOLD_MIN, // 允许的最小 delta 插入阈值 + Tsdb::DELTAINSERT_THRESHOLD_MAX // 允许的最大 delta 插入阈值 }, #endif - {{ "max_batchrow", "the upmost rows at each batch inserting", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, - RelDefaultFullCuSize, - 10 * BatchMaxSize, - RelMaxFullCuSize }, - {{ "deltarow_threshold", "if smaller than it, insert into delta table; otherwise insert into normal table", - RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, - RelDefaultDletaRows, - 0, - 9999 }, - {{ "partial_cluster_rows", "row numbers of partial cluster feature", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, - RelDefaultPartialClusterRows, - -1, - 0x7fffffff }, - {{ "internal_mask", "internal mask", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, 0, 0, 0x7fffffff }, - {{ "gin_pending_list_limit", "Maximum size of the pending list for this GIN index, in kilobytes.", - RELOPT_KIND_GIN }, - -1, - 64, - MAX_KILOBYTES }, - {{ "gram_size", "Gram size for N-gram text search praser.", RELOPT_KIND_NPARSER }, 2, 1, 4 }, + + // "max_batchrow" 选项适用于 HEAP 和 PSORT 类型的关系 + {{ "max_batchrow", "每次批量插入的最大行数", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, + RelDefaultFullCuSize, // HEAP 和 PSORT 类型关系的默认批量行数 + 10 * BatchMaxSize, // 允许的最小批量行数 + RelMaxFullCuSize }, // 允许的最大批量行数 + + // "deltarow_threshold" 选项���用于 HEAP 和 PSORT 类型的关系 + {{ "deltarow_threshold", "如果小于此值,则插入到 delta 表;否则插入到正常表", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, + RelDefaultDletaRows, // HEAP 和 PSORT 类型关系的默认 delta 行阈值 + 0, // 允许的最小 delta 行阈值 + 9999 }, // 允许的最大 delta 行阈值 + + // "partial_cluster_rows" 选项适用于 HEAP 和 PSORT 类型的关系 + {{ "partial_cluster_rows", "部分集群特性的行数", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, + RelDefaultPartialClusterRows, // HEAP 和 PSORT 类型关系的默认部分集群行数 + -1, // 允许的最小部分集群行数 + 0x7fffffff }, // 允许的最大部分集群行数 + + // "internal_mask" 选项适用于 HEAP 和 PSORT 类型的关系 + {{ "internal_mask", "内部掩码", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, + 0, // 默认的内部掩码 + 0, // 允许的最小内部掩码 + 0x7fffffff }, // 允许的最大内部掩码 + + // "gin_pending_list_limit" 选项适用于 GIN 类型的关系 + {{ "gin_pending_list_limit", "此 GIN 索引的挂起列表的最大大小,以千字节为单位", RELOPT_KIND_GIN }, + -1, // 默认的 GIN 索引挂起列表大小 + 64, // 允许的最小 GIN 索引挂起列表大小 + MAX_KILOBYTES }, // 允许的最大 GIN 索引挂起列表大小 + + // "gram_size" 选项适用于 NPARSER 类型的关系 + {{ "gram_size", "N-gram 文本搜索解析器的 gram 大小", RELOPT_KIND_NPARSER }, + 2, // 默认的 gram 大小 + 1, // 允许的最小 gram 大小 + 4 }, // 允许的最大 gram 大小 +}; + /* COMPRESSLEVEL option */ { @@ -225,28 +260,66 @@ static relopt_int intRelOpts[] = { REDIS_REL_DESTINATION /* REDIS_REL_DESTINATION is the max value of append mode that can set by users. */ }, - {{ "rel_cn_oid", "rel oid on coordinator", RELOPT_KIND_HEAP }, 0, 0, 2000000000 }, - {{ "exec_step", "redis exec step", RELOPT_KIND_HEAP }, 0, 1, 4 }, - {{ "init_td", "number of td slots", RELOPT_KIND_HEAP }, UHEAP_DEFAULT_TD, UHEAP_MIN_TD, UHEAP_MAX_TD }, - {{ "bucketcnt", "number of bucket map counts", RELOPT_KIND_HEAP }, 0, 32, 16384 }, - { - { - "parallel_workers", - "Number of parallel processes that can be used per executor node for this relation.", - RELOPT_KIND_HEAP, - }, - 0, 1, 32 - }, - {{ "compress_level", "Level of page compression.", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, 0, -31, 31}, - {{ "compresstype", "compress type (none, pglz or zstd).", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, 0, 0, 2}, - {{ "compress_chunk_size", "Size of chunk to store compressed page.", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, - BLCKSZ / 2, - BLCKSZ / 16, - BLCKSZ / 2}, - {{ "compress_prealloc_chunks", "Number of prealloced chunks for each block.", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, - 0, - 0, - 7}, + static relopt_int intRelOpts[] = { + // "rel_cn_oid" 选项适用于 HEAP 类型的关系 + {{ "rel_cn_oid", "协调节点上的关系 OID", RELOPT_KIND_HEAP }, + 0, // 默认的协调节点关系 OID + 0, // 允许的最小协调节点关系 OID + 2000000000 }, // 允许的最大协调节点关系 OID + + // "exec_step" 选项适用于 HEAP 类型的关系 + {{ "exec_step", "Redis 执行步骤", RELOPT_KIND_HEAP }, + 0, // 默认的 Redis 执行步骤 + 1, // 允许的最小 Redis 执行步骤 + 4 }, // 允许的最大 Redis 执行步骤 + + // "init_td" 选项适用于 HEAP 类型的关系 + {{ "init_td", "td 槽数量", RELOPT_KIND_HEAP }, + UHEAP_DEFAULT_TD, // 默认的 td 槽数量 + UHEAP_MIN_TD, // 允许的最小 td 槽数量 + UHEAP_MAX_TD }, // 允许的最大 td 槽数量 + + // "bucketcnt" 选项适用于 HEAP 类型的关系 + {{ "bucketcnt", "桶映射计数的数量", RELOPT_KIND_HEAP }, + 0, // 默认的桶映射计数 + 32, // 允许的最小桶映射计数 + 16384 }, // 允许的最大桶映射计数 + + // "parallel_workers" 选项适用于 HEAP 类型的关系 + {{ + "parallel_workers", + "每个执行器节点可用于此关系的并行进程数。", + RELOPT_KIND_HEAP, + }, + 0, // 默认的并行进程数 + 1, // 允许的最小并行进程数 + 32 }, // 允许的最大并行进程数 + + // "compress_level" 选项适用于 HEAP 和 BTREE 类型的关系 + {{ "compress_level", "页面压缩级别", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, + 0, // 默认的页面压缩级别 + -31, // 允许的最小页面压缩级别 + 31 }, // 允许的最大页面压缩级别 + + // "compresstype" 选项适用于 HEAP 和 BTREE 类型的关系 + {{ "compresstype", "压缩类型(无、pglz 或 zstd)", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, + 0, // 默认的压缩类型 + 0, // 允许的最小压缩类型 + 2 }, // 允许的最大压缩类型 + + // "compress_chunk_size" 选项适用于 HEAP 和 BTREE 类型的关系 + {{ "compress_chunk_size", "用于存储压缩页的块大小", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, + BLCKSZ / 2, // 默认的压缩块大小 + BLCKSZ / 16, // 允许的最小压缩块大小 + BLCKSZ / 2 }, // 允许的最大压缩块大小 + + // "compress_prealloc_chunks" 选项适用于 HEAP 和 BTREE 类型的关系 + {{ "compress_prealloc_chunks", "每个块的预分配块数量", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE}, + 0, // 默认的预分配块数量 + 0, // 允许的最小预分配块数量 + 7 }, // 允许的最大预分配块数量 +}; + /* list terminator */ {{NULL}} }; @@ -317,195 +390,246 @@ static relopt_real realRelOpts[] = { }; static relopt_string stringRelOpts[] = { - {{ "split_flag", "split flag for pound text search praser.", RELOPT_KIND_PPARSER }, 2, false, NULL, "#" }, - {{ "buffering", "Enables buffering build for this GiST index", RELOPT_KIND_GIST }, - 4, + // "split_flag" 选项适用于 PPARSER 类型的关系 + {{ "split_flag", "分割符号文本搜索解析器的分割标志", RELOPT_KIND_PPARSER }, 2, false, NULL, "#" }, + + // "buffering" 选项适用于 GIST 类型的关系 + {{ "buffering", "启用此 GiST 索引的构建缓冲", RELOPT_KIND_GIST }, + 4, // 默认的缓冲选项 false, gistValidateBufferingOption, "auto" }, + // "orientation" 选项适用于 HEAP 类型的关系 { - { "orientation", "row-store, col-store, orc-store, inplace-store or timeseries", RELOPT_KIND_HEAP }, - 10, + { "orientation", "行存储、列存储、ORC 存储、原地存储或时间序列", RELOPT_KIND_HEAP }, + 10, // 默认的存储方向 false, ValidateStrOptOrientation, ORIENTATION_ROW, }, + + // "indexsplit" 选项适用于 BTREE 类型的关系 { - {"indexsplit", "default, insertpt", RELOPT_KIND_BTREE}, - 7, + {"indexsplit", "默认、insertpt", RELOPT_KIND_BTREE}, + 7, // 默认的索引拆分选项 false, ValidateStrOptIndexsplit, INDEXSPLIT_OPT_DEFAULT, }, + + // "ttl" 选项适用于 HEAP 类型的关系 { - { "ttl", "time to live for timeseries data management", RELOPT_KIND_HEAP }, - 9, + { "ttl", "时间序列数据管理的生存时间", RELOPT_KIND_HEAP }, + 9, // 默认的生存时间 false, ValidateStrOptTTL, TIME_UNDEFINED, }, + + // "period" 选项适用于 HEAP 类型的关系 { - { "period", "partition range for timeseries data management", RELOPT_KIND_HEAP }, - 9, + { "period", "时间序列数据管理的分区范围", RELOPT_KIND_HEAP }, + 9, // 默认的分区范围 false, ValidateStrOptPeriod, TIME_UNDEFINED, }, + + // "partition_interval" 选项适用于 HEAP 类型的关系 { - { "partition_interval", "partition interval for streaming contview table", RELOPT_KIND_HEAP }, - 9, + { "partition_interval", "流式连续视图表的分区间隔", RELOPT_KIND_HEAP }, + 9, // 默认的分区间隔 false, ValidateStrOptPartitionInterval, TIME_UNDEFINED, }, + + // "time_column" 选项适用于 HEAP 类型的关系 { - { "time_column", "time column for streaming contview table", RELOPT_KIND_HEAP }, - 9, + { "time_column", "流式连续视图表的时间列", RELOPT_KIND_HEAP }, + 9, // 默认的时间列 false, ValidateStrOptTimeColumn, COLUMN_UNDEFINED, }, + + // "ttl_interval" 选项适用于 HEAP 类型的关系 { - { "ttl_interval", "ttl interval for streaming contview table", RELOPT_KIND_HEAP }, - 9, + { "ttl_interval", "流式连续视图表的 TTL 间隔", RELOPT_KIND_HEAP }, + 9, // 默认的 TTL 间隔 false, ValidateStrOptTTLInterval, TIME_UNDEFINED, }, + + // "gather_interval" 选项适用于 HEAP 类型的关系 { - { "gather_interval", "gather interval for streaming contview table", RELOPT_KIND_HEAP }, - 9, + { "gather_interval", "流式连续视图表的收集间隔", RELOPT_KIND_HEAP }, + 9, // 默认的收集间隔 false, ValidateStrOptGatherInterval, TIME_UNDEFINED, }, + + // "sw_interval" 选项适用于 HEAP 类型的关系 { - { "sw_interval", "sliding window interval for streaming contquery table", RELOPT_KIND_HEAP }, - 9, + { "sw_interval", "流式连续查询表的滑动窗口间隔", RELOPT_KIND_HEAP }, + 9, // 默认的滑动窗口间隔 false, ValidateStrOptSwInterval, TIME_UNDEFINED, }, + + // "version" 选项适用于 HEAP 类型的关系 { - { "version", "store version", RELOPT_KIND_HEAP }, - 4, + { "version", "存储版本", RELOPT_KIND_HEAP }, + 4, // 默认的存储版本 false, ValidateStrOptVersion, ORC_VERSION_012, }, + + // "compression" 选项适用于 HEAP 类型的关系 { - { "compression", "which compression level applied to, or not compressed", RELOPT_KIND_HEAP }, - 6, + { "compression", "应用的压缩级别或不压缩", RELOPT_KIND_HEAP }, + 6, // 默认的压缩级别 false, ValidateStrOptCompression, COMPRESSION_LOW, }, + + // "filesystem" 选项适用于 TABLESPACE 类型的关系 { - { "filesystem", "which filesystem applied", RELOPT_KIND_TABLESPACE }, - 7, + { "filesystem", "应用的文件系统", RELOPT_KIND_TABLESPACE }, + 7, // 默认的文件系统 false, ValidateStrOptSpcFileSystem, FILESYSTEM_GENERAL, }, + + // "address" 选项适用于 TABLESPACE 类型的关系 { - { "address", "which address server applied", RELOPT_KIND_TABLESPACE }, - 6, + { "address", "应用的地址服务器", RELOPT_KIND_TABLESPACE }, + 6, // 默认的地址服务器 false, ValidateStrOptSpcAddress, "", }, + + // "cfgpath" 选项适用于 TABLESPACE 类型的关系 { - { "cfgpath", "config information path", RELOPT_KIND_TABLESPACE }, - 6, + { "cfgpath", "配置信息路径", RELOPT_KIND_TABLESPACE }, + 6, // 默认的配置信息路径 false, ValidateStrOptSpcCfgPath, "", }, + + // "storepath" 选项适用于 TABLESPACE 类型的关系 { - { "storepath", "store information path", RELOPT_KIND_TABLESPACE }, - 6, + { "storepath", "存储信息路径", RELOPT_KIND_TABLESPACE }, + 6, // 默认的存储信息路径 false, ValidateStrOptSpcStorePath, "", }, + + // "append_mode" 选项适用于 HEAP 类型的关系 { - { "append_mode", "set relation insert under append mode", RELOPT_KIND_HEAP }, - 6, + { "append_mode", "设置关系在附加模式下插入", RELOPT_KIND_HEAP }, + 6, // 默认的附加模式 false, check_append_mode, "", }, + // "start_ctid_internal" 选项适用于 HEAP 类型的关系 { - { "start_ctid_internal", "set relation start ctid during redistribution", RELOPT_KIND_HEAP }, - 6, + { "start_ctid_internal", "设置关系在重新分布期间的起始 CTID", RELOPT_KIND_HEAP }, + 6, // 默认的起始 CTID false, NULL, "", }, + // "end_ctid_internal" 选项适用于 HEAP 类型的关系 { - { "end_ctid_internal", "set relation end ctid during redistribution", RELOPT_KIND_HEAP }, - 6, + { "end_ctid_internal", "设置关系在重新分布期间的结束 CTID", RELOPT_KIND_HEAP }, + 6, // 默认的结束 CTID false, NULL, "", }, + // "merge_list" 选项适用于 HEAP 类型的关系 { - { "merge_list", "set merge_list as bucketid1:start1:end1;bucketid1:start1:end1...", RELOPT_KIND_HEAP }, - 0, + { "merge_list", "设置合并列表为 bucketid1:start1:end1;bucketid1:start1:end1...", RELOPT_KIND_HEAP }, + 0, // 默认的合并列表 true, NULL, "", }, + + // "storage_type" 选项适用于 HEAP、BTREE 和 TOAST 类型的关系 { - {"storage_type", "Specifies the Table accessor routines", + {"storage_type", "指定表存储器例程", RELOPT_KIND_HEAP | RELOPT_KIND_BTREE | RELOPT_KIND_TOAST}, - strlen(TABLE_ACCESS_METHOD_ASTORE), + strlen(TABLE_ACCESS_METHOD_ASTORE), // 默认的表存储器例程 false, ValidateStrOptTableAccessMethod, TABLE_ACCESS_METHOD_ASTORE, }, + + // "dek_cipher" 选项适用于 HEAP 类型的关系 { - { "dek_cipher", "The cipher of TDE dek", RELOPT_KIND_HEAP }, - 0, + { "dek_cipher", "TDE dek 的密码", RELOPT_KIND_HEAP }, + 0, // 默认的 TDE dek 密码 false, ValidateStrOptDekCipher, "", }, + + // "cmk_id" 选项适用于 HEAP 类型的关系 { - { "cmk_id", "The id of TDE cmk", RELOPT_KIND_HEAP }, - 0, + { "cmk_id", "TDE cmk 的 ID", RELOPT_KIND_HEAP }, + 0, // 默认的 TDE cmk ID false, ValidateStrOptCmkId, "", }, + + // "encrypt_algo" 选项适用于 HEAP 类型的关系 { - { "encrypt_algo", "The algo of TDE", RELOPT_KIND_HEAP }, - 0, + { "encrypt_algo", "TDE 的算法", RELOPT_KIND_HEAP }, + 0, // 默认的 TDE 算法 false, ValidateStrOptEncryptAlgo, "", }, + + // "wait_clean_gpi" 选项适用于 HEAP 类型的关系 { - {"wait_clean_gpi", "Whether to wait for gpi cleanup", RELOPT_KIND_HEAP }, + {"wait_clean_gpi", "是否等待 GPI 清理", RELOPT_KIND_HEAP }, 1, false, CheckWaitCleanGpi, "n", }, + + // "wait_clean_cbi" 选项适用于 BTREE 类型的关系 { - {"wait_clean_cbi", "Whether to wait for cbi cleanup", RELOPT_KIND_BTREE }, + {"wait_clean_cbi", "是否等待 CBI 清理", RELOPT_KIND_BTREE }, 1, false, CheckWaitCleanCbi, "n", }, + + // "string_optimize" 选项适用于 HEAP 类型的关系 { - { "string_optimize", "string optimize for streaming contview table", RELOPT_KIND_HEAP }, - 9, + { "string_optimize", "字符串优化选项,用于流式连续视图表", RELOPT_KIND_HEAP }, + 9, // 默认的字符串优化选项 false, ValidateStrOptStringOptimize, COLUMN_UNDEFINED, -- 2.34.1 From c1e4d73b7a7289d501f538ee6f755b1e92bdb784 Mon Sep 17 00:00:00 2001 From: Cachuela Date: Thu, 5 Oct 2023 22:57:24 +0800 Subject: [PATCH 17/19] enter --- src/gausskernel/storage/dfs/dfs_connector.cpp | 67 ++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/gausskernel/storage/dfs/dfs_connector.cpp b/src/gausskernel/storage/dfs/dfs_connector.cpp index a71323912..676b00559 100644 --- a/src/gausskernel/storage/dfs/dfs_connector.cpp +++ b/src/gausskernel/storage/dfs/dfs_connector.cpp @@ -31,24 +31,31 @@ namespace dfs { * @Return: dfs connecort * @See also: */ +// 创建一个 DFSConnector 连接器对象 DFSConnector *createConnector(MemoryContext ctx, Oid foreignTableId) { + // 获取外部表的服务器类型 ServerTypeOption srvType = getServerType(foreignTableId); switch (srvType) { case T_OBS_SERVER: case T_TXT_CSV_OBS_SERVER: { #ifndef ENABLE_LITE_MODE + // 如果服务器类型是 OBS 或 TXT_CSV_OBS,则创建一个 OBSConnector 对象 + // 在 LITE_MODE 未启用时创建连接器对象 return New(ctx) OBSConnector(ctx, foreignTableId); #else + // 如果启用了 LITE_MODE,则报告不支持的特性,并返回 NULL FEATURE_ON_LITE_MODE_NOT_SUPPORTED(); return NULL; #endif } case T_HDFS_SERVER: { + // 如果服务器类型是 HDFS,则报告不支持的错误,并返回 NULL FEATURE_NOT_PUBLIC_ERROR("HDFS is not yet supported."); return NULL; } default: { + // 如果服务器类型未知或不支持,则报告错误,并返回 NULL ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmodule(MOD_DFS), errmsg("dfs foreign server type error. type = %d", srvType))); return NULL; @@ -56,6 +63,7 @@ DFSConnector *createConnector(MemoryContext ctx, Oid foreignTableId) } } + /* * @Description: create connector for foreign table * @IN ctx: memory context @@ -63,31 +71,39 @@ DFSConnector *createConnector(MemoryContext ctx, Oid foreignTableId) * @Return: dfs connector * @See also: */ +// 创建 DFSConnector 连接器对象 DFSConnector *createConnector(MemoryContext ctx, ServerTypeOption srvType, void *options) { + // 根据服务器类型进行分支处理 switch (srvType) { case T_OBS_SERVER: { #ifndef ENABLE_LITE_MODE + // 如果服务器类型为 OBS + // 在非 LITE_MODE 模式下,创建一个 OBSConnector 连接器对象 return New(ctx) OBSConnector(ctx, (ObsOptions *)options); #else + // 如果启用了 LITE_MODE,报告不支持的特性,并返回 NULL FEATURE_ON_LITE_MODE_NOT_SUPPORTED(); return NULL; #endif - break; + break; // 可选的,不过由于函数会在 return 之后退出,所以不会执行到这里 } case T_HDFS_SERVER: { + // 如果服务器类型为 HDFS,报告不支持的错误,并返回 NULL FEATURE_NOT_PUBLIC_ERROR("HDFS is not yet supported."); return NULL; - break; + break; // 可选的,不过由于函数会在 return 之后退出,所以不会执行到这里 } default: { + // 如果服务器类型未知或不支持,报告错误,并返回 NULL ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmodule(MOD_DFS), - errmsg("dfs forign server type error. type = %d", srvType))); + errmsg("dfs foreign server type error. type = %d", srvType))); return NULL; } } } + /* * @Description: create connctor for table space, only support hdfs * @IN ctx: memory context @@ -110,40 +126,51 @@ DFSConnector *createConnector(MemoryContext ctx, DfsSrvOptions *srvOptions, Oid * @Return: true for skip, false for not skip * @See also: */ +// 检查文件是否应该被跳过的函数 bool checkFileShouldSkip(char *fileName, int end_pos) { + // 如果文件名为空或者为空字符串,则不跳过文件,返回 false if (fileName == NULL || *fileName == '\0') { return false; } + // 使用断言确保 end_pos 不超过文件名的长度 Assert(end_pos <= (int)strlen(fileName)); - /* start with . _ or # will be skip */ + // 如果文件名以 '.'、'_' 或 '#' 开头,则跳过文件,返回 true if (*fileName == '.' || *fileName == '_' || *fileName == '#') { return true; } - /* end with ~ will be skip */ + // 获取文件名的最后一个字符 char *endChar = fileName + end_pos - 1; + + // 如果文件名以 '~' 结尾,则跳过文件,返回 true if (*endChar == '~') { return true; } + // 否则,不跳过文件,返回 false return false; } + /* * @Description: check file path should skip * @IN fileName: file name * @Return: true for skip, false for not skip * @See also: */ +// 封装函数,检查文件是否应该被跳过 bool checkFileShouldSkip(char *fileName) { + // 如果文件名为空或者为空字符串,则不跳过文件,返回 false if (fileName == NULL || *fileName == '\0') { return false; } + // 调用之前的 checkFileShouldSkip 函数,传递文件名和文件名的长度作为参数 + // 获取返回值并返回 bool isSkip = checkFileShouldSkip(fileName, strlen(fileName)); return isSkip; @@ -155,45 +182,55 @@ bool checkFileShouldSkip(char *fileName) * @Return: true for skip, false for not skip * @See also: */ +// 检查路径中的文件是否应该被跳过的函数 bool checkPathShouldSkip(char *pathName) { + // 如果路径名为空或为空字符串,则不跳过文件,返回 false if (pathName == NULL || *pathName == '\0') { return false; } - int end_pos = 0; - bool isSkip = false; - char *pos = pathName; + int end_pos = 0; // 用于存储文件名的结束位置 + bool isSkip = false; // 用于存储是否应该跳过文件的标志 + char *pos = pathName; // 用于迭代遍历路径名中的各个文件名 - /* first char is slash */ + // 如果路径名的第一个字符是斜杠 '/',则跳过它 if (*pos == '/') { pos += 1; } - /* find next slash */ + // 查找下一个斜杠 '/' char *next_slash = strchr(pos, '/'); - /* get next slash position or the last char position */ + + // 获取下一个斜杠的位置或路径名的最后一个字符位置 end_pos = (int)((next_slash != NULL) ? (next_slash - pos) : strlen(pos)); - /* check path name */ + // 检查当前文件名是否应该跳过 isSkip = checkFileShouldSkip(pos, end_pos); + // 遍历路径中的各个文件名,直到找到一个应该跳过的文件名或遍历完整个路径 while (!isSkip) { pos = next_slash; + + // 如果 pos 为 NULL 或为空字符,则退出循环 if (pos == NULL || *pos == '\0') { break; } pos += 1; - /* find next slash */ + + // 查找下一个斜杠 '/' next_slash = strchr(pos, '/'); - /* get next slash position or the last char position */ + + // 获取下一个斜杠的位置或路径名的最后一个字符位置 end_pos = (int)((next_slash != NULL) ? (next_slash - pos) : strlen(pos)); - /* check path name */ + // 检查当前文件名是否应该跳过 isSkip = checkFileShouldSkip(pos, end_pos); } + // 返回是否应该跳过路径中的文件 return isSkip; } + -- 2.34.1 From 3c3d687a9e0c965986d800b25ba28d5504853c8d Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 23:14:49 +0800 Subject: [PATCH 18/19] Enter --- .../storage/access/common/reloptions.cpp | 344 +++++++++++++----- 1 file changed, 243 insertions(+), 101 deletions(-) diff --git a/src/gausskernel/storage/access/common/reloptions.cpp b/src/gausskernel/storage/access/common/reloptions.cpp index 1bb8cfdcf..7b57555f0 100644 --- a/src/gausskernel/storage/access/common/reloptions.cpp +++ b/src/gausskernel/storage/access/common/reloptions.cpp @@ -208,7 +208,7 @@ static relopt_int intRelOpts[] = { 10 * BatchMaxSize, // 允许的最小批量行数 RelMaxFullCuSize }, // 允许的最大批量行数 - // "deltarow_threshold" 选项���用于 HEAP 和 PSORT 类型的关系 + // "deltarow_threshold" 选项�����用于 HEAP 和 PSORT 类型的关系 {{ "deltarow_threshold", "如果小于此值,则插入到 delta 表;否则插入到正常表", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, RelDefaultDletaRows, // HEAP 和 PSORT 类型关系的默认 delta 行阈值 0, // 允许的最小 delta 行阈值 @@ -742,25 +742,34 @@ relopt_kind add_reloption_kind(void) */ static void add_reloption(relopt_gen *newoption) { + // 检查是否已经达到了自定义选项的最大数量限制 if (t_thrd.relopt_cxt.num_custom_options >= t_thrd.relopt_cxt.max_custom_options) { MemoryContext oldcxt; + // 切换当前内存上下文为 MEMORY_CONTEXT_STORAGE,并将之前的上下文保存在 oldcxt 中 oldcxt = MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE)); if (t_thrd.relopt_cxt.max_custom_options == 0) { + // 如果还没有自定义选项,分配初始空间 t_thrd.relopt_cxt.max_custom_options = 8; t_thrd.relopt_cxt.custom_options = (relopt_gen **)palloc(t_thrd.relopt_cxt.max_custom_options * sizeof(relopt_gen *)); } else { + // 否则,将自定义选项数组的大小扩大一倍 t_thrd.relopt_cxt.max_custom_options *= 2; t_thrd.relopt_cxt.custom_options = (relopt_gen **)repalloc(t_thrd.relopt_cxt.custom_options, t_thrd.relopt_cxt.max_custom_options * sizeof(relopt_gen *)); } + + // 恢复之前保存的内存上下文,切换回原来的上下文 MemoryContextSwitchTo(oldcxt); } + + // 将新的自定义选项添加到数组中,并增加已有自定义选项的数量 t_thrd.relopt_cxt.custom_options[t_thrd.relopt_cxt.num_custom_options++] = newoption; + // 标记自定义选项需要初始化 t_thrd.relopt_cxt.need_initialization = true; } @@ -775,8 +784,10 @@ static relopt_gen *allocate_reloption(bits32 kinds, int type, const char *name, size_t size; relopt_gen *newoption = NULL; + // 切换当前内存上下文为 MEMORY_CONTEXT_STORAGE,并将之前的上下文保存在 oldcxt 中 oldcxt = MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE)); + // 根据传入的 type 参数选择合适的数据类型所需的内存大小 switch (type) { case RELOPT_TYPE_BOOL: size = sizeof(relopt_bool); @@ -794,23 +805,33 @@ static relopt_gen *allocate_reloption(bits32 kinds, int type, const char *name, size = sizeof(relopt_string); break; default: + // 如果传入的 type 不支持,发出错误消息并返回 NULL ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported option type"))); - return NULL; /* keep compiler quiet */ + return NULL; /* 保持编译器不报错 */ } + // 分配 size 大小的内存块,并将指针赋给 newoption newoption = (relopt_gen *)palloc(size); + // 复制选项的名称字符串到 newoption->name,使用 pstrdup 来复制 newoption->name = pstrdup(name); + + // 如果传入的描述字符串不为空,复制它到 newoption->desc;否则,将 newoption->desc 设置为 NULL if (desc != NULL) newoption->desc = pstrdup(desc); else newoption->desc = NULL; - newoption->kinds = kinds; - newoption->namelen = strlen(name); - newoption->type = (relopt_type)type; + // 设置 newoption 的其他属性,包括 kinds、namelen 和 type + + newoption->kinds = kinds; // 关系选项适用于哪些关系类型的位掩码 + newoption->namelen = strlen(name); // 选项名称的长度 + newoption->type = (relopt_type)type; // 选项的数据类型 + + // 恢复之前保存的内存上下文,切换回原来的上下文 MemoryContextSwitchTo(oldcxt); + // 返回新分配的关系选项 return newoption; } @@ -822,12 +843,17 @@ void add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool d { relopt_bool *newoption = NULL; + // 分配一个新的布尔类型的自定义选项 newoption = (relopt_bool *)allocate_reloption(kinds, RELOPT_TYPE_BOOL, name, desc); + + // 设置新选项的默认值为传入的 default_val newoption->default_val = default_val; + // 调用 add_reloption 函数将新的自定义选项添加到自定义选项数组中 add_reloption((relopt_gen *)newoption); } + /* * add_int_reloption * Add a new integer reloption @@ -836,26 +862,44 @@ void add_int_reloption(bits32 kinds, const char *name, const char *desc, int def { relopt_int *newoption = NULL; + // 分配一个新的整数类型的自定义选项 newoption = (relopt_int *)allocate_reloption(kinds, RELOPT_TYPE_INT, name, desc); + + // 设置新选项的默认值为传入的 default_val newoption->default_val = default_val; + + // 设置新选项的最小值为传入的 min_val newoption->min = min_val; + + // 设置新选项的最大值为传入的 max_val newoption->max = max_val; + // 调用 add_reloption 函数将新的自定义选项添加到自定义选项数组中 add_reloption((relopt_gen *)newoption); } + /* * add_int64_reloption * Add a new 64-bit integer reloption */ -void add_int64_reloption(bits32 kinds, const char *name, const char *desc, int64 default_val, int64 min_val, - int64 max_val) +void add_int64_reloption(bits32 kinds, const char *name, const char *desc, int64 default_val, int64 min_val, int64 max_val) { relopt_int64 *newoption = NULL; + + // 分配一个新的 int64 类型的自定义选项 newoption = (relopt_int64 *)allocate_reloption(kinds, RELOPT_TYPE_INT64, name, desc); + + // 设置新选项的默认值为传入的 default_val newoption->default_val = default_val; + + // 设置新选项的最小值为传入的 min_val newoption->min = min_val; + + // 设置新选项的最大值为传入的 max_val newoption->max = max_val; + + // 调用 add_reloption 函数将新的自定义选项添加到自定义选项数组中 add_reloption((relopt_gen *)newoption); } @@ -863,19 +907,27 @@ void add_int64_reloption(bits32 kinds, const char *name, const char *desc, int64 * add_real_reloption * Add a new float reloption */ -void add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val, double min_val, - double max_val) +void add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val, double min_val, double max_val) { relopt_real *newoption = NULL; + // 分配一个新的 real(浮点数)类型的自定义选项 newoption = (relopt_real *)allocate_reloption(kinds, RELOPT_TYPE_REAL, name, desc); + + // 设置新选项的默认值为传入的 default_val newoption->default_val = default_val; + + // 设置新选项的最小值为传入的 min_val newoption->min = min_val; + + // 设置新选项的最大值为传入的 max_val newoption->max = max_val; + // 调用 add_reloption 函数将新的自定义选项添加到自定义选项数组中 add_reloption((relopt_gen *)newoption); } + /* * add_string_reloption * Add a new string reloption @@ -1110,27 +1162,37 @@ List *untransformRelOptions(Datum options) /* Nothing to do if no options */ if (!PointerIsValid(DatumGetPointer(options))) - return result; + return result; - array = DatumGetArrayTypeP(options); +// 将传入的 options 转换为数组类型 +array = DatumGetArrayTypeP(options); - Assert(ARR_ELEMTYPE(array) == TEXTOID); +// 断言数组的元素类型为 TEXT(文本) +Assert(ARR_ELEMTYPE(array) == TEXTOID); - deconstruct_array(array, TEXTOID, -1, false, 'i', &optiondatums, NULL, &noptions); +// 解构数组,将其拆分为单独的文本元素 +deconstruct_array(array, TEXTOID, -1, false, 'i', &optiondatums, NULL, &noptions); - for (i = 0; i < noptions; i++) { - char *s = NULL; - char *p = NULL; - Node *val = NULL; +for (i = 0; i < noptions; i++) { + char *s = NULL; + char *p = NULL; + Node *val = NULL; - s = TextDatumGetCString(optiondatums[i]); - p = strchr(s, '='); - if (p != NULL) { - *p++ = '\0'; - val = (Node *)makeString(pstrdup(p)); - } - result = lappend(result, makeDefElem(s, val)); + // 将文本元素转换为 C 字符串 + s = TextDatumGetCString(optiondatums[i]); + + // 查找等号,分隔键和值 + p = strchr(s, '='); + if (p != NULL) { + *p++ = '\0'; // 将等号替换为空字符,并移动指针 p 到值的起始位置 + // 创建一个字符串节点,表示键值对中的值 + val = (Node *)makeString(pstrdup(p)); } + + // 创建一个 DefElem 节点,表示选项中的一个键值对,并将其添加到结果列表中 + result = lappend(result, makeDefElem(s, val)); +} + /* Free the memory used by array. */ if (DatumGetPointer(options) != DatumGetPointer(array)) { @@ -1154,17 +1216,22 @@ List *untransformRelOptions(Datum options) */ bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions) { - bytea *options = NULL; - bool isnull = false; - Datum datum; - Form_pg_class classForm; + bytea *options = NULL; // 用于存储提取出的选项值 + bool isnull = false; // 标记是否为 NULL + Datum datum; // 用于存储从元组中提取的数据 + Form_pg_class classForm; // 用于表示 pg_class 表中的行的结构体 + // 从元组中获取 "reloptions" 字段的数据 datum = fastgetattr(tuple, Anum_pg_class_reloptions, tupdesc, &isnull); + + // 如果 "reloptions" 字段的值为 NULL,则返回 NULL if (isnull) return NULL; + // 获取元组的 pg_class 结构体,以便后续操作 classForm = (Form_pg_class)GETSTRUCT(tuple); + /* Parse into appropriate format; don't error out here */ switch (classForm->relkind) { case RELKIND_RELATION: @@ -1229,72 +1296,108 @@ relopt_value *parseRelOptions(Datum options, bool validate, relopt_kind kind, in initialize_reloptions(); /* Build a list of expected options, based on kind */ - for (i = 0; t_thrd.relopt_cxt.relOpts[i]; i++) - if (t_thrd.relopt_cxt.relOpts[i]->kinds & kind) - numoptions++; - - if (numoptions == 0) { - *numrelopts = 0; - return NULL; + // 初始化一个循环计数器 i,并从 0 开始,遍历 t_thrd.relopt_cxt.relOpts 数组 +for (i = 0; t_thrd.relopt_cxt.relOpts[i]; i++) { + // 检查当前 relOpts 元素的 kinds 属性是否包含指定的 kind 位 + if (t_thrd.relopt_cxt.relOpts[i]->kinds & kind) { + // 如果条件成立,增加 numoptions 计数 + numoptions++; } +} - reloptions = (relopt_value *)palloc(numoptions * sizeof(relopt_value)); +// 如果没有找到符合条件的选项,则 numoptions 仍然为 0 +if (numoptions == 0) { + // 将 numrelopts 指向的整数设置为 0 + *numrelopts = 0; + // 返回空指针(NULL) + return NULL; +} - for (i = 0, j = 0; t_thrd.relopt_cxt.relOpts[i]; i++) { - if (t_thrd.relopt_cxt.relOpts[i]->kinds & kind) { - reloptions[j].gen = t_thrd.relopt_cxt.relOpts[i]; - reloptions[j].isset = false; - j++; - } +// 分配一个大小为 numoptions 倍 sizeof(relopt_value) 字节的内存块,并将指针存储在 reloptions 变量中 +reloptions = (relopt_value *)palloc(numoptions * sizeof(relopt_value)); + +// 重新初始化循环计数器 i 和另一个计数器 j,从 0 开始遍历 t_thrd.relopt_cxt.relOpts 数组 +for (i = 0, j = 0; t_thrd.relopt_cxt.relOpts[i]; i++) { + // 再次检查当前 relOpts 元素的 kinds 属性是否包含指定的 kind 位 + if (t_thrd.relopt_cxt.relOpts[i]->kinds & kind) { + // 如果条件成立,将当前 relOpts 元素存储在 reloptions 数组的下一个位置,并将 isset 设置为 false + reloptions[j].gen = t_thrd.relopt_cxt.relOpts[i]; + reloptions[j].isset = false; + // 增加 j,以便下一次循环时存储在下一个位置 + j++; } +} /* Done if no options */ - if (PointerIsValid(DatumGetPointer(options))) { - ArrayType *array = NULL; - Datum *optiondatums = NULL; - int noptions; + // 检查 options 是否是一个有效的指针(不为 NULL) +if (PointerIsValid(DatumGetPointer(options))) { + ArrayType *array = NULL; + Datum *optiondatums = NULL; + int noptions; - array = DatumGetArrayTypeP(options); - AssertEreport(ARR_ELEMTYPE(array) == TEXTOID, MOD_MAX, "The option type should be text."); + // 将 options 转换为 ArrayType 类型的指针 + array = DatumGetArrayTypeP(options); - deconstruct_array(array, TEXTOID, -1, false, 'i', &optiondatums, NULL, &noptions); + // 使用断言(AssertEreport)来确保数组元素类型为 TEXTOID(文本类型) + AssertEreport(ARR_ELEMTYPE(array) == TEXTOID, MOD_MAX, "The option type should be text."); + + // 将数组解构为一维数组,并将结果存储在 optiondatums 数组中,同时获取数组元素的数量存储在 noptions 中 + deconstruct_array(array, TEXTOID, -1, false, 'i', &optiondatums, NULL, &noptions); + + // 遍历 optiondatums 数组中的每个元素 + for (i = 0; i < noptions; i++) { + // 获取 optiondatums 数组中的文本元素,并将其存储在 optiontext 中 + text *optiontext = DatumGetTextP(optiondatums[i]); + + // 获取文本数据的指针,即文本内容的起始位置 + char *text_str = VARDATA(optiontext); + + // 获取文本的长度,不包括文本头部信息(VARHDRSZ) + int text_len = VARSIZE(optiontext) - VARHDRSZ; - for (i = 0; i < noptions; i++) { - text *optiontext = DatumGetTextP(optiondatums[i]); - char *text_str = VARDATA(optiontext); - int text_len = VARSIZE(optiontext) - VARHDRSZ; /* Search for a match in reloptions */ for (j = 0; j < numoptions; j++) { - int kw_len = reloptions[j].gen->namelen; + // 获取当前选项的名称长度 + int kw_len = reloptions[j].gen->namelen; - if (text_len > kw_len && text_str[kw_len] == '=' && - pg_strncasecmp(text_str, reloptions[j].gen->name, kw_len) == 0) { - parse_one_reloption(&reloptions[j], text_str, text_len, validate); - break; - } - } - - if (j >= numoptions && validate) { - char *s = NULL; - char *p = NULL; - - s = TextDatumGetCString(optiondatums[i]); - p = strchr(s, '='); - if (p != NULL) - *p = '\0'; - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized parameter \"%s\"", s))); - } - } - - if (DatumGetPointer(options) != DatumGetPointer(array)) { - pfree(array); - } - pfree(optiondatums); + // 检查文本长度是否大于名称长度,并且名称后紧跟着一个等号 + if (text_len > kw_len && text_str[kw_len] == '=' && + // 比较文本的前缀与当前选项的名称,不区分大小写 + pg_strncasecmp(text_str, reloptions[j].gen->name, kw_len) == 0) { + // 解析并处理符合条件的选项 + parse_one_reloption(&reloptions[j], text_str, text_len, validate); + // 跳出循环,处理下一个文本选项 + break; } +} - *numrelopts = numoptions; - return reloptions; +// 如果 j >= numoptions 且 validate 为真,表示没有匹配的选项且需要验证 +if (j >= numoptions && validate) { + char *s = NULL; + char *p = NULL; + + // 将文本数据转换为 C 字符串 + s = TextDatumGetCString(optiondatums[i]); + // 在字符串中查找等号的位置 + p = strchr(s, '='); + if (p != NULL) + *p = '\0'; + // 报告错误,指示未识别的参数 + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized parameter \"%s\"", s))); +} + +// 如果 options 不等于 array,则释放之前分配的内存 +if (DatumGetPointer(options) != DatumGetPointer(array)) { + pfree(array); +} +// 释放 optiondatums 数组的内存 +pfree(optiondatums); + +// 设置输出参数,指示解析的选项数量 +*numrelopts = numoptions; +// 返回解析的选项数组 +return reloptions; } /* @@ -1303,24 +1406,29 @@ relopt_value *parseRelOptions(Datum options, bool validate, relopt_kind kind, in */ static void parse_one_reloption(relopt_value *option, const char *text_str, int text_len, bool validate) { - char *value = NULL; - int value_len; - bool parsed = false; - bool nofree = false; - errno_t rc = EOK; + char *value = NULL; // 存储选项的值(字符串) + int value_len; // 选项值的长度 + bool parsed = false; // 指示是否成功解析选项值 + bool nofree = false; // 指示是否需要释放选项值内存 + errno_t rc = EOK; // 用于错误检查的返回代码 if (option->isset && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("parameter \"%s\" specified more than once", option->gen->name))); + // 计算选项值的长度 value_len = text_len - option->gen->namelen - 1; + // 分配内存以存储选项值 value = (char *)palloc(value_len + 1); + // 复制选项值到新分配的内存 rc = memcpy_s(value, value_len + 1, text_str + option->gen->namelen + 1, value_len); securec_check(rc, "\0", "\0"); + // 在字符串末尾添加终止符 value[value_len] = '\0'; switch (option->gen->type) { case RELOPT_TYPE_BOOL: { + // 解析布尔类型的选项值 parsed = parse_bool(value, &option->values.bool_val); if (validate && !parsed) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1329,6 +1437,7 @@ static void parse_one_reloption(relopt_value *option, const char *text_str, int case RELOPT_TYPE_INT: { relopt_int *optint = (relopt_int *)option->gen; + // 解析整数类型的选项值 parsed = parse_int(value, &option->values.int_val, 0, NULL); if (validate && !parsed) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1341,6 +1450,7 @@ static void parse_one_reloption(relopt_value *option, const char *text_str, int case RELOPT_TYPE_INT64: { relopt_int64 *optint = (relopt_int64 *)option->gen; + // 解析64位整数类型的选项值 parsed = parse_int64(value, &option->values.int64_val, NULL); if (validate && !parsed) ereport(ERROR, @@ -1355,6 +1465,7 @@ static void parse_one_reloption(relopt_value *option, const char *text_str, int case RELOPT_TYPE_REAL: { relopt_real *optreal = (relopt_real *)option->gen; + // 解析浮点数类型的选项值 parsed = parse_real(value, &option->values.real_val); if (validate && !parsed) ereport(ERROR, @@ -1369,6 +1480,7 @@ static void parse_one_reloption(relopt_value *option, const char *text_str, int case RELOPT_TYPE_STRING: { relopt_string *optstring = (relopt_string *)option->gen; + // 将选项值作为字符串直接存储 option->values.string_val = value; nofree = true; if (validate && optstring->validate_cb) @@ -1376,51 +1488,65 @@ static void parse_one_reloption(relopt_value *option, const char *text_str, int parsed = true; } break; default: + // 不支持的选项类型,报告错误 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported reloption type %d", option->gen->type))); parsed = true; /* quiet compiler */ break; } + // 如果成功解析选项值,则将 option->isset 设置为 true if (parsed) option->isset = true; + // 如果不需要保留选项值的内存,则释放内存 if (!nofree) pfree(value); } + bool CheckRelOptionValue(Datum options, const char *opt_name) { int i; - bool ret = false; + bool ret = false; // 用于存储结果,默认为 false + // 如果 options 是空值(NULL),直接返回 false if (options == (Datum)0) return false; - /* Done if no options */ + // 只有在 options 不为空(非 NULL)时才会执行下面的逻辑 if (PointerIsValid(DatumGetPointer(options))) { - ArrayType *array = NULL; - Datum *optiondatums = NULL; - int noptions; + ArrayType *array = NULL; // 用于存储选项数组的指针 + Datum *optiondatums = NULL; // 用于存储选项数据的指针数组 + int noptions; // 存储选项数量 + // 将 options 转换为 ArrayType 类型的指针 array = DatumGetArrayTypeP(options); + // 使用断言来确保数组元素类型为 TEXTOID(文本类型) Assert(ARR_ELEMTYPE(array) == TEXTOID); + // 解构数组,将选项数据存储在 optiondatums 数组中,并获取选项数量 deconstruct_array(array, TEXTOID, -1, false, 'i', &optiondatums, NULL, &noptions); + // 遍历选项数据数组 for (i = 0; i < noptions; i++) { + // 获取当前选项的文本表示 const char *s = TextDatumGetCString(optiondatums[i]); + // 比较当前选项文本与目标选项名称,不区分大小写 if (pg_strncasecmp(s, opt_name, strlen(opt_name)) == 0) { + // 如果找到匹配的选项,将结果标记为 true 并跳出循环 ret = true; break; } } } + // 返回结果,表示是否找到匹配的选项 return ret; } + /* * Given the result from parseRelOptions, allocate a struct that's of the * specified base size plus any extra space that's needed for string variables. @@ -1430,16 +1556,23 @@ bool CheckRelOptionValue(Datum options, const char *opt_name) */ void *allocateReloptStruct(Size base, relopt_value *options, int numoptions) { - Size size = base; + Size size = base; // 初始化分配的内存大小为 base int i; - for (i = 0; i < numoptions; i++) - if (options[i].gen->type == RELOPT_TYPE_STRING) + // 遍历选项数组,计算需要分配的额外内存大小 + for (i = 0; i < numoptions; i++) { + // 如果选项的类型是字符串类型(RELOPT_TYPE_STRING),则需要额外分配内存 + if (options[i].gen->type == RELOPT_TYPE_STRING) { + // 使用宏 GET_STRING_RELOPTION_LEN 获取字符串选项的长度,并加上 1 用于存储字符串终止符 size += GET_STRING_RELOPTION_LEN(options[i]) + 1; + } + } + // 使用 palloc0 分配内存,并将所有字节初始化为零 return palloc0(size); } + /* * @Description: Given user options, find the first invalid option from * invalidOptions[invalidOptionsNum]. firstInvalidOpt will remember @@ -1454,19 +1587,22 @@ void *allocateReloptStruct(Size base, relopt_value *options, int numoptions) static bool FindInvalidOption(List *userOptions, const char *invalidOptions[], int invalidOptionsNum, int *firstInvalidOpt) { - ListCell *opt = NULL; + ListCell *opt = NULL; // 用于遍历用户选项列表的迭代器 for (int i = 0; i < invalidOptionsNum; ++i) { foreach (opt, userOptions) { - DefElem *def = (DefElem *)lfirst(opt); + DefElem *def = (DefElem *)lfirst(opt); // 获取用户选项中的一个选项 + // 比较当前选项的名称与无效选项数组中的名称,不区分大小写 if (pg_strcasecmp(def->defname, invalidOptions[i]) == 0) { + // 如果找到匹配的无效选项,将其索引存储在 firstInvalidOpt 中,返回 true *firstInvalidOpt = i; return true; } } } + // 如果没有找到任何无效选项,将 firstInvalidOpt 设置为 -1,并返回 false *firstInvalidOpt = -1; return false; } @@ -1482,12 +1618,15 @@ static bool FindInvalidOption(List *userOptions, const char *invalidOptions[], i void ForbidUserToSetUnsupportedOptions(List *userOptions, const char *unsupported[], int unsupportedNum, const char *errorDetail) { - if (userOptions != NIL) { - int firstInvalidOpt = -1; + if (userOptions != NIL) { // 检查用户选项列表是否为空 + int firstInvalidOpt = -1; // 存储第一个无效选项的索引,初始化为 -1 + // 调用 FindInvalidOption 函数检查用户选项列表中是否包含无效选项 if (FindInvalidOption(userOptions, unsupported, unsupportedNum, &firstInvalidOpt)) { + // 断言确保 firstInvalidOpt 在有效范围内 Assert(firstInvalidOpt >= 0 && firstInvalidOpt < unsupportedNum); + // 如果找到无效选项,报告错误,指明不支持的功能和无效选项的名称 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Un-support feature"), errdetail("Forbid to set option \"%s\" for %s", unsupported[firstInvalidOpt], errorDetail))); @@ -1510,33 +1649,36 @@ void fillRelOptions(void *rdopts, Size basesize, relopt_value *options, int numo const relopt_parse_elt *elems, int numelems) { int i; - int offset = basesize; - errno_t rc = EOK; - + int offset = basesize; // 初始化偏移量为基本大小 + errno_t rc = EOK; // 用于错误检查的返回代码 for (i = 0; i < numoptions; i++) { int j; bool found = false; + // 在选项解析元素数组中查找与当前选项匹配的元素 for (j = 0; j < numelems; j++) { if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0) { relopt_string *optstring = NULL; - char *itempos = ((char *)rdopts) + elems[j].offset; + char *itempos = ((char *)rdopts) + elems[j].offset; // 计算选项存储位置的指针 char *string_val = NULL; - switch (options[i].gen->type) { case RELOPT_TYPE_BOOL: + // 如果选项已设置,使用选项的值;否则使用默认值 *(bool *)itempos = options[i].isset ? options[i].values.bool_val : ((relopt_bool *)options[i].gen)->default_val; break; case RELOPT_TYPE_INT: + // 如果选项已设置,使用选项的值;否则使用默认值 *(int *)itempos = options[i].isset ? options[i].values.int_val : ((relopt_int *)options[i].gen)->default_val; break; case RELOPT_TYPE_INT64: + // 如果选项已设置,使用选项的值;否则使用默认值 *(int64 *)itempos = options[i].isset ? options[i].values.int64_val : ((relopt_int64 *)options[i].gen)->default_val; break; case RELOPT_TYPE_REAL: + // 如果选项已设置,使用选项的值;否则使用默认值 *(double *)itempos = options[i].isset ? options[i].values.real_val : ((relopt_real *)options[i].gen)->default_val; break; -- 2.34.1 From 651f96ef89934140bdfa0df022c49ba00544826b Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Thu, 5 Oct 2023 23:18:13 +0800 Subject: [PATCH 19/19] Enter --- .../storage/access/common/reloptions.cpp | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/gausskernel/storage/access/common/reloptions.cpp b/src/gausskernel/storage/access/common/reloptions.cpp index 7b57555f0..5d4b41dd7 100644 --- a/src/gausskernel/storage/access/common/reloptions.cpp +++ b/src/gausskernel/storage/access/common/reloptions.cpp @@ -208,7 +208,7 @@ static relopt_int intRelOpts[] = { 10 * BatchMaxSize, // 允许的最小批量行数 RelMaxFullCuSize }, // 允许的最大批量行数 - // "deltarow_threshold" 选项�����用于 HEAP 和 PSORT 类型的关系 + // "deltarow_threshold" 选项�������用于 HEAP 和 PSORT 类型的关系 {{ "deltarow_threshold", "如果小于此值,则插入到 delta 表;否则插入到正常表", RELOPT_KIND_HEAP | RELOPT_KIND_PSORT }, RelDefaultDletaRows, // HEAP 和 PSORT 类型关系的默认 delta 行阈值 0, // 允许的最小 delta 行阈值 @@ -1733,14 +1733,17 @@ void fillTdeRelOptions(List *options, char relkind) bool algo_flag = false; bool dek_flag = false; bool cmk_flag = false; - DefElem *opt_dek = makeNode(DefElem); - DefElem *opt_cmk = makeNode(DefElem); - DefElem *opt_algo = makeNode(DefElem); + DefElem *opt_dek = makeNode(DefElem); // 创建用于 DEK 选项的 DefElem 结构 + DefElem *opt_cmk = makeNode(DefElem); // 创建用于 CMK 选项的 DefElem 结构 + DefElem *opt_algo = makeNode(DefElem); // 创建用于算法选项的 DefElem 结构 foreach(listptr1, options) { - DefElem *defs = reinterpret_cast(lfirst(listptr1)); + DefElem *defs = reinterpret_cast(lfirst(listptr1)); // 获取用户选项中的一个选项 + if (pg_strcasecmp(defs->defname, "enable_tde") == 0) { + // 如果用户设置了 "enable_tde" 选项 if (t_thrd.proc->workingVersionNum < TDE_VERSION_NUM) { + // 如果数据库版本不支持 TDE,报告错误 ereport(ERROR, (errmodule(MOD_SEC_TDE), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("create TDE table failed"), errdetail("current version does not support TDE feature"), @@ -1748,47 +1751,55 @@ void fillTdeRelOptions(List *options, char relkind) erraction("check database version about create TDE table"))); } if (relkind == RELKIND_MATVIEW) { + // 如果正在创建的是材料化视图,报告错误 ereport(ERROR, (errmodule(MOD_SEC_TDE), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("create matview with TDE failed"), errdetail("materialized views do not support TDE feature"), errcause("TDE feature is not supported for Create materialized views"), erraction("check CREATE syntax about create the materialized views"))); } - spec_encrypt = true; + spec_encrypt = true; // 标记启用了 TDE 特性 continue; } if (pg_strcasecmp(defs->defname, "encrypt_algo") == 0) { + // 如果用户设置了 "encrypt_algo" 选项 if (defs->arg == NULL) { + // 如果选项未指定参数,报告错误 ereport(ERROR, (errmodule(MOD_SEC_TDE), errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("set relation option %s failed", defs->defname), errdetail("%s requires a string parameter", defs->defname))); } - algo_flag = true; + algo_flag = true; // 标记设置了算法选项 continue; } if (pg_strcasecmp(defs->defname, "dek_cipher") == 0) { + // 如果用户设置了 "dek_cipher" 选项 if (defs->arg == NULL) { + // 如果选项未指定参数,报告错误 ereport(ERROR, (errmodule(MOD_SEC_TDE), errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("set relation option %s failed", defs->defname), errdetail("%s requires a string parameter", defs->defname))); } - dek_flag = true; + dek_flag = true; // 标记设置了 DEK 选项 continue; } if (pg_strcasecmp(defs->defname, "cmk_id") == 0) { + // 如果用户设置了 "cmk_id" 选项 if (defs->arg == NULL) { + // 如果选项未指定参数,报告错误 ereport(ERROR, (errmodule(MOD_SEC_TDE), errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("set relation option %s failed", defs->defname), errdetail("%s requires a string parameter", defs->defname))); } - cmk_flag = true; + cmk_flag = true; // 标记设置了 CMK 选项 continue; } } + // 检查选项的组合和相互关系,如果不符合要求,报告错误 if (!spec_encrypt && (dek_flag || cmk_flag)) { ereport(ERROR, (errmodule(MOD_SEC_TDE), errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("set relation option failed"), @@ -1831,6 +1842,7 @@ void fillTdeRelOptions(List *options, char relkind) rc = strcpy_s(cmk_id, strlen(tde_data->cmk_id) + 1, tde_data->cmk_id); securec_check(rc, "\0", "\0"); + // 创建并设置 DEK 和 CMK 选项 opt_dek->type = T_DefElem; opt_dek->defnamespace = NULL; opt_dek->defname = "dek_cipher"; @@ -1844,6 +1856,8 @@ void fillTdeRelOptions(List *options, char relkind) opt_cmk->defaction = DEFELEM_UNSPEC; opt_cmk->arg = reinterpret_cast(makeString(cmk_id)); options = lappend(options, opt_cmk); + + // 在主备节点上保存密钥 if (IS_PGXC_DATANODE) { tde_key_manager->save_key(tde_data); } @@ -1851,6 +1865,7 @@ void fillTdeRelOptions(List *options, char relkind) } if (!algo_flag) { + // 如果用户没有设置算法选项,默认使用 AES_128_CTR 算法 opt_algo->type = T_DefElem; opt_algo->defnamespace = NULL; opt_algo->defname = "encrypt_algo"; @@ -1901,15 +1916,16 @@ void RowTblCheckCompressionOption(List *options, int8 rowCompress) void RowTblCheckHashBucketOption(List* options, StdRdOptions* std_opt) { - int bucketcnt = std_opt->bucketcnt; - bool hashbucket = std_opt->hashbucket; - bool segment = std_opt->segment; + int bucketcnt = std_opt->bucketcnt; // 从标准选项中获取 bucketcnt 的值 + bool hashbucket = std_opt->hashbucket; // 从标准选项中获取 hashbucket 的值 + bool segment = std_opt->segment; // 从标准选项中获取 segment 的值 ListCell *opt = NULL; if (options == NULL) { - return; /* nothing to do */ + return; /* 没有选项需要检查,直接返回 */ } + // 根据标准选项和用户提供的选项,确定是否需要检查 hashbucket 和 segment 的设置 bool check_hashbucket = (bucketcnt != 0 && hashbucket == false); bool check_segment = ((segment == false) && (bucketcnt != 0 || hashbucket == true)); @@ -1926,6 +1942,7 @@ void RowTblCheckHashBucketOption(List* options, StdRdOptions* std_opt) } } + // 根据检查的结果和用户设置的选项,报告错误(如果设置不符合要求) if ((check_segment && set_segment) || (check_hashbucket && set_hashbucket)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -1934,18 +1951,21 @@ void RowTblCheckHashBucketOption(List* options, StdRdOptions* std_opt) } } + // 如果 hashbucket 未设置但 bucketcnt 不为零,发出 NOTICE 并将 hashbucket 设置为 true if (!hashbucket && bucketcnt != 0) { ereport(NOTICE, - (errmsg("bucketcnt can only used for hashbucket table, set hashbucket to on by default"))); + (errmsg("bucketcnt can only be used for hashbucket table, set hashbucket to on by default"))); hashbucket = true; } + // 如果 hashbucket 设置为 true 但 segment 未设置,发出 NOTICE 并将 segment 设置为 true if (hashbucket && !segment) { ereport(NOTICE, - (errmsg("hashbucket table need segment storage, set segment to on by default"))); + (errmsg("hashbucket table needs segment storage, set segment to on by default"))); segment = true; } + // 更新标准选项中的 hashbucket 和 segment 的值 std_opt->hashbucket = hashbucket; std_opt->segment = segment; } -- 2.34.1