pull/6393/merge
Naisila Puka 2025-06-24 10:02:26 -07:00 committed by GitHub
commit 1c7fe1c602
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 226 additions and 1 deletions

View File

@ -1363,7 +1363,7 @@ static DistributeObjectOps View_AlterView = {
static DistributeObjectOps Type_Drop = { static DistributeObjectOps Type_Drop = {
.deparse = DeparseDropTypeStmt, .deparse = DeparseDropTypeStmt,
.qualify = NULL, .qualify = NULL,
.preprocess = PreprocessDropDistributedObjectStmt, .preprocess = PreprocessDropTypeStmt,
.postprocess = NULL, .postprocess = NULL,
.operationType = DIST_OPS_DROP, .operationType = DIST_OPS_DROP,
.address = NULL, .address = NULL,

View File

@ -103,6 +103,7 @@ static CompositeTypeStmt * RecreateCompositeTypeStmt(Oid typeOid);
static List * CompositeTypeColumnDefList(Oid typeOid); static List * CompositeTypeColumnDefList(Oid typeOid);
static CreateEnumStmt * RecreateEnumStmt(Oid typeOid); static CreateEnumStmt * RecreateEnumStmt(Oid typeOid);
static List * EnumValsList(Oid typeOid); static List * EnumValsList(Oid typeOid);
static ObjectAddress * typeDependentDistributionColumn(Oid typeId);
/* /*
* PreprocessRenameTypeAttributeStmt is called for changes of attribute names for composite * PreprocessRenameTypeAttributeStmt is called for changes of attribute names for composite
@ -144,6 +145,63 @@ PreprocessRenameTypeAttributeStmt(Node *node, const char *queryString,
} }
/*
* PreprocessDropTypeStmt is called to check whether this is a
* DROP TYPE ... CASCADE statement. If yes, it checks whether there are any
* columns part of distributed tables included. If yes, it errors out.
* After that it calls the general PreprocessDropDistributedObjectStmt
*/
List *
PreprocessDropTypeStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
DropStmt *stmt = castNode(DropStmt, node);
Assert(stmt->removeType == OBJECT_TYPE);
if (stmt->behavior == DROP_CASCADE)
{
ListCell *cell;
foreach(cell, stmt->objects)
{
Node *object = lfirst(cell);
Relation relation = NULL;
/* Get an ObjectAddress for the type. */
ObjectAddress typeAddress = get_object_address(OBJECT_TYPE,
object,
&relation,
AccessExclusiveLock,
stmt->missing_ok);
ObjectAddress *distTableAndColumn = typeDependentDistributionColumn(
typeAddress.objectId);
if (distTableAndColumn != NULL)
{
Oid distTableId = distTableAndColumn->objectId;
int32 attNum = distTableAndColumn->objectSubId;
HeapTuple attTuple = SearchSysCacheAttNum(distTableId, attNum);
Form_pg_attribute targetAttr = (Form_pg_attribute) GETSTRUCT(attTuple);
char *columnName = NameStr(targetAttr->attname);
ReleaseSysCache(attTuple);
TypeName *typeName = castNode(TypeName, object);
Oid typeOid = LookupTypeNameOid(NULL, typeName, false);
const char *identifier = format_type_be_qualified(typeOid);
ereport(ERROR,
(errmsg("Can't drop type \"%s\" "
"because it is used in the partition column \"%s\" "
"of Citus table \"%s\"",
identifier, columnName, get_rel_name(distTableId))));
}
}
}
return PreprocessDropDistributedObjectStmt(node, queryString, processUtilityContext);
}
/* /*
* CreateTypeStmtByObjectAddress returns a parsetree for the CREATE TYPE statement to * CreateTypeStmtByObjectAddress returns a parsetree for the CREATE TYPE statement to
* recreate the type by its object address. * recreate the type by its object address.
@ -672,3 +730,57 @@ LookupNonAssociatedArrayTypeNameOid(ParseState *pstate, const TypeName *typeName
return typeOid; return typeOid;
} }
/*
* typeDependentDistributionColumn checks whether the given type has a dependency
* to a Citus table as the type of the table's distribution column
* If there exists at least one, it returns the first object seen in the list.
*/
static ObjectAddress *
typeDependentDistributionColumn(Oid typeId)
{
ObjectAddress *distTableAndColumn = NULL;
ScanKeyData key[2];
Relation depRel = table_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_refclassid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(TypeRelationId));
ScanKeyInit(&key[1],
Anum_pg_depend_refobjid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeId));
SysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,
NULL, 2, key);
HeapTuple tup;
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend pgDependEntry = (Form_pg_depend) GETSTRUCT(tup);
if (pgDependEntry->classid == RelationRelationId &&
get_rel_relkind(pgDependEntry->objid) == RELKIND_RELATION &&
IsCitusTable(pgDependEntry->objid))
{
Var *partitionColumn = DistPartitionKey(pgDependEntry->objid);
if (partitionColumn != NULL &&
partitionColumn->varattno == pgDependEntry->objsubid)
{
distTableAndColumn = palloc0(sizeof(ObjectAddress));
ObjectAddressSubSet(*distTableAndColumn, RelationRelationId,
pgDependEntry->objid, pgDependEntry->objsubid);
break;
}
}
}
systable_endscan(scan);
table_close(depRel, AccessShareLock);
return distTableAndColumn;
}

View File

@ -712,6 +712,8 @@ extern void PreprocessTruncateStatement(TruncateStmt *truncateStatement);
extern List * PreprocessRenameTypeAttributeStmt(Node *stmt, const char *queryString, extern List * PreprocessRenameTypeAttributeStmt(Node *stmt, const char *queryString,
ProcessUtilityContext ProcessUtilityContext
processUtilityContext); processUtilityContext);
extern List * PreprocessDropTypeStmt(Node *stmt, const char *queryString,
ProcessUtilityContext processUtilityContext);
extern Node * CreateTypeStmtByObjectAddress(const ObjectAddress *address); extern Node * CreateTypeStmtByObjectAddress(const ObjectAddress *address);
extern List * CompositeTypeStmtObjectAddress(Node *stmt, bool missing_ok, bool extern List * CompositeTypeStmtObjectAddress(Node *stmt, bool missing_ok, bool
isPostprocess); isPostprocess);

View File

@ -605,6 +605,68 @@ DETAIL: "type temp_type" will be created only locally
CREATE TYPE pg_temp.temp_enum AS ENUM ('one', 'two', 'three'); CREATE TYPE pg_temp.temp_enum AS ENUM ('one', 'two', 'three');
WARNING: "type temp_enum" has dependency on unsupported object "schema pg_temp_xxx" WARNING: "type temp_enum" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL: "type temp_enum" will be created only locally DETAIL: "type temp_enum" will be created only locally
-- verify that dropping a type which is used in the distribution column
-- of a distributed table fails
-- create a custom type...
CREATE TYPE my_type AS (
i integer,
i2 integer
);
CREATE FUNCTION equal_my_type_function(my_type, my_type) RETURNS boolean
LANGUAGE 'internal'
AS 'record_eq'
IMMUTABLE
RETURNS NULL ON NULL INPUT;
-- ... use that function to create a custom equality operator...
CREATE OPERATOR = (
LEFTARG = my_type,
RIGHTARG = my_type,
PROCEDURE = equal_my_type_function,
HASHES
);
-- ... create a test HASH function. Though it is a poor hash function,
-- it is acceptable for our tests
CREATE FUNCTION my_type_hash(my_type) RETURNS int
AS 'SELECT hashtext( ($1.i + $1.i2)::text);'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
-- ... and create a custom operator family for hash indexes...
CREATE OPERATOR FAMILY cats_op_fam USING hash;
-- We need to define a default operator classes for my_type
-- that uses HASH
CREATE OPERATOR CLASS cats_op_fam_class
DEFAULT FOR TYPE my_type USING HASH AS
OPERATOR 1 = (my_type, my_type),
FUNCTION 1 my_type_hash(my_type);
CREATE TABLE tbl (a my_type);
SELECT create_distributed_table('tbl', 'a');
create_distributed_table
---------------------------------------------------------------------
(1 row)
DROP TYPE my_type CASCADE;
ERROR: Can't drop type "type_tests.my_type" because it is used in the partition column "a" of Citus table "tbl"
ALTER TABLE tbl DROP COLUMN a;
ERROR: cannot execute ALTER TABLE command involving partition column
SELECT undistribute_table('tbl');
NOTICE: creating a new table for type_tests.tbl
NOTICE: moving the data of type_tests.tbl
NOTICE: dropping the old type_tests.tbl
NOTICE: renaming the new table to type_tests.tbl
undistribute_table
---------------------------------------------------------------------
(1 row)
DROP TYPE my_type CASCADE;
NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to function equal_my_type_function(my_type,my_type)
drop cascades to operator =(my_type,my_type)
drop cascades to function my_type_hash(my_type)
drop cascades to operator class cats_op_fam_class for access method hash
drop cascades to column a of table tbl
-- clear objects -- clear objects
SET client_min_messages TO error; -- suppress cascading objects dropping SET client_min_messages TO error; -- suppress cascading objects dropping
DROP SCHEMA type_tests CASCADE; DROP SCHEMA type_tests CASCADE;

View File

@ -361,6 +361,55 @@ SELECT create_distributed_table('table_text_local_def','id');
CREATE TYPE pg_temp.temp_type AS (int_field int); CREATE TYPE pg_temp.temp_type AS (int_field int);
CREATE TYPE pg_temp.temp_enum AS ENUM ('one', 'two', 'three'); CREATE TYPE pg_temp.temp_enum AS ENUM ('one', 'two', 'three');
-- verify that dropping a type which is used in the distribution column
-- of a distributed table fails
-- create a custom type...
CREATE TYPE my_type AS (
i integer,
i2 integer
);
CREATE FUNCTION equal_my_type_function(my_type, my_type) RETURNS boolean
LANGUAGE 'internal'
AS 'record_eq'
IMMUTABLE
RETURNS NULL ON NULL INPUT;
-- ... use that function to create a custom equality operator...
CREATE OPERATOR = (
LEFTARG = my_type,
RIGHTARG = my_type,
PROCEDURE = equal_my_type_function,
HASHES
);
-- ... create a test HASH function. Though it is a poor hash function,
-- it is acceptable for our tests
CREATE FUNCTION my_type_hash(my_type) RETURNS int
AS 'SELECT hashtext( ($1.i + $1.i2)::text);'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
-- ... and create a custom operator family for hash indexes...
CREATE OPERATOR FAMILY cats_op_fam USING hash;
-- We need to define a default operator classes for my_type
-- that uses HASH
CREATE OPERATOR CLASS cats_op_fam_class
DEFAULT FOR TYPE my_type USING HASH AS
OPERATOR 1 = (my_type, my_type),
FUNCTION 1 my_type_hash(my_type);
CREATE TABLE tbl (a my_type);
SELECT create_distributed_table('tbl', 'a');
DROP TYPE my_type CASCADE;
ALTER TABLE tbl DROP COLUMN a;
SELECT undistribute_table('tbl');
DROP TYPE my_type CASCADE;
-- clear objects -- clear objects
SET client_min_messages TO error; -- suppress cascading objects dropping SET client_min_messages TO error; -- suppress cascading objects dropping
DROP SCHEMA type_tests CASCADE; DROP SCHEMA type_tests CASCADE;