【OpenGauss源码学习 —— (ALTER TABLE(ExecRewriteCStoreTable))】

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

概述

  【OpenGauss源码学习 —— (ALTER TABLE(ExecRewriteRowTable))】这篇文章详细描述了 OpenGauss 数据库管理系统中执行 ALTER TABLE (ExecRewriteRowTable) 命令的源码实现。ExecRewriteCStoreTableExecRewriteRowTable 都是用于重写表的数据的函数,但是它们针对的表类型不同ExecRewriteCStoreTable 主要用于重写列存储表的数据,而 ExecRewriteRowTable 则用于重写行存储表的数据。因此,它们的关系在于它们都是用于表数据重写的功能,但是针对的表类型不同,分别适用于列存储表和行存储表
  本文,我们来学习一下 ExecRewriteCStoreTable 函数的内容。

ExecRewriteCStoreTable 函数

  ExecRewriteCStoreTable 函数的主要功能是重写列存储表的列关系数据,并在重写完成后重新构建表的索引。首先,通过表的 OID 打开待重写的原始堆表,然后调用 ATCStoreRewriteTable 函数来执行实际的重写操作,重写完成后关闭原始堆表。最后,通过调用 ReindexRelation 函数重新构建列存储表的所有索引,以确保索引与表数据的一致性。函数源码如下所示:(路径:src\gausskernel\optimizer\commands\tablecmds.cpp

/*
 * @Description: 重写列存储表的列关系数据,并重新构建索引。
 * @Param[IN] lockmode: 重写数据时使用的锁模式
 * @Param[IN] NewTableSpace: 列存储表新的表空间
 * @Param[IN] tab: 修改表信息
 * @See also: ATCStoreRewriteTable
 */
static void ExecRewriteCStoreTable(AlteredTableInfo* tab, Oid NewTableSpace, LOCKMODE lockmode)
{
    // 打开待重写的原始堆表
    Relation OldHeap = heap_open(tab->relid, NoLock);
    // 调用 ATCStoreRewriteTable 函数重写表
    ATCStoreRewriteTable(tab, OldHeap, lockmode, NewTableSpace);
    // 关闭原始堆表
    heap_close(OldHeap, NoLock);

    /* 然后,重新构建其索引。*/
    (void)ReindexRelation(
        tab->relid, REINDEX_REL_SUPPRESS_INDEX_USE | REINDEX_REL_CHECK_CONSTRAINTS, REINDEX_ALL_INDEX, NULL);
}

ATCStoreRewriteTable 函数

  ATCStoreRewriteTable 函数是用于重写列存储表数据的函数。它根据传入的 AlterTableInfo 结构体、旧的堆关系锁模式以及目标表空间对列存储表进行数据重写。函数首先根据表结构变更信息和表空间变更情况,决定是否需要重新分配表空间。然后,根据表结构变更信息,重新计算表达式设置 NOT NULL 约束。接着,使用列存储重写器,根据指定的规则,逐列对数据进行重写。在这个过程中,还需要处理与 Delta 关系CU 描述符关系CU 描述符索引关系相关的锁定和表空间变更。最后,完成数据重写后,对相应的表和索引进行更新,确保变更的可见性,并释放相关的资源。
  这个函数的执行流程可以分为以下几个步骤:

  1. 初始化变量: 函数开始时,初始化了一系列变量,包括新旧 TupleDesc重写信息数组重写标志数组等。
  2. 获取重写信息: 根据传入的 AlterTableInfo 结构体,获取需要重写的列信息,包括是否添加列设置数据类型等。
  3. 设置表达式和约束: 对于需要添加的列,函数会为其设置计算表达式,并根据需要设置 NOT NULL 约束
  4. 准备重写器: 创建一个列存储重写器,该重写器用于实际执行数据重写操作。
  5. 处理表空间变更: 如果目标表空间与当前表空间不同,则需要将表数据移动到新的表空间
  6. 重写列数据: 根据重写器的设定,逐列对表数据进行重写操作。这包括根据表达式计算新列值复制旧数据以及应用新的约束等。
  7. 更新元数据: 完成数据重写后,需要更新表索引的元数据信息,确保变更对外可见。
  8. 释放资源: 释放申请的内存和关闭相关的数据库连接。

  整个流程确保了列存储表数据的正确重写,并处理了表空间变更等特殊情况,以确保数据库操作的完整性和一致性。函数源码如下所示:(路径:src\gausskernel\optimizer\commands\tablecmds.cpp

/*
 * @Description: 重写列存储表的数据。该函数根据传入的 AlterTableInfo 结构体,旧堆关系,锁模式以及目标表空间来重写列存储表的数据。
 * @Param[IN] tab: 修改表信息结构体,包含了进行表变更的所有信息。
 * @Param[IN] oldHeapRel: 旧的堆关系,即需要被重写的列存储表。
 * @Param[IN] lockMode: 在重写数据期间使用的锁模式。
 * @Param[IN] targetTblspc: 新的目标表空间,用于重写后的列存储表。
 * @See also: ATCStoreGetRewriteAttrs, CStoreRewriter, CStoreCopyColumnData
 */
static void ATCStoreRewriteTable(AlteredTableInfo* tab, Relation oldHeapRel, LOCKMODE lockMode, Oid targetTblspc)
{
    bool tblspcChanged = NeedToSetTableSpace(oldHeapRel, targetTblspc); // 检查是否需要更改表空间
    Oid newfilenode = InvalidOid; // 新文件节点
    Oid cudescOid = InvalidOid; // CU 描述符关系的对象标识符
    Oid cudescIdxOid = InvalidOid; // CU 描述符索引关系的对象标识符
    Relation cudescRel = NULL; // CU 描述符关系
    Relation pg_class = NULL; // pg_class 关系
    Relation CUReplicationRel = NULL; // 用于复制 CU 的关系
    HeapTuple pgclass_tuple = NULL; // pg_class 元组
    Form_pg_class pgclass_form = NULL; // pg_class 表的形式
    CStoreRewriter* rewriter = NULL; // 列存储重写器
    errno_t rc;

    /*
     * 注意:旧的 TupleDesc 已经被复制并保存在 tab->oldDesc 中。
     *   现在新的 TupleDesc 可以在 oldHeapRel 中找到并使用。
     */
    TupleDesc newTupDesc = RelationGetDescr(oldHeapRel); // 获取新的 TupleDesc
    TupleDesc oldTupDesc = tab->oldDesc; // 获取旧的 TupleDesc
    Assert(newTupDesc->natts >= oldTupDesc->natts); // 断言新的 TupleDesc 的属性数量大于等于旧的 TupleDesc 的属性数量

    /* 不支持的表/列约束:CHECK、FOREIGN KEY */
    Assert(tab->constraints == NIL); // 断言约束为空

    int nColsOfEachType[CSRT_NUM]; // 每种类型的列数数组
    rc = memset_s(nColsOfEachType, sizeof(int) * CSRT_NUM, 0, sizeof(int) * CSRT_NUM); // 初始化数组为零
    securec_check(rc, "", "");

    int maxCols = newTupDesc->natts; // 最大列数
    bool* rewriteFlags = (bool*)palloc0(sizeof(bool) * maxCols); // 重写标志数组
    CStoreRewriteColumn** rewriteInfo = (CStoreRewriteColumn**)palloc0(sizeof(void*) * maxCols); // 重写信息数组

    /* 拆分出:ADD COLUMN、SET DATA TYPE COLUMN 和其他情况 */
    ATCStoreGetRewriteAttrs(tab, oldTupDesc, newTupDesc, rewriteInfo, rewriteFlags, nColsOfEachType); // 获取重写属性

    /* 为更新的列设置重新计算表达式 */
    ListCell* l = NULL;
    foreach (l, tab->newvals) {
        NewColumnValue* ex = (NewColumnValue*)lfirst(l); // 获取每个新列的值
        ex->exprstate = ExecInitExpr((Expr*)ex->expr, NULL); // 初始化表达式状态

        /* 我们预期每个属性只有一个 NewColumnValue */
        Assert(rewriteInfo[ex->attnum - 1] != NULL); // 断言重写信息不为空
        Assert(rewriteInfo[ex->attnum - 1]->newValue == NULL); // 断言新值为空

        ColumnNewValue* newValExp = (ColumnNewValue*)palloc(sizeof(ColumnNewValue)); // 分配新的列值结构体内存
        newValExp->expr = ex->expr; // 设置表达式
        newValExp->exprstate = ex->exprstate; // 设置表达式状态
        rewriteInfo[ex->attnum - 1]->newValue = newValExp; // 将新值存入重写信息数组
    }

    /* 为更新的列设置 NOT NULL 约束 */
    if (tab->rewrite > 0 || tab->new_notnull) {
        for (int i = 0; i < maxCols; ++i) {
            if (rewriteInfo[i] != NULL && !rewriteInfo[i]->isDropped && newTupDesc->attrs[i].attnotnull) {
                rewriteInfo[i]->notNull = true; // 设置非空约束
            }
        }
    }

    /* 重写列存储表 */
    rewriter = New(CurrentMemoryContext) CStoreRewriter(oldHeapRel, oldTupDesc, newTupDesc); // 创建列存储重写器

    /* 锁顺序:
     * 1. 列关系
     * 2. Delta 关系 [ Delta 索引关系 ]
     * 3. Cudesc 关系 + Cudesc 索引关系
     */
    if (OidIsValid(oldHeapRel->rd_rel->reldeltarelid)) { // 如果 Delta 关系的对象标识符有效
        LockRelationOid(oldHeapRel->rd_rel->reldeltarelid, lockMode); // 锁定 Delta 关系
    }
    cudescOid = oldHeapRel->rd_rel->relcudescrelid; // 获取 CU 描述符关系的对象标识符
    cudescRel = heap_open(cudescOid, lockMode); // 打开 CU 描述符关系
    cudescIdxOid = cudescRel->rd_rel->relcudescidx; // 获取 CU 描述符索引关系的对象标识符
    LockRelationOid(cudescIdxOid, lockMode); // 锁定 CU 描述符索引关系

    if (tblspcChanged) { // 如果表空间发生了改变
        /* 处理 Delta 和 Delta 索引关系
         * 现在可以直接通过块复制关系数据,因为 Delta 关系现在不可用且没有数据。
         *
         * 当 Delta 关系可用时,必须扫描其中的所有元组,重写并合并/追加到 CU 文件中
         */
        ChangeTableSpaceForDeltaRelation(oldHeapRel->rd_rel->reldeltarelid, targetTblspc, lockMode); // 修改 Delta 关系的表空间

        /* 处理每个列的数据 */
        /* 在这里可以安全地打开 pg_class 关系,因为:
         * 1. 不能更改分区表的表空间;
         * 2. 不能为一个分区表添加列/设置列数据类型;
         * 3. 更改一个分区表的表空间不会触发此分支;
         * 因此它是一个普通的 pg_class 关系。
         */
        pg_class = heap_open(RelationRelationId, RowExclusiveLock); // 打开 pg_class 关系

        /* 获取关系的可修改副本 */
        pgclass_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tab->relid)); // 获取关系的元组
        if (!HeapTupleIsValid(pgclass_tuple)) { // 如果元组无效
            ereport(ERROR,
                (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", tab->relid)));
        }
        pgclass_form = (Form_pg_class)GETSTRUCT(pgclass_tuple); // 获取表的形式

        /*
         * Relfilenodes 在表空间中不是唯一的,因此我们需要在新的表空间中分配一个新的。
         */
        newfilenode = GetNewRelFileNode(targetTblspc, NULL, oldHeapRel->rd_rel->relpersistence); // 获取新的文件节点

        RelFileNode CUReplicationFile = {
            ConvertToRelfilenodeTblspcOid(targetTblspc), oldHeapRel->rd_node.dbNode, newfilenode, InvalidBktId};
        CUReplicationRel = CreateCUReplicationRelation(CUReplicationFile,
            oldHeapRel->rd_backend,
            oldHeapRel->rd_rel->relpersistence,
            RelationGetRelationName(oldHeapRel)); // 创建 CU 复制关系

        for (int i = 0; i < maxCols; ++i) {
            Form_pg_attribute thisattr = &newTupDesc->attrs[i]; // 获取当前属性

            /* 跳过已删除和重写的列 */
            if (!thisattr->attisdropped && !rewriteFlags[i]) {
                CStoreCopyColumnData(CUReplicationRel, oldHeapRel, thisattr->attnum); // 复制列数据
            }
        }

        /* 准备处理 ADD COLUMN 和 SET DATA TYPE COLUMN */
        rewriter->ChangeTableSpace(CUReplicationRel); // 修改表空间
    }

    /* 处理 Cudesc 关系 + ADD COLUMN + SET DATA TYPE COLUMN */
    rewriter->BeginRewriteCols(maxCols, rewriteInfo, nColsOfEachType, rewriteFlags); // 开始重写列
    rewriter->RewriteColsData(); // 重写列数据
    rewriter->EndRewriteCols(); // 结束重写列
    DELETE_EX(rewriter); // 释放重写器内存

    if (tblspcChanged) { // 如果表空间发生了改变
        CStoreCopyColumnDataEnd(oldHeapRel, targetTblspc, newfilenode); // 结束列数据复制

        /* 销毁虚假关系 */
        FreeFakeRelcacheEntry(CUReplicationRel); // 释放虚假关系

        /* 更新 pg_class 行 */
        pgclass_form->reltablespace = ConvertToPgclassRelTablespaceOid(targetTblspc); // 更新表空间
        pgclass_form->relfilenode = newfilenode; // 更新文件节点
        simple_heap_update(pg_class, &pgclass_tuple->t_self, pgclass_tuple); // 简单堆更新
        CatalogUpdateIndexes(pg_class, pgclass_tuple); // 更新索引

        tableam_tops_free_tuple(pgclass_tuple); // 释放元组内存
        heap_close(pg_class, RowExclusiveLock); // 关闭 pg_class 关系

        /* 确保 reltablespace 变更可见 */
        CommandCounterIncrement(); // 增加命令计数器

        /* 处理 Cudesc 索引关系 */
        ATExecSetTableSpace(cudescIdxOid, ConvertToRelfilenodeTblspcOid(targetTblspc), lockMode); // 修改表空间
    }

    /* 直到提交之前都解锁 */
    heap_close(cudescRel, NoLock); // 关闭 CU 描述符关系

    /* 最后清理工作 */
    for (int k = 0; k < maxCols; ++k) {
        if (rewriteInfo[k]) {
            CStoreRewriteColumn::Destroy(&rewriteInfo[k]); // 销毁重写信息
        }
    }
    pfree_ext(rewriteInfo); // 释放重写信息内存
    pfree_ext(rewriteFlags); // 释放重写标志数组内存
}

  下面针对以上函数中的部分代码段进行解释。


 	/* 为更新的列设置重新计算表达式 */
    ListCell* l = NULL;
    foreach (l, tab->newvals) {
        NewColumnValue* ex = (NewColumnValue*)lfirst(l); // 获取每个新列的值
        ex->exprstate = ExecInitExpr((Expr*)ex->expr, NULL); // 初始化表达式状态

        /* 我们预期每个属性只有一个 NewColumnValue */
        Assert(rewriteInfo[ex->attnum - 1] != NULL); // 断言重写信息不为空
        Assert(rewriteInfo[ex->attnum - 1]->newValue == NULL); // 断言新值为空

        ColumnNewValue* newValExp = (ColumnNewValue*)palloc(sizeof(ColumnNewValue)); // 分配新的列值结构体内存
        newValExp->expr = ex->expr; // 设置表达式
        newValExp->exprstate = ex->exprstate; // 设置表达式状态
        rewriteInfo[ex->attnum - 1]->newValue = newValExp; // 将新值存入重写信息数组
    }

  假设我们有一个表 employee,需要对其进行 ALTER TABLE 操作,添加一个新列 salary,并为该列设置默认值。现在假设我们使用以下 SQL 语句进行操作:

ALTER TABLE employee ADD COLUMN salary INTEGER DEFAULT 50000;

  在数据库系统内部,这个操作会被解析成一系列的操作步骤,其中就包括对新添加的列 salary 进行默认值的设置。这时候,就涉及到了如何计算并设置默认值的问题,而上述代码片段就是用来处理这个问题的。
  假设我们的 AlteredTableInfo 结构体中有一个 tab->newvals 的列表,其中包含了新添加的列信息,比如列名默认值等。这个列表是一个存储了新列信息的数据结构
  上述代码片段的作用就是遍历 tab->newvals 列表对其中的每个新列进行处理。对于每个新列,首先会初始化其表达式状态,然后将表达式和其状态保存到一个新的结构体 ColumnNewValue 中,并将该结构体存入 rewriteInfo 数组中的相应位置。
  举例来说,假设我们要添加的新列 salary 的默认值为 50000,那么这段代码就会将这个默认值表达式以及其状态存入 rewriteInfo 数组中的 salary 对应位置。这样,在后续的操作中,数据库系统就可以根据这些信息来计算并设置新列的默认值。


RelFileNode CUReplicationFile = {
    ConvertToRelfilenodeTblspcOid(targetTblspc), oldHeapRel->rd_node.dbNode, newfilenode, InvalidBktId};
CUReplicationRel = CreateCUReplicationRelation(CUReplicationFile,
    oldHeapRel->rd_backend,
    oldHeapRel->rd_rel->relpersistence,
    RelationGetRelationName(oldHeapRel)); // 创建 CU 复制关系

  以上代码的作用是创建一个用于复制列存表数据的 CUColumn Unit)复制关系。具体来说,代码中首先构建了一个 RelFileNode 结构体 CUReplicationFile,用于表示新创建的 CU 复制关系的物理文件信息。其中,ConvertToRelfilenodeTblspcOid(targetTblspc) 用于获取目标表空间的 RelFileNode 中的表空间 OIDoldHeapRel->rd_node.dbNode 表示原始列存表的数据库 OIDnewfilenode新的 CU 复制关系的文件节点 OIDInvalidBktId 则表示无效的桶 ID
  然后,利用这个文件节点信息,调用 CreateCUReplicationRelation 函数来创建一个 CU 复制关系。这个函数需要传入以下参数:

  • CUReplicationFile: 表示新创建的 CU 复制关系的物理文件信息。
  • oldHeapRel->rd_backend: 表示原始列存表的后端 ID,用于关联新的 CU 复制关系。
  • oldHeapRel->rd_rel->relpersistence: 表示原始列存表的持久性,用于设置新的 CU 复制关系的持久性。
  • RelationGetRelationName(oldHeapRel): 表示原始列存表的名称,用于设置新的 CU 复制关系的名称。

ATCStoreGetRewriteAttrs 函数

  ATCStoreGetRewriteAttrs 函数的功能是在执行 ALTER TABLE 操作时获取需要检查或重写的所有属性信息。它遍历了 ALTER TABLE 命令的两个阶段添加列修改列数据类型),并根据命令的类型在新旧 Tuple 描述之间进行匹配,以确定哪些属性需要进行重写操作。

  • 对于添加列的操作,函数首先检查命令的定义,确保它是列定义。然后,它倒序遍历新 Tuple 描述的属性,以尽可能减少循环次数。对于每个属性,它检查列名是否与要添加的列名匹配,如果匹配,则创建用于添加列的重写信息对象,并记录要添加的新列数
  • 对于修改列数据类型的操作,函数遍历旧 Tuple 描述的所有属性,查找与要修改数据类型的列名匹配的属性。如果找到匹配项,则创建用于修改数据类型的重写信息对象,并记录要修改数据类型的列数

  函数源码如下所示:(路径:src\gausskernel\optimizer\commands\tablecmds.cpp

// 获取需要检查或重写的所有属性。
//
static void ATCStoreGetRewriteAttrs(_in_ AlteredTableInfo* tab, _in_ TupleDesc oldTupDesc, _in_ TupleDesc newTupDesc,
    _out_ CStoreRewriteColumn** rewriteInfo, _out_ bool* rewriteFlags, _out_ int* nColsOfEachType)
{
    // 断言新 Tuple 描述的属性数量不小于旧 Tuple 描述的属性数量。
    Assert(newTupDesc->natts >= oldTupDesc->natts);
    // 断言重写标志和每种类型的列数不为空。
    Assert(nColsOfEachType && rewriteFlags);

    // 遍历 ALTER TABLE 命令的两个执行阶段:添加列和修改列数据类型。
    for (int pass = 0; pass < AT_NUM_PASSES; ++pass) {
        // 如果当前阶段的子命令列表为空,则跳过。
        if (tab->subcmds[pass] == NIL) {
            continue;
        }

        // 获取当前阶段的子命令列表。
        List* subcmds = tab->subcmds[pass];
        ListCell* cmdCell = NULL;

        // 遍历子命令列表中的每个 ALTER TABLE 命令。
        foreach (cmdCell, subcmds) {
            AlterTableCmd* cmd = (AlterTableCmd*)lfirst(cmdCell);

            // 添加列。
            if (pass == AT_PASS_ADD_COL) {
                // 断言命令定义存在且类型为列定义。
                Assert(cmd->def && cmd->def->type == T_ColumnDef);
                ColumnDef* colDef = (ColumnDef*)cmd->def;
                // 断言列名存在且不为空。
                Assert(colDef->colname && colDef->colname[0] != '\0');

                // 倒序搜索新 Tuple 描述的属性,以尽可能减少循环次数。
                for (int attrIdx = newTupDesc->natts - 1; attrIdx >= 0; --attrIdx) {
                    // 如果找到与要添加列名匹配的属性。
                    if (pg_strcasecmp(colDef->colname, RelAttrName(newTupDesc, attrIdx)) == 0) {
                        // 断言重写信息不存在。
                        Assert(rewriteInfo[attrIdx] == NULL);
                        // 创建用于添加列的重写信息对象。
                        rewriteInfo[attrIdx] = CStoreRewriteColumn::CreateForAddColumn(attrIdx + 1);

                        // 记录要添加的新列数。
                        ++nColsOfEachType[CSRT_ADD_COL];
                        // 设置重写标志为真。
                        rewriteFlags[attrIdx] = true;
                        break;
                    }
                }
                continue;
            }

            // 修改列数据类型。
            if (pass == AT_PASS_ALTER_TYPE) {
                // 断言列名存在且不为空。
                Assert(cmd->name && cmd->name[0] != '\0');
                // 遍历旧 Tuple 描述的所有属性。
                for (int attrIdx = 0; attrIdx < oldTupDesc->natts; ++attrIdx) {
                    // 如果找到与要修改数据类型的列名匹配的属性。
                    if (pg_strcasecmp(cmd->name, RelAttrName(oldTupDesc, attrIdx)) == 0) {
                        // 禁止对同一列进行多次 ALTER TYPE。
                        Assert(rewriteInfo[attrIdx] == NULL);
                        // 创建用于修改数据类型的重写信息对象。
                        rewriteInfo[attrIdx] = CStoreRewriteColumn::CreateForSetDataType(attrIdx + 1);

                        // 记录要修改数据类型的列数。
                        ++nColsOfEachType[CSRT_SET_DATA_TYPE];
                        // 设置重写标志为真。
                        rewriteFlags[attrIdx] = true;
                        break;
                    }
                }
                continue;
            }
        }
    }
}

注释: AT_NUM_PASSES 是一个宏定义,表示 ALTER TABLE 命令执行的不同阶段的数量。在 OpenGauss 中,ALTER TABLE 命令通常会分为多个阶段执行,比如添加列删除列修改列数据类型等。这个宏定义的值指示了 ALTER TABLE 命令的总执行阶段数

ChangeTableSpaceForDeltaRelation 函数

  ChangeTableSpaceForDeltaRelation 函数实现了一个用于更改 Delta 关系表空间的函数Delta 关系通常用于存储基表发生变化时的增量数据。该函数首先检查提供的 Delta 关系对象标识符是否有效,然后将目标表空间转换为有效表空间,并使用给定的锁模式打开 Delta 关系。接着,通过调用 ATExecSetTableSpace 函数来改变 Delta 关系的表空间。最后,解锁 Delta 关系并处理 Delta 索引关系的表空间改变(代码中未给出)。函数源码如下所示:(路径:src\gausskernel\optimizer\commands\tablecmds.cpp

/*
 * @Description: 为 Delta 关系改变表空间。
 * @Param[IN] deltaOid: Delta 关系的对象标识符。
 * @Param[IN] lockmode: 在改变表空间过程中使用的锁模式。
 * @Param[IN] targetTableSpace: 新的表空间。
 * @See also:
 */
static inline void ChangeTableSpaceForDeltaRelation(Oid deltaOid, Oid targetTableSpace, LOCKMODE lockmode)
{
    // 如果 deltaOid 是有效的
    if (OidIsValid(deltaOid)) {
        /* ATExecSetTableSpace() 需要 targetTableSpace 不是 InvalidOid */
        targetTableSpace = ConvertToRelfilenodeTblspcOid(targetTableSpace);
        // 断言 targetTableSpace 是有效的
        Assert(OidIsValid(targetTableSpace));

        // 使用给定的锁模式打开 delta 关系
        Relation deltaRel = heap_open(deltaOid, lockmode);

        // 改变 Delta 关系的表空间
        ATExecSetTableSpace(deltaOid, targetTableSpace, lockmode);

        // 直到提交前解锁
        relation_close(deltaRel, NoLock);

        // 改变 Delta 索引关系的表空间
    }
}

相关推荐

  1. 开源openGauss数据库中事务管理解析

    2024-05-09 20:32:10       45 阅读
  2. 开源数据库 OpenGauss 的 SQL 解析分析

    2024-05-09 20:32:10       41 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-09 20:32:10       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-09 20:32:10       106 阅读
  3. 在Django里面运行非项目文件

    2024-05-09 20:32:10       87 阅读
  4. Python语言-面向对象

    2024-05-09 20:32:10       96 阅读

热门阅读

  1. 黑马点评项目笔记

    2024-05-09 20:32:10       29 阅读
  2. LabVIEW实现多张图像拼接

    2024-05-09 20:32:10       27 阅读
  3. c++ 读写锁对比试验

    2024-05-09 20:32:10       37 阅读
  4. Redis-1 缓存穿透、缓存击穿、缓存雪崩

    2024-05-09 20:32:10       25 阅读
  5. 接收文件(文件上传)

    2024-05-09 20:32:10       32 阅读
  6. 内联函数为什么不能声明定义分离?

    2024-05-09 20:32:10       24 阅读
  7. (五)Linux的编程环境

    2024-05-09 20:32:10       31 阅读
  8. uniapp遍历数组对象的常见方法

    2024-05-09 20:32:10       30 阅读