mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Refactor: separate function to find all objects depending on a column
Move code from ATExecAlterColumnType() that finds the all the objects that depend on the column to a separate function. A future patch will reuse this code. Author: Amul Sul <sulamul@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/CAAJ_b94yyJeGA-5M951_Lr+KfZokOp-2kXicpmEhi5FXhBeTog@mail.gmail.com
This commit is contained in:
		
							parent
							
								
									359a3c586d
								
							
						
					
					
						commit
						d4e66a39eb
					
				| @ -561,6 +561,8 @@ static void ATPrepAlterColumnType(List **wqueue, | ||||
| static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno); | ||||
| static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, | ||||
| 										   AlterTableCmd *cmd, LOCKMODE lockmode); | ||||
| static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, | ||||
| 											  Relation rel, AttrNumber attnum, const char *colName); | ||||
| static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab); | ||||
| static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab); | ||||
| static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab); | ||||
| @ -13298,215 +13300,15 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, | ||||
| 	 * 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 if (relKind == RELKIND_SEQUENCE) | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * This must be a SERIAL column's sequence.  We need | ||||
| 						 * not do anything to it. | ||||
| 						 */ | ||||
| 						Assert(foundObject.objectSubId == 0); | ||||
| 					} | ||||
| 					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; | ||||
| 
 | ||||
| 			case OCLASS_REWRITE: | ||||
| 				/* XXX someday see if we can cope with revising views */ | ||||
| 				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, false), | ||||
| 								   colName))); | ||||
| 				break; | ||||
| 
 | ||||
| 			case OCLASS_TRIGGER: | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * A trigger can depend on a column because the column is | ||||
| 				 * specified as an update target, or because the column is | ||||
| 				 * used in the trigger's WHEN condition.  The first case would | ||||
| 				 * not require any extra work, but the second case would | ||||
| 				 * require updating the WHEN expression, which will take a | ||||
| 				 * significant amount of new code.  Since we can't easily tell | ||||
| 				 * which case applies, we punt for both.  FIXME someday. | ||||
| 				 */ | ||||
| 				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, false), | ||||
| 								   colName))); | ||||
| 				break; | ||||
| 
 | ||||
| 			case OCLASS_POLICY: | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * A policy can depend on a column because the column is | ||||
| 				 * specified in the policy's USING or WITH CHECK qual | ||||
| 				 * expressions.  It might be possible to rewrite and recheck | ||||
| 				 * the policy expression, but punt for now.  It's certainly | ||||
| 				 * easy enough to remove and recreate the policy; still, FIXME | ||||
| 				 * someday. | ||||
| 				 */ | ||||
| 				ereport(ERROR, | ||||
| 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 						 errmsg("cannot alter type of a column used in a policy definition"), | ||||
| 						 errdetail("%s depends on column \"%s\"", | ||||
| 								   getObjectDescription(&foundObject, false), | ||||
| 								   colName))); | ||||
| 				break; | ||||
| 
 | ||||
| 			case OCLASS_DEFAULT: | ||||
| 				{ | ||||
| 					ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId); | ||||
| 
 | ||||
| 					if (col.objectId == RelationGetRelid(rel) && | ||||
| 						col.objectSubId == attnum) | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * Ignore the column's own default expression, which | ||||
| 						 * we will deal with below. | ||||
| 						 */ | ||||
| 						Assert(defaultexpr); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * This must be a reference from the expression of a | ||||
| 						 * generated column elsewhere in the same table. | ||||
| 						 * Changing the type of a column that is used by a | ||||
| 						 * generated column is not allowed by SQL standard, so | ||||
| 						 * just punt for now.  It might be doable with some | ||||
| 						 * thinking and effort. | ||||
| 						 */ | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 								 errmsg("cannot alter type of a column used by a generated column"), | ||||
| 								 errdetail("Column \"%s\" is used by generated column \"%s\".", | ||||
| 										   colName, | ||||
| 										   get_attname(col.objectId, | ||||
| 													   col.objectSubId, | ||||
| 													   false)))); | ||||
| 					} | ||||
| 					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); | ||||
| 	RememberAllDependentForRebuilding(tab, rel, attnum, colName); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Now scan for dependencies of this column on other things.  The only | ||||
| 	 * things we should find are the dependency on the column datatype and | ||||
| 	 * possibly a collation dependency.  Those can be removed. | ||||
| 	 */ | ||||
| 	depRel = table_open(DependRelationId, RowExclusiveLock); | ||||
| 
 | ||||
| 	ScanKeyInit(&key[0], | ||||
| 				Anum_pg_depend_classid, | ||||
| 				BTEqualStrategyNumber, F_OIDEQ, | ||||
| @ -13694,6 +13496,224 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, | ||||
| 	return address; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Subroutine for ATExecAlterColumnType: Find everything that depends on the | ||||
|  * column (constraints, indexes, etc), and record enough information to let us | ||||
|  * recreate the objects. | ||||
|  */ | ||||
| static void | ||||
| RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumber attnum, const char *colName) | ||||
| { | ||||
| 	Relation	depRel; | ||||
| 	ScanKeyData key[3]; | ||||
| 	SysScanDesc scan; | ||||
| 	HeapTuple	depTup; | ||||
| 
 | ||||
| 	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 if (relKind == RELKIND_SEQUENCE) | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * This must be a SERIAL column's sequence.  We need | ||||
| 						 * not do anything to it. | ||||
| 						 */ | ||||
| 						Assert(foundObject.objectSubId == 0); | ||||
| 					} | ||||
| 					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; | ||||
| 
 | ||||
| 			case OCLASS_REWRITE: | ||||
| 				/* XXX someday see if we can cope with revising views */ | ||||
| 				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, false), | ||||
| 								   colName))); | ||||
| 				break; | ||||
| 
 | ||||
| 			case OCLASS_TRIGGER: | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * A trigger can depend on a column because the column is | ||||
| 				 * specified as an update target, or because the column is | ||||
| 				 * used in the trigger's WHEN condition.  The first case would | ||||
| 				 * not require any extra work, but the second case would | ||||
| 				 * require updating the WHEN expression, which will take a | ||||
| 				 * significant amount of new code.  Since we can't easily tell | ||||
| 				 * which case applies, we punt for both.  FIXME someday. | ||||
| 				 */ | ||||
| 				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, false), | ||||
| 								   colName))); | ||||
| 				break; | ||||
| 
 | ||||
| 			case OCLASS_POLICY: | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * A policy can depend on a column because the column is | ||||
| 				 * specified in the policy's USING or WITH CHECK qual | ||||
| 				 * expressions.  It might be possible to rewrite and recheck | ||||
| 				 * the policy expression, but punt for now.  It's certainly | ||||
| 				 * easy enough to remove and recreate the policy; still, FIXME | ||||
| 				 * someday. | ||||
| 				 */ | ||||
| 				ereport(ERROR, | ||||
| 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 						 errmsg("cannot alter type of a column used in a policy definition"), | ||||
| 						 errdetail("%s depends on column \"%s\"", | ||||
| 								   getObjectDescription(&foundObject, false), | ||||
| 								   colName))); | ||||
| 				break; | ||||
| 
 | ||||
| 			case OCLASS_DEFAULT: | ||||
| 				{ | ||||
| 					ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId); | ||||
| 
 | ||||
| 					if (col.objectId == RelationGetRelid(rel) && | ||||
| 						col.objectSubId == attnum) | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * Ignore the column's own default expression.  The | ||||
| 						 * caller deals with it. | ||||
| 						 */ | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * This must be a reference from the expression of a | ||||
| 						 * generated column elsewhere in the same table. | ||||
| 						 * Changing the type of a column that is used by a | ||||
| 						 * generated column is not allowed by SQL standard, so | ||||
| 						 * just punt for now.  It might be doable with some | ||||
| 						 * thinking and effort. | ||||
| 						 */ | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 								 errmsg("cannot alter type of a column used by a generated column"), | ||||
| 								 errdetail("Column \"%s\" is used by generated column \"%s\".", | ||||
| 										   colName, | ||||
| 										   get_attname(col.objectId, | ||||
| 													   col.objectSubId, | ||||
| 													   false)))); | ||||
| 					} | ||||
| 					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, NoLock); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Subroutine for ATExecAlterColumnType: remember that a replica identity | ||||
|  * needs to be reset. | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user