【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 函数
函数 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;// 定义目标数据类型的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(列存修改列类型) ATExecAlterColumnType 函数1. 检查和处理列存储表的字符集:2. 处理自动递增列的数据类型检查:3. 处理生成列的类型转换检查:4. 处理生成列的数据类型转换: build_column_defa…...
【大数据 复习】第7章 MapReduce(重中之重)
一、概念 1.MapReduce 设计就是“计算向数据靠拢”,而不是“数据向计算靠拢”,因为移动,数据需要大量的网络传输开销。 2.Hadoop MapReduce是分布式并行编程模型MapReduce的开源实现。 3.特点 (1)非共享式,…...
Zookeeper:节点
文章目录 一、节点类型二、监听器及节点删除三、创建节点四、监听节点变化五、判断节点是否存在 一、节点类型 持久(Persistent):客户端和服务器端断开连接后,创建的节点不删除。 持久化目录节点:客户端与Zookeeper断…...
生产级别的 vue
生产级别的 vue 拆分组件的标识更好的组织你的目录如何解决 props-base 设计的问题transparent component (透明组件)可减缓上述问题provide 和 inject vue-meta 在路由中的使用如何确保用户导航到某个路由自己都重新渲染?测试最佳实践如何制…...
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中的设计模式深度解析 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在软件开发领域,设计模式是一种被广泛应用的经验总结和解决方案&#x…...
鸿蒙 HarmonyOS NEXT星河版APP应用开发—上篇
一、鸿蒙开发环境搭建 DevEco Studio安装 下载 访问官网:https://developer.huawei.com/consumer/cn/deveco-studio/选择操作系统版本后并注册登录华为账号既可下载安装包 安装 建议:软件和依赖安装目录不要使用中文字符软件安装包下载完成后࿰…...
[FreeRTOS 基础知识] 互斥访问与回环队列 概念
文章目录 为什么需要互斥访问?使用队列实现互斥访问休眠和唤醒机制环形缓冲区 为什么需要互斥访问? 在裸机中,假设有两个函数(func_A, func_B)都要修改a的值(a),那么将a定义为全局变…...
音视频的Buffer处理
最近在做安卓下UVC的一个案子。正好之前搞过ST方案的开机广告,这个也是我少数最后没搞成功的项目。当时也有点客观原因,当时ST要退出机顶盒市场,所以一切的支持都停了,当时啃他家播放器几十万行的代码,而且几乎没有文档…...
【总结】攻击 AI 模型的方法
数据投毒 污染训练数据 后门攻击 通过设计隐蔽的触发器,使得模型在正常测试时无异常,而面对触发器样本时被操纵输出。后门攻击可以看作是特殊的数据投毒,但是也可以通过修改模型参数来实现 对抗样本 只对输入做微小的改动,使模型…...
Linux配置中文环境
文章目录 前言中文语言包中文输入法中文字体 前言 在Linux系统中修改为中文环境,通常涉及以下几个步骤: 中文语言包 更新源列表: 更新系统的软件源列表和语言环境设置,确保可以安装所需的语言包。 sudo apt update sudo apt ins…...
深入解析 iOS 应用启动过程:main() 函数前的四大步骤
深入解析 iOS 应用启动过程:main() 函数前的四大步骤 背景描述:使用 Objective-C 开发的 iOS 或者 MacOS 应用 在开发 iOS 应用时,我们通常会关注 main() 函数及其之后的执行逻辑,但在 main() 函数之前,系统已经为我们…...
textarea标签改写为富文本框编辑器KindEditor
下载 - KindEditor - 在线HTML编辑器 KindEditor的简单使用-CSDN博客 一、 Maven需要的依赖: 如果依赖无法下载,可以多添加几个私服地址: 在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 输入事件,比如获取按键上报信息、获…...
HTML 事件
HTML 事件 HTML 事件是发生在 HTML 元素上的交互瞬间,它们可以由用户的行为(如点击、按键、鼠标移动等)或浏览器自身的行为(如页面加载完成、图片加载失败等)触发。在 HTML 和 JavaScript 的交互中,事件扮演着核心角色,允许开发者创建动态和响应式的网页。 常见的 HTM…...
Mysql 官方提供的公共测试数据集 Example Databases
数据集: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后为两个版本: 社区版(Community Edition,缩写为 CE&#x…...
Java中的集合框架详解:List、Set、Map的使用场景
Java中的集合框架详解:List、Set、Map的使用场景 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在Java编程中,集合框架是一个非常重要…...
[Django学习]前端+后端两种方式处理图片流数据
方式1:数据库存放图片地址,图片存放在Django项目文件中 1.首先,我们现在models.py文件中定义模型来存放该图片数据,前端传来的数据都会存放在Django项目文件里的images文件夹下 from django.db import modelsclass Image(models.Model):title models.C…...
如何配置IOMMU或者SWIOTLB
1. 前言 这篇文章说明了如何在Linux内核中启用和配置IOMMU和SWOTLB。 当今的计算或者嵌入设备使用一种内存分区的方法进行外设的管理,如显卡、PCI设备或USB设备,都将设备映射为一段内存,用于设备的读写。 传统意义上的IOMMU用于内存映射&a…...
【大数据 复习】第3章 分布式文件系统HDFS(重中之重)
一、概念 1.分布式文件系统把文件分布存储到多个计算机节点上,通过网络实现、文件在多台主机上进行分布式存储的文件系统。(就是你的电脑存a,我的电脑存pple) 2.降低了硬件开销: 与之前使用多个处理器和专用高级硬件的并行化处理装…...
element-ui里message抖动问题
由于element默认屏蔽滚动条,导致取消时弹message时 侧边滚动栏突然回来后引起抖动问题 是由于打开弹窗时出现遮罩层dialog对话框 时引起了元素内容超出自身尺寸 对应的overflow样式内容为hidden,且新建了一个class类内容为增加17 内右边距,当…...
Attention系列总结-粘贴自知乎
1. 梦想做个翟老师:阿里:Behavior Sequence Transformer 解读48 赞同 7 评论文章 优点:捕捉用户行为历史序列中的顺序信息。w2v也是捕捉用户序列信息的,本质差异在于啥? 添加图片注释,不超过 140 字(可选࿰…...
swagger下载文件名中文乱码、swagger导出文件名乱码、swagger文件导出名称乱码、解决swagger中文下载乱码bug
文章目录 一、场景描述:swagger导出文件名称乱码二、乱码原因三、解决方法3.1、方法一、在浏览器中输入地址下载3.2、方法二、swagger升级为2.10.0及以上 四、可能遇到的问题4.1、DocumentationPluginsManager.java:152 一、场景描述:swagger导出文件名称…...
191.回溯算法:组合总和|||(力扣)
代码解决 class Solution { public:vector<vector<int>> result; // 存储所有符合条件的组合vector<int> res; // 当前组合// 回溯函数void backtracing(int k, int n, int index, int sum) {// 如果当前组合的长度等于k,且总和等于nif (res.si…...
JupyterLab使用指南(二):JupyterLab基础
第2章 JupyterLab基础 2.1 JupyterLab界面介绍 JupyterLab的用户界面非常直观和灵活。它包括文件浏览器、工作区、多标签页、命令面板和侧边栏等功能。以下是各个部分的详细介绍: 2.1.1 文件浏览器 文件浏览器位于界面左侧,用于导航和管理文件。你可…...
ubuntu18.04 + openssl + engine + pkcs11+ softhsm2 双向认证测试
安装环境 openssl 1.1.1 pkcs11-tool (由sudo apt-get install opensc 安装) libpksc11 (需源码安装apt install 只有libp11, 源码安装才有 libpksc11.so -> pkcs11.so) softhsm2 (由sudo apt-get install softhsm…...
【C++】类和对象2.0
俺来写笔记了,哈哈哈,浅浅介绍类和对象的知识点! 1.类的6个默认成员函数 俺们定义一个空类: class N {}; 似乎这个类N里面什么都没有,其实不是这样子的。这个空类有6个默认的成员函数 。 默认成员函数:…...
【LLM之KG】KoPA论文阅读笔记
研究背景 知识图谱补全(KGC)是通过预测知识图谱中缺失的三元组来完善知识图谱的信息。传统方法主要基于嵌入和预训练语言模型,但这些方法往往忽视了知识图谱的结构信息,导致预测效果不佳。 研究目标 本文的研究目标是探索如何将…...
UI设计速成课:理解模态窗口与非模态窗口的区别
我们日常所说的弹性框架是非常笼统的概念。我们习惯性地称之为对话框架、浮动层和提示条。弹性框架可以分为两种:模态弹性框架和非模态弹性框架。产品需要弹性框架来传递信息,用户需要弹性框架来接受反馈,但是没有经过推敲的弹出窗口设计很容易让用户感到…...
建站合同模板/成都黑帽seo
Print也许是Python中使用频率最高的一个函数。很多小白都是从Hello World程序开始认识Python,而Python的Hello World程序只有一行,那就是调用内置的Print函数,向控制台输出字符串“Hello World”。 不仅小白,哪怕是Python开发者&a…...
手机网站图片自适应代码/关键词优化排名
文章目录一、对《青春有你2》的参赛选手区域进行可视化分析二、爬《青春有你2》的参赛选手体重进行可视化分析一、对《青春有你2》的参赛选手区域进行可视化分析 """ 对《青春有你2》的参赛选手区域进行可视化分析 """ import matplotlib.pyplo…...
阿里云服务器部署网站/营业推广策略
AOAPC I: Beginning Algorithm Contests (Rujia Liu) :: Volume 1. Elementary Problem Solving StringBig NumberSorting/SearchingMaths - MiscMaths - Number TheoryMaths - Simple Geometry...
网站建设与维护txt下载/seo网络推广到底是做什么的
最近想学习一下iOS开发,不过一点基础都没有,得先从熟悉object-C语法开始,在此把学习中的一些知识点作为记录。 mac pro常用操作 学习ios【1】Objective-C 基本语法 学习ios【2】Objective-C 数字和字符串 学习iOS【3】数组、词典和集合 本文转…...
wordpress閱讀主题/软文营销经典案例200字
概念字符集的内容包含:字符集(character set)和排序规则(collation rule)每种字符集可对应一到多个排序规则,每种排序规则对应一种字符集字符集是一套字符与一套编码的映射集合,像这样:排序规则是字符集内用来比较每个字符的一套规…...
网站开发的账务处理/seo数据
题意 给你n种长方体 每种都有无穷个 当一个长方体的长和宽都小于还有一个时 这个长方体能够放在还有一个上面 要求输出这样累积起来的最大高度 由于每一个长方体都有3种放法 比較不好控制 能够把一个长宽高分成三个长方体 高度是固定的 这样…...