当前位置: 首页 > news >正文

【OpenGauss源码学习 —— (ALTER TABLE(列存修改列类型))】

ALTER TABLE(列存修改列类型)

  • ATExecAlterColumnType 函数
        • 1. 检查和处理列存储表的字符集:
        • 2. 处理自动递增列的数据类型检查:
        • 3. 处理生成列的类型转换检查:
        • 4. 处理生成列的数据类型转换:
    • build_column_default 函数

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

  在数据库管理中,改变表中列的类型是一个常见的操作。这种操作通常通过ALTER TABLE ALTER COLUMN TYPE语句来实现,它允许数据库管理员在不删除和重新创建表的情况下更改列的数据类型。这种操作需要确保数据的正确性和一致性,并且在某些情况下,还需要重写表中的数据来适应新的数据类型。
  在 OpenGauss 中,函数 ATExecAlterColumnType 用于执行这种列类型的更改。这个函数负责处理列类型变更的所有细节,包括检查新类型的兼容性、处理默认值和约束、以及在必要时重写表的数据。该函数确保在类型更改过程中不会破坏表的完整性和性能。本文将围绕 ATExecAlterColumnType 函数来展开学习列存储修改列类型的过程。

ATExecAlterColumnType 函数

  函数 ATExecAlterColumnTypeOpenGauss 中用于执行 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;// 定义目标数据类型的OIDOid targettype = InvalidOid;// 定义目标类型的修饰符int32 targettypmod = -1;// 定义目标类型的排序规则OIDOid 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 TYPEif (!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))));}}}}} elsedefaultexpr = 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和objectSubIdfoundObject.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);}// 如果仍然没有找到默认值,则返回NULLif (expr == NULL)return NULL;// 如果是自动递增列,将表达式设为常量0if (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;
}

相关文章:

【OpenGauss源码学习 —— (ALTER TABLE(列存修改列类型))】

ALTER TABLE&#xff08;列存修改列类型&#xff09; ATExecAlterColumnType 函数1. 检查和处理列存储表的字符集&#xff1a;2. 处理自动递增列的数据类型检查&#xff1a;3. 处理生成列的类型转换检查&#xff1a;4. 处理生成列的数据类型转换&#xff1a; build_column_defa…...

【大数据 复习】第7章 MapReduce(重中之重)

一、概念 1.MapReduce 设计就是“计算向数据靠拢”&#xff0c;而不是“数据向计算靠拢”&#xff0c;因为移动&#xff0c;数据需要大量的网络传输开销。 2.Hadoop MapReduce是分布式并行编程模型MapReduce的开源实现。 3.特点 &#xff08;1&#xff09;非共享式&#xff0c;…...

Zookeeper:节点

文章目录 一、节点类型二、监听器及节点删除三、创建节点四、监听节点变化五、判断节点是否存在 一、节点类型 持久&#xff08;Persistent&#xff09;&#xff1a;客户端和服务器端断开连接后&#xff0c;创建的节点不删除。 持久化目录节点&#xff1a;客户端与Zookeeper断…...

生产级别的 vue

生产级别的 vue 拆分组件的标识更好的组织你的目录如何解决 props-base 设计的问题transparent component &#xff08;透明组件&#xff09;可减缓上述问题provide 和 inject vue-meta 在路由中的使用如何确保用户导航到某个路由自己都重新渲染&#xff1f;测试最佳实践如何制…...

kafka(五)spring-kafka(1)集成方法

一、集成 1、pom依赖 <!--kafka--><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></dependency><dependency><groupId>org.springframework.kafka</groupId><artif…...

Java中的设计模式深度解析

Java中的设计模式深度解析 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在软件开发领域&#xff0c;设计模式是一种被广泛应用的经验总结和解决方案&#x…...

鸿蒙 HarmonyOS NEXT星河版APP应用开发—上篇

一、鸿蒙开发环境搭建 DevEco Studio安装 下载 访问官网&#xff1a;https://developer.huawei.com/consumer/cn/deveco-studio/选择操作系统版本后并注册登录华为账号既可下载安装包 安装 建议&#xff1a;软件和依赖安装目录不要使用中文字符软件安装包下载完成后&#xff0…...

[FreeRTOS 基础知识] 互斥访问与回环队列 概念

文章目录 为什么需要互斥访问&#xff1f;使用队列实现互斥访问休眠和唤醒机制环形缓冲区 为什么需要互斥访问&#xff1f; 在裸机中&#xff0c;假设有两个函数&#xff08;func_A, func_B&#xff09;都要修改a的值&#xff08;a&#xff09;&#xff0c;那么将a定义为全局变…...

音视频的Buffer处理

最近在做安卓下UVC的一个案子。正好之前搞过ST方案的开机广告&#xff0c;这个也是我少数最后没搞成功的项目。当时也有点客观原因&#xff0c;当时ST要退出机顶盒市场&#xff0c;所以一切的支持都停了&#xff0c;当时啃他家播放器几十万行的代码&#xff0c;而且几乎没有文档…...

【总结】攻击 AI 模型的方法

数据投毒 污染训练数据 后门攻击 通过设计隐蔽的触发器&#xff0c;使得模型在正常测试时无异常&#xff0c;而面对触发器样本时被操纵输出。后门攻击可以看作是特殊的数据投毒&#xff0c;但是也可以通过修改模型参数来实现 对抗样本 只对输入做微小的改动&#xff0c;使模型…...

Linux配置中文环境

文章目录 前言中文语言包中文输入法中文字体 前言 在Linux系统中修改为中文环境&#xff0c;通常涉及以下几个步骤&#xff1a; 中文语言包 更新源列表&#xff1a; 更新系统的软件源列表和语言环境设置&#xff0c;确保可以安装所需的语言包。 sudo apt update sudo apt ins…...

深入解析 iOS 应用启动过程:main() 函数前的四大步骤

深入解析 iOS 应用启动过程&#xff1a;main() 函数前的四大步骤 背景描述&#xff1a;使用 Objective-C 开发的 iOS 或者 MacOS 应用 在开发 iOS 应用时&#xff0c;我们通常会关注 main() 函数及其之后的执行逻辑&#xff0c;但在 main() 函数之前&#xff0c;系统已经为我们…...

textarea标签改写为富文本框编辑器KindEditor

下载 - KindEditor - 在线HTML编辑器 KindEditor的简单使用-CSDN博客 一、 Maven需要的依赖&#xff1a; 如果依赖无法下载&#xff0c;可以多添加几个私服地址&#xff1a; 在Maven框架中加入镜像私服 <mirrors><!-- mirror| Specifies a repository mirror site to…...

高通安卓12-Input子系统

1.Input输入子系统架构 Input Driver(Input设备驱动层)->Input core(输入子系统核心层)->Event handler(事件处理层)->User space(用户空间) 2.getevent获取Input事件的用法 getevent 指令用于获取android系统中 input 输入事件&#xff0c;比如获取按键上报信息、获…...

HTML 事件

HTML 事件 HTML 事件是发生在 HTML 元素上的交互瞬间,它们可以由用户的行为(如点击、按键、鼠标移动等)或浏览器自身的行为(如页面加载完成、图片加载失败等)触发。在 HTML 和 JavaScript 的交互中,事件扮演着核心角色,允许开发者创建动态和响应式的网页。 常见的 HTM…...

Mysql 官方提供的公共测试数据集 Example Databases

数据集&#xff1a;GitHub - datacharmer/test_db: A sample MySQL database with an integrated test suite, used to test your applications and database servers 下载 test_db: https://github.com/datacharmer/test_db/releases/download/v1.0.7/test_db-1.0.7.tar.gz …...

Docker 下载与安装以及配置

安装yum工具 yum install -y yum-ulits配置yum源 阿里云源 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装Docker 17.03后为两个版本&#xff1a; 社区版&#xff08;Community Edition&#xff0c;缩写为 CE&#x…...

Java中的集合框架详解:List、Set、Map的使用场景

Java中的集合框架详解&#xff1a;List、Set、Map的使用场景 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;集合框架是一个非常重要…...

[Django学习]前端+后端两种方式处理图片流数据

方式1&#xff1a;数据库存放图片地址,图片存放在Django项目文件中 1.首先&#xff0c;我们现在models.py文件中定义模型来存放该图片数据,前端传来的数据都会存放在Django项目文件里的images文件夹下 from django.db import modelsclass Image(models.Model):title models.C…...

如何配置IOMMU或者SWIOTLB

1. 前言 这篇文章说明了如何在Linux内核中启用和配置IOMMU和SWOTLB。 当今的计算或者嵌入设备使用一种内存分区的方法进行外设的管理&#xff0c;如显卡、PCI设备或USB设备&#xff0c;都将设备映射为一段内存&#xff0c;用于设备的读写。 传统意义上的IOMMU用于内存映射&a…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

Linux基础开发工具——vim工具

文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...

Linux操作系统共享Windows操作系统的文件

目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项&#xff0c;设置文件夹共享为总是启用&#xff0c;点击添加&#xff0c;可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download&#xff08;这是我共享的文件夹&#xff09;&…...

工厂方法模式和抽象工厂方法模式的battle

1.案例直接上手 在这个案例里面&#xff0c;我们会实现这个普通的工厂方法&#xff0c;并且对比这个普通工厂方法和我们直接创建对象的差别在哪里&#xff0c;为什么需要一个工厂&#xff1a; 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类&#xff1a; 两个发…...