Skip to content

Commit

Permalink
Add chunk exclusion for UPDATE for PG14
Browse files Browse the repository at this point in the history
Currently only IMMUTABLE constraints will exclude chunks from an UPDATE plan,
with this patch STABLE expressions will be used to exclude chunks as well.
This is a big performance improvement as chunks not matching partitioning
column constraints don't have to be scanned for UPDATEs.
Since the codepath for UPDATE is different for PG < 14 this patch only adds
the optimization for PG14.

With this patch the plan for UPDATE on hypertables looks like this:

 Custom Scan (HypertableModify) (actual rows=0 loops=1)
   ->  Update on public.metrics_int2 (actual rows=0 loops=1)
         Update on public.metrics_int2 metrics_int2_1
         Update on _timescaledb_internal._hyper_1_1_chunk metrics_int2
         Update on _timescaledb_internal._hyper_1_2_chunk metrics_int2
         Update on _timescaledb_internal._hyper_1_3_chunk metrics_int2
         ->  Custom Scan (ChunkAppend) on public.metrics_int2 (actual rows=0 loops=1)
               Output: '123'::text, metrics_int2.tableoid, metrics_int2.ctid
               Startup Exclusion: true
               Runtime Exclusion: false
               Chunks excluded during startup: 3
               ->  Seq Scan on public.metrics_int2 metrics_int2_1 (actual rows=0 loops=1)
                     Output: metrics_int2_1.tableoid, metrics_int2_1.ctid
                     Filter: (metrics_int2_1."time" = length(version()))
  • Loading branch information
svenklemm committed Apr 4, 2022
1 parent a154ae5 commit bceee3e
Show file tree
Hide file tree
Showing 6 changed files with 1,208 additions and 20 deletions.
6 changes: 6 additions & 0 deletions src/nodes/hypertable_modify.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,12 @@ hypertable_modify_plan_create(PlannerInfo *root, RelOptInfo *rel, CustomPath *be
{
cscan->scan.plan.targetlist =
ts_replace_rowid_vars(root, cscan->scan.plan.targetlist, mt->nominalRelation);

if (mt->operation == CMD_UPDATE && ts_is_chunk_append_plan(mt->plan.lefttree))
{
mt->plan.lefttree->targetlist =
ts_replace_rowid_vars(root, mt->plan.lefttree->targetlist, mt->nominalRelation);
}
}
#else
/*
Expand Down
22 changes: 12 additions & 10 deletions src/planner.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,18 +679,19 @@ static inline bool
should_chunk_append(Hypertable *ht, PlannerInfo *root, RelOptInfo *rel, Path *path, bool ordered,
int order_attno)
{
if (!(root->parse->commandType == CMD_SELECT || root->parse->commandType == CMD_DELETE) ||
if (
#if PG14_LT
root->parse->commandType != CMD_SELECT ||
#else
/*
* We only support chunk exclusion on UPDATE/DELETE when no JOIN is involved on PG14+.
*/
((root->parse->commandType == CMD_DELETE || root->parse->commandType == CMD_UPDATE) &&
bms_num_members(root->all_baserels) > 1) ||
#endif
!ts_guc_enable_chunk_append || hypertable_is_distributed(ht))
return false;

#if PG14_GE
/*
* We only support chunk exclusion on DELETE when no JOIN is involved on PG14+.
*/
if (root->parse->commandType == CMD_DELETE && bms_num_members(root->all_baserels) > 1)
return false;
#endif

switch (nodeTag(path))
{
case T_AppendPath:
Expand Down Expand Up @@ -932,7 +933,8 @@ apply_optimizations(PlannerInfo *root, TsRelType reltype, RelOptInfo *rel, Range

if (reltype == TS_REL_HYPERTABLE &&
#if PG14_GE
(root->parse->commandType == CMD_SELECT || root->parse->commandType == CMD_DELETE)
(root->parse->commandType == CMD_SELECT || root->parse->commandType == CMD_DELETE ||
root->parse->commandType == CMD_UPDATE)
#else
/*
* For PG < 14 commandType will be CMD_SELECT even when planning DELETE so we
Expand Down
18 changes: 9 additions & 9 deletions test/expected/rowsecurity-14.out
Original file line number Diff line number Diff line change
Expand Up @@ -2265,19 +2265,19 @@ INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check
ERROR: new row violates row-level security policy for table "b1"
INSERT INTO bv1 VALUES (12, 'xxx'); -- ok
EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
QUERY PLAN
-----------------------------------------------------------------------------------------------
QUERY PLAN
-----------------------------------------------------------------------------------------
Custom Scan (HypertableModify)
-> Update on b1
Update on b1 b1_1
Update on _hyper_8_41_chunk b1_2
-> Result
-> Append
-> Seq Scan on b1 b1_1
Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0) AND f_leak(b))
-> Index Scan using _hyper_8_41_chunk_b1_a_idx on _hyper_8_41_chunk b1_2
Index Cond: ((a > 0) AND (a = 4))
Filter: (((a % 2) = 0) AND f_leak(b))
-> Custom Scan (ChunkAppend) on b1
Chunks excluded during startup: 0
-> Seq Scan on b1 b1_1
Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0) AND f_leak(b))
-> Index Scan using _hyper_8_41_chunk_b1_a_idx on _hyper_8_41_chunk b1_2
Index Cond: ((a > 0) AND (a = 4))
Filter: (((a % 2) = 0) AND f_leak(b))
(11 rows)

UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
Expand Down
Loading

0 comments on commit bceee3e

Please sign in to comment.