【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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/10652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript与Nest.js:打造高性能的服务器端应用

JavaScript与Nest.js&#xff1a;打造高性能的服务器端应用 在现代Web开发的广阔天地里&#xff0c;JavaScript已经不再局限于浏览器的疆域&#xff0c;而是凭借Node.js的强大力量&#xff0c;向服务器端领域大步迈进。Nest.js&#xff0c;一个基于Node.js的渐进式框架&#x…

【计算机网络】物理层 通信基础、奈氏准则、香农公式 习题2

下列说法中正确的是( )。 A. 信道与通信电路类似&#xff0c;一条可通信的电路往往包含一个信道 B.调制是指把模拟数据转换为数字信号的过程 C. 信息传输速率是指通信信道上每秒传输的码元数 D.在数值上&#xff0c;波特率等于比特率与每符号所含的比特数的比值 信息传输速率&a…

65-CPLD电路设计(安路为例)

视频链接 CPLD电路设计&#xff08;安路为例&#xff09;01_哔哩哔哩_bilibili CPLD电路设计&#xff08;以安路为例&#xff09; 浅谈板级电源设计的三种方法_哔哩哔哩_bilibili 参考【浅谈板级电源设计的三种方法】 FPGA板级硬件实战S1&#xff5e;7课 实战Power2-电…

一次基类类型对象无法被传递问题的分析

看下面一段代码&#xff1a; // proj2.cpp #include <iostream> using namespace std; class CharShape { public:CharShape(char ch) : _ch(ch) {};virtual void Show() 0; protected:char _ch; // 组成图形的字符 }; class Triangle : public CharShape { public:Tr…

^_^填坑备忘^_^C#自动化编程实现STK+Exata对卫星互联网星座进行网络仿真

C#实际选择 STK11版本 or STK12版本的问题备注。 【C#自动化客户端调用STK时&#xff0c;实际选择 STK11版本 or STK12版本 的调试运行备注】 以下代码“更新并重新打包备份为”〔testSTKQualNetInterface备份08.1_★避坑★【种子卫星&#xff1a;天线直接安装在卫星上&#…

自贡在线教育系统报价,报班、网课还是自学怎么选择呢?

现在的教育模式已经不局限于传统的教育模式了&#xff0c;教育模式已经有很多模式&#xff0c;有各种辅导班、网课课程&#xff0c;很多人都在想报班好还是选网课&#xff0c;或者自学呢&#xff1f; 自学&#xff1a;成本最低&#xff0c;效率因人而异。只需要花上百把块钱买教…

1.下午试题1

1.15分 拿到10分之上 前三个问 12分 最后一个3分 前三个都是固定的题目 2.写出实体名称 写出数据存储问题 补充数据流起点与终点 3.数据流图 也称为数据流程图 Data Flow Diagram DFD 4.分为外部实体Entity 加工Process 数据存储Data Store 数据流 5.外部实体 当前系统之外的 人…

centos7.9系统安全加固

1、限制用户登陆 vim /etc/hosts.deny&#xff0c;若禁止192.168.0.158对服务器进行ssh的登陆&#xff0c;添加如下内容 sshd : 192.168.0.158 添加完毕后就生效了&#xff0c;直接用192.168.0.158访问主机&#xff0c;就无法连接了&#xff0c;显示 Connection closing...Soc…

系统需求开发和管理指南(软件标准文件Word)

1.需求获取的方式 2.需求分析的准则 3.需求分析的方法 4.需求开发考虑的方面 5.需求确认的方法 6.需求优先级的设定 7.需求文档编制规范要求 软件全文档获取方式一&#xff1a;本文末个人名片直接获取。 软件全文档获取二&#xff1a;软件项目开发全套文档下载_软件项目文档-C…

相机3:曝光三要素之光圈与快门

背景介绍 曝光三要素&#xff1a;光圈&#xff0c;快门&#xff0c;感光度 光圈&#xff1a; 光圈数值越小&#xff0c;画面清晰范围越少&#xff0c;画面背景虚化越明显&#xff1b; 光圈数值越大&#xff0c;画面清晰范围越多&#xff0c;背景虚化越不明显。 快门&#xff1a…

Xilinx FPGA底层逻辑资源简介(1):关于LC,CLB,SLICE,LUT,FF的概念

LC&#xff1a;Logic Cell 逻辑单元 Logic Cell是Xilinx定义的一种标准&#xff0c;用于定义不同系列器件的大小。对于7系列芯片&#xff0c;通常在名字中就已经体现了LC的大小&#xff0c;在UG474中原话为&#xff1a; 对于7a75t芯片&#xff0c;LC的大小为75K&#xff0c;6输…

VPN方案和特点

VPN方案和特点 VPN&#xff0c;或者称为虚拟专用网络&#xff0c;是一种保护你的在线安全和隐私的技术。它可以创建一个加密的连接&#xff0c;使你的在线活动对其他人不可见。以下是一些常见的VPN协议和它们的特点&#xff1a; 开放VPN (OpenVPN)&#xff1a;这是一种极为可…

利用Spring Boot与Redisson实时排行榜功能

在现代的Web应用程序中&#xff0c;排行榜功能是常见且重要的需求之一。它可以用于展示热门内容、评选优秀用户等场景。 本文将介绍如何利用Spring Boot和Redisson实现排行榜功能&#xff0c;让你的应用程序更具吸引力和竞争力。 1. 概述 排行榜功能通常涉及到大量的数据计算…

阿里云和AWS负载均衡服务对比分析

在云计算时代,负载均衡作为一种关键的网络基础设施,承担着在多个服务器之间分发网络流量的重要任务。作为全球两大主要的云服务提供商,阿里云和Amazon Web Services(AWS)都提供了强大的负载均衡解决方案。本文将从性能、功能、可用性和成本等方面对两者进行对比分析。我们九河云…

600/天,海外项目值班,接不接?

朋友介绍了一个海外项目&#xff0c;广告系统短期维护&#xff0c;刚上线需要维护14天也就是2个星期&#xff0c;费用单价600/天&#xff0c;主要工作内容&#xff1a;北京晚上12点-早上8点值班&#xff0c;如果有问题及时响应并修复。 如果我年轻10岁&#xff0c;这个项目我倒…

【牛客】SQL202 找出所有员工当前薪水salary情况

1、描述 有一个薪水表&#xff0c;salaries简况如下&#xff1a; 请你找出所有员工具体的薪水salary情况&#xff0c;对于相同的薪水只显示一次,并按照逆序显示&#xff0c;以上例子输出如下&#xff1a; 2、题目建表 drop table if exists salaries ; CREATE TABLE sala…

【全开源】Java知识付费教育付费资源付费平台公众号小程序源码

特色功能&#xff1a; 多样化的内容呈现&#xff1a;资源付费平台小程序支持图文、音视频、直播等多种形式的内容呈现&#xff0c;为用户提供了丰富的学习体验。直播课程&#xff1a;专家或讲师可以通过小程序进行在线授课&#xff0c;与用户实时互动&#xff0c;增强了学习的…

【Linux】AlmaLinux 9.4版本发布

AlmaLinux 9.4 正式版发布&#xff0c;该版本基于 Redhat Enterprise 9.4&#xff0c;内核版本号&#xff1a; 5.14.0-427.13.1.el9_4.x86_64 相对于Rocky Linux&#xff0c; AlmaLinux更加的稳定&#xff0c;生产环境建议使用AlmaLinux来替代CentOS 7.x AlmaLinux 9.4版本系统…

【多电压流程 Multivoltage Flow】- 5.特定工具使用建议(1.VCS NLP VC LP)

本章提供了关于使用Synopsys工具进行低功耗设计和分析的信息。它包含以下部分: • 使用VCS NLP和VC LP进行多电压验证 • 使用Design Compiler进行逻辑综合 • 使用IC Compiler进行设计规划 • 使用IC Compiler进行物理实现 • 使用IC Compiler II和Fusion Compiler进行物…

【Python-爬虫】

Python-爬虫 ■ 爬虫分类■ 1. 通用网络爬虫&#xff1a;&#xff08;搜索引擎使用&#xff0c;遵守robots协议&#xff09;■ robots协议&#xff08;君子协议&#xff09; ■ 2. 聚集网络爬虫&#xff1a;自己写的爬虫程序 ■ urllib.request&#xff08;要导入的模块&#x…