ALTER TABLE(列存修改列类型)
声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 OpenGauss5.1.0 的开源代码和《OpenGauss数据库源码解析》一书
在数据库管理中,改变表中列的类型是一个常见的操作。这种操作通常通过ALTER TABLE ALTER COLUMN TYPE
语句来实现,它允许数据库管理员在不删除和重新创建表的情况下更改列的数据类型。这种操作需要确保数据的正确性和一致性,并且在某些情况下,还需要重写表中的数据来适应新的数据类型。
在 OpenGauss 中,函数 ATExecAlterColumnType 用于执行这种列类型的更改。这个函数负责处理列类型变更的所有细节,包括检查新类型的兼容性、处理默认值和约束、以及在必要时重写表的数据。该函数确保在类型更改过程中不会破坏表的完整性和性能。本文将围绕 ATExecAlterColumnType 函数来展开学习列存储修改列类型的过程。
ATPrepAlterColumnType 函数
为了更好地理解 ATExecAlterColumnType 的工作原理,我们首先需要了解其准备阶段,即 ATPrepAlterColumnType 函数。ATPrepAlterColumnType 负责准备更改列类型所需的所有准备工作。以下是对 ATPrepAlterColumnType 函数的详细解释:
- 权限和继承检查:检查用户是否有权限修改列类型,检查列是否是系统列或继承列,若是则抛出错误。
- 查找目标类型:通过列的新类型名查找目标类型 ID、类型修饰符和排序规则。确保目标类型合法且支持。
- 类型转换:根据提供的表达式或直接将旧值转换为新类型。进行类型检查和转换,确保转换合法。
- 更新表结构:如果需要,将新的列值表达式添加到工作队列中,并标记是否需要重写表。
- 递归处理:如果需要,递归处理继承关系中的子表。
ATPrepAlterColumnType 函数源码如下所示:(路径:src\gausskernel\optimizer\commands\tablecmds.cpp
)
/*
* ALTER COLUMN TYPE
*/
static void ATPrepAlterColumnType(List** wqueue, AlteredTableInfo* tab, Relation rel, bool recurse, bool recursing,
AlterTableCmd* cmd, LOCKMODE lockmode)
{
char* colName = cmd->name; // 获取要修改的列名
ColumnDef* def = (ColumnDef*)cmd->def; // 获取列定义信息
TypeName* typname = def->typname; // 获取列的新类型名
Node* transform = def->raw_default; // 获取原始默认值
HeapTuple tuple; // 定义堆元组变量
Form_pg_attribute attTup; // 定义属性元组变量
AttrNumber attnum; // 定义属性编号变量
Oid targettype = InvalidOid; // 目标类型OID初始化为无效
int32 targettypmod = -1; // 目标类型修饰符初始化为-1
Oid targetcollid = InvalidOid; // 目标排序规则OID初始化为无效
int target_charset = PG_INVALID_ENCODING; // 目标字符集初始化为无效
NewColumnValue* newval = NULL; // 新列值初始化为空
ParseState* pstate = make_parsestate(NULL); // 创建解析状态
AclResult aclresult; // 定义权限检查结果变量
if (rel->rd_rel->reloftype && !recursing) // 如果是类型化表并且不在递归中,抛出错误
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot alter column type of typed table")));
// 查找属性元组以检查继承状态
tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
if (!HeapTupleIsValid(tuple)) // 如果属性元组无效,抛出错误
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist", colName, RelationGetRelationName(rel))));
attTup = (Form_pg_attribute)GETSTRUCT(tuple); // 获取属性元组结构
attnum = attTup->attnum; // 获取属性编号
if (attnum <= 0) // 如果是系统属性,抛出错误
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter system column \"%s\"", colName)));
if (attTup->attinhcount > 0 && !recursing) // 如果是继承属性并且不在递归中,抛出错误
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("cannot alter inherited column \"%s\"", colName)));
if (typname && list_length(typname->names) == 1 && !typname->pct_type) { // 如果类型名有效且长度为1并且不是百分比类型
char* tname = strVal(linitial(typname->names)); // 获取类型名
if (strcmp(tname, "smallserial") == 0 || strcmp(tname, "serial2") == 0 || strcmp(tname, "serial") == 0 ||
strcmp(tname, "serial4") == 0 || strcmp(tname, "bigserial") == 0 || strcmp(tname, "serial8") == 0 ||
strcmp(tname, "largeserial") == 0 || strcmp(tname, "serial16") == 0) // 如果类型名是某种serial类型,抛出错误
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("cannot alter column type to \"%s\"", tname)));
}
if (typname == NULL) { // 如果类型名为空,抛出错误
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("typname is null")));
}
typenameTypeIdAndMod(NULL, typname, &targettype, &targettypmod); // 查找目标类型ID和修饰符
// 查找目标排序规则
Oid rel_coll_oid = rel->rd_options == NULL ? InvalidOid : ((StdRdOptions*)(rel)->rd_options)->collate;
targetcollid = GetColumnDefCollation(NULL, def, targettype, rel_coll_oid);
if (DB_IS_CMPT(B_FORMAT)) { // 如果是B格式数据库,转换目标类型ID和排序规则
targettype = binary_need_transform_typeid(targettype, &targetcollid);
target_charset = get_charset_by_collation(targetcollid);
}
// 检查不支持的数据类型
if (RelationIsColStore(rel) && !IsTypeSupportedByCStore(targettype)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("type \"%s\" is not supported in column store",
format_type_with_typemod(targettype, targettypmod))));
}
aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE); // 检查类型使用权限
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, targettype);
// 确保数据类型对于列是合法的
CheckAttributeType(colName, targettype, targetcollid, list_make1_oid(rel->rd_rel->reltype), false);
if (tab->relkind == RELKIND_RELATION) { // 如果表的类型是普通表
if (IS_PGXC_COORDINATOR && !IsSystemRelation(rel) && !IsCStoreNamespace(rel->rd_rel->relnamespace)) {
HeapTuple tup = SearchSysCache(PGXCCLASSRELID, ObjectIdGetDatum(RelationGetRelid(rel)), 0, 0, 0);
if (!HeapTupleIsValid(tup)) // 如果缓存查找失败,抛出错误
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for pgxc_class %u", RelationGetRelid(rel))));
Form_pgxc_class pgxc_class = (Form_pgxc_class)GETSTRUCT(tup);
if ((pgxc_class->pclocatortype == 'H' || pgxc_class->pclocatortype == 'M' ||
pgxc_class->pclocatortype == 'L' || pgxc_class->pclocatortype == 'G') &&
IsDistribColumn(RelationGetRelid(rel), attnum))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter data type of distribute column")));
ReleaseSysCache(tup);
}
// 设置转换旧数据值到新类型的表达式
if (transform != NULL) {
RangeTblEntry* rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true); // 添加范围表项
addRTEtoQuery(pstate, rte, false, true, true); // 添加范围表项到查询中
transform = transformExpr(pstate, transform, EXPR_KIND_ALTER_COL_TRANSFORM); // 转换表达式
if (RelationIsColStore(rel)) { // 如果是列存储表
Bitmapset* attrs_referred = NULL;
pull_varattnos(transform, 1, &attrs_referred); // 收集表达式中引用的所有属性
if (attrs_referred != NULL) {
attrs_referred = bms_del_member(attrs_referred, attnum - FirstLowInvalidHeapAttributeNumber); // 移除自身
if (!bms_is_empty(attrs_referred)) {
bms_free_ext(attrs_referred);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot refer to other columns in transform expression for column store table")));
}
bms_free_ext(attrs_referred);
}
}
// 确保表达式不返回集合
if (expression_returns_set(transform))
ereport(
ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("transform expression must not return a set")));
// 确保没有子计划或聚合
if (pstate->p_hasSubLinks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use subquery in transform expression")));
if (pstate->p_hasAggs)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in transform expression")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR), errmsg("cannot use window function in transform expression")));
} else {
transform = (Node*)makeVar(1, attnum, attTup->atttypid, attTup->atttypmod, attTup->attcollation, 0); // 创建变量
}
transform = coerce_to_target_type(pstate,
transform,
exprType(transform),
targettype,
targettypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1); // 强制转换到目标类型
if (transform == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg(
"column \"%s\" cannot be cast automatically to type %s", colName, format_type_be(targettype)),
errhint("Specify a USING expression to perform the conversion.")));
#ifndef ENABLE_MULTIPLE_NODES
transform = coerce_to_target_charset(transform, target_charset, attTup->atttypid, attTup->atttypmod, targetcollid); // 强制转换到目标字符集
#endif
assign_expr_collations(pstate, transform); // 分配表达式排序规则
transform = (Node*)expression_planner((Expr*)transform); // 计划表达式
// 添加工作队列项以使ATRewriteTable更新列内容
newval = (NewColumnValue*)palloc0(sizeof(NewColumnValue));
newval->attnum = attnum;
newval->expr = (Expr*)transform;
newval->is_generated = false;
newval->is_autoinc = false;
newval->is_addloc = false;
newval->newattnum = 0;
newval->col_name = pstrdup(colName);
newval->generate_attnum = 0;
tab->newvals = lappend(tab->newvals, newval); // 添加新值到表
if (ATColumnChangeRequiresRewrite(transform, attnum)) // 如果列更改需要重写,设置重写标志
tab->rewrite = AT_REWRITE_COLUMN_REWRITE;
if (targettype != CLOBOID && targettype != BLOBOID) { // 检查巨大TOAST
CheckHugeToast(tab, rel, attnum);
}
} else if (transform != NULL)
ereport(
ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", RelationGetRelationName(rel))));
if (tab->relkind == RELKIND_COMPOSITE_TYPE || tab->relkind == RELKIND_FOREIGN_TABLE
|| tab->relkind == RELKIND_STREAM) {
find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL); // 查找复合类型依赖项
}
ReleaseSysCache(tuple); // 释放系统缓存
if (recurse) // 如果需要递归,调用简单递归函数
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
else if (!recursing && find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("type of inherited column \"%s\" must be changed in child tables too", colName)));
if (tab->relkind == RELKIND_COMPOSITE_TYPE)
ATTypedTableRecursion(wqueue, rel, cmd, lockmode); // 调用类型化表递归函数
}
我们这里重点来看一下列存相关的内容:
- 检查列存表是否支持目标数据类型
if (RelationIsColStore(rel) && !IsTypeSupportedByCStore(targettype)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("type \"%s\" is not supported in column store",
format_type_with_typemod(targettype, targettypmod))));
}
这部分代码用于检查列存表是否支持目标数据类型。如果不支持,则会报错。
- 设置转换表达式时处理列存表特有的限制
if (RelationIsColStore(rel)) {
Bitmapset* attrs_referred = NULL;
// 收集表达式中引用的所有属性
pull_varattnos(transform, 1, &attrs_referred);
if (attrs_referred != NULL) {
attrs_referred =
bms_del_member(attrs_referred, attnum - FirstLowInvalidHeapAttributeNumber); // 移除自身
if (!bms_is_empty(attrs_referred)) {
bms_free_ext(attrs_referred);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg(
"cannot refer to other columns in transform expression for column store table")));
}
bms_free_ext(attrs_referred);
}
}
这部分代码在设置转换表达式时,确保列存表的转换表达式不能引用其他列。如果引用了其他列,会报错。
ATExecAlterColumnType 函数
函数 ATExecAlterColumnType 在 OpenGauss 中用于执行 ALTER TABLE ALTER COLUMN TYPE
命令,具体实现了修改表中列的数据类型的操作。该函数首先获取要修改的列和目标数据类型的信息,然后检查是否存在分区键或其他不允许修改的条件。接下来,函数处理该列的默认值和依赖关系(如约束、索引等),确保所有相关的依赖关系能够正确更新或重建。随后,函数更新列的数据类型和相关属性(如类型修饰符、排序规则等),并在必要时重写列的数据。最后,函数处理新的默认值和约束,并返回该列的新对象地址。这个过程确保了数据类型变更操作的完整性和一致性。
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, LOCKMODE lockmode)
{
// 定义列名称
char* colName = cmd->name;
// 定义列的描述信息
ColumnDef* def = (ColumnDef*)cmd->def;
// 定义列的数据类型名称
TypeName* typname = def->typname;
// 定义HeapTuple类型的变量,用于存储系统缓存中检索到的元组
HeapTuple heapTup;
// 定义指向列属性结构体的指针,用于存储列的描述信息
Form_pg_attribute attTup;
// 定义列号
AttrNumber attnum;
// 定义HeapTuple类型的变量,用于存储数据类型元组
HeapTuple typeTuple;
// 定义指向类型描述结构体的指针
Form_pg_type tform;
// 定义目标数据类型的OID
Oid targettype = InvalidOid;
// 定义目标类型的修饰符
int32 targettypmod = -1;
// 定义目标类型的排序规则OID
Oid targetcollid = InvalidOid;
// 定义指向默认表达式节点的指针
Node* defaultexpr = NULL;
// 定义Relation类型的变量,用于存储属性关系
Relation attrelation;
// 定义Relation类型的变量,用于存储依赖关系
Relation depRel;
// 定义ScanKeyData数组,用于扫描依赖关系
ScanKeyData key[3];
// 定义SysScanDesc类型的变量,用于系统表扫描描述
SysScanDesc scan;
// 定义HeapTuple类型的变量,用于存储扫描到的依赖关系元组
HeapTuple depTup;
// 定义生成列的标志
char generatedCol = '\0';
// 定义指向更新表达式节点的指针
Node* update_expr = NULL;
// 定义删除更新时间戳的标志
bool flagDropOnUpdateTimestamp = false;
// 定义存在更新时间戳的标志
bool existOnUpdateTimestamp = false;
// 定义ObjectAddress类型的变量,用于存储对象地址
ObjectAddress address;
// 打开属性表
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
// 查找目标列
heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
if (!HeapTupleIsValid(heapTup)) // 如果找不到列
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist", colName, RelationGetRelationName(rel))));
// 获取列的描述信息
attTup = (Form_pg_attribute)GETSTRUCT(heapTup);
// 获取列号
attnum = attTup->attnum;
// 检查是否为分区表的分区键
if (RELATION_IS_PARTITIONED(rel) && is_partition_column(rel, attnum)) {
// 获取分区键
int2vector* partKey = ((RangePartitionMap*)rel->partMap)->partitionKey;
int i = 0;
// 遍历分区键,检查是否为分区列
for (; i < partKey->dim1; i++) {
if (attnum == partKey->values[i]) {
// 如果是分区列,抛出错误,不能修改分区列的数据类型
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter data type of partitioning column \"%s\"", colName)));
}
}
// 检查是否为子分区表
if ((rel)->rd_rel->parttype == PARTTYPE_SUBPARTITIONED_RELATION) {
// 获取父分区的分区列表
List *partTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_PARTITION, RelationGetRelid(rel));
if (partTupleList != NIL) {
bool isnull = false;
// 获取第一个分区元组
HeapTuple partTuple = (HeapTuple)linitial(partTupleList);
// 获取分区键
Datum datum = SysCacheGetAttr(PARTRELID, partTuple, Anum_pg_partition_partkey, &isnull);
if (!isnull) {
int2vector *subpartkey = (int2vector *)DatumGetPointer(datum);
// 遍历子分区键,检查是否为子分区列
for (int j = 0; j < subpartkey->dim1; j++) {
if (attnum == subpartkey->values[j]) {
// 如果是子分区列,抛出错误,不能修改子分区列的数据类型
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter data type of subpartitioning column \"%s\"", colName)));
}
}
}
// 释放分区列表
list_free_ext(partTupleList);
}
}
}
// 检查是否对同一列进行多次ALTER TYPE
if (!tab->is_first_after) {
if (attTup->atttypid != tab->oldDesc->attrs[attnum - 1].atttypid ||
attTup->atttypmod != tab->oldDesc->attrs[attnum - 1].atttypmod)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter type of column \"%s\" twice", colName)));
}
// 查找目标类型
typeTuple = typenameType(NULL, typname, &targettypmod);
tform = (Form_pg_type)GETSTRUCT(typeTuple);
targettype = HeapTupleGetOid(typeTuple);
// 查找目标类型的排序规则
Oid rel_coll_oid = rel->rd_options == NULL ? InvalidOid : ((StdRdOptions*)(rel)->rd_options)->collate;
targetcollid = GetColumnDefCollation(NULL, def, targettype, rel_coll_oid);
if (DB_IS_CMPT(B_FORMAT)) {
targettype = binary_need_transform_typeid(targettype, &targetcollid);
if (RelationIsColStore(rel) || RelationIsTsStore(rel)) {
check_unsupported_charset_for_column(targetcollid, colName);
}
}
if (attnum == RelAutoIncAttrNum(rel)) {
CheckAutoIncrementDatatype(targettype, colName);
}
generatedCol = GetGeneratedCol(rel->rd_att, attnum -1);
// 检查是否存在默认表达式
if (attTup->atthasdef) {
if (RelAutoIncAttrNum(rel) == attnum) {
defaultexpr = RecookAutoincAttrDefault(rel, attnum, targettype, targettypmod);
if (defaultexpr == NULL) {
if (generatedCol == ATTRIBUTE_GENERATED_STORED) {
ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
colName, format_type_be(targettype))));
} else {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("default for column \"%s\" cannot be cast automatically to type %s", colName,
format_type_be(targettype))));
}
}
} else {
defaultexpr = build_column_default(rel, attnum);
if (defaultexpr != NULL) {
defaultexpr = strip_implicit_coercions(defaultexpr);
defaultexpr = coerce_to_target_type(NULL,
defaultexpr,
exprType(defaultexpr),
targettype,
targettypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1);
if (defaultexpr == NULL) {
if (generatedCol == ATTRIBUTE_GENERATED_STORED) {
ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
colName, format_type_be(targettype))));
} else {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("default for column \"%s\" cannot be cast automatically to type %s", colName,
format_type_be(targettype))));
}
}
}
}
} else
defaultexpr = NULL;
// 查找列的依赖对象(如约束、索引等)
depRel = heap_open(DependRelationId, RowExclusiveLock);
// 初始化扫描键,用于查找依赖关系
ScanKeyInit(
&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationRelationId));
ScanKeyInit(
&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel)));
ScanKeyInit(&key[2], Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum((int32)attnum));
// 开始扫描依赖关系表
scan = systable_beginscan(depRel, DependReferenceIndexId, true, NULL, 3, key);
// 循环获取每一个依赖关系元组
while (HeapTupleIsValid(depTup = systable_getnext(scan))) {
// 获取依赖关系元组的结构体
Form_pg_depend foundDep = (Form_pg_depend)GETSTRUCT(depTup);
ObjectAddress foundObject;
// 检查依赖类型,如果是固定依赖,则抛出错误
if (foundDep->deptype == DEPENDENCY_PIN)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter type of a pinned column")));
// 设置找到的对象的classId、objectId和objectSubId
foundObject.classId = foundDep->classid;
foundObject.objectId = foundDep->objid;
foundObject.objectSubId = foundDep->objsubid;
// 根据对象的类别进行处理
switch (getObjectClass(&foundObject)) {
case OCLASS_CLASS: {
char relKind = get_rel_relkind(foundObject.objectId);
// 如果是索引或全局索引
if (relKind == RELKIND_INDEX || relKind == RELKIND_GLOBAL_INDEX) {
Assert(foundObject.objectSubId == 0);
Oid refobjid;
// 如果索引是约束的一部分
if (!list_member_oid(tab->changedConstraintOids, foundObject.objectId) &&
CheckIndexIsConstraint(depRel, foundObject.objectId, &refobjid)) {
tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids, refobjid);
tab->changedConstraintDefs =
lappend(tab->changedConstraintDefs, pg_get_constraintdef_string(refobjid));
// 如果索引不是约束的一部分
} else if (!list_member_oid(tab->changedIndexOids, foundObject.objectId)) {
LockRelationOid(foundObject.objectId, AccessExclusiveLock);
tab->changedIndexOids = lappend_oid(tab->changedIndexOids, foundObject.objectId);
tab->changedIndexDefs =
lappend(tab->changedIndexDefs, pg_get_indexdef_string(foundObject.objectId));
}
// 如果是序列
} else if (RELKIND_IS_SEQUENCE(relKind)) {
Assert(foundObject.objectSubId == 0);
// 如果是关系且是生成列
} else if (relKind == RELKIND_RELATION && foundObject.objectSubId != 0 &&
GetGenerated(foundObject.objectId, foundObject.objectSubId)) {
ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot alter type of a column used by a generated column"),
errdetail("Column \"%s\" is used by generated column \"%s\".", colName,
get_attname(foundObject.objectId, foundObject.objectSubId))));
// 处理其他关系类型
} else {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unexpected object depending on column: %s", getObjectDescription(&foundObject))));
}
break;
}
// 处理约束
case OCLASS_CONSTRAINT:
Assert(foundObject.objectSubId == 0);
if (!list_member_oid(tab->changedConstraintOids, foundObject.objectId)) {
char* defstring = pg_get_constraintdef_string(foundObject.objectId);
if (foundDep->deptype == DEPENDENCY_NORMAL) {
tab->changedConstraintOids = lcons_oid(foundObject.objectId, tab->changedConstraintOids);
tab->changedConstraintDefs = lcons(defstring, tab->changedConstraintDefs);
} else {
tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids, foundObject.objectId);
tab->changedConstraintDefs = lappend(tab->changedConstraintDefs, defstring);
}
}
break;
// 处理视图或规则
case OCLASS_REWRITE:
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter type of a column used by a view or rule"),
errdetail("%s depends on column \"%s\"", getObjectDescription(&foundObject), colName)));
break;
// 处理触发器
case OCLASS_TRIGGER:
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter type of a column used in a trigger definition"),
errdetail("%s depends on column \"%s\"", getObjectDescription(&foundObject), colName)));
break;
// 处理默认值
case OCLASS_DEFAULT:
break;
// 处理其他类型
case OCLASS_PROC:
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
case OCLASS_OPERATOR:
case OCLASS_OPCLASS:
case OCLASS_OPFAMILY:
case OCLASS_AMOP:
case OCLASS_AMPROC:
case OCLASS_SCHEMA:
case OCLASS_TSPARSER:
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_FDW:
case OCLASS_FOREIGN_SERVER:
case OCLASS_USER_MAPPING:
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_DATA_SOURCE:
case OCLASS_GLOBAL_SETTING_ARGS:
case OCLASS_GS_CL_PROC:
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unexpected object depending on column: %s", getObjectDescription(&foundObject))));
break;
// 处理未识别的对象类型
default:
ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized object class: %u", foundObject.classId)));
}
}
systable_endscan(scan);
DelDependencONDataType(rel, depRel, attTup);
heap_close(depRel, RowExclusiveLock);
// 修改列的类型和排序规则
attTup->atttypid = targettype;
attTup->atttypmod = targettypmod;
attTup->attcollation = targetcollid;
attTup->attndims = list_length(typname->arrayBounds);
attTup->attlen = tform->typlen;
attTup->attbyval = tform->typbyval;
attTup->attalign = tform->typalign;
attTup->attstorage = tform->typstorage;
ReleaseSysCache(typeTuple);
simple_heap_update(attrelation, &heapTup->t_self, heapTup);
CatalogUpdateIndexes(attrelation, heapTup);
heap_close(attrelation, RowExclusiveLock);
// 为新数据类型和排序规则添加依赖关系
add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
// 删除统计信息
if (RELATION_IS_GLOBAL_TEMP(rel)) {
remove_gtt_att_statistic(RelationGetRelid(rel), attnum);
} else {
RemoveStatistics<'c'>(RelationGetRelid(rel), attnum);
}
// 处理约束
if (cmd->subtype == AT_AlterColumnType && def->constraints && def->constraints->head) {
Constraint* temp_cons = (Constraint*)lfirst(def->constraints->head);
if (temp_cons->contype == CONSTR_DEFAULT && temp_cons->update_expr != NULL) {
update_expr = temp_cons->update_expr;
}
}
if (rel->rd_att->constr && rel->rd_att->constr->num_defval > 0) {
int ndef = rel->rd_att->constr->num_defval -1;
while (ndef >= 0 && rel->rd_att->constr->defval[ndef].adnum != attnum) {
--ndef;
}
if (ndef >= 0) {
if (pg_strcasecmp(rel->rd_att->constr->defval[ndef].adbin, "") == 0 &&
rel->rd_att->constr->defval[ndef].has_on_update) {
existOnUpdateTimestamp = true;
if (update_expr == NULL) {
CommandCounterIncrement();
RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true, true);
flagDropOnUpdateTimestamp = true;
}
}
}
}
if ((defaultexpr != NULL || update_expr != NULL) && !flagDropOnUpdateTimestamp) {
CommandCounterIncrement();
if (defaultexpr != NULL || (update_expr != NULL && existOnUpdateTimestamp)) {
RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true, true);
}
if (update_expr != NULL) {
ParseState* pstate = make_parsestate(NULL);
RangeTblEntry* rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
addRTEtoQuery(pstate, rte, true, true, true);
pstate->p_rawdefaultlist = NULL;
update_expr = cookDefault(pstate, update_expr, attTup->atttypid, attTup->atttypmod,
attTup->attcollation, NameStr(attTup->attname), def->generatedCol);
}
StoreAttrDefault(rel, attnum, defaultexpr, generatedCol, update_expr);
}
ObjectAddressSubSet(address, RelationRelationId,
RelationGetRelid(rel), attnum);
tableam_tops_free_tuple(heapTup);
return address;
}
在本文中,我们主要关注列存储相关的内容。以上代码中与列存储相关的部分包括以下几处:
1. 检查和处理列存储表的字符集:
// 检查如果表是列存储格式或时间序列存储格式,确保字符集是支持的
if (RelationIsColStore(rel) || RelationIsTsStore(rel)) {
check_unsupported_charset_for_column(targetcollid, colName);
}
2. 处理自动递增列的数据类型检查:
// 如果列是自动递增列,检查目标数据类型是否兼容
if (attnum == RelAutoIncAttrNum(rel)) {
CheckAutoIncrementDatatype(targettype, colName);
}
3. 处理生成列的类型转换检查:
// 获取生成列的标志
generatedCol = GetGeneratedCol(rel->rd_att, attnum - 1);
// 检查是否存在默认表达式
if (attTup->atthasdef) {
// 如果列是自动递增列,重新生成默认表达式
if (RelAutoIncAttrNum(rel) == attnum) {
defaultexpr = RecookAutoincAttrDefault(rel, attnum, targettype, targettypmod);
// 如果生成失败,检查是否是生成列,并报告错误
if (defaultexpr == NULL) {
if (generatedCol == ATTRIBUTE_GENERATED_STORED) {
ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
colName, format_type_be(targettype))));
} else {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("default for column \"%s\" cannot be cast automatically to type %s", colName,
format_type_be(targettype))));
}
}
}
}
4. 处理生成列的数据类型转换:
// 如果存在默认表达式,处理类型转换
if (attTup->atthasdef) {
// 构建列的默认表达式
defaultexpr = build_column_default(rel, attnum);
if (defaultexpr != NULL) {
// 去掉隐式转换
defaultexpr = strip_implicit_coercions(defaultexpr);
// 将默认表达式转换为目标类型
defaultexpr = coerce_to_target_type(NULL,
defaultexpr,
exprType(defaultexpr),
targettype,
targettypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1);
// 如果转换失败,检查是否是生成列,并报告错误
if (defaultexpr == NULL) {
if (generatedCol == ATTRIBUTE_GENERATED_STORED) {
ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
colName, format_type_be(targettype))));
} else {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("default for column \"%s\" cannot be cast automatically to type %s", colName,
format_type_be(targettype))));
}
}
}
}
build_column_default 函数
函数 build_column_default 的主要作用是为数据库表中的列创建一个默认值的表达式树。当列没有默认值时,返回 NULL。它首先检查列是否有定义的默认值,如果有,则将字符串形式的默认值转换为节点树。如果列没有默认值且不是生成列,则查找数据类型的默认值。然后,确保默认值被强制转换为目标列的类型。如果启用了自动截断功能且是插入操作,会对默认值进行显式转换。最后,如果表达式中包含 nextval 函数调用,会锁定引用的序列以避免死锁。函数源码如下所示:(路径:gausskernel\optimizer\rewrite\rewriteHandler.cpp
)
/*
* 为列的默认值创建表达式树。
*
* 如果没有默认值,则返回NULL。
* 添加一个输入参数isInsertCmd以指示当前语句是否为插入语句。
* 如果启用了自动截断功能且是插入语句,则使用此参数确定默认值是否需要显式转换。
*/
Node* build_column_default(Relation rel, int attrno, bool isInsertCmd, bool needOnUpdate)
{
// 获取关系的元组描述信息
TupleDesc rd_att = rel->rd_att;
// 获取列的属性元组
Form_pg_attribute att_tup = &rd_att->attrs[attrno - 1];
// 获取列的数据类型
Oid atttype = att_tup->atttypid;
// 获取列的数据类型修饰符
int32 atttypmod = att_tup->atttypmod;
// 定义用于存储表达式的指针
Node* expr = NULL;
// 定义表达式的类型
Oid exprtype;
/*
* 查看关系是否有此列的默认值。
*/
if (rd_att->constr && rd_att->constr->num_defval > 0) {
// 获取列的默认值数组
AttrDefault* defval = rd_att->constr->defval;
// 获取默认值的数量
int ndef = rd_att->constr->num_defval;
// 遍历默认值数组
while (--ndef >= 0) {
if (attrno == defval[ndef].adnum) {
/*
* 找到默认值,将字符串表示转换为节点树。
*
* isInsertCmd为false,has_on_update为true且adbin_on_update不为空字符串时,
* 将adbin_on_update转换为表达式。
* 如果adbin不为空字符串,则将adbin转换为表达式。
*/
if (needOnUpdate && (!isInsertCmd) && defval[ndef].adbin_on_update != nullptr &&
pg_strcasecmp(defval[ndef].adbin_on_update, "") != 0) {
expr = (Node*)stringToNode_skip_extern_fields(defval[ndef].adbin_on_update);
} else if (defval[ndef].adbin != nullptr && pg_strcasecmp(defval[ndef].adbin, "") != 0) {
expr = (Node*)stringToNode_skip_extern_fields(defval[ndef].adbin);
}
if (t_thrd.proc->workingVersionNum < LARGE_SEQUENCE_VERSION_NUM) {
(void)check_sequence_return_numeric_walker(expr, &(u_sess->opt_cxt.nextval_default_expr_type));
}
break;
}
}
}
// 如果没有找到默认值且不是生成列,则查找数据类型的默认值
if (expr == NULL && !ISGENERATEDCOL(rd_att, attrno - 1)) {
expr = get_typdefault(atttype);
}
// 如果仍然没有找到默认值,则返回NULL
if (expr == NULL)
return NULL;
// 如果是自动递增列,将表达式设为常量0
if (IsA(expr, AutoIncrement)) {
expr = (Node*)makeConst(INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(0), !att_tup->attnotnull, true);
}
/*
* 确保值被强制转换为目标列类型;这通常已经是这样,但在某些涉及域默认值的边缘情况中可能不是真实的。
* 这应该与解析器对非默认表达式的处理相匹配 --- 参见transformAssignedExpr()。
*/
exprtype = exprType(expr);
expr = coerce_to_target_type(NULL, /* 此处没有UNKNOWN参数 */
expr,
exprtype,
atttype,
atttypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1);
/*
* 当td_compatible_truncation设置为on时,这部分代码会将列默认值设置为显式转换参数,
* 以告知bpchar已经对该默认值添加了一个显式转换。
*/
if (u_sess->attr.attr_sql.td_compatible_truncation && u_sess->attr.attr_sql.sql_compatibility == C_FORMAT &&
isInsertCmd && (atttype == BPCHAROID || atttype == VARCHAROID) && expr != NULL) {
AssertEreport(IsA(expr, FuncExpr), MOD_OPT, "");
FuncExpr* fe = (FuncExpr*)expr;
Const* const_arg = (Const*)llast(fe->args);
if (IsA(const_arg, Const) && const_arg->consttype == BOOLOID)
const_arg->constvalue = (Datum) true;
}
// 如果表达式为空,报告错误
if (expr == NULL)
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" is of type %s but %s expression is of type %s", NameStr(att_tup->attname),
format_type_be(atttype), ISGENERATEDCOL(rd_att, attrno - 1) ? "generated column" : "deault",
format_type_be(exprtype)), errhint("You will need to rewrite or cast the expression.")));
/*
* 如果表达式中有nextval函数调用,应该锁定引用的序列以避免死锁,
* 这在transformFuncExpr中已经完成。详见lockNextvalOnCn。
*/
(void)lockNextvalWalker(expr, NULL);
return expr;
}