Skip to content

Commit 5a12ecc

Browse files
committed
make UPDATE and DELETE queries work with multilevel partitioning
1 parent 29619a5 commit 5a12ecc

File tree

1 file changed

+106
-76
lines changed

1 file changed

+106
-76
lines changed

src/planner_tree_modification.c

Lines changed: 106 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ static void partition_filter_visitor(Plan *plan, void *context);
3636

3737
static 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)
238240
static void
239241
handle_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

Comments
 (0)