当前位置: 首页 > 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…...

【大数据 复习】第3章 分布式文件系统HDFS(重中之重)

一、概念 1.分布式文件系统把文件分布存储到多个计算机节点上&#xff0c;通过网络实现、文件在多台主机上进行分布式存储的文件系统。&#xff08;就是你的电脑存a&#xff0c;我的电脑存pple&#xff09; 2.降低了硬件开销: 与之前使用多个处理器和专用高级硬件的并行化处理装…...

element-ui里message抖动问题

由于element默认屏蔽滚动条&#xff0c;导致取消时弹message时 侧边滚动栏突然回来后引起抖动问题 是由于打开弹窗时出现遮罩层dialog对话框 时引起了元素内容超出自身尺寸 对应的overflow样式内容为hidden&#xff0c;且新建了一个class类内容为增加17 内右边距&#xff0c;当…...

Attention系列总结-粘贴自知乎

1. 梦想做个翟老师&#xff1a;阿里&#xff1a;Behavior Sequence Transformer 解读48 赞同 7 评论文章 优点:捕捉用户行为历史序列中的顺序信息。w2v也是捕捉用户序列信息的,本质差异在于啥&#xff1f; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff0…...

swagger下载文件名中文乱码、swagger导出文件名乱码、swagger文件导出名称乱码、解决swagger中文下载乱码bug

文章目录 一、场景描述&#xff1a;swagger导出文件名称乱码二、乱码原因三、解决方法3.1、方法一、在浏览器中输入地址下载3.2、方法二、swagger升级为2.10.0及以上 四、可能遇到的问题4.1、DocumentationPluginsManager.java:152 一、场景描述&#xff1a;swagger导出文件名称…...

191.回溯算法:组合总和|||(力扣)

代码解决 class Solution { public:vector<vector<int>> result; // 存储所有符合条件的组合vector<int> res; // 当前组合// 回溯函数void backtracing(int k, int n, int index, int sum) {// 如果当前组合的长度等于k&#xff0c;且总和等于nif (res.si…...

JupyterLab使用指南(二):JupyterLab基础

第2章 JupyterLab基础 2.1 JupyterLab界面介绍 JupyterLab的用户界面非常直观和灵活。它包括文件浏览器、工作区、多标签页、命令面板和侧边栏等功能。以下是各个部分的详细介绍&#xff1a; 2.1.1 文件浏览器 文件浏览器位于界面左侧&#xff0c;用于导航和管理文件。你可…...

ubuntu18.04 + openssl + engine + pkcs11+ softhsm2 双向认证测试

安装环境 openssl 1.1.1 pkcs11-tool &#xff08;由sudo apt-get install opensc 安装&#xff09; libpksc11 &#xff08;需源码安装apt install 只有libp11, 源码安装才有 libpksc11.so -> pkcs11.so&#xff09; softhsm2 &#xff08;由sudo apt-get install softhsm…...

【C++】类和对象2.0

俺来写笔记了&#xff0c;哈哈哈&#xff0c;浅浅介绍类和对象的知识点&#xff01; 1.类的6个默认成员函数 俺们定义一个空类&#xff1a; class N {}; 似乎这个类N里面什么都没有&#xff0c;其实不是这样子的。这个空类有6个默认的成员函数 。 默认成员函数&#xff1a…...

【LLM之KG】KoPA论文阅读笔记

研究背景 知识图谱补全&#xff08;KGC&#xff09;是通过预测知识图谱中缺失的三元组来完善知识图谱的信息。传统方法主要基于嵌入和预训练语言模型&#xff0c;但这些方法往往忽视了知识图谱的结构信息&#xff0c;导致预测效果不佳。 研究目标 本文的研究目标是探索如何将…...

UI设计速成课:理解模态窗口与非模态窗口的区别

我们日常所说的弹性框架是非常笼统的概念。我们习惯性地称之为对话框架、浮动层和提示条。弹性框架可以分为两种:模态弹性框架和非模态弹性框架。产品需要弹性框架来传递信息&#xff0c;用户需要弹性框架来接受反馈&#xff0c;但是没有经过推敲的弹出窗口设计很容易让用户感到…...

【Linux】基础IO_4

文章目录 六、基础I/O4. 动静态库 未完待续 六、基础I/O 4. 动静态库 既然我们能够成功创建静态库了&#xff0c;接下来我们将这个代码打包成动态库&#xff1a; shared: 表示生成共享库格式 fPIC&#xff1a;产生位置无关码(position independent code) 动态库库名规则&…...

C++模板类原理讲解

C模板类原理讲解 C模板是一种强大的编译期工具&#xff0c;它允许我们创建通用的、类型无关的类和函数。模板的主要目的是实现代码的重用和泛型编程。模板类的原理涉及以下几个方面&#xff1a; 模板的定义和实例化模板的类型参数模板特化模板的编译过程模板的优点和缺点 1.…...

scratch编程03-反弹球

这篇文章和上一篇文章《scratch3编程02-使用克隆来编写小游戏》类似&#xff08;已经完全掌握了克隆的可以忽略这篇文章&#xff09;&#xff0c;两篇文章都使用到了克隆来编写一个小游戏&#xff0c;这篇文章与上篇文章不同的是&#xff0c;本体在进行克隆操作时&#xff0c;不…...

postgresql数据库进阶知识

postgresql数据库进阶知识 # 如果表存在就先删除 drop table if exists student; # 创建学生表 # id serial not null 表示id自增 # id integer not null 表示id不自增 create table student (id serial not nullconstraint student_pkprimary…...

关于HTTP劫持,该如何理解、防范和应对

一、引言 HTTP劫持&#xff08;HTTP Hijacking&#xff09;是一种网络安全威胁&#xff0c;它发生在HTTP通信过程中&#xff0c;攻击者试图通过拦截、篡改或监控用户与服务器之间的数据流量&#xff0c;以达到窃取敏感信息或执行恶意操作的目的。今天我们就来详细了解HTTP劫持…...

System.Data.OracleClient.OracleException:“ORA-12571: TNS: 包写入程序失败

System.Data.OracleClient.OracleException:“ORA-12571: TNS: 包写入程序失败 解决方法&#xff1a; 首先%oracle_home%/network/admin下的sqlnet.ora文件&#xff0c;把SQLNET.AUTHENTICATION_SERVICES (NTS)加个 # 注释掉就好了...

saas产品运营案例 | 联盟营销计划如何帮助企业提高销售额?

在当今数字化时代&#xff0c;SaaS&#xff08;软件即服务&#xff09;产品已成为企业提高效率、降低成本的重要工具。然而&#xff0c;面对激烈的市场竞争&#xff0c;如何有效地推广SaaS产品、提高销售额&#xff0c;成为许多企业面临的挑战。林叔将以ClickFunnels为例&#…...

模式分解算法-满足3NF的无损且保持函数依赖的分解算法、满足BCNF的无损连接分解算法

一、引言 1、对指定的关系模式&#xff0c;若范式级别较低&#xff0c;为第一范式或第二范式&#xff0c;由于存在数据冗余或更新异常问题&#xff0c;在实际中一般是不可用的&#xff0c;关系模式的规范化就是将满足低一级的关系模式分解为若干满足高一级范式的关系模式的集合…...

荷兰与法国战平,双方能携手出现?

就在昨天晚上&#xff0c;荷兰队经历了90分钟的鏖战&#xff0c;最终0-0与法国队握手言和。此役&#xff0c;哈维-西蒙斯为荷兰队打进一球&#xff0c;但进球被判无效。从目前的积分形势来看&#xff0c;双方基本上确定携手晋级16强赛。本场比赛&#xff0c;荷兰队后卫内森-阿克…...

数据可视化实验二:回归分析、判别分析与聚类分析

目录 一、使用回归分析方法分析某病毒是否与温度呈线性关系 1.1 代码实现 1.2 线性回归结果 1.3 相关系数验证 二、使用判别分析方法预测某病毒在一定的温度下是否可以存活&#xff0c;分别使用三种判别方法&#xff0c;包括Fish判别、贝叶斯判别、LDA 2.1 数据集展示&am…...

FL论文专栏|设备异构、异步联邦

论文&#xff1a;Asynchronous Federated Optimization&#xff08;12th Annual Workshop on Optimization for Machine Learning&#xff09; 链接 实现Server的异步更新。每次Server广播全局Model的时候附带一个时间戳&#xff0c;Client跑完之后上传将时间戳和Model同时带回…...

【Java毕业设计】基于JavaWeb的礼服租赁系统

文章目录 摘 要Abstract目录1 绪论1.1 课题背景和意义1.2 国内外研究现状1.2.1 国外研究现状 1.3 课题主要内容 2 开发相关技术介绍2.1 Spring Boot框架2.2 Vue框架2.3 MySQL数据库2.4 Redis数据库 3 系统分析3.1 需求分析3.1.1 用户需求分析3.1.2 功能需求分析 3.2 可行性分析…...

代码随想录训练营Day 66|卡码网101.孤岛的总面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿

1.孤岛的总面积 101. 孤岛的总面积 | 代码随想录 代码&#xff1a;(bfs广搜) #include <iostream> #include <vector> #include <queue> using namespace std; int dir[4][2] {1,0,0,1,-1,0,0,-1}; int count; void bfs(vector<vector<int>>&a…...

根据状态转移写状态机-二段式

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 描述 题目描述&#xff1a; 如图所示为两种状态机中的一种&#xff0c;请根据状态转移图写出代码&#xff0c;状态转移线上的0/0等表示的意思是过程中data/flag的值。 要求&#xff1a; 1、 必须使用对应类型的状…...

PyTorch C++扩展用于AMD GPU

PyTorch C Extension on AMD GPU — ROCm Blogs 本文演示了如何使用PyTorch C扩展&#xff0c;并通过示例讨论了它相对于常规PyTorch模块的优势。实验在AMD GPU和ROCm 5.7.0软件上进行。有关支持的GPU和操作系统的更多信息&#xff0c;请参阅系统要求&#xff08;Linux&#xf…...

Hadoop archive

Index of /dist/hadoop/commonhttps://archive.apache.org/dist/hadoop/common/...

R语言——R语言基础

1、用repeat、for、while计算从1-10的所有整数的平方和 2、编写一个函数&#xff0c;给出两个正整数&#xff0c;计算他们的最小公倍数 3、编写一个函数&#xff0c;让用户输入姓名、年龄&#xff0c;得出他明年的年龄。用paste打印出来。例如&#xff1a;"Hi xiaoming …...

VFB电压反馈和CFB电流反馈运算放大器(运放)选择指南

VFB电压反馈和CFB电流反馈运算放大器(运放)选择指南 电流反馈和电压反馈具有不同的应用优势。在很多应用中&#xff0c;CFB和VFB的差异并不明显。当今的许多高速CFB和VFB放大器在性能上不相上下&#xff0c;但各有其优缺点。本指南将考察与这两种拓扑结构相关的重要考虑因素。…...

elasticsearch安装(centos7)

先给出网址 elasticsearch&#xff1a;Download Elasticsearch | Elastic elasticKibana&#xff1a;Download Kibana Free | Get Started Now | Elastic Logstash&#xff1a;Download Logstash Free | Get Started Now | Elastic ik分词&#xff1a;Releases infinilabs/…...

Java高手的30k之路|面试宝典|精通JVM(二)

JVM基本结构 类加载子系统&#xff1a;负责将.class文件加载到内存中&#xff0c;并进行验证、准备、解析和初始化。运行时数据区&#xff1a;包括堆&#xff08;Heap&#xff09;、方法区&#xff08;Method Area&#xff09;、Java栈&#xff08;Java Stack&#xff09;、本…...