@@ -36,6 +36,8 @@ static void partition_filter_visitor(Plan *plan, void *context);
3636
3737static rel_parenthood_status tag_extract_parenthood_status (List * relation_tag );
3838
39+ static Oid find_deepest_partition (Oid relid , Index idx , Expr * quals );
40+
3941
4042/*
4143 * HACK: We have to mark each Query with a unique
@@ -238,14 +240,10 @@ disable_standard_inheritance(Query *parse)
238240static void
239241handle_modification_query (Query * parse )
240242{
241- const PartRelationInfo * prel ;
242- Node * prel_expr ;
243- List * ranges ;
244243 RangeTblEntry * rte ;
245- WrapperNode * wrap ;
246- Expr * expr ;
247- WalkerContext context ;
244+ Expr * quals ;
248245 Index result_rel ;
246+ Oid child ;
249247
250248 /* Fetch index of result relation */
251249 result_rel = parse -> resultRelation ;
@@ -261,101 +259,133 @@ handle_modification_query(Query *parse)
261259 /* Exit if it's DELETE FROM ONLY table */
262260 if (!rte -> inh ) return ;
263261
264- prel = get_pathman_relation_info (rte -> relid );
265-
266- /* Exit if it's not partitioned */
267- if (!prel ) return ;
268-
269- /* Exit if we must include parent */
270- if (prel -> enable_parent ) return ;
271-
272- /* Parse syntax tree and extract partition ranges */
273- ranges = list_make1_irange_full (prel , IR_COMPLETE );
274- expr = (Expr * ) eval_const_expressions (NULL , parse -> jointree -> quals );
275-
276- /* Exit if there's no expr (no use) */
277- if (!expr ) return ;
278-
279- /* Prepare partitioning expression */
280- prel_expr = PrelExpressionForRelid (prel , result_rel );
281-
282- /* Parse syntax tree and extract partition ranges */
283- InitWalkerContext (& context , prel_expr , prel , NULL , false);
284- wrap = walk_expr_tree (expr , & context );
262+ quals = (Expr * ) eval_const_expressions (NULL , parse -> jointree -> quals );
285263
286- ranges = irange_list_intersection (ranges , wrap -> rangeset );
264+ /*
265+ * Parse syntax tree and extract deepest partition (if there is only one
266+ * satisfying quals)
267+ */
268+ child = find_deepest_partition (rte -> relid , result_rel , quals );
287269
288270 /*
289271 * If only one partition is affected,
290272 * substitute parent table with partition.
291273 */
292- if (irange_list_length ( ranges ) == 1 )
274+ if (OidIsValid ( child ) )
293275 {
294- IndexRange irange = linitial_irange (ranges );
276+ Relation child_rel ,
277+ parent_rel ;
295278
296- /* Exactly one partition (bounds are equal) */
297- if (irange_lower (irange ) == irange_upper (irange ))
279+ void * tuple_map ; /* we don't need the map itself */
280+
281+ LOCKMODE lockmode = RowExclusiveLock ; /* UPDATE | DELETE */
282+
283+ HeapTuple syscache_htup ;
284+ char child_relkind ;
285+ Oid parent = rte -> relid ;
286+
287+ /* Lock 'child' table */
288+ LockRelationOid (child , lockmode );
289+
290+ /* Make sure that 'child' exists */
291+ syscache_htup = SearchSysCache1 (RELOID , ObjectIdGetDatum (child ));
292+ if (HeapTupleIsValid (syscache_htup ))
298293 {
299- Oid * children = PrelGetChildrenArray (prel ),
300- child = children [irange_lower (irange )],
301- parent = rte -> relid ;
294+ Form_pg_class reltup = (Form_pg_class ) GETSTRUCT (syscache_htup );
302295
303- Relation child_rel ,
304- parent_rel ;
296+ /* Fetch child's relkind and free cache entry */
297+ child_relkind = reltup -> relkind ;
298+ ReleaseSysCache (syscache_htup );
299+ }
300+ else
301+ {
302+ UnlockRelationOid (child , lockmode );
303+ return ; /* nothing to do here */
304+ }
305305
306- void * tuple_map ; /* we don't need the map itself */
306+ /* Both tables are already locked */
307+ child_rel = heap_open (child , NoLock );
308+ parent_rel = heap_open (parent , NoLock );
307309
308- LOCKMODE lockmode = RowExclusiveLock ; /* UPDATE | DELETE */
310+ /* Build a conversion map (may be trivial, i.e. NULL) */
311+ tuple_map = build_part_tuple_map (parent_rel , child_rel );
312+ if (tuple_map )
313+ free_conversion_map ((TupleConversionMap * ) tuple_map );
309314
310- HeapTuple syscache_htup ;
311- char child_relkind ;
315+ /* Close relations (should remain locked, though) */
316+ heap_close (child_rel , NoLock );
317+ heap_close (parent_rel , NoLock );
312318
313- /* Lock 'child' table */
314- LockRelationOid (child , lockmode );
319+ /* Exit if tuple map was NOT trivial */
320+ if (tuple_map ) /* just checking the pointer! */
321+ return ;
315322
316- /* Make sure that 'child' exists */
317- syscache_htup = SearchSysCache1 (RELOID , ObjectIdGetDatum (child ));
318- if (HeapTupleIsValid (syscache_htup ))
319- {
320- Form_pg_class reltup = (Form_pg_class ) GETSTRUCT (syscache_htup );
323+ /* Update RTE's relid and relkind (for FDW) */
324+ rte -> relid = child ;
325+ rte -> relkind = child_relkind ;
321326
322- /* Fetch child's relkind and free cache entry */
323- child_relkind = reltup -> relkind ;
324- ReleaseSysCache (syscache_htup );
325- }
326- else
327- {
328- UnlockRelationOid (child , lockmode );
329- return ; /* nothing to do here */
330- }
327+ /* HACK: unset the 'inh' flag (no children) */
328+ rte -> inh = false;
329+ }
330+ }
331+
332+ /*
333+ * Find a single deepest subpartition. If there are more than one partitions
334+ * satisfies quals or no such partition at all then return InvalidOid.
335+ */
336+ static Oid
337+ find_deepest_partition (Oid relid , Index idx , Expr * quals )
338+ {
339+ const PartRelationInfo * prel ;
340+ Node * prel_expr ;
341+ WalkerContext context ;
342+ List * ranges ;
343+ WrapperNode * wrap ;
344+
345+ /* Exit if there's no quals (no use) */
346+ if (!quals ) return InvalidOid ;
347+
348+ prel = get_pathman_relation_info (relid );
349+
350+ /* Exit if it's not partitioned */
351+ if (!prel ) return InvalidOid ;
331352
332- /* Both tables are already locked */
333- child_rel = heap_open (child , NoLock );
334- parent_rel = heap_open (parent , NoLock );
353+ /* Exit if we must include parent */
354+ if (prel -> enable_parent ) return InvalidOid ;
335355
336- /* Build a conversion map (may be trivial, i.e. NULL) */
337- tuple_map = build_part_tuple_map (parent_rel , child_rel );
338- if (tuple_map )
339- free_conversion_map ((TupleConversionMap * ) tuple_map );
356+ /* Prepare partitioning expression */
357+ prel_expr = PrelExpressionForRelid (prel , idx );
340358
341- /* Close relations (should remain locked, though) */
342- heap_close (child_rel , NoLock );
343- heap_close (parent_rel , NoLock );
359+ ranges = list_make1_irange_full (prel , IR_COMPLETE );
344360
345- /* Exit if tuple map was NOT trivial */
346- if (tuple_map ) /* just checking the pointer! */
347- return ;
361+ /* Parse syntax tree and extract partition ranges */
362+ InitWalkerContext (& context , prel_expr , prel , NULL , false);
363+ wrap = walk_expr_tree (quals , & context );
364+ ranges = irange_list_intersection (ranges , wrap -> rangeset );
348365
349- /* Update RTE's relid and relkind (for FDW) */
350- rte -> relid = child ;
351- rte -> relkind = child_relkind ;
366+ if (irange_list_length (ranges ) == 1 )
367+ {
368+ IndexRange irange = linitial_irange (ranges );
369+
370+ if (irange_lower (irange ) == irange_upper (irange ))
371+ {
372+ Oid * children = PrelGetChildrenArray (prel ),
373+ partition = children [irange_lower (irange )],
374+ subpartition ;
375+
376+ /*
377+ * Try to go deeper and see if there is subpartition
378+ */
379+ subpartition = find_deepest_partition (partition , idx , quals );
380+ if (OidIsValid (subpartition ))
381+ return subpartition ;
352382
353- /* HACK: unset the 'inh' flag (no children) */
354- rte -> inh = false;
383+ return partition ;
355384 }
356385 }
357- }
358386
387+ return InvalidOid ;
388+ }
359389
360390/*
361391 * -------------------------------
0 commit comments