@@ -36,6 +36,8 @@ static void partition_filter_visitor(Plan *plan, void *context);
36
36
37
37
static rel_parenthood_status tag_extract_parenthood_status (List * relation_tag );
38
38
39
+ static Oid find_deepest_partition (Oid relid , Index idx , Expr * quals );
40
+
39
41
40
42
/*
41
43
* HACK: We have to mark each Query with a unique
@@ -238,14 +240,10 @@ disable_standard_inheritance(Query *parse)
238
240
static void
239
241
handle_modification_query (Query * parse )
240
242
{
241
- const PartRelationInfo * prel ;
242
- Node * prel_expr ;
243
- List * ranges ;
244
243
RangeTblEntry * rte ;
245
- WrapperNode * wrap ;
246
- Expr * expr ;
247
- WalkerContext context ;
244
+ Expr * quals ;
248
245
Index result_rel ;
246
+ Oid child ;
249
247
250
248
/* Fetch index of result relation */
251
249
result_rel = parse -> resultRelation ;
@@ -261,101 +259,133 @@ handle_modification_query(Query *parse)
261
259
/* Exit if it's DELETE FROM ONLY table */
262
260
if (!rte -> inh ) return ;
263
261
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 );
285
263
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 );
287
269
288
270
/*
289
271
* If only one partition is affected,
290
272
* substitute parent table with partition.
291
273
*/
292
- if (irange_list_length ( ranges ) == 1 )
274
+ if (OidIsValid ( child ) )
293
275
{
294
- IndexRange irange = linitial_irange (ranges );
276
+ Relation child_rel ,
277
+ parent_rel ;
295
278
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 ))
298
293
{
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 );
302
295
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
+ }
305
305
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 );
307
309
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 );
309
314
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 );
312
318
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 ;
315
322
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 ;
321
326
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 ;
331
352
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 ;
335
355
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 );
340
358
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 );
344
360
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 );
348
365
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 ;
352
382
353
- /* HACK: unset the 'inh' flag (no children) */
354
- rte -> inh = false;
383
+ return partition ;
355
384
}
356
385
}
357
- }
358
386
387
+ return InvalidOid ;
388
+ }
359
389
360
390
/*
361
391
* -------------------------------
0 commit comments