mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Ignore whole-rows in INSERT/CONFLICT with partitioned tables
We had an Assert() preventing whole-row expressions from being used in the SET clause of INSERT ON CONFLICT, but it seems unnecessary, given some tests, so remove it. Add a new test to exercise the case. Still at ExecInitPartitionInfo, we used map_partition_varattnos (which constructs an attribute map, then calls map_variable_attnos) using the same two relations many times in different expressions and with different parameters. Constructing the map over and over is a waste. To avoid this repeated work, construct the map once, and use map_variable_attnos() directly instead. Author: Amit Langote, per comments by me (Álvaro) Discussion: https://postgr.es/m/20180326142016.m4st5e34chrzrknk@alvherre.pgsql
This commit is contained in:
		
							parent
							
								
									3a2d636598
								
							
						
					
					
						commit
						158b7bc6d7
					
				| @ -24,6 +24,7 @@ | |||||||
| #include "nodes/makefuncs.h" | #include "nodes/makefuncs.h" | ||||||
| #include "partitioning/partbounds.h" | #include "partitioning/partbounds.h" | ||||||
| #include "partitioning/partprune.h" | #include "partitioning/partprune.h" | ||||||
|  | #include "rewrite/rewriteManip.h" | ||||||
| #include "utils/lsyscache.h" | #include "utils/lsyscache.h" | ||||||
| #include "utils/partcache.h" | #include "utils/partcache.h" | ||||||
| #include "utils/rel.h" | #include "utils/rel.h" | ||||||
| @ -307,8 +308,12 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 	ModifyTable *node = (ModifyTable *) mtstate->ps.plan; | 	ModifyTable *node = (ModifyTable *) mtstate->ps.plan; | ||||||
| 	Relation	rootrel = resultRelInfo->ri_RelationDesc, | 	Relation	rootrel = resultRelInfo->ri_RelationDesc, | ||||||
| 				partrel; | 				partrel; | ||||||
|  | 	Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; | ||||||
| 	ResultRelInfo *leaf_part_rri; | 	ResultRelInfo *leaf_part_rri; | ||||||
| 	MemoryContext oldContext; | 	MemoryContext oldContext; | ||||||
|  | 	AttrNumber *part_attnos = NULL; | ||||||
|  | 	bool		found_whole_row; | ||||||
|  | 	bool		equalTupdescs; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * We locked all the partitions in ExecSetupPartitionTupleRouting | 	 * We locked all the partitions in ExecSetupPartitionTupleRouting | ||||||
| @ -356,6 +361,10 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 						(node != NULL && | 						(node != NULL && | ||||||
| 						 node->onConflictAction != ONCONFLICT_NONE)); | 						 node->onConflictAction != ONCONFLICT_NONE)); | ||||||
| 
 | 
 | ||||||
|  | 	/* if tuple descs are identical, we don't need to map the attrs */ | ||||||
|  | 	equalTupdescs = equalTupleDescs(RelationGetDescr(partrel), | ||||||
|  | 									RelationGetDescr(firstResultRel)); | ||||||
|  | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Build WITH CHECK OPTION constraints for the partition.  Note that we | 	 * Build WITH CHECK OPTION constraints for the partition.  Note that we | ||||||
| 	 * didn't build the withCheckOptionList for partitions within the planner, | 	 * didn't build the withCheckOptionList for partitions within the planner, | ||||||
| @ -369,7 +378,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 		List	   *wcoExprs = NIL; | 		List	   *wcoExprs = NIL; | ||||||
| 		ListCell   *ll; | 		ListCell   *ll; | ||||||
| 		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; | 		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; | ||||||
| 		Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; |  | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * In the case of INSERT on a partitioned table, there is only one | 		 * In the case of INSERT on a partitioned table, there is only one | ||||||
| @ -397,8 +405,22 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Convert Vars in it to contain this partition's attribute numbers. | 		 * Convert Vars in it to contain this partition's attribute numbers. | ||||||
| 		 */ | 		 */ | ||||||
| 		wcoList = map_partition_varattnos(wcoList, firstVarno, | 		if (!equalTupdescs) | ||||||
| 										  partrel, firstResultRel, NULL); | 		{ | ||||||
|  | 			part_attnos = | ||||||
|  | 				convert_tuples_by_name_map(RelationGetDescr(partrel), | ||||||
|  | 										   RelationGetDescr(firstResultRel), | ||||||
|  | 										   gettext_noop("could not convert row type")); | ||||||
|  | 			wcoList = (List *) | ||||||
|  | 				map_variable_attnos((Node *) wcoList, | ||||||
|  | 									firstVarno, 0, | ||||||
|  | 									part_attnos, | ||||||
|  | 									RelationGetDescr(firstResultRel)->natts, | ||||||
|  | 									RelationGetForm(partrel)->reltype, | ||||||
|  | 									&found_whole_row); | ||||||
|  | 			/* We ignore the value of found_whole_row. */ | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		foreach(ll, wcoList) | 		foreach(ll, wcoList) | ||||||
| 		{ | 		{ | ||||||
| 			WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll)); | 			WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll)); | ||||||
| @ -425,7 +447,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 		ExprContext *econtext; | 		ExprContext *econtext; | ||||||
| 		List	   *returningList; | 		List	   *returningList; | ||||||
| 		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; | 		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; | ||||||
| 		Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; |  | ||||||
| 
 | 
 | ||||||
| 		/* See the comment above for WCO lists. */ | 		/* See the comment above for WCO lists. */ | ||||||
| 		Assert((node->operation == CMD_INSERT && | 		Assert((node->operation == CMD_INSERT && | ||||||
| @ -443,12 +464,26 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 		 */ | 		 */ | ||||||
| 		returningList = linitial(node->returningLists); | 		returningList = linitial(node->returningLists); | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		if (!equalTupdescs) | ||||||
| 		 * Convert Vars in it to contain this partition's attribute numbers. | 		{ | ||||||
| 		 */ | 			/*
 | ||||||
| 		returningList = map_partition_varattnos(returningList, firstVarno, | 			 * Convert Vars in it to contain this partition's attribute numbers. | ||||||
| 												partrel, firstResultRel, | 			 */ | ||||||
| 												NULL); | 			if (part_attnos == NULL) | ||||||
|  | 				part_attnos = | ||||||
|  | 					convert_tuples_by_name_map(RelationGetDescr(partrel), | ||||||
|  | 											   RelationGetDescr(firstResultRel), | ||||||
|  | 											   gettext_noop("could not convert row type")); | ||||||
|  | 			returningList = (List *) | ||||||
|  | 				map_variable_attnos((Node *) returningList, | ||||||
|  | 									firstVarno, 0, | ||||||
|  | 									part_attnos, | ||||||
|  | 									RelationGetDescr(firstResultRel)->natts, | ||||||
|  | 									RelationGetForm(partrel)->reltype, | ||||||
|  | 									&found_whole_row); | ||||||
|  | 			/* We ignore the value of found_whole_row. */ | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		leaf_part_rri->ri_returningList = returningList; | 		leaf_part_rri->ri_returningList = returningList; | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| @ -473,7 +508,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 	{ | 	{ | ||||||
| 		TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx]; | 		TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx]; | ||||||
| 		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; | 		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; | ||||||
| 		Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; |  | ||||||
| 		TupleDesc	partrelDesc = RelationGetDescr(partrel); | 		TupleDesc	partrelDesc = RelationGetDescr(partrel); | ||||||
| 		ExprContext *econtext = mtstate->ps.ps_ExprContext; | 		ExprContext *econtext = mtstate->ps.ps_ExprContext; | ||||||
| 		ListCell   *lc; | 		ListCell   *lc; | ||||||
| @ -549,17 +583,33 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 				 * target relation (firstVarno). | 				 * target relation (firstVarno). | ||||||
| 				 */ | 				 */ | ||||||
| 				onconflset = (List *) copyObject((Node *) node->onConflictSet); | 				onconflset = (List *) copyObject((Node *) node->onConflictSet); | ||||||
| 				onconflset = | 				if (!equalTupdescs) | ||||||
| 					map_partition_varattnos(onconflset, INNER_VAR, partrel, | 				{ | ||||||
| 											firstResultRel, &found_whole_row); | 					if (part_attnos == NULL) | ||||||
| 				Assert(!found_whole_row); | 						part_attnos = | ||||||
| 				onconflset = | 							convert_tuples_by_name_map(RelationGetDescr(partrel), | ||||||
| 					map_partition_varattnos(onconflset, firstVarno, partrel, | 													   RelationGetDescr(firstResultRel), | ||||||
| 											firstResultRel, &found_whole_row); | 													   gettext_noop("could not convert row type")); | ||||||
| 				Assert(!found_whole_row); | 					onconflset = (List *) | ||||||
|  | 						map_variable_attnos((Node *) onconflset, | ||||||
|  | 											INNER_VAR, 0, | ||||||
|  | 											part_attnos, | ||||||
|  | 											RelationGetDescr(firstResultRel)->natts, | ||||||
|  | 											RelationGetForm(partrel)->reltype, | ||||||
|  | 											&found_whole_row); | ||||||
|  | 					/* We ignore the value of found_whole_row. */ | ||||||
|  | 					onconflset = (List *) | ||||||
|  | 						map_variable_attnos((Node *) onconflset, | ||||||
|  | 											firstVarno, 0, | ||||||
|  | 											part_attnos, | ||||||
|  | 											RelationGetDescr(firstResultRel)->natts, | ||||||
|  | 											RelationGetForm(partrel)->reltype, | ||||||
|  | 											&found_whole_row); | ||||||
|  | 					/* We ignore the value of found_whole_row. */ | ||||||
| 
 | 
 | ||||||
| 				/* Finally, adjust this tlist to match the partition. */ | 					/* Finally, adjust this tlist to match the partition. */ | ||||||
| 				onconflset = adjust_partition_tlist(onconflset, map); | 					onconflset = adjust_partition_tlist(onconflset, map); | ||||||
|  | 				} | ||||||
| 
 | 
 | ||||||
| 				/*
 | 				/*
 | ||||||
| 				 * Build UPDATE SET's projection info.  The user of this | 				 * Build UPDATE SET's projection info.  The user of this | ||||||
| @ -587,14 +637,25 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, | |||||||
| 					List	   *clause; | 					List	   *clause; | ||||||
| 
 | 
 | ||||||
| 					clause = copyObject((List *) node->onConflictWhere); | 					clause = copyObject((List *) node->onConflictWhere); | ||||||
| 					clause = map_partition_varattnos(clause, INNER_VAR, | 					if (!equalTupdescs) | ||||||
| 													 partrel, firstResultRel, | 					{ | ||||||
| 													 &found_whole_row); | 						clause = (List *) | ||||||
| 					Assert(!found_whole_row); | 							map_variable_attnos((Node *) clause, | ||||||
| 					clause = map_partition_varattnos(clause, firstVarno, | 												INNER_VAR, 0, | ||||||
| 													 partrel, firstResultRel, | 												part_attnos, | ||||||
| 													 &found_whole_row); | 												RelationGetDescr(firstResultRel)->natts, | ||||||
| 					Assert(!found_whole_row); | 												RelationGetForm(partrel)->reltype, | ||||||
|  | 												&found_whole_row); | ||||||
|  | 						/* We ignore the value of found_whole_row. */ | ||||||
|  | 						clause = (List *) | ||||||
|  | 							map_variable_attnos((Node *) clause, | ||||||
|  | 												firstVarno, 0, | ||||||
|  | 												part_attnos, | ||||||
|  | 												RelationGetDescr(firstResultRel)->natts, | ||||||
|  | 												RelationGetForm(partrel)->reltype, | ||||||
|  | 												&found_whole_row); | ||||||
|  | 						/* We ignore the value of found_whole_row. */ | ||||||
|  | 					} | ||||||
| 					leaf_part_rri->ri_onConflict->oc_WhereClause = | 					leaf_part_rri->ri_onConflict->oc_WhereClause = | ||||||
| 						ExecInitQual((List *) clause, &mtstate->ps); | 						ExecInitQual((List *) clause, &mtstate->ps); | ||||||
| 				} | 				} | ||||||
|  | |||||||
| @ -884,4 +884,20 @@ insert into parted_conflict values (40, 'forty'); | |||||||
| insert into parted_conflict_1 values (40, 'cuarenta') | insert into parted_conflict_1 values (40, 'cuarenta') | ||||||
|   on conflict (a) do update set b = excluded.b; |   on conflict (a) do update set b = excluded.b; | ||||||
| ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification | ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification | ||||||
|  | -- test whole-row Vars in ON CONFLICT expressions | ||||||
|  | create unique index on parted_conflict (a, b); | ||||||
|  | alter table parted_conflict add c int; | ||||||
|  | truncate parted_conflict; | ||||||
|  | insert into parted_conflict values (50, 'cincuenta', 1); | ||||||
|  | insert into parted_conflict values (50, 'cincuenta', 2) | ||||||
|  |   on conflict (a, b) do update set (a, b, c) = row(excluded.*) | ||||||
|  |   where parted_conflict = (50, text 'cincuenta', 1) and | ||||||
|  |         excluded = (50, text 'cincuenta', 2); | ||||||
|  | -- should see (50, 'cincuenta', 2) | ||||||
|  | select * from parted_conflict order by a; | ||||||
|  |  a  |     b     | c  | ||||||
|  | ----+-----------+--- | ||||||
|  |  50 | cincuenta | 2 | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
| drop table parted_conflict; | drop table parted_conflict; | ||||||
|  | |||||||
| @ -559,3 +559,21 @@ insert into parted_conflict values (40, 'forty'); | |||||||
| insert into parted_conflict_1 values (40, 'cuarenta') | insert into parted_conflict_1 values (40, 'cuarenta') | ||||||
|   on conflict (a) do update set b = excluded.b; |   on conflict (a) do update set b = excluded.b; | ||||||
| drop table parted_conflict; | drop table parted_conflict; | ||||||
|  | 
 | ||||||
|  | -- test whole-row Vars in ON CONFLICT expressions | ||||||
|  | create table parted_conflict (a int, b text, c int) partition by range (a); | ||||||
|  | create table parted_conflict_1 (drp text, c int, a int, b text); | ||||||
|  | alter table parted_conflict_1 drop column drp; | ||||||
|  | create unique index on parted_conflict (a, b); | ||||||
|  | alter table parted_conflict attach partition parted_conflict_1 for values from (0) to (1000); | ||||||
|  | truncate parted_conflict; | ||||||
|  | insert into parted_conflict values (50, 'cincuenta', 1); | ||||||
|  | insert into parted_conflict values (50, 'cincuenta', 2) | ||||||
|  |   on conflict (a, b) do update set (a, b, c) = row(excluded.*) | ||||||
|  |   where parted_conflict = (50, text 'cincuenta', 1) and | ||||||
|  |         excluded = (50, text 'cincuenta', 2); | ||||||
|  | 
 | ||||||
|  | -- should see (50, 'cincuenta', 2) | ||||||
|  | select * from parted_conflict order by a; | ||||||
|  | 
 | ||||||
|  | drop table parted_conflict; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user