From c22c271db9dd382a3259eec0d029dd562822df95 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Fri, 3 May 2024 19:59:22 +0200 Subject: [PATCH] Allow DELETE on compressed chunks without decompression When the constraints of a DELETE on a compressed chunks fully cover the batches we can optimize the DELETE to work directly on the compressed batches and skip the expensive decompression part. --- src/nodes/hypertable_modify.c | 2 + src/nodes/hypertable_modify.h | 1 + tsl/src/compression/compression.c | 119 +++++++++++++++++++++++++++++- tsl/src/compression/compression.h | 3 + 4 files changed, 121 insertions(+), 4 deletions(-) diff --git a/src/nodes/hypertable_modify.c b/src/nodes/hypertable_modify.c index 415fe47d27a..abd281fece7 100644 --- a/src/nodes/hypertable_modify.c +++ b/src/nodes/hypertable_modify.c @@ -257,6 +257,8 @@ hypertable_modify_explain(CustomScanState *node, List *ancestors, ExplainState * state->tuples_decompressed += cds->tuples_decompressed; } } + if (state->batches_deleted > 0) + ExplainPropertyInteger("Batches deleted", NULL, state->batches_deleted, es); if (state->batches_decompressed > 0) ExplainPropertyInteger("Batches decompressed", NULL, state->batches_decompressed, es); if (state->tuples_decompressed > 0) diff --git a/src/nodes/hypertable_modify.h b/src/nodes/hypertable_modify.h index 2674c182e49..5ce9271c298 100644 --- a/src/nodes/hypertable_modify.h +++ b/src/nodes/hypertable_modify.h @@ -29,6 +29,7 @@ typedef struct HypertableModifyState Snapshot snapshot; int64 tuples_decompressed; int64 batches_decompressed; + int64 batches_deleted; } HypertableModifyState; extern void ts_hypertable_modify_fixup_tlist(Plan *plan); diff --git a/tsl/src/compression/compression.c b/tsl/src/compression/compression.c index 08214bbf60d..650e73ebec8 100644 --- a/tsl/src/compression/compression.c +++ b/tsl/src/compression/compression.c @@ -2865,8 +2865,15 @@ decompress_batches(RowDecompressor *decompressor, ScanKeyData *scankeys, int num table_endscan(scan); report_error(result); } - row_decompressor_decompress_row_to_table(decompressor); - *chunk_status_changed = true; + if (decompressor->delete_only) + { + decompressor->batches_deleted++; + } + else + { + row_decompressor_decompress_row_to_table(decompressor); + *chunk_status_changed = true; + } } if (scankeys) pfree(scankeys); @@ -3043,8 +3050,15 @@ decompress_batches_using_index(RowDecompressor *decompressor, Relation index_rel index_close(index_rel, AccessShareLock); report_error(result); } - row_decompressor_decompress_row_to_table(decompressor); - *chunk_status_changed = true; + if (decompressor->delete_only) + { + decompressor->batches_deleted++; + } + else + { + row_decompressor_decompress_row_to_table(decompressor); + *chunk_status_changed = true; + } } if (ts_guc_debug_compression_path_info) @@ -3062,6 +3076,96 @@ decompress_batches_using_index(RowDecompressor *decompressor, Relation index_rel return true; } +static bool +extract_operands(Node *node, Var **var, Const **arg_value) +{ + switch (nodeTag(node)) + { + case T_OpExpr: + { + OpExpr *opexpr = (OpExpr *) node; + + Expr *leftop = linitial(opexpr->args); + Expr *rightop = lsecond(opexpr->args); + + if (IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; + if (IsA(rightop, RelabelType)) + rightop = ((RelabelType *) rightop)->arg; + + if (IsA(leftop, Var) && IsA(rightop, Const)) + { + *var = (Var *) leftop; + *arg_value = (Const *) rightop; + return true; + } + else if (IsA(rightop, Var) && IsA(leftop, Const)) + { + *var = (Var *) rightop; + *arg_value = (Const *) leftop; + return true; + } + break; + } + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node; + + Expr *leftop = linitial(opexpr->args); + Expr *rightop = lsecond(opexpr->args); + + if (IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; + if (IsA(rightop, RelabelType)) + rightop = ((RelabelType *) rightop)->arg; + + if (IsA(leftop, Var) && IsA(rightop, Const)) + { + *var = (Var *) leftop; + *arg_value = (Const *) rightop; + return true; + } + else if (IsA(rightop, Var) && IsA(leftop, Const)) + { + *var = (Var *) rightop; + *arg_value = (Const *) leftop; + return true; + } + break; + } + default: + elog(NOTICE, "Unsupported node type %d", nodeTag(node)); + break; + } + + return false; +} + +static bool +can_delete_without_decompression(CompressionSettings *settings, Chunk *chunk, List *predicates) +{ + ListCell *lc; + + foreach (lc, predicates) + { + Node *node = lfirst(lc); + Var *var; + Const *arg_value; + + if (extract_operands(node, &var, &arg_value)) + { + char *column_name = get_attname(chunk->table_id, var->varattno, false); + if (ts_array_is_member(settings->fd.segmentby, column_name)) + { + continue; + } + } + return false; + } + + return true; +} + /* * This method will: * 1. Evaluate WHERE clauses and check if SEGMENT BY columns @@ -3103,6 +3207,12 @@ decompress_batches_for_update_delete(HypertableModifyState *ht_state, Chunk *chu comp_chunk_rel = table_open(comp_chunk->table_id, RowExclusiveLock); decompressor = build_decompressor(comp_chunk_rel, chunk_rel); + if (ht_state->mt->operation == CMD_DELETE && + can_delete_without_decompression(settings, chunk, predicates)) + { + decompressor.delete_only = true; + } + if (index_filters) { matching_index_rel = find_matching_index(comp_chunk_rel, &index_filters, &heap_filters); @@ -3163,6 +3273,7 @@ decompress_batches_for_update_delete(HypertableModifyState *ht_state, Chunk *chu filter = lfirst(lc); pfree(filter); } + ht_state->batches_deleted += decompressor.batches_deleted; ht_state->batches_decompressed += decompressor.batches_decompressed; ht_state->tuples_decompressed += decompressor.tuples_decompressed; } diff --git a/tsl/src/compression/compression.h b/tsl/src/compression/compression.h index 6e8293171b8..78e9472d7f2 100644 --- a/tsl/src/compression/compression.h +++ b/tsl/src/compression/compression.h @@ -138,6 +138,8 @@ typedef struct RowDecompressor CommandId mycid; BulkInsertState bistate; + bool delete_only; + Datum *compressed_datums; bool *compressed_is_nulls; @@ -147,6 +149,7 @@ typedef struct RowDecompressor MemoryContext per_compressed_row_ctx; int64 batches_decompressed; int64 tuples_decompressed; + int64 batches_deleted; TupleTableSlot **decompressed_slots;