From e159afbb82f8789e609b4b82013e5a8ddb761816 Mon Sep 17 00:00:00 2001 From: Amul Sul Date: Fri, 17 Nov 2023 17:21:59 +0530 Subject: [PATCH v4 2/2] POC - FK and CHECK constraint check --- src/backend/commands/tablecmds.c | 163 ++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 63debf1c376..0bb6143937b 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4856,7 +4856,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION AS */ ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); - pass = AT_PASS_MISC; + pass = AT_PASS_ALTER_TYPE; break; case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */ ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); @@ -8345,6 +8345,12 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, NewColumnValue *newval; RawColumnDefault *rawEnt; + Relation newrel; + Relation depRel; + ScanKeyData key[3]; + SysScanDesc scan; + HeapTuple depTup; + tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); if (!HeapTupleIsValid(tuple)) ereport(ERROR, @@ -8415,6 +8421,161 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, tab->newvals = lappend(tab->newvals, newval); tab->rewrite |= AT_REWRITE_DEFAULT_VAL; + /* + * TODO: needed? + * + * Clear all the missing values if we're rewriting the table, since this + * renders them pointless. + */ + newrel = table_open(RelationGetRelid(rel), NoLock); + RelationClearMissing(newrel); + relation_close(newrel, NoLock); + /* make sure we don't conflict with later attribute modifications */ + CommandCounterIncrement(); + + /* + * Find everything that depends on the column (constraints, indexes, etc), + * and record enough information to let us recreate the objects. + * + * The actual recreation does not happen here, but only after we have + * performed all the individual ALTER TYPE operations. We have to save + * the info before executing ALTER TYPE, though, else the deparser will + * get confused. + */ + depRel = table_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; + + foundObject.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_PARTITIONED_INDEX) + { + Assert(foundObject.objectSubId == 0); + RememberIndexForRebuilding(foundObject.objectId, tab); + } + else + { + /* Not expecting any other direct dependencies... */ + elog(ERROR, "unexpected object depending on column: %s", + getObjectDescription(&foundObject, false)); + } + break; + } + + case OCLASS_CONSTRAINT: + Assert(foundObject.objectSubId == 0); + RememberConstraintForRebuilding(foundObject.objectId, tab); + break; + + /* TODO: need an explanation for the ignorance */ + case OCLASS_REWRITE: + case OCLASS_TRIGGER: + case OCLASS_POLICY: + break; + + case OCLASS_DEFAULT: + /* Ignore this since we adding a column default */ + break; + + case OCLASS_STATISTIC_EXT: + + /* + * Give the extended-stats machinery a chance to fix anything + * that this column type change would break. + */ + RememberStatisticsForRebuilding(foundObject.objectId, tab); + 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_AM: + 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_ROLE_MEMBERSHIP: + 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_EVENT_TRIGGER: + case OCLASS_PARAMETER_ACL: + case OCLASS_PUBLICATION: + case OCLASS_PUBLICATION_NAMESPACE: + case OCLASS_PUBLICATION_REL: + case OCLASS_SUBSCRIPTION: + case OCLASS_TRANSFORM: + + /* + * We don't expect any of these sorts of objects to depend on + * a column. + */ + elog(ERROR, "unexpected object depending on column: %s", + getObjectDescription(&foundObject, false)); + break; + + /* + * There's intentionally no default: case here; we want the + * compiler to warn if a new OCLASS hasn't been handled above. + */ + } + } + + systable_endscan(scan); + + table_close(depRel, RowExclusiveLock); + + /* + * Drop any pg_statistic entry for the column, no longer valid + */ + RemoveStatistics(RelationGetRelid(rel), attnum); + + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), attnum); + ObjectAddressSubSet(address, RelationRelationId, RelationGetRelid(rel), attnum); return address; -- 2.18.0