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

2024-05-12 16:36

本文主要是介绍【OpenGauss源码学习 —— (ALTER TABLE(ExecRewriteCStoreTable))】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ALTER TABLE(ExecRewriteCStoreTable)

  • 概述
  • ExecRewriteCStoreTable 函数
    • ATCStoreRewriteTable 函数
    • ATCStoreGetRewriteAttrs 函数
    • ChangeTableSpaceForDeltaRelation 函数
    • ATOnlyCheckCStoreTable 函数

声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 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); // 获取新的 TupleDescTupleDesc oldTupDesc = tab->oldDesc; // 获取旧的 TupleDescAssert(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 索引关系的表空间}
}

ATOnlyCheckCStoreTable 函数

  函数的作用是对列存储表进行非空约束的检查。它首先检查是否有新的非空约束,如果,则获取所有非空属性,并标记需要进行扫描。然后,它通过列存储扫描器进行扫描,逐行检查非空约束是否满足。如果发现某一行的某一非空属性为空值,则抛出错误。函数源码如下所示:(路径:src\gausskernel\optimizer\commands\tablecmds.cpp

/** ATOnlyCheckCStoreTable* Only support not-null constraint check currently.*//** ATOnlyCheckCStoreTable* 目前仅支持非空约束检查。*/static void ATOnlyCheckCStoreTable(const AlteredTableInfo* tab, Relation rel)
{// 旧的元组描述符和新的元组描述符TupleDesc oldTupDesc = NULL;TupleDesc newTupDesc = NULL;// 非空属性列表和是否需要扫描的标志List* notnullAttrs = NIL;bool needscan = false;// 获取旧的元组描述符和新的元组描述符oldTupDesc = tab->oldDesc;newTupDesc = RelationGetDescr(rel); /* 包含所有的修改 */// 如果有约束或新值,则报错,因为列存储关系不支持这个特性if (tab->constraints != NIL || tab->newvals != NIL) {ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("Un-support feature"),errdetail("column stored relation doesn't support this feature")));}// 如果有新的非空约束,则检查所有非空约束if (tab->new_notnull) {/** 如果我们添加了任何新的非空约束,则检查所有非空约束。*/for (int i = 0; i < newTupDesc->natts; i++) {if (newTupDesc->attrs[i].attnotnull && !newTupDesc->attrs[i].attisdropped)notnullAttrs = lappend_int(notnullAttrs, i);}if (notnullAttrs != NULL)needscan = true;}// 如果需要扫描,则进行扫描检查非空约束if (needscan) {// 列扫描器和向量扫描批次CStoreScanDesc cstoreScan = NULL;VectorBatch* vecScanBatch = NULL;// 属性数量和属性索引数组int attrNum = list_length(notnullAttrs);AttrNumber* colIdx = (AttrNumber*)palloc(sizeof(AttrNumber) * attrNum);int n = 0;// 矢量和非空属性列表的迭代器ListCell* cell = NULL;ScalarVector *vec = NULL;// 遍历非空属性列表,构建属性索引数组foreach (cell, notnullAttrs) {colIdx[n++] = lfirst_int(cell) + 1;}// 打印调试信息,验证表ereport(DEBUG1, (errmsg("verifying table \"%s\"", RelationGetRelationName(rel))));/** 通过列存扫描,如果需要则生成新的行,然后检查约束。*/cstoreScan = CStoreBeginScan(rel, attrNum, colIdx, SnapshotNow, false);do {vecScanBatch = CStoreGetNextBatch(cstoreScan);vec = vecScanBatch->m_arr;for (int rowIdx = 0; rowIdx < vecScanBatch->m_rows; rowIdx++) {for (n = 0; n < attrNum; ++n ) {int attn = colIdx[n] - 1;if (vec[attn].IsNull(rowIdx)) {ereport(ERROR,(errcode(ERRCODE_NOT_NULL_VIOLATION),errmsg("column \"%s\" contains null values", NameStr(newTupDesc->attrs[attn].attname))));}}}} while (!CStoreIsEndScan(cstoreScan));CStoreEndScan(cstoreScan);pfree_ext(colIdx);}// 释放非空属性列表的内存list_free_ext(notnullAttrs);
}

这篇关于【OpenGauss源码学习 —— (ALTER TABLE(ExecRewriteCStoreTable))】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/983155

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识