diff --git a/doc/src/sgml/ref/create_type.sgmlin b/doc/src/sgml/ref/create_type.sgmlin index 3d445d22f598447611ca11fbeb6213647af140d5..86fb5eed9dec6f8cc9aa5a2164db702a66e9e0d6 100755 --- a/doc/src/sgml/ref/create_type.sgmlin +++ b/doc/src/sgml/ref/create_type.sgmlin @@ -16,7 +16,7 @@ -CREATE TYPE name AS +CREATE [ OR REPLACE ] TYPE name AS ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] ) CREATE TYPE name AS ENUM @@ -48,7 +48,7 @@ CREATE TYPE name CREATE TYPE name AS ENUM ( [ 'lable' [, ... ] ] ) -CREATE TYPE name AS TABLE OF data_type +CREATE [ OR REPLACE ] TYPE name AS TABLE OF data_type diff --git a/src/common/backend/catalog/dependency.cpp b/src/common/backend/catalog/dependency.cpp index 8a375de9fc949ba71a95220c25f2c5fb32ae7e58..608708f15ae978935d898c9899bdcadf654bd4ba 100644 --- a/src/common/backend/catalog/dependency.cpp +++ b/src/common/backend/catalog/dependency.cpp @@ -3490,6 +3490,47 @@ Datum pg_describe_object(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(cstring_to_text(description)); } +void ReplaceTypeCheckRef(const ObjectAddress* object) +{ + ObjectAddresses* targetObjects = NULL; + Relation depRel; + targetObjects = new_object_addresses(); + depRel = heap_open(DependRelationId, RowExclusiveLock); + + findDependentObjects(object, + DEPFLAG_ORIGINAL, + NULL, /* empty stack */ + targetObjects, + NULL, /* no pendingObjects */ + &depRel); + + heap_close(depRel, RowExclusiveLock); + + for (int i = targetObjects->numrefs - 1; i >= 0; i--) { + const ObjectAddress* refobj = &targetObjects->refs[i]; + switch (getObjectClass(refobj)) { + case OCLASS_CLASS: + if (refobj->objectSubId != 0) { + ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot replace type because table %u depends on it", refobj->objectId))); + } + break; + + case OCLASS_PROC: + case OCLASS_PACKAGE: + ereport(NOTICE, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("%s depends on this type", format_procedure(refobj->objectId)))); + break; + + default: + break; + } + } + + /* clean up */ + free_object_addresses(targetObjects); +} + #ifdef ENABLE_MULTIPLE_NODES namespace Tsdb { diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 77a94562297b1d87fbc4c3cb1c93829661f9fdac..8c641d9dc32b7570e01359e8b27301d141d76a18 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -5070,6 +5070,7 @@ static AlterTableStmt* _copyAlterTableStmt(const AlterTableStmt* from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(missing_ok); COPY_SCALAR_FIELD(fromCreate); + COPY_SCALAR_FIELD(fromReplace); return newnode; } @@ -5678,6 +5679,7 @@ static CompositeTypeStmt* _copyCompositeTypeStmt(const CompositeTypeStmt* from) { CompositeTypeStmt* newnode = makeNode(CompositeTypeStmt); + COPY_SCALAR_FIELD(replace); COPY_NODE_FIELD(typevar); COPY_NODE_FIELD(coldeflist); @@ -5688,6 +5690,7 @@ static TableOfTypeStmt* _copyTableOfTypeStmt(const TableOfTypeStmt* from) { TableOfTypeStmt* newnode = makeNode(TableOfTypeStmt); + COPY_SCALAR_FIELD(replace); COPY_NODE_FIELD(typname); COPY_NODE_FIELD(reftypname); diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index e52af561c27697a14ea33896cc8fc816ac10342d..b46499b0c393219ba2ef98ec3eef5bf89442615f 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -1102,6 +1102,7 @@ static bool _equalAlterTableStmt(const AlterTableStmt* a, const AlterTableStmt* COMPARE_NODE_FIELD(cmds); COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(missing_ok); + COMPARE_SCALAR_FIELD(fromCreate); return true; } @@ -1666,6 +1667,7 @@ static bool _equalTransactionStmt(const TransactionStmt* a, const TransactionStm static bool _equalCompositeTypeStmt(const CompositeTypeStmt* a, const CompositeTypeStmt* b) { + COMPARE_SCALAR_FIELD(replace); COMPARE_NODE_FIELD(typevar); COMPARE_NODE_FIELD(coldeflist); @@ -1674,6 +1676,7 @@ static bool _equalCompositeTypeStmt(const CompositeTypeStmt* a, const CompositeT static bool _equalTableOfTypeStmt(const TableOfTypeStmt* a, const TableOfTypeStmt* b) { + COMPARE_SCALAR_FIELD(replace); COMPARE_NODE_FIELD(typname); COMPARE_NODE_FIELD(reftypname); diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index 8c43809588e64d6ec0c7c8ea3f32bcdc51a0f77c..75c666659f47fad08bd897a9fc9117bfe4c2f25b 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -4061,6 +4061,7 @@ static void _outAlterTableStmt(StringInfo str, AlterTableStmt* node) { WRITE_NODE_TYPE("ALTERTABLE"); WRITE_NODE_FIELD(relation); + WRITE_BOOL_FIELD(fromReplace); } static void _outCopyStmt(StringInfo str, CopyStmt* node) diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index af47a0c713a620c2bd4700a2d246a85df5cd9e6b..98f7aa7f90deb06f0273e60d1c86361eccff0e9d 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -1698,6 +1698,7 @@ static AlterTableStmt* _readAlterTableStmt(void) { READ_LOCALS(AlterTableStmt); READ_NODE_FIELD(relation); + READ_BOOL_FIELD(fromReplace); READ_DONE(); } diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index b650bb27b687b9c9b318507fb6dfe5d99f625e5c..2e263822599ed4d7e991159a489466f33b52ccbb 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -31,6 +31,7 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_proc.h" +#include "catalog/pg_type_fn.h" #include "catalog/pgxc_class.h" #include "catalog/indexing.h" #include "catalog/namespace.h" @@ -82,6 +83,7 @@ #include "utils/rel.h" #include "utils/rel_gs.h" #include "utils/acl.h" +#include "utils/typcache.h" #include "commands/explain.h" #include "commands/sec_rls_cmds.h" #include "streaming/streaming_catalog.h" @@ -479,6 +481,86 @@ Query* transformVariableAlterEventStmt(ParseState* pstate, AlterEventStmt* stmt) return result; } +static Query* TransformCompositeTypeStmt(ParseState* pstate, CompositeTypeStmt* stmt) +{ + Query* result = makeNode(Query); + result->commandType = CMD_UTILITY; + + Oid old_type_oid = InvalidOid; + Oid typeNamespace = InvalidOid; + RangeVar* typevar = stmt->typevar; + + typeNamespace = RangeVarGetAndCheckCreationNamespace(typevar, NoLock, NULL, RELKIND_COMPOSITE_TYPE); + RangeVarAdjustRelationPersistence(typevar, typeNamespace); + old_type_oid = GetSysCacheOid2(TYPENAMENSP, CStringGetDatum(typevar->relname), ObjectIdGetDatum(typeNamespace)); + + if (OidIsValid(old_type_oid) && stmt->replace) { + HeapTuple typtuple = NULL; + Form_pg_type typform = NULL; + typtuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(old_type_oid)); + if (!HeapTupleIsValid(typtuple)) { + ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + errmsg("cache lookup failed for type %u", old_type_oid))); + } + typform = (Form_pg_type)GETSTRUCT(typtuple); + + /* shell type or autogenerated array type */ + if (moveArrayTypeName(old_type_oid, NameStr(typform->typname), typeNamespace)) { + ReleaseSysCache(typtuple); + result->utilityStmt = (Node*)stmt; + return result; + } + + if (TYPTYPE_COMPOSITE != typform->typtype) { + ReleaseSysCache(typtuple); + ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("type already exists but not a composite type"))); + } + + ObjectAddress address; + ObjectAddressSet(address, TypeRelationId, old_type_oid); + /* Check if any table/function depends on this type */ + ReplaceTypeCheckRef(&address); + + TupleDesc tupledesc = lookup_rowtype_tupdesc(old_type_oid, typform->typtypmod); + + AlterTableStmt* alterStmt = makeNode(AlterTableStmt); + alterStmt->fromReplace = true; + alterStmt->relation = stmt->typevar; + alterStmt->relkind = OBJECT_TYPE; + /* lappend DROP attribute stmt */ + for (int i = 0; i < tupledesc->natts; i++) { + Form_pg_attribute oldAttribute = &tupledesc->attrs[i]; + if (oldAttribute->attisdropped) { + continue; + } + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropColumn; + n->name = NameStr(oldAttribute->attname); + n->behavior = DROP_RESTRICT; + n->missing_ok = FALSE; + alterStmt->cmds = lappend(alterStmt->cmds, n); + } + /* lappend ADD attribute stmt */ + ListCell* lc; + foreach(lc, stmt->coldeflist) { + ColumnDef* newAttr = (ColumnDef*)lfirst(lc); + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddColumn; + n->def = (Node*)newAttr; + n->behavior = DROP_RESTRICT; + alterStmt->cmds = lappend(alterStmt->cmds, n); + } + ReleaseTupleDesc(tupledesc); + ReleaseSysCache(typtuple); + + result->utilityStmt = (Node*)alterStmt; + } else { + result->utilityStmt = (Node*)stmt; + } + + return result; +} /* * transformStmt - @@ -588,6 +670,10 @@ Query* transformStmt(ParseState* pstate, Node* parseTree, bool isFirstNode, bool result = transformVariableAlterEventStmt(pstate, (AlterEventStmt*) parseTree); break; + case T_CompositeTypeStmt: + result = TransformCompositeTypeStmt(pstate, (CompositeTypeStmt*) parseTree); + break; + default: /* diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 45abf0fce5d6dccd5ffd7049e69e974316937556..f481c89b2f2b1a4ddb852d92c5c84cf4f7d8d29a 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -12455,16 +12455,36 @@ DefineStmt: /* can't use qualified_name, sigh */ n->typevar = makeRangeVarFromAnyName($3, @3, yyscanner); + n->replace = false; n->coldeflist = $6; $$ = (Node *)n; } + | CREATE OR REPLACE TYPE_P any_name as_is '(' OptTableFuncElementList ')' + { + CompositeTypeStmt *n = makeNode(CompositeTypeStmt); + + /* can't use qualified_name, sigh */ + n->typevar = makeRangeVarFromAnyName($5, @5, yyscanner); + n->replace = true; + n->coldeflist = $8; + $$ = (Node *)n; + } | CREATE TYPE_P any_name as_is TABLE OF func_type { TableOfTypeStmt *n = makeNode(TableOfTypeStmt); + n->replace = false; n->typname = $3; n->reftypname = $7; $$ = (Node *)n; } + | CREATE OR REPLACE TYPE_P any_name as_is TABLE OF func_type + { + TableOfTypeStmt *n = makeNode(TableOfTypeStmt); + n->replace = true; + n->typname = $5; + n->reftypname = $9; + $$ = (Node *)n; + } | CREATE TYPE_P any_name as_is ENUM_P '(' opt_enum_val_list ')' { CreateEnumStmt *n = makeNode(CreateEnumStmt); diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index a0a2e5348a8702adf91776bb82e9936707988f30..f522d1a9828366aff84bc7ede5e891878ab8d15a 100755 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -453,12 +453,12 @@ static void validateCheckConstraintForBucket(Relation rel, Partition part, HeapT static void validateForeignKeyConstraint(char* conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid); static void createForeignKeyTriggers( Relation rel, Oid refRelOid, Constraint* fkconstraint, Oid constraintOid, Oid indexOid); -static void ATController(AlterTableStmt *parsetree, Relation rel, List* cmds, bool recurse, LOCKMODE lockmode); +static void ATController(AlterTableStmt *parsetree, Relation rel, List* cmds, bool recurse, LOCKMODE lockmode, bool fromReplace); static bool ATCheckLedgerTableCmd(Relation rel, AlterTableCmd* cmd); static void ATPrepCmd(List** wqueue, Relation rel, AlterTableCmd* cmd, bool recurse, bool recursing, LOCKMODE lockmode, bool isDeltaTable = false); -static void ATRewriteCatalogs(List** wqueue, LOCKMODE lockmode); -static void ATExecCmd(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, LOCKMODE lockmode); +static void ATRewriteCatalogs(List** wqueue, LOCKMODE lockmode, bool fromReplace); +static void ATExecCmd(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, LOCKMODE lockmode, bool fromReplace); static void ATRewriteTables(AlterTableStmt *parsetree, List** wqueue, LOCKMODE lockmode); static void ATRewriteTable(AlteredTableInfo* tab, Relation oldrel, Relation newrel); static void ATCStoreRewriteTable(AlteredTableInfo* tab, Relation heapRel, LOCKMODE lockMode, Oid targetTblspc); @@ -555,7 +555,7 @@ static bool CheckLastColumn(Relation rel, AttrNumber attrnum); static void ATPrepDropColumn( List** wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd* cmd, LOCKMODE lockmode); static ObjectAddress ATExecDropColumn(List** wqueue, Relation rel, const char* colName, DropBehavior behavior, bool recurse, - bool recursing, bool missing_ok, LOCKMODE lockmode); + bool recursing, bool missing_ok, LOCKMODE lockmode, bool fromReplace); static ObjectAddress ATExecAddIndex(AlteredTableInfo* tab, Relation rel, IndexStmt* stmt, bool is_rebuild, LOCKMODE lockmode); static ObjectAddress ATExecAddConstraint(List** wqueue, AlteredTableInfo* tab, Relation rel, Constraint* newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode); @@ -7881,7 +7881,7 @@ void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt* stmt) // Next version remove hack patch for 'ALTER FOREIGN TABLE ... ADD NODE' if (stmt->cmds != NIL) { /* process 'ALTER TABLE' cmd */ - ATController(stmt, rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt), lockmode); + ATController(stmt, rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt), lockmode, stmt->fromReplace); if (enable_plpgsql_gsdependency_guc()) { (void)gsplsql_build_ref_type_dependency(get_rel_type_id(relid)); } @@ -7900,7 +7900,7 @@ void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt* stmt) /* open error table releation, closed in ATController */ Relation errtablerel = relation_open(errtableid, lockmode); - ATController(stmt, errtablerel, addNodeCmds, interpretInhOption(stmt->relation->inhOpt), lockmode); + ATController(stmt, errtablerel, addNodeCmds, interpretInhOption(stmt->relation->inhOpt), lockmode, stmt->fromReplace); } list_free_ext(addNodeCmds); } @@ -7925,7 +7925,7 @@ void AlterTableInternal(Oid relid, List* cmds, bool recurse) rel = relation_open(relid, lockmode); EventTriggerAlterTableRelid(relid); - ATController(NULL, rel, cmds, recurse, lockmode); + ATController(NULL, rel, cmds, recurse, lockmode, false); } static LOCKMODE set_lockmode(LOCKMODE mode, LOCKMODE cmd_mode) @@ -8029,7 +8029,7 @@ LOCKMODE AlterTableGetLockLevel(List* cmds) return lockmode; } -static void ATController(AlterTableStmt *parsetree, Relation rel, List* cmds, bool recurse, LOCKMODE lockmode) +static void ATController(AlterTableStmt *parsetree, Relation rel, List* cmds, bool recurse, LOCKMODE lockmode, bool fromReplace) { List* wqueue = NIL; ListCell* lcmd = NULL; @@ -8096,7 +8096,7 @@ static void ATController(AlterTableStmt *parsetree, Relation rel, List* cmds, bo relation_close(rel, NoLock); /* Phase 2: update system catalogs */ - ATRewriteCatalogs(&wqueue, lockmode); + ATRewriteCatalogs(&wqueue, lockmode, fromReplace); #ifdef PGXC /* Invalidate cache for redistributed relation */ @@ -8660,7 +8660,7 @@ static void UpdateGeneratedExpr(AlteredTableInfo* tab) * dispatched in a "safe" execution order (designed to avoid unnecessary * conflicts). */ -static void ATRewriteCatalogs(List** wqueue, LOCKMODE lockmode) +static void ATRewriteCatalogs(List** wqueue, LOCKMODE lockmode, bool fromReplace) { int pass; ListCell* ltab = NULL; @@ -8689,7 +8689,7 @@ static void ATRewriteCatalogs(List** wqueue, LOCKMODE lockmode) rel = relation_open(tab->relid, NoLock); foreach (lcmd, subcmds) - ATExecCmd(wqueue, tab, rel, (AlterTableCmd*)lfirst(lcmd), lockmode); + ATExecCmd(wqueue, tab, rel, (AlterTableCmd*)lfirst(lcmd), lockmode, fromReplace); /* * After the ALTER TYPE pass, do cleanup work (this is not done in @@ -8907,7 +8907,7 @@ static void ATCreateColumComments(Oid relOid, ColumnDef* columnDef) /* * ATExecCmd: dispatch a subcommand to appropriate execution routine */ -static void ATExecCmd(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, LOCKMODE lockmode) +static void ATExecCmd(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, LOCKMODE lockmode, bool fromReplace) { ObjectAddress address = InvalidObjectAddress; elog(ES_LOGLEVEL, "[ATExecCmd] cmd subtype: %d", cmd->subtype); @@ -8971,10 +8971,10 @@ static void ATExecCmd(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterT address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode); break; case AT_DropColumn: /* DROP COLUMN */ - address = ATExecDropColumn(wqueue, rel, cmd->name, cmd->behavior, false, false, cmd->missing_ok, lockmode); + address = ATExecDropColumn(wqueue, rel, cmd->name, cmd->behavior, false, false, cmd->missing_ok, lockmode, fromReplace); break; case AT_DropColumnRecurse: /* DROP COLUMN with recursion */ - address = ATExecDropColumn(wqueue, rel, cmd->name, cmd->behavior, true, false, cmd->missing_ok, lockmode); + address = ATExecDropColumn(wqueue, rel, cmd->name, cmd->behavior, true, false, cmd->missing_ok, lockmode, fromReplace); break; case AT_DropPartition: /* drop partition */ ATExecDropPartition(rel, cmd); @@ -13382,7 +13382,7 @@ static void ResetTempAutoIncrement(Relation rel, AttrNumber attnum) * Return value is that of the dropped column. */ static ObjectAddress ATExecDropColumn(List** wqueue, Relation rel, const char* colName, DropBehavior behavior, bool recurse, - bool recursing, bool missing_ok, LOCKMODE lockmode) + bool recursing, bool missing_ok, LOCKMODE lockmode, bool fromReplace) { HeapTuple tuple; Form_pg_attribute targetatt; @@ -13445,7 +13445,7 @@ static ObjectAddress ATExecDropColumn(List** wqueue, Relation rel, const char* * We have to check if the drop column is the last column. * If it is, not allow to drop it. */ - if (GetLocatorType(rel->rd_id) != LOCATOR_TYPE_HASH) { + if (!fromReplace && GetLocatorType(rel->rd_id) != LOCATOR_TYPE_HASH) { bool lastColumn = CheckLastColumn(rel, attnum); if (lastColumn) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("must have at least one column"))); @@ -13524,7 +13524,7 @@ static ObjectAddress ATExecDropColumn(List** wqueue, Relation rel, const char* /* * Delete this column of the delta table. */ - ATExecDropColumn(wqueue, childrel, colName, behavior, true, true, false, lockmode); + ATExecDropColumn(wqueue, childrel, colName, behavior, true, true, false, lockmode, fromReplace); } else if (childatt->attinhcount == 1 && !childatt->attislocal) { /* * If the child column has other definition sources, just @@ -13533,7 +13533,7 @@ static ObjectAddress ATExecDropColumn(List** wqueue, Relation rel, const char* * Time to delete this child column, too */ - ATExecDropColumn(wqueue, childrel, colName, behavior, true, true, false, lockmode); + ATExecDropColumn(wqueue, childrel, colName, behavior, true, true, false, lockmode, fromReplace); } else { /* Child column must survive my deletion */ childatt->attinhcount--; @@ -13619,7 +13619,7 @@ static ObjectAddress ATExecDropColumn(List** wqueue, Relation rel, const char* Oid tag_relid = get_tag_relid(RelationGetRelationName(rel), rel->rd_rel->relnamespace); Relation tagrel = heap_open(tag_relid, lockmode); CheckTableNotInUse(tagrel, "ALTER TABLE"); - ATExecDropColumn(wqueue, tagrel, colName, behavior, false, false, true, lockmode); + ATExecDropColumn(wqueue, tagrel, colName, behavior, false, false, true, lockmode, fromReplace); TagsCacheMgr::GetInstance().clear(); heap_close(tagrel, NoLock); @@ -13627,7 +13627,7 @@ static ObjectAddress ATExecDropColumn(List** wqueue, Relation rel, const char* /* if drop TSField columns, update delta table simultaneously */ Relation delta_rel = Tsdb::RelationGetDeltaRelation(rel, lockmode); CheckTableNotInUse(delta_rel, "ALTER TABLE"); - ATExecDropColumn(wqueue, delta_rel, colName, behavior, false, false, true, lockmode); + ATExecDropColumn(wqueue, delta_rel, colName, behavior, false, false, true, lockmode, fromReplace); heap_close(delta_rel, NoLock); } } diff --git a/src/gausskernel/optimizer/commands/typecmds.cpp b/src/gausskernel/optimizer/commands/typecmds.cpp index 373f650b79058f129da227c3a97a1a31e798ed1f..099778782bd196be4c5c413de4cba6b2413f8558 100644 --- a/src/gausskernel/optimizer/commands/typecmds.cpp +++ b/src/gausskernel/optimizer/commands/typecmds.cpp @@ -83,6 +83,7 @@ #include "utils/rel_gs.h" #include "utils/syscache.h" #include "utils/snapmgr.h" +#include "utils/typcache.h" #include "catalog/gs_dependencies_fn.h" #include "catalog/pg_object.h" @@ -2088,6 +2089,75 @@ Oid AssignTypeArrayOid(void) return type_array_oid; } +static ObjectAddress ReplaceTableOfType(Oid oldTypeOid, Oid refTypeOid) +{ + Relation pg_type_desc = NULL; + HeapTuple typtuple = NULL; + Form_pg_type typform = NULL; + Oid old_elemtype = InvalidOid; + ObjectAddress address; + + ObjectAddressSet(address, TypeRelationId, oldTypeOid); + /* if any table depend on this type, report ERROR */ + ReplaceTypeCheckRef(&address); + + /* change typelem in pg_type */ + pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); + typtuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(oldTypeOid)); + if (!HeapTupleIsValid(typtuple)) { + ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + errmsg("cache lookup failed for type %u", oldTypeOid))); + } + typform = (Form_pg_type)GETSTRUCT(typtuple); + if (typform->typtype != TYPTYPE_TABLEOF) { + tableam_tops_free_tuple(typtuple); + ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("type already exists but not a table of type"))); + } + old_elemtype = typform->typelem; + typform->typelem = refTypeOid; + simple_heap_update(pg_type_desc, &typtuple->t_self, typtuple); + /* update the system catalog indexes */ + CatalogUpdateIndexes(pg_type_desc, typtuple); + + tableam_tops_free_tuple(typtuple); + heap_close(pg_type_desc, RowExclusiveLock); + + /* find record between type and old_elemtype in pg_depend, and remove it */ + Relation depRel = NULL; + ScanKeyData key[2]; + int nkeys = 2; + SysScanDesc scan = NULL; + HeapTuple tup = NULL; + depRel = heap_open(DependRelationId, RowExclusiveLock); + ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(TypeRelationId)); + ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(oldTypeOid)); + scan = systable_beginscan(depRel, DependDependerIndexId, true, NULL, nkeys, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) { + Form_pg_depend foundDep = (Form_pg_depend)GETSTRUCT(tup); + if (foundDep->refobjid == old_elemtype) { + simple_heap_delete(depRel, &tup->t_self); + } + } + systable_endscan(scan); + heap_close(depRel, RowExclusiveLock); + + /* record with new elemtype */ + ObjectAddress myself, referenced; + myself.classId = TypeRelationId; + myself.objectId = oldTypeOid; + myself.objectSubId = 0; + referenced.classId = TypeRelationId; + referenced.objectId = refTypeOid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + CommandCounterIncrement(); + + return address; +} + /* * DefineRange * Registers a new table of type. @@ -2135,7 +2205,7 @@ ObjectAddress DefineTableOfType(const TableOfTypeStmt* stmt) */ typoid = GetSysCacheOid2(TYPENAMENSP, CStringGetDatum(typname), ObjectIdGetDatum(typeNamespace)); if (OidIsValid(typoid)) { - if (!moveArrayTypeName(typoid, typname, typeNamespace)) + if (!moveArrayTypeName(typoid, typname, typeNamespace) && !stmt->replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", typname))); } @@ -2180,38 +2250,43 @@ ObjectAddress DefineTableOfType(const TableOfTypeStmt* stmt) } ReleaseSysCache(type_tup); - /* Create the pg_type entry */ - return TypeCreate(InvalidOid, /* no predetermined type OID */ - typname, /* type name */ - typeNamespace, /* namespace */ - InvalidOid, /* relation oid (n/a here) */ - 0, /* relation kind (ditto) */ - typowner, /* owner's ID */ - -1, /* internal size (always varlena) */ - TYPTYPE_TABLEOF, /* type-type (table of type) */ - TYPCATEGORY_TABLEOF, /* type-category (table of type) */ - false, /* table of types are never preferred */ - DEFAULT_TYPDELIM, /* array element delimiter */ - F_ARRAY_IN, /* array input proc */ - F_ARRAY_OUT, /* array output proc */ - F_ARRAY_RECV, /* array recv (bin) proc */ - F_ARRAY_SEND, /* array send (bin) proc */ - InvalidOid, /* typmodin procedure - none */ - InvalidOid, /* typmodout procedure - none */ - F_ARRAY_TYPANALYZE, /* array analyze procedure */ - refTypeOid, /* element type ID - none */ - false, /* this is not an array type */ - InvalidOid, /* array type we are about to create */ - InvalidOid, /* base type ID (only for domains) */ - NULL, /* never a default type value */ - NULL, /* no binary form available either */ - false, /* never passed by value */ - 'd', /* alignment */ - 'x', /* TOAST strategy (always extended) */ - -1, /* typMod (Domains only) */ - 0, /* Array dimensions of typbasetype */ - false, /* Type NOT NULL */ - InvalidOid); /* type's collation (ranges never have one) */ + if (OidIsValid(typoid) && get_typisdefined(typoid)) { + return ReplaceTableOfType(typoid, refTypeOid); + } else { + /* Create the pg_type entry */ + return TypeCreate(InvalidOid, /* no predetermined type OID */ + typname, /* type name */ + typeNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + typowner, /* owner's ID */ + -1, /* internal size (always varlena) */ + TYPTYPE_TABLEOF, /* type-type (table of type) */ + TYPCATEGORY_TABLEOF, /* type-category (table of type) */ + false, /* table of types are never preferred */ + DEFAULT_TYPDELIM, /* array element delimiter */ + F_ARRAY_IN, /* array input proc */ + F_ARRAY_OUT, /* array output proc */ + F_ARRAY_RECV, /* array recv (bin) proc */ + F_ARRAY_SEND, /* array send (bin) proc */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + F_ARRAY_TYPANALYZE, /* array analyze procedure */ + refTypeOid, /* element type ID - none */ + false, /* this is not an array type */ + InvalidOid, /* array type we are about to create */ + InvalidOid, /* base type ID (only for domains) */ + NULL, /* never a default type value */ + NULL, /* no binary form available either */ + false, /* never passed by value */ + 'd', /* alignment */ + 'x', /* TOAST strategy (always extended) */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false, /* Type NOT NULL */ + InvalidOid); /* type's collation (ranges never have one) */ + } + } /* ------------------------------------------------------------------- diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 2d641cc2fc2910253d0cd6a5d43cd525d8bb1a0c..18331aabd44644da8a73fd3ec5f90c5660fceab2 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -397,6 +397,7 @@ extern void prepareDatabaseCFunLibrary(Oid databaseId); extern void deleteDictionaryTSFile(Oid dictId); extern void deleteDatabaseTSFile(Oid databaseId); extern void changeDependencyOnObjfile(Oid objectId, Oid refobjId, const char *newObjfile); +extern void ReplaceTypeCheckRef(const ObjectAddress* object); #ifdef ENABLE_MULTIPLE_NODES namespace Tsdb { diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index cef077333b6d1585bed1cf5718cca115ce6127b9..09022911ce332cc7dc1619fa5234f95db7dada28 100755 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1499,6 +1499,7 @@ typedef struct UnlistenStmt { */ typedef struct CompositeTypeStmt { NodeTag type; + bool replace; RangeVar* typevar; /* the composite type to be created */ List* coldeflist; /* list of ColumnDef nodes */ } CompositeTypeStmt; @@ -1509,6 +1510,7 @@ typedef struct CompositeTypeStmt { */ typedef struct TableOfTypeStmt { NodeTag type; + bool replace; List* typname; /* the table of type to be quoted */ TypeName* reftypname; /* the name of the type being referenced */ } TableOfTypeStmt; diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index 1a832a4f726e3e8a8de610f041672fd3ae1c2741..7588f863085c6b8bc47d7270f2c7588b69c357ad 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -814,6 +814,7 @@ typedef struct AlterTableStmt { ObjectType relkind; /* type of object */ bool missing_ok; /* skip error if table missing */ bool fromCreate; /* from create stmt */ + bool fromReplace; /* from create or replace stmt */ bool need_rewrite_sql; /* after rewrite rule, need to rewrite query string */ } AlterTableStmt; diff --git a/src/test/regress/expected/type_replace.out b/src/test/regress/expected/type_replace.out new file mode 100644 index 0000000000000000000000000000000000000000..b929e0667760c3a92452f98b4cd78674f8795732 --- /dev/null +++ b/src/test/regress/expected/type_replace.out @@ -0,0 +1,103 @@ +--测试type重建,OID不变功能 +create schema test_type_replace; +set search_path = test_type_replace; +-- CREATE OR REPLACE TYPE name AS ( xxx ) +-- Case 1:若类型不存在,按create type语法创建。 +-- Case 2:若类型已存在(同命名空间下,与create 语法的判断保持一致): +-- Case 2.1:原类型为shell类型或自动生成的数组类型,按create type语法创建。 +-- Case 2.2:原类型为复合类型: +-- Case2.2.1:原类型被表引用,报错。 +-- Case2.2.2:原类型无表引用,转换为AlterTableStmt。 +-- Case2.3:原类型为其他类型,报错。 +--case 1 +create or replace type typ1 as (a int, b text); +--case 2 +-- case 2.1 原类型为shell类型 +create type shell1; +create or replace type shell1 as (a int, b text); +-- case 2.1 原类型为自动生成的数组类型 +create or replace type _typ1 as (a int, b text); +-- case 2.2 原类型为复合类型 +-- case 2.2.1 原类型被表直接引用 +create table tb1 (a typ1); +create or replace type typ1 as (a int, b text); --报错提示有表依赖 +--?ERROR: cannot replace type because table .* depends on it +drop table tb1; +-- case 2.2.1 原类型被表间接引用 +create type typ2 as (a typ1); +create table tb1 (a typ2); +create or replace type typ1 as (a int, b text); --报错提示有表依赖,tb1->typ2->typ1 +--?ERROR: cannot replace type because table .* depends on it +drop table tb1; +drop type typ2; +-- case 2.2.1 原类型无表引用 +--作为函数入参 +create procedure proc1 (a typ1) as +begin + raise info 'a = %',a; +end; +/ +create or replace type typ1 as (a int, b text, c varchar); +NOTICE: proc1(typ1) depends on this type +declare + var typ1; +begin + proc1(var); +end; +/ +INFO: a = (,,) +CONTEXT: SQL statement "CALL proc1(var)" +PL/pgSQL function inline_code_block line 3 at PERFORM +-- case 2.3 原类型为其他类型 +create type enum1 AS ENUM ('one', 'two', 'three'); +create or replace type enum1 AS (a text); +ERROR: type already exists but not a composite type +drop type typ1 cascade; +NOTICE: drop cascades to function proc1(typ1) +drop type _typ1 cascade; +drop type shell1; +drop type enum1; +-- CREATE OR REPLACE TYPE name AS TABLE OF data_type +-- Case1:若类型不存在,则按create语法创建(TableOfTypeStmt)。 +-- Case2:若类型已存在(同命名空间下,与create 语法的判断保持一致): +-- Case2.1: 若类型为shell类型或自动生成的数组类型,按create语法创建。 +-- Case2.2:若类型为table of 类型(o类型): +-- Case2.2.1:若类型无表引用且新的被引用类型为非table of 类型(o类型),替换引用类型,更新依赖关系 +-- Case2.2.2:其他:报错。 +-- Case2.3:若类型为其他类型,报错。 +--case 1 +create or replace type typ1 as table of int; +--case 2 +-- case 2.1 原类型为shell类型 +create type shell1; +create or replace type shell1 as table of varchar; +-- case 2.1 原类型为自动生成的数组类型 +create or replace type _typ1 as table of varchar; +-- case 2.2 原类型为复合类型 +-- case 2.2.1 原类型被表引用 +create table tb1 (a typ1); +ERROR: type "typ1" is not supported as column type +DETAIL: "typ1" is a nest table type +create or replace type typ1 as table of varchar; --报错提示有表依赖 +drop table tb1; +ERROR: table "tb1" does not exist +-- case 2.2.1 原类型无表引用 +--作为函数入参 +create procedure proc1 (a typ1) as +begin + raise info 'a = %',a; +end; +/ +create or replace type typ1 as table of varchar; +NOTICE: test_type_replace.proc1(_varchar[]) depends on this type +-- case 2.3 原类型为其他类型 +create type enum1 AS ENUM ('one', 'two', 'three'); +create or replace type enum1 AS table of varchar; +ERROR: type already exists but not a table of type +drop schema test_type_replace cascade; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to type _varchar[] +drop cascades to type _varchar[] +drop cascades to type _varchar[] +drop cascades to function test_type_replace.proc1(_varchar[]) +drop cascades to type enum1 diff --git a/src/test/regress/output/create_basetype.source b/src/test/regress/output/create_basetype.source index 37c321b404618b5fee11aa360b4fe98c10424dce..f26ecad8059ede0be7e43bacc40e06d9825615da 100644 --- a/src/test/regress/output/create_basetype.source +++ b/src/test/regress/output/create_basetype.source @@ -111,7 +111,7 @@ drop table t1_rep; Command: CREATE TYPE Description: define a new data type Syntax: -CREATE TYPE name AS +CREATE [ OR REPLACE ] TYPE name AS ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] ) CREATE TYPE name AS ENUM @@ -143,7 +143,7 @@ CREATE TYPE name CREATE TYPE name AS ENUM ( [ 'lable' [, ... ] ] ) -CREATE TYPE name AS TABLE OF data_type +CREATE [ OR REPLACE ] TYPE name AS TABLE OF data_type \h alter type Command: ALTER TYPE diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 23582c26115f2bc19e31b0d5fbe58a0b75464f97..50ff740cf6493fcbf70e8be213df1e439962ae99 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -485,7 +485,7 @@ test: hw_procedure_define #test: hw_anonymous_block #test: hw_procedure# test: hw_grant_all hw_dynamic_sql hw_func_return_out -test: hw_package_function +test: hw_package_function type_replace #show plan #test: plan_hint diff --git a/src/test/regress/parallel_schedule0B b/src/test/regress/parallel_schedule0B index 6ce031e1ae984b5737c2e56b8ba373c591c0b779..90c4612056c0d2864fccae2d510203c2097266b9 100644 --- a/src/test/regress/parallel_schedule0B +++ b/src/test/regress/parallel_schedule0B @@ -44,7 +44,7 @@ test: hw_procedure_define #test: hw_anonymous_block #test: hw_procedure# test: hw_grant_all hw_dynamic_sql hw_func_return_out -test: hw_package_function +test: hw_package_function type_replace #show plan #test: plan_hint diff --git a/src/test/regress/sql/type_replace.sql b/src/test/regress/sql/type_replace.sql new file mode 100644 index 0000000000000000000000000000000000000000..37b91d91e8aed59689591df2ff9c687f90eb005e --- /dev/null +++ b/src/test/regress/sql/type_replace.sql @@ -0,0 +1,105 @@ +--测试type重建,OID不变功能 +create schema test_type_replace; +set search_path = test_type_replace; +-- CREATE OR REPLACE TYPE name AS ( xxx ) + +-- Case 1:若类型不存在,按create type语法创建。 +-- Case 2:若类型已存在(同命名空间下,与create 语法的判断保持一致): +-- Case 2.1:原类型为shell类型或自动生成的数组类型,按create type语法创建。 +-- Case 2.2:原类型为复合类型: +-- Case2.2.1:原类型被表引用,报错。 +-- Case2.2.2:原类型无表引用,转换为AlterTableStmt。 +-- Case2.3:原类型为其他类型,报错。 + +--case 1 +create or replace type typ1 as (a int, b text); + +--case 2 +-- case 2.1 原类型为shell类型 +create type shell1; +create or replace type shell1 as (a int, b text); + +-- case 2.1 原类型为自动生成的数组类型 +create or replace type _typ1 as (a int, b text); + +-- case 2.2 原类型为复合类型 +-- case 2.2.1 原类型被表直接引用 +create table tb1 (a typ1); +create or replace type typ1 as (a int, b text); --报错提示有表依赖 +drop table tb1; + +-- case 2.2.1 原类型被表间接引用 +create type typ2 as (a typ1); +create table tb1 (a typ2); +create or replace type typ1 as (a int, b text); --报错提示有表依赖,tb1->typ2->typ1 +drop table tb1; +drop type typ2; + +-- case 2.2.1 原类型无表引用 +--作为函数入参 +create procedure proc1 (a typ1) as +begin + raise info 'a = %',a; +end; +/ + +create or replace type typ1 as (a int, b text, c varchar); + +declare + var typ1; +begin + proc1(var); +end; +/ + +-- case 2.3 原类型为其他类型 +create type enum1 AS ENUM ('one', 'two', 'three'); +create or replace type enum1 AS (a text); + +drop type typ1 cascade; +drop type _typ1 cascade; +drop type shell1; +drop type enum1; + +-- CREATE OR REPLACE TYPE name AS TABLE OF data_type + +-- Case1:若类型不存在,则按create语法创建(TableOfTypeStmt)。 +-- Case2:若类型已存在(同命名空间下,与create 语法的判断保持一致): +-- Case2.1: 若类型为shell类型或自动生成的数组类型,按create语法创建。 +-- Case2.2:若类型为table of 类型(o类型): +-- Case2.2.1:若类型无表引用且新的被引用类型为非table of 类型(o类型),替换引用类型,更新依赖关系 +-- Case2.2.2:其他:报错。 +-- Case2.3:若类型为其他类型,报错。 + +--case 1 +create or replace type typ1 as table of int; + +--case 2 +-- case 2.1 原类型为shell类型 +create type shell1; +create or replace type shell1 as table of varchar; + +-- case 2.1 原类型为自动生成的数组类型 +create or replace type _typ1 as table of varchar; + +-- case 2.2 原类型为复合类型 +-- case 2.2.1 原类型被表引用 +create table tb1 (a typ1); +create or replace type typ1 as table of varchar; --报错提示有表依赖 +drop table tb1; + +-- case 2.2.1 原类型无表引用 +--作为函数入参 +create procedure proc1 (a typ1) as +begin + raise info 'a = %',a; +end; +/ + +create or replace type typ1 as table of varchar; + +-- case 2.3 原类型为其他类型 +create type enum1 AS ENUM ('one', 'two', 'three'); +create or replace type enum1 AS table of varchar; + +drop schema test_type_replace cascade;