首页 > 编程语言 >【OpenGauss源码学习 —— (ALTER TABLE(列存删除列))】

【OpenGauss源码学习 —— (ALTER TABLE(列存删除列))】

时间:2024-06-18 17:59:27浏览次数:26  
标签:cudesc 存储 删除 tuple 源码 rel TABLE OpenGauss attrnum

ALTER TABLE(列存删除列)

声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 OpenGauss5.1.0 的开源代码和《OpenGauss数据库源码解析》一书

  在数据库管理系统中,ALTER TABLE 命令用于修改表的结构,允许用户添加、删除或修改表中的列。在处理列存储表(CStore 表)时,删除列的操作尤为复杂,因为列存储表的数据存储和管理方式与行存储表不同CStore 表将每一列的数据独立存储,这意味着在删除列时,不仅需要更新系统元数据,还需处理相关的物理存储,确保数据的一致性和完整性
  在执行 ALTER TABLE DROP COLUMN 命令时,核心函数 ATExecDropColumn 扮演着重要角色。该函数负责处理删除列的具体操作,包括验证列的存在性、更新系统元数据、删除物理存储以及维护依赖关系。特别是在列存储表中,ATExecDropColumn 需要对列描述信息(cudesc)进行清理,并安排在事务提交时删除相应的物理存储,以确保整个操作的原子性。
  综上所述,删除列存储表中的列是一项复杂的操作,涉及多个步骤和考虑因素。ATExecDropColumn 函数在这一过程中起到了至关重要的作用,确保删除操作能够顺利完成,并维护系统的完整性和一致性。本文将围绕 ATExecDropColumn 函数来展开学习。

ATExecDropColumn 函数

  ATExecDropColumn 函数接受三个参数:

  1. wqueue:操作队列的指针,用于记录需要执行的表结构变更操作。
  2. rel:需要删除列的表的关系描述符。
  3. colName:需要删除的列的名称。
  4. behavior:删除行为(如级联删除或限制删除)。
  5. recurse:是否递归删除子表中的相应列。
  6. recursing:当前操作是否是递归调用。
  7. missing_ok:是否忽略缺失列的错误。
  8. lockmode:操作需要的锁模式。

  ATExecDropColumn 函数的主要作用是从指定表中删除指定列,并处理删除列的各种情况,包括权限检查、子表处理、系统属性检查等。具体过程如下:

  1. 检查权限,确保用户有删除列的权限。
  2. 获取列的元数据信息,确保列存在并且合法。
  3. 检查列是否为分区键系统属性继承列,避免删除不允许删除的列。
  4. 确保表中至少保留一列,避免删除所有列。
  5. 如果表有子表delta 表,递归处理这些子表,确保删除操作的一致性。
  6. 更新相关系统表元数据,删除列相关的依赖对象。
  7. 特殊处理 OID 列,确保删除 OID 列后系统表的正确性。

  详细代码描述如下所示:(路径:src\gausskernel\optimizer\commands\tablecmds.cpp

// 返回值是被删除的列。
static ObjectAddress ATExecDropColumn(List** wqueue,
									  Relation rel,
									  const char* colName,
									  DropBehavior behavior,
									  bool recurse,
									  bool recursing,
									  bool missing_ok,
									  LOCKMODE lockmode)
{
    HeapTuple tuple;
    Form_pg_attribute targetatt;
    AttrNumber attnum;
    List* children = NIL;
    ObjectAddress object;

    // 如果是在递归调用中,检查权限
    if (recursing)
        ATSimplePermissions(rel, ATT_TABLE);

    // 获取属性的编号
    tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    if (!HeapTupleIsValid(tuple)) {
        if (!missing_ok) {
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_COLUMN),
                    errmsg("列 \"%s\" 在关系 \"%s\" 中不存在", colName, RelationGetRelationName(rel))));
        } else {
            ereport(NOTICE,
                (errmsg("列 \"%s\" 在关系 \"%s\" 中不存在,跳过",
                    colName,
                    RelationGetRelationName(rel))));
            return InvalidObjectAddress;
        }
    }
    targetatt = (Form_pg_attribute)GETSTRUCT(tuple);

    attnum = targetatt->attnum;

#ifdef ENABLE_MULTIPLE_NODES
    if (RelationIsTsStore(rel)) {
        CheckTsStoreDropColumn(rel, attnum, colName);
    }
#endif   /* ENABLE_MULTIPLE_NODES */

    // 分区表的分区键列不能被删除
    if (is_partition_column(rel, attnum)) {
        ereport(
            ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("不能删除分区列 \"%s\"", colName)));
    }

    // 不能删除系统属性,除非是OID
    if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("不能删除系统列 \"%s\"", colName)));

    // 不要删除继承的列
    if (targetatt->attinhcount > 0 && !recursing)
        ereport(
            ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("不能删除继承列 \"%s\"", colName)));

    ReleaseSysCache(tuple);

    // 对于一个表,不允许删除所有列。检查是否是最后一列,如果是,不允许删除。
    if (GetLocatorType(rel->rd_id) != LOCATOR_TYPE_HASH) {
        bool lastColumn = CheckLastColumn(rel, attnum);
        if (lastColumn) {
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("必须至少有一列")));
        }
    }

    // 适当传播到子表。与大多数其他ALTER操作不同,我们必须一次递归一层;不能用find_all_inheritors一次完成。
    if (RelationIsPAXFormat(rel)) {
        // 为delta表添加列
        children = lappend_oid(children, RelationGetDeltaRelId(rel));

        // 获取锁以同步对并发删除的操作
        LockRelationOid(RelationGetDeltaRelId(rel), lockmode);
        elog(DEBUG1,
            "[获取锁] 成功在 %s 的delta表上获取锁 %d 进行ALTER操作。",
            RelationGetRelationName(rel), lockmode);
#ifdef ENABLE_MULTIPLE_NODES
    } else if (g_instance.attr.attr_storage.enable_delta_store && RelationIsCUFormat(rel)) {
#else
    // 在集中模式下,delta表上可能有唯一索引。在检查唯一约束时,将使用delta上的唯一索引。因此我们忽略enable_delta_store并同时更改delta表。
    } else if (RelationIsCUFormat(rel)) {
#endif
        // 为cstore关系的delta表添加递归,如果列支持继承特性,我们也需要调用find_inheritance_children。
        children = find_cstore_delta(rel, lockmode);
    } else {
        children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    }

    if (children != NULL) {
        Relation attr_rel;
        ListCell* child = NULL;

        attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
        foreach (child, children) {
            Oid childrelid = lfirst_oid(child);
            Relation childrel;
            Form_pg_attribute childatt;

            // find_inheritance_children已获得锁
            childrel = heap_open(childrelid, NoLock);
            CheckTableNotInUse(childrel, "ALTER TABLE");

            tuple = SearchSysCacheCopyAttName(childrelid, colName);
            if (!HeapTupleIsValid(tuple)) {
                // 不应发生
                Assert(0);
                ereport(ERROR,
                    (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                        errmsg("为关系 %u 的属性 \"%s\" 缓存查找失败", childrelid, colName)));
            }
            childatt = (Form_pg_attribute)GETSTRUCT(tuple);

            // delta表不是继承表
            if (!RelationIsPAXFormat(rel) && !RelationIsCUFormat(rel) &&
                childatt->attinhcount <= 0) // 不应发生
                ereport(ERROR,
                    (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                        errmsg("关系 %u 有非继承属性 \"%s\"", childrelid, colName)));

            if (recurse) {
                if (RelationIsPAXFormat(rel) || RelationIsCUFormat(rel)) {
                    // 删除delta表的此列
                    ATExecDropColumn(wqueue, childrel, colName, behavior, true, true, false, lockmode);
                } else if (childatt->attinhcount == 1 && !childatt->attislocal) {
                    // 如果子列有其他定义来源,只需减少其继承计数;如果没有,则递归删除它。
                    ATExecDropColumn(wqueue, childrel, colName, behavior, true, true, false, lockmode);
                } else {
                    // 子列必须在我删除时保留
                    childatt->attinhcount--;

                    simple_heap_update(attr_rel, &tuple->t_self, tuple);

                    // 保持系统目录索引更新
                    CatalogUpdateIndexes(attr_rel, tuple);

                    // 使更新可见
                    CommandCounterIncrement();
                }
            } else {
                // 如果我们被告知仅在此表中删除(不递归),我们需要将继承者的属性标记为本地定义而非继承。
                childatt->attinhcount--;
                childatt->attislocal = true;

                simple_heap_update(attr_rel, &tuple->t_self, tuple);

                // 保持系统目录索引更新
                CatalogUpdateIndexes(attr_rel, tuple);

                // 使更新可见
                CommandCounterIncrement();
            }

            tableam_tops_free_tuple(tuple);

            heap_close(childrel, NoLock);
        }
        heap_close(attr_rel, RowExclusiveLock);
    }

    // 如果删除的列有部分集群键,必须更新pg_class中的relhasclusterkey。
    if (rel->rd_rel->relhasclusterkey && colHasPartialClusterKey(rel, attnum)) {
        SetRelHasClusterKey(rel, false);
    }

    // 删除相关对象并更新关系目录
    object.classId = RelationRelationId;
    object.objectId = RelationGetRelid(rel);
    object.objectSubId = attnum;

    performDeletion(&object, behavior, 0);

    // 如果是列存表,删除列时,还需要从cudesc中删除列信息,并将列文件放入待删除列表。
    if (RelationIsCUFormat(rel)) {
        CStoreRelDropColumn(rel, attnum, rel->rd_rel->relowner);
    }
    ResetTempAutoIncrement(rel, attnum);

#ifdef ENABLE_MOT
    if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(RelationGetRelid(rel))) {
        AlterForeingTableCmd fcmd = {
            T_AlterForeingTableCmd,
            AT_DropColumn,
            rel,
            colName,
            nullptr,
            InvalidOid,
            nullptr
        };
        ATExecMOTAlterTable(&fcmd);
    }
#endif

#ifdef ENABLE_MULTIPLE_NODES
    if (unlikely(RelationIsTsStore(rel))) {
        // 删除标签表中的列
        if (rel->rd_att->attrs[attnum - 1]->attkvtype == ATT_KV_TAG) {
            Oid tag_relid = get_tag_relid(RelationGetRelationName(rel), rel->rd_rel->relnamespace);
            Relation tagrel = heap_open(tag_relid, lockmode);
            CheckTableNotInUse(tagrel, "ALTER TABLE");
            ATExecDropColumn(wqueue, tagrel, colName, behavior, false, false, true, lockmode);
            TagsCacheMgr::GetInstance().clear();

            heap_close(tagrel, NoLock);
        } else if (rel->rd_att->attrs[attnum - 1]->attkvtype == ATT_KV_FIELD && Tsdb::RelationEnablesTsdbDelta(rel)) {
            // 如果删除TSField列,同时更新delta表
            Relation delta_rel = Tsdb::RelationGetDeltaRelation(rel, lockmode);
            CheckTableNotInUse(delta_rel, "ALTER TABLE");
            ATExecDropColumn(wqueue, delta_rel, colName, behavior, false, false, true, lockmode);
            heap_close(delta_rel, NoLock);
        }
    }
#endif   /* ENABLE_MULTIPLE_NODES */

    // 如果删除了OID列,必须调整pg_class.relhasoids并通知阶段3实际删除该列。以前我们保留了该列,但这会导致一些细微问题。参见http://archives.postgresql.org/pgsql-hackers/2009-02/msg00363.php
    if (attnum == ObjectIdAttributeNumber) {
        Relation class_rel;
        Form_pg_class tuple_class;
        AlteredTableInfo* tab = NULL;

        class_rel = heap_open(RelationRelationId, RowExclusiveLock);

        tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(RelationGetRelid(rel)));
        if (!HeapTupleIsValid(tuple)) {
            ereport(ERROR,
                (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                    errmsg("关系 %u 的缓存查找失败", RelationGetRelid(rel))));
        }
        tuple_class = (Form_pg_class)GETSTRUCT(tuple);

        tuple_class->relhasoids = false;
        simple_heap_update(class_rel, &tuple->t_self, tuple);

        // 保持目录索引更新
        CatalogUpdateIndexes(class_rel, tuple);

        heap_close(class_rel, RowExclusiveLock);

        // 为此表找到或创建工作队列条目
        tab = ATGetQueueEntry(wqueue, rel);

        // 通知阶段3实际移除OID列
        tab->rewrite |= AT_REWRITE_ALTER_OID;
    }
    return object;
}

  在本文中,我们主要关注列存储相关的内容。以上代码中与列存储相关的部分包括以下几处:

1. 检查是否是列存储表:
if (RelationIsTsStore(rel)) {
    CheckTsStoreDropColumn(rel, attnum, colName);
}
2. 获取子表的递归处理:
if (RelationIsPAXFormat(rel)) {
    children = lappend_oid(children, RelationGetDeltaRelId(rel));
    LockRelationOid(RelationGetDeltaRelId(rel), lockmode);
} else if (g_instance.attr.attr_storage.enable_delta_store && RelationIsCUFormat(rel)) {
    children = find_cstore_delta(rel, lockmode);
} else {
    children = find_inheritance_children(RelationGetRelid(rel), lockmode);
}
3. 处理子表的列删除:
if (RelationIsPAXFormat(rel) || RelationIsCUFormat(rel)) {
    ATExecDropColumn(wqueue, childrel, colName, behavior, true, true, false, lockmode);
}
4. 删除列存储表的列信息:
if (RelationIsCUFormat(rel)) {
    CStoreRelDropColumn(rel, attnum, rel->rd_rel->relowner);
}
5. 处理MOT表:
if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(RelationGetRelid(rel))) {
    AlterForeingTableCmd fcmd = {
        T_AlterForeingTableCmd,
        AT_DropColumn,
        rel,
        colName,
        nullptr,
        InvalidOid,
        nullptr
    };
    ATExecMOTAlterTable(&fcmd);
}
6. 处理TSDB表:
if (unlikely(RelationIsTsStore(rel))) {
    if (rel->rd_att->attrs[attnum - 1]->attkvtype == ATT_KV_TAG) {
        Oid tag_relid = get_tag_relid(RelationGetRelationName(rel), rel->rd_rel->relnamespace);
        Relation tagrel = heap_open(tag_relid, lockmode);
        CheckTableNotInUse(tagrel, "ALTER TABLE");
        ATExecDropColumn(wqueue, tagrel, colName, behavior, false, false, true, lockmode);
        TagsCacheMgr::GetInstance().clear();

        heap_close(tagrel, NoLock);
    } else if (rel->rd_att->attrs[attnum - 1]->attkvtype == ATT_KV_FIELD && Tsdb::RelationEnablesTsdbDelta(rel)) {
        Relation delta_rel = Tsdb::RelationGetDeltaRelation(rel, lockmode);
        CheckTableNotInUse(delta_rel, "ALTER TABLE");
        ATExecDropColumn(wqueue, delta_rel, colName, behavior, false, false, true, lockmode);
        heap_close(delta_rel, NoLock);
    }
}

  以上这些代码部分处理了列存储表(包括 PAX 格式、CU 格式和 TSDB 表)在删除列时的特殊操作,如递归处理子表删除列信息、以及更新相关的元数据

CStoreRelDropColumn 函数

  CStoreRelDropColumn 函数的主要作用是为列存储(CStore)表删除指定的列,并在事务提交时处理该列的物理存储。具体功能包括:

  1. 检查表格式: 首先确保传入的关系是列存储格式(CUFormat)。
  2. 处理非分区表:
  • 如果表不是分区表,从 cudesc 中删除列信息。
  • 将列的物理存储放入待删除列表,在事务提交时删除。
  1. 处理分区表:
  • 如果表是分区表获取所有分区列表
  • 遍历每个分区,获取分区的关系。
  • 从每个分区的 cudesc 中删除列信息。
  • 将每个分区的列物理存储放入待删除列表,在事务提交时删除。
  • 释放分区关系和分区列表。

  通过这些步骤,函数确保在删除列时,对应的列信息和物理存储能够正确地从列存储表及其分区中移除,并安排在事务提交时实际删除物理存储。详细代码描述如下所示:(路径:src\common\backend\catalog\storage.cpp

/*
 * - 简要描述: 为列存储表删除列,包括:
 *      1. 从cudesc中删除列信息
 *      2. 在事务提交时安排删除列的物理存储。
 * - 参数:
 *      @rel: 要删除列的目标关系
 *      @attrnum: 要删除的列
 *      @ownerid: 目标表的所有者ID
 * - 返回值:
 *      无返回值
 */
void CStoreRelDropColumn(Relation rel, AttrNumber attrnum, Oid ownerid)
{
    // 确保传入的关系是列存储格式
    Assert(RelationIsCUFormat(rel));

    // 如果关系不是分区表
    if (!RELATION_IS_PARTITIONED(rel)) {
        // 从cudesc中删除列信息
        CStoreDropColumnInCuDesc(rel, attrnum);
        // 将列的物理存储放入待删除列表中,在事务提交时处理
        InsertStorageIntoPendingList(&rel->rd_node, attrnum, rel->rd_backend, ownerid, true);
    } else { // 如果关系是分区表
        List* partitions = NIL;
        ListCell* cell = NULL;
        Partition partition = NULL;
        Relation partRel = NULL;
        // 获取分区表的所有分区
        partitions = relationGetPartitionList(rel, AccessExclusiveLock);

        // 遍历每个分区
        foreach (cell, partitions) {
            // 获取当前分区
            partition = (Partition)lfirst(cell);
            // 获取当前分区的关系
            partRel = partitionGetRelation(rel, partition);

            // 从当前分区的cudesc中删除列信息
            CStoreDropColumnInCuDesc(partRel, attrnum);
            // 将当前分区的列物理存储放入待删除列表中,在事务提交时处理
            InsertStorageIntoPendingList(&partRel->rd_node, attrnum, partRel->rd_backend, ownerid, true);

            // 释放分区关系
            releaseDummyRelation(&partRel);
        }

        // 释放分区列表
        releasePartitionList(rel, &partitions, AccessExclusiveLock);
    }
}

  以上为 opengauss 中删除列存表中指定列的函数,其中,我们了解到,在 pg 中,分区表的设定方式与 opengauss 不同,为继承表的方式。那么能不能将以上函数修改为 pg 版本的呢?或许是可以的。(虽然 pg 中目前没有 og 中的列存储实现方案,目前仅是一个假设的方案

代码修改要点
  1. 分区表处理: openGauss 中用 RELATION_IS_PARTITIONED 来判断分区表,并对每个分区单独处理。PostgreSQL没有分区表的概念,使用继承表代替分区表,所以在 PostgreSQL 版本中,我们使用 find_inheritance_children 获取继承表列表。
  2. 遍历继承表:PostgreSQL 中,我们遍历继承表列表,对每个子表进行同样的列删除处理。
  3. 删除列信息和安排删除物理存储: 对于每个子表,我们调用 CStoreDropColumnInCuDescInsertStorageIntoPendingList 来删除列的信息和安排删除物理存储。

  明确了以上修改要点,一下给出我们的方案所修改得到的源码:

/*
 * - 简要描述: 为列存储表删除列,包括:
 *      1. 从cudesc中删除列信息
 *      2. 在事务提交时安排删除列的物理存储。
 * - 参数:
 *      @rel: 要删除列的目标关系
 *      @attrnum: 要删除的列
 *      @ownerid: 目标表的所有者ID
 * - 返回值:
 *      无返回值
 */
void CStoreRelDropColumn(Relation rel, AttrNumber attrnum, Oid ownerid)
{
    // 确保传入的关系是列存储格式
    Assert(RelationIsCUFormat(rel));

    // 从cudesc中删除列信息
    CStoreDropColumnInCuDesc(rel, attrnum);
    // 将列的物理存储放入待删除列表中,在事务提交时处理
    InsertStorageIntoPendingList(&rel->rd_node, attrnum, rel->rd_backend, ownerid, true);

    // 获取继承表(子表)列表
    List* children = find_inheritance_children(RelationGetRelid(rel), AccessExclusiveLock);
    ListCell* cell = NULL;

    // 遍历每个继承表
    foreach (cell, children) {
        Oid childrelid = lfirst_oid(cell);
        Relation childrel = heap_open(childrelid, AccessExclusiveLock);

        // 从子表的cudesc中删除列信息
        CStoreDropColumnInCuDesc(childrel, attrnum);
        // 将子表的列物理存储放入待删除列表中,在事务提交时处理
        InsertStorageIntoPendingList(&childrel->rd_node, attrnum, childrel->rd_backend, ownerid, true);

        // 关闭子表
        heap_close(childrel, NoLock);
    }

    // 释放继承表列表
    list_free(children);
}

CStoreDropColumnInCuDesc 函数

  CStoreDropColumnInCuDesc 函数的主要作用是从列存储表(CStore)的 cudesc 表中删除指定列的信息。具体功能包括:

  1. 打开 cudesc 表: 根据传入的关系描述符,获取对应 cudesc 表的 OID,并以行排他锁模式打开该表。
  2. 初始化扫描键: 根据传入的列编号(attrnum),初始化扫描键,用于匹配 cudesc 表中对应的列信息。
  3. 扫描 cudesc 表: 使用系统扫描描述符开始扫描 cudesc 表,利用指定的索引和扫描键进行匹配。
  4. 删除匹配元组: 遍历扫描结果,对于每个匹配的元组,进行删除操作。
  5. 关闭扫描和表: 扫描结束后,关闭扫描描述符和 cudesc 表。

  通过这些步骤,函数确保能够从 cudesc 表中精确地删除指定列的信息,为列存储表的删除列操作提供支持。详细代码描述如下所示:(路径:src\gausskernel\storage\cstore\cstore_am.cpp

/*
 * @Description: 从列存储表的cudesc表中删除列信息。
 *               用于列存储表的删除列操作。
 * @Param[IN] rel: 目标关系
 * @Param[IN] attrnum: 要删除的列的attrnum
 * @Return: void
 * @See also:
 */
void CStoreDropColumnInCuDesc(Relation rel, AttrNumber attrnum)
{
    ScanKeyData key[1]; // 定义扫描键数组
    SysScanDesc scan; // 定义系统扫描描述符
    HeapTuple tup; // 定义堆元组
    Oid cudescOid = rel->rd_rel->relcudescrelid; // 获取cudesc表的OID
    Relation cudescHeap = heap_open(cudescOid, RowExclusiveLock); // 以行排他锁打开cudesc表

    int attrno = attrnum; // 将attrnum赋值给attrno
    // 初始化扫描键,用于匹配要删除的列
    ScanKeyInit(&key[0], (AttrNumber)CUDescColIDAttr, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(attrno));

    // 开始扫描cudesc表,使用指定的索引和扫描键
    scan = systable_beginscan(cudescHeap, rel->rd_rel->relcudescidx, false, NULL, 1, key);

    // 遍历扫描结果
    while (HeapTupleIsValid(tup = systable_getnext(scan))) {
        // 删除扫描到的元组
        simple_heap_delete(cudescHeap, &tup->t_self);
    }

    // 结束扫描
    systable_endscan(scan);

    // 关闭cudesc表
    heap_close(cudescHeap, RowExclusiveLock);
}

InsertStorageIntoPendingList 函数

  InsertStorageIntoPendingList 函数的主要作用是将需要删除的关系存储信息添加到事务管理的待删除列表中。具体功能包括:

  1. 分配内存:PendingRelDelete 结构体分配内存。
  2. 设置关系节点信息: 将传入的关系节点信息(rnode)复制到 PendingRelDelete 结构体中。
  3. 设置存储区编号: 根据列属性编号attrnum),确定并设置存储区编号(forknum),如果是用户定义的列则转换为列存储表的存储区编号,否则为主存储区。
  4. 设置其他属性:
  • 设置 backend 为传入的后台 ID
  • 初始化 relOid 为无效值。
  • 设置所有者 IDownerid)。
  • 根据 atCommit 标志决定在提交还是中止时删除。
  • 获取并设置当前事务嵌套级别(nestLevel)。
  1. 添加到待删除列表:PendingRelDelete 结构体添加到当前会话的 pendingDeletes 链表头部。
  2. 处理临时表和全局临时表:
  • 如果是临时表,设置 tempTable 标志为 true,否则为 false
  • 如果是全局临时表,设置 relOid 为关系 ID
  1. 控制并发: 如果启用了堆 BCM 数据复制,并且不允许集群调整大小,则锁定 RelFileNode 以控制与 Catchup 线程的并发操作。\

  通过这些步骤,函数确保在事务提交或中止时,正确处理需要删除的关系存储信息,维护数据库的一致性和完整性。详细代码描述如下所示:(路径:src\common\backend\catalog\storage.cpp

/* 将关系添加到在事务中止时需要删除的列表中。
 * 如果是行存储表,*whichAttr* 必须是 *AllTheAttrs*。
 * 如果是列存储表,*whichAttr* >= *AllTheAttrs*。
 */
void InsertStorageIntoPendingList(_in_ const RelFileNode* rnode, _in_ AttrNumber attrnum, _in_ BackendId backend,
    _in_ Oid ownerid, _in_ bool atCommit, _in_ bool isDfsTruncate, Relation rel)
{
    // 分配PendingRelDelete结构体的内存
    PendingRelDelete* pending = (PendingRelDelete*)MemoryContextAlloc(
        SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), sizeof(PendingRelDelete));
    // 将关系节点信息复制到pending结构体中
    pending->relnode = *rnode;

    // 如果attrnum表示用户定义的属性
    if (AttrNumberIsForUserDefinedAttr(attrnum)) {
        // 将列ID转换为列存储表的ForkNum
        pending->forknum = ColumnId2ColForkNum(attrnum);
    } else {
        // 否则,使用主存储区的ForkNum
        pending->forknum = MAIN_FORKNUM;
    }
    // 设置backend ID
    pending->backend = backend;
    // 初始化relOid为无效值
    pending->relOid = InvalidOid;
    // 设置所有者ID
    pending->ownerid = ownerid;
    // 设置atCommit标志,决定在提交还是中止时删除
    pending->atCommit = atCommit; /* false: 如果中止则删除; true: 如果提交则删除 */
    // 获取当前事务嵌套级别
    pending->nestLevel = GetCurrentTransactionNestLevel();
    // 将pending结构体添加到pendingDeletes链表的头部
    pending->next = u_sess->catalog_cxt.pendingDeletes;
    u_sess->catalog_cxt.pendingDeletes = pending;

    // 如果是临时表
    if (rel && RELATION_IS_TEMP(rel)) {
        // 设置tempTable标志为true
        pending->tempTable = true;
    } else {
        // 否则,设置tempTable标志为false
        pending->tempTable = false;
    }
    // 如果是全局临时表
    if (RELATION_IS_GLOBAL_TEMP(rel)) {
        // 设置relOid为关系ID
        pending->relOid = RelationGetRelid(rel);
    } else if (!u_sess->attr.attr_sql.enable_cluster_resize && enable_heap_bcm_data_replication()) {
        // 如果启用了堆BCM数据复制,且不允许集群调整大小
        // 锁定RelFileNode以控制与Catchup线程的并发
        LockRelFileNode(*rnode, AccessExclusiveLock);
    }
}

标签:cudesc,存储,删除,tuple,源码,rel,TABLE,OpenGauss,attrnum
From: https://blog.csdn.net/qq_43899283/article/details/139686619

相关文章