From 8fe31a59b1cd160413f877e62afc11640cebabc7 Mon Sep 17 00:00:00 2001 From: Jan Nidzwetzki Date: Tue, 5 Sep 2023 08:43:29 +0200 Subject: [PATCH] Implemented the first vectorized aggregation This commit introduces a vectorized version of the sum() aggregate function for compressed data. This optimization is enabled if (1) the data is compressed, (2) no filters are applied, and (3) the aggregation can be pushed down to the chunk level. --- .unreleased/feature_6050 | 1 + src/compat/compat.h | 9 +- src/cross_module_fn.c | 9 + src/cross_module_fn.h | 4 + src/guc.c | 12 + src/guc.h | 1 + src/planner/partialize.c | 123 +-- src/planner/partialize.h | 2 + tsl/src/CMakeLists.txt | 1 + tsl/src/init.c | 4 + .../nodes/decompress_chunk/decompress_chunk.h | 5 + tsl/src/nodes/decompress_chunk/exec.c | 246 +++++- tsl/src/nodes/decompress_chunk/exec.h | 4 + tsl/src/nodes/decompress_chunk/planner.c | 9 +- tsl/src/partialize_agg.c | 109 +++ tsl/src/partialize_agg.h | 10 + .../merge_append_partially_compressed-13.out | 50 +- .../merge_append_partially_compressed-14.out | 50 +- .../merge_append_partially_compressed-15.out | 50 +- tsl/test/expected/vectorized_aggregation.out | 724 ++++++++++++++++++ tsl/test/sql/CMakeLists.txt | 3 +- tsl/test/sql/vectorized_aggregation.sql | 100 +++ 22 files changed, 1396 insertions(+), 130 deletions(-) create mode 100644 .unreleased/feature_6050 create mode 100644 tsl/src/partialize_agg.c create mode 100644 tsl/src/partialize_agg.h create mode 100644 tsl/test/expected/vectorized_aggregation.out create mode 100644 tsl/test/sql/vectorized_aggregation.sql diff --git a/.unreleased/feature_6050 b/.unreleased/feature_6050 new file mode 100644 index 00000000000..60cbc49f6e5 --- /dev/null +++ b/.unreleased/feature_6050 @@ -0,0 +1 @@ +Implements: #6050 Vectorized aggregation execution for sum() diff --git a/src/compat/compat.h b/src/compat/compat.h index 6998dbbeb7a..76b9973238a 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -417,11 +417,18 @@ get_reindex_options(ReindexStmt *stmt) #endif /* - * define lfifth macro for convenience + * define some list macros for convenience */ #define lfifth(l) lfirst(list_nth_cell(l, 4)) #define lfifth_int(l) lfirst_int(list_nth_cell(l, 4)) +#define lsixth(l) lfirst(list_nth_cell(l, 5)) +#define lsixth_int(l) lfirst_int(list_nth_cell(l, 5)) + +#define list_make6(x1, x2, x3, x4, x5, x6) lappend(list_make5(x1, x2, x3, x4, x5), x6) +#define list_make6_oid(x1, x2, x3, x4, x5, x6) lappend_oid(list_make5_oid(x1, x2, x3, x4, x5), x6) +#define list_make6_int(x1, x2, x3, x4, x5, x6) lappend_int(list_make5_int(x1, x2, x3, x4, x5), x6) + /* PG14 adds estinfo parameter to estimate_num_groups for additional context * about the estimation * https://github.com/postgres/postgres/commit/ed934d4fa3 diff --git a/src/cross_module_fn.c b/src/cross_module_fn.c index a59a4027f68..67abb57b52a 100644 --- a/src/cross_module_fn.c +++ b/src/cross_module_fn.c @@ -193,6 +193,13 @@ job_execute_default_fn(BgwJob *job) pg_unreachable(); } +static bool +should_skip_partial_agg_node_default_fn(PlannerInfo *root, AggPath *aggregation_path, Path *subpath) +{ + /* Don't skip adding the agg node on top of the path */ + return false; +} + static bool process_compress_table_default(AlterTableCmd *cmd, Hypertable *ht, WithClauseResult *with_clause_options) @@ -475,6 +482,8 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = { .policies_alter = error_no_default_fn_pg_community, .policies_show = error_no_default_fn_pg_community, + .should_skip_partial_agg_node = should_skip_partial_agg_node_default_fn, + .partialize_agg = error_no_default_fn_pg_community, .finalize_agg_sfunc = error_no_default_fn_pg_community, .finalize_agg_ffunc = error_no_default_fn_pg_community, diff --git a/src/cross_module_fn.h b/src/cross_module_fn.h index e31ec3e203a..5c409bb0a6b 100644 --- a/src/cross_module_fn.h +++ b/src/cross_module_fn.h @@ -98,6 +98,10 @@ typedef struct CrossModuleFunctions void (*ddl_command_end)(EventTriggerData *command); void (*sql_drop)(List *dropped_objects); + /* Vectorized queries */ + bool (*should_skip_partial_agg_node)(PlannerInfo *root, AggPath *aggregation_path, + Path *subpath); + /* Continuous Aggregates */ PGFunction partialize_agg; PGFunction finalize_agg_sfunc; diff --git a/src/guc.c b/src/guc.c index 5027c930229..49c9a9bc9ca 100644 --- a/src/guc.c +++ b/src/guc.c @@ -84,6 +84,7 @@ bool ts_guc_enable_per_data_node_queries = true; bool ts_guc_enable_parameterized_data_node_scan = true; bool ts_guc_enable_async_append = true; bool ts_guc_enable_chunkwise_aggregation = true; +bool ts_guc_enable_vectorized_aggregation = true; TSDLLEXPORT bool ts_guc_enable_compression_indexscan = true; TSDLLEXPORT bool ts_guc_enable_bulk_decompression = true; TSDLLEXPORT bool ts_guc_enable_skip_scan = true; @@ -570,6 +571,17 @@ _guc_init(void) NULL, NULL); + DefineCustomBoolVariable("timescaledb.vectorized_aggregation", + "Enable vectorized aggregation", + "Enable vectorized aggregation", + &ts_guc_enable_vectorized_aggregation, + true, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + DefineCustomBoolVariable("timescaledb.enable_remote_explain", "Show explain from remote nodes when using VERBOSE flag", "Enable getting and showing EXPLAIN output from remote nodes", diff --git a/src/guc.h b/src/guc.h index 409ba38df7c..5e9984c43e2 100644 --- a/src/guc.h +++ b/src/guc.h @@ -36,6 +36,7 @@ extern TSDLLEXPORT bool ts_guc_enable_parameterized_data_node_scan; extern TSDLLEXPORT bool ts_guc_enable_async_append; extern TSDLLEXPORT bool ts_guc_enable_skip_scan; extern TSDLLEXPORT bool ts_guc_enable_chunkwise_aggregation; +extern TSDLLEXPORT bool ts_guc_enable_vectorized_aggregation; extern bool ts_guc_restoring; extern int ts_guc_max_open_chunks_per_insert; extern int ts_guc_max_cached_chunks_per_hypertable; diff --git a/src/planner/partialize.c b/src/planner/partialize.c index 713d082366b..95b83cea5b1 100644 --- a/src/planner/partialize.c +++ b/src/planner/partialize.c @@ -21,6 +21,7 @@ #include #include +#include "cross_module_fn.h" #include "debug_assert.h" #include "partialize.h" #include "planner.h" @@ -329,7 +330,7 @@ copy_append_like_path(PlannerInfo *root, Path *path, List *new_subpaths, PathTar /* * Generate a partially sorted aggregated agg path on top of a path */ -static Path * +static AggPath * create_sorted_partial_agg_path(PlannerInfo *root, Path *path, PathTarget *target, double d_num_groups, GroupPathExtraData *extra_data) { @@ -345,16 +346,16 @@ create_sorted_partial_agg_path(PlannerInfo *root, Path *path, PathTarget *target path = (Path *) create_sort_path(root, path->parent, path, root->group_pathkeys, -1.0); } - Path *sorted_agg_path = (Path *) create_agg_path(root, - path->parent, - path, - target, - parse->groupClause ? AGG_SORTED : AGG_PLAIN, - AGGSPLIT_INITIAL_SERIAL, - parse->groupClause, - NIL, - agg_partial_costs, - d_num_groups); + AggPath *sorted_agg_path = create_agg_path(root, + path->parent, + path, + target, + parse->groupClause ? AGG_SORTED : AGG_PLAIN, + AGGSPLIT_INITIAL_SERIAL, + parse->groupClause, + NIL, + agg_partial_costs, + d_num_groups); return sorted_agg_path; } @@ -362,7 +363,7 @@ create_sorted_partial_agg_path(PlannerInfo *root, Path *path, PathTarget *target /* * Generate a partially hashed aggregated add path on top of a path */ -static Path * +static AggPath * create_hashed_partial_agg_path(PlannerInfo *root, Path *path, PathTarget *target, double d_num_groups, GroupPathExtraData *extra_data) { @@ -371,16 +372,16 @@ create_hashed_partial_agg_path(PlannerInfo *root, Path *path, PathTarget *target /* Determine costs for aggregations */ AggClauseCosts *agg_partial_costs = &extra_data->agg_partial_costs; - Path *hash_path = (Path *) create_agg_path(root, - path->parent, - path, - target, - AGG_HASHED, - AGGSPLIT_INITIAL_SERIAL, - parse->groupClause, - NIL, - agg_partial_costs, - d_num_groups); + AggPath *hash_path = create_agg_path(root, + path->parent, + path, + target, + AGG_HASHED, + AGGSPLIT_INITIAL_SERIAL, + parse->groupClause, + NIL, + agg_partial_costs, + d_num_groups); return hash_path; } @@ -405,22 +406,32 @@ add_partially_aggregated_subpaths(PlannerInfo *root, Path *parent_path, if (can_sort) { - *sorted_paths = lappend(*sorted_paths, - create_sorted_partial_agg_path(root, - subpath, - chunktarget, - d_num_groups, - extra_data)); + AggPath *agg_path = + create_sorted_partial_agg_path(root, subpath, chunktarget, d_num_groups, extra_data); + + if (ts_cm_functions->should_skip_partial_agg_node(root, agg_path, subpath)) + { + *sorted_paths = lappend(*sorted_paths, subpath); + } + else + { + *sorted_paths = lappend(*sorted_paths, (Path *) agg_path); + } } if (can_hash) { - *hashed_paths = lappend(*hashed_paths, - create_hashed_partial_agg_path(root, - subpath, - chunktarget, - d_num_groups, - extra_data)); + AggPath *agg_path = + create_hashed_partial_agg_path(root, subpath, chunktarget, d_num_groups, extra_data); + + if (ts_cm_functions->should_skip_partial_agg_node(root, agg_path, subpath)) + { + *hashed_paths = lappend(*hashed_paths, subpath); + } + else + { + *hashed_paths = lappend(*hashed_paths, (Path *) agg_path); + } } } @@ -646,27 +657,51 @@ get_best_total_path(RelOptInfo *output_rel) return output_rel->cheapest_total_path; } +/* + Is the provided path a agg path that uses a sorted or plain agg strategy? +*/ +static bool +is_path_sorted_or_plain_agg_path(Path *path) +{ + AggPath *agg_path = castNode(AggPath, path); + Assert(agg_path->aggstrategy == AGG_SORTED || agg_path->aggstrategy == AGG_PLAIN || + agg_path->aggstrategy == AGG_HASHED); + return agg_path->aggstrategy == AGG_SORTED || agg_path->aggstrategy == AGG_PLAIN; +} + /* * Check if this path belongs to a plain or sorted aggregation */ static bool -is_plain_or_sorted_agg_path(Path *path) +contains_path_plain_or_sorted_agg(Path *path) { List *subpaths = get_subpaths_from_append_path(path, true); Ensure(subpaths != NIL, "Unable to determine aggregation type"); - Path *subpath = linitial(subpaths); - - if (IsA(subpath, AggPath)) + ListCell *lc; + foreach (lc, subpaths) { - AggPath *agg_path = castNode(AggPath, linitial(subpaths)); - Assert(agg_path->aggstrategy == AGG_SORTED || agg_path->aggstrategy == AGG_PLAIN || - agg_path->aggstrategy == AGG_HASHED); - return agg_path->aggstrategy == AGG_SORTED || agg_path->aggstrategy == AGG_PLAIN; + Path *subpath = lfirst(lc); + + if (IsA(subpath, AggPath)) + return is_path_sorted_or_plain_agg_path(subpath); + + List *subsubpaths = get_subpaths_from_append_path(path, true); + + ListCell *lc2; + foreach (lc2, subsubpaths) + { + Path *subsubpath = lfirst(lc2); + + if (IsA(subsubpath, AggPath)) + is_path_sorted_or_plain_agg_path(subsubpath); + } } - return is_plain_or_sorted_agg_path(subpath); + /* No dedicated aggregation nodes found (e.g., only vectorized aggregation is used). The sorted + * finalizer is used in that case to finalize the aggregation. */ + return true; } /* @@ -832,7 +867,7 @@ ts_pushdown_partial_agg(PlannerInfo *root, Hypertable *ht, RelOptInfo *input_rel { Path *append_path = lfirst(lc); - if (is_plain_or_sorted_agg_path(append_path)) + if (contains_path_plain_or_sorted_agg(append_path)) { bool is_sorted; diff --git a/src/planner/partialize.h b/src/planner/partialize.h index a1e6e7cd61b..d054f43f1f7 100644 --- a/src/planner/partialize.h +++ b/src/planner/partialize.h @@ -5,7 +5,9 @@ */ #ifndef TIMESCALEDB_PLAN_PARTIALIZE_H #define TIMESCALEDB_PLAN_PARTIALIZE_H + #include +#include #include #include "chunk.h" diff --git a/tsl/src/CMakeLists.txt b/tsl/src/CMakeLists.txt index ee1a2de3e6b..9940d356ab8 100644 --- a/tsl/src/CMakeLists.txt +++ b/tsl/src/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES dist_backup.c hypertable.c init.c + partialize_agg.c partialize_finalize.c planner.c process_utility.c diff --git a/tsl/src/init.c b/tsl/src/init.c index 2366f98ab4f..148a0e4b9f3 100644 --- a/tsl/src/init.c +++ b/tsl/src/init.c @@ -43,6 +43,7 @@ #include "nodes/decompress_chunk/planner.h" #include "nodes/skip_scan/skip_scan.h" #include "nodes/gapfill/gapfill_functions.h" +#include "partialize_agg.h" #include "partialize_finalize.h" #include "planner.h" #include "process_utility.h" @@ -144,6 +145,9 @@ CrossModuleFunctions tsl_cm_functions = { .policies_alter = policies_alter, .policies_show = policies_show, + /* Vectorized queries */ + .should_skip_partial_agg_node = tsl_try_change_to_vector_path, + /* Continuous Aggregates */ .partialize_agg = tsl_partialize_agg, .finalize_agg_sfunc = tsl_finalize_agg_sfunc, diff --git a/tsl/src/nodes/decompress_chunk/decompress_chunk.h b/tsl/src/nodes/decompress_chunk/decompress_chunk.h index 01661e2efb9..4d9cf4ef294 100644 --- a/tsl/src/nodes/decompress_chunk/decompress_chunk.h +++ b/tsl/src/nodes/decompress_chunk/decompress_chunk.h @@ -87,6 +87,11 @@ typedef struct DecompressChunkPath */ DecompressChunkColumnCompression *uncompressed_chunk_attno_to_compression_info; + /* + * Are we able to execute a vectorized aggregation + */ + bool perform_vectorized_aggregation; + List *compressed_pathkeys; bool needs_sequence_num; bool reverse; diff --git a/tsl/src/nodes/decompress_chunk/exec.c b/tsl/src/nodes/decompress_chunk/exec.c index 38c05b03503..45d6d127e56 100644 --- a/tsl/src/nodes/decompress_chunk/exec.c +++ b/tsl/src/nodes/decompress_chunk/exec.c @@ -149,12 +149,13 @@ decompress_chunk_state_create(CustomScan *cscan) chunk_state->sortinfo = lfifth(cscan->custom_private); Assert(IsA(settings, IntList)); - Assert(list_length(settings) == 5); + Assert(list_length(settings) == 6); chunk_state->hypertable_id = linitial_int(settings); chunk_state->chunk_relid = lsecond_int(settings); chunk_state->reverse = lthird_int(settings); chunk_state->batch_sorted_merge = lfourth_int(settings); chunk_state->enable_bulk_decompression = lfifth_int(settings); + chunk_state->perform_vectorized_aggregation = lsixth_int(settings); Assert(IsA(cscan->custom_exprs, List)); Assert(list_length(cscan->custom_exprs) == 1); @@ -475,6 +476,237 @@ decompress_chunk_begin(CustomScanState *node, EState *estate, int eflags) } } +static TupleTableSlot * +perform_vectorized_aggregation(DecompressChunkState *chunk_state) +{ + Assert(chunk_state->num_total_columns == 2); + DecompressChunkColumnDescription *column_description = &chunk_state->template_columns[0]; + Assert(chunk_state->template_columns[1].type == COUNT_COLUMN); + + /* When creating vectorized aggregates, only one result tuple is produced. So, if we have + * already initialized a batch state, we are not in the first call. */ + if (bms_num_members(chunk_state->unused_batch_states) != chunk_state->n_batch_states) + { + ExecClearTuple(chunk_state->csstate.ss.ss_ScanTupleSlot); + return chunk_state->csstate.ss.ss_ScanTupleSlot; + } + + const int new_batch_index = batch_array_get_free_slot(chunk_state); + Assert(new_batch_index >= 0); + DecompressBatchState *batch_state = batch_array_get_at(chunk_state, new_batch_index); + + batch_state->per_batch_context = AllocSetContextCreate(CurrentMemoryContext, + "DecompressChunk per_batch", + 0, + chunk_state->batch_memory_context_bytes, + chunk_state->batch_memory_context_bytes); + + Assert(batch_state); + + if (chunk_state->bulk_decompression_context == NULL) + { + chunk_state->bulk_decompression_context = + AllocSetContextCreate(MemoryContextGetParent(batch_state->per_batch_context), + "bulk decompression", + /* minContextSize = */ 0, + /* initBlockSize = */ 64 * 1024, + /* maxBlockSize = */ 64 * 1024); + } + + /* Get a reference the the output TupleTableSlot */ + TupleTableSlot *decompressed_scan_slot = chunk_state->csstate.ss.ss_ScanTupleSlot; + decompressed_scan_slot->tts_values[0] = 0; /* Initial value */ + + if (column_description->type == SEGMENTBY_COLUMN) + { + /* We have promised during query planning also to process segment by columns. So, perform + * the aggregation here */ + DecompressChunkColumnDescription *column_description_count = + &chunk_state->template_columns[1]; + + while (true) + { + TupleTableSlot *compressed_slot = + ExecProcNode(linitial(chunk_state->csstate.custom_ps)); + if (TupIsNull(compressed_slot)) + { + /* All segment by values are processed. */ + break; + } + + bool isnull_value, isnull_elements; + Datum value = slot_getattr(compressed_slot, + column_description->compressed_scan_attno, + &isnull_value); + + /* We have multiple compressed tuples for this segment by value. Get number of + * compressed tuples */ + Datum elements = slot_getattr(compressed_slot, + column_description_count->compressed_scan_attno, + &isnull_elements); + + if (!isnull_value && !isnull_elements) + { + uint32 intvalue = DatumGetInt32(value); + uint32 amount = DatumGetInt32(elements); + + Assert(amount > 0); + + /* Multiply the number of tuples with the actual value */ + if (unlikely(pg_mul_u32_overflow(intvalue, amount, &intvalue))) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + } + + /* Add the value to our sum */ + if (unlikely( + pg_add_u32_overflow(decompressed_scan_slot->tts_values[0], + intvalue, + ((uint32 *) &decompressed_scan_slot->tts_values[0])))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + } + } + } + else if (column_description->type == COMPRESSED_COLUMN) + { + Assert(chunk_state->enable_bulk_decompression); + Assert(column_description->bulk_decompression_supported); + + while (true) + { + TupleTableSlot *compressed_slot = + ExecProcNode(linitial(chunk_state->csstate.custom_ps)); + if (TupIsNull(compressed_slot)) + { + /* All compressed batches are processed. */ + break; + } + + /* Decompress data */ + bool isnull; + Datum value = + slot_getattr(compressed_slot, column_description->compressed_scan_attno, &isnull); + if (isnull) + { + continue; /* TODO: Or maybe error out? */ + } + + /* We have at least one value */ + decompressed_scan_slot->tts_isnull[0] = false; + + // TODO: Handle segment by values + CompressedDataHeader *header = (CompressedDataHeader *) PG_DETOAST_DATUM(value); + ArrowArray *arrow = NULL; + + DecompressAllFunction decompress_all = + tsl_get_decompress_all_function(header->compression_algorithm); + Assert(decompress_all != NULL); + + MemoryContext context_before_decompression = + MemoryContextSwitchTo(chunk_state->bulk_decompression_context); + + /* Fixme, type for column is wrong because its partial(sum(int4)) */ + column_description->typid = 23; + + arrow = decompress_all(PointerGetDatum(header), + column_description->typid, + batch_state->per_batch_context); + + Assert(arrow != NULL); + + MemoryContextReset(chunk_state->bulk_decompression_context); + MemoryContextSwitchTo(context_before_decompression); + +#if defined(HAVE__BUILTIN_OP_OVERFLOW) && defined(USE_CUSTOM_SUM_IMPL) +#define SUM_LANES 4 + uint32 lane_result[] = { 0, 0, 0, 0 }; + uint32 lane_buffer[] = { 0, 0, 0, 0 }; + bool lane_overflow[] = { false, false, false, false }; + + int cur_lane = 0; + + for (int i = 0; i < arrow->length; i++) + { + const bool arrow_isnull = !arrow_row_is_valid(arrow->buffers[0], i); + + if (!arrow_isnull) + { + const uint32 arrow_value = ((uint32 *) arrow->buffers[1])[i]; + lane_buffer[cur_lane] = arrow_value; + cur_lane = (cur_lane + 1) % 4; + } + + if (cur_lane == 0) + { + for (int lane_selector = 0; lane_selector < SUM_LANES; lane_selector++) + { + lane_overflow[lane_selector] = + __builtin_add_overflow(lane_result[lane_selector], + lane_buffer[lane_selector], + &lane_result[lane_selector]); + } + + for (int lane_selector = 0; lane_selector < SUM_LANES; lane_selector++) + { + if (unlikely(lane_overflow[lane_selector])) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + } + } + } + + /* Sum the partials */ + for (int lane_selector = 0; lane_selector < SUM_LANES; lane_selector++) + { + pg_add_u32_overflow(decompressed_scan_slot->tts_values[0], + lane_result[lane_selector], + (uint32 *) &decompressed_scan_slot->tts_values[0]); + } + + /* If we have pending results add them too */ + if (cur_lane != 0) + { + for (int lane_selector = cur_lane; lane_selector < SUM_LANES; lane_selector++) + { + pg_add_u32_overflow(decompressed_scan_slot->tts_values[0], + lane_buffer[lane_selector], + (uint32 *) &decompressed_scan_slot->tts_values[0]); + } + } +#else + for (int i = 0; i < arrow->length; i++) + { + const bool arrow_isnull = !arrow_row_is_valid(arrow->buffers[0], i); + + if (!arrow_isnull) + { + const uint32 arrow_value = ((uint32 *) arrow->buffers[1])[i]; + if (unlikely(pg_add_u32_overflow(decompressed_scan_slot->tts_values[0], + arrow_value, + ((uint32 *) &decompressed_scan_slot + ->tts_values[0])))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + } + } +#endif + } + } + else + { + elog(ERROR, "unsupported column type"); + } + + ExecStoreVirtualTuple(decompressed_scan_slot); + return decompressed_scan_slot; +} + /* * The exec function for the DecompressChunk node. It takes the explicit queue * functions pointer as an optimization, to allow these functions to be @@ -485,6 +717,11 @@ pg_attribute_always_inline static TupleTableSlot * decompress_chunk_exec_impl(DecompressChunkState *chunk_state, const struct BatchQueueFunctions *queue) { + if (chunk_state->perform_vectorized_aggregation) + { + return perform_vectorized_aggregation(chunk_state); + } + queue->pop(chunk_state); while (queue->needs_next_batch(chunk_state)) { @@ -579,5 +816,12 @@ decompress_chunk_explain(CustomScanState *node, List *ancestors, ExplainState *e { ExplainPropertyBool("Bulk Decompression", chunk_state->enable_bulk_decompression, es); } + + if (chunk_state->perform_vectorized_aggregation) + { + ExplainPropertyBool("Vectorized Aggregation", + chunk_state->perform_vectorized_aggregation, + es); + } } } diff --git a/tsl/src/nodes/decompress_chunk/exec.h b/tsl/src/nodes/decompress_chunk/exec.h index f49395e8407..4ce5d271b12 100644 --- a/tsl/src/nodes/decompress_chunk/exec.h +++ b/tsl/src/nodes/decompress_chunk/exec.h @@ -83,6 +83,10 @@ typedef struct DecompressChunkState bool enable_bulk_decompression; + /* Perform calculation of the aggregate directly in the decompress chunk node and emit partials + */ + bool perform_vectorized_aggregation; + /* * Scratch space for bulk decompression which might need a lot of temporary * data. diff --git a/tsl/src/nodes/decompress_chunk/planner.c b/tsl/src/nodes/decompress_chunk/planner.c index bc83707e0b1..7ac77f09575 100644 --- a/tsl/src/nodes/decompress_chunk/planner.c +++ b/tsl/src/nodes/decompress_chunk/planner.c @@ -476,6 +476,10 @@ decompress_chunk_plan_create(PlannerInfo *root, RelOptInfo *rel, CustomPath *pat /* input target list */ decompress_plan->custom_scan_tlist = NIL; + // TODO + if (dcpath->perform_vectorized_aggregation) + decompress_plan->custom_scan_tlist = decompressed_tlist; + if (IsA(compressed_path, IndexPath)) { /* @@ -793,11 +797,12 @@ decompress_chunk_plan_create(PlannerInfo *root, RelOptInfo *rel, CustomPath *pat } #endif - settings = list_make5_int(dcpath->info->hypertable_id, + settings = list_make6_int(dcpath->info->hypertable_id, dcpath->info->chunk_rte->relid, dcpath->reverse, dcpath->batch_sorted_merge, - enable_bulk_decompression); + enable_bulk_decompression, + dcpath->perform_vectorized_aggregation); /* * Vectorized quals must go into custom_exprs, because Postgres has to see diff --git a/tsl/src/partialize_agg.c b/tsl/src/partialize_agg.c new file mode 100644 index 00000000000..c2a206a198f --- /dev/null +++ b/tsl/src/partialize_agg.c @@ -0,0 +1,109 @@ +/* + * This file and its contents are licensed under the Timescale License. + * Please see the included NOTICE for copyright information and + * LICENSE-TIMESCALE for a copy of the license. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "nodes/decompress_chunk/decompress_chunk.h" +#include "partialize_agg.h" +#include "utils.h" +#include "debug_assert.h" + +#define is_restricted_path(path) list_length(path->parent->baserestrictinfo) + +static bool +is_vectorizable_agg_path(PlannerInfo *root, AggPath *agg_path, Path *path) +{ + Assert(agg_path->aggstrategy == AGG_SORTED || agg_path->aggstrategy == AGG_PLAIN || + agg_path->aggstrategy == AGG_HASHED); + + /* Having is not supported at the moment */ + if (root->hasHavingQual) + return false; + + /* Only vectorizing within the decompress node is supported so far */ + bool is_decompress_chunk = ts_is_decompress_chunk_path(path); + if (!is_decompress_chunk) + return false; + + DecompressChunkPath *decompress_path = (DecompressChunkPath *) path; + Assert(decompress_path->custom_path.custom_paths != NIL); + Path *compressed_path = linitial(decompress_path->custom_path.custom_paths); + + /* No filters are supported at the moment */ + if (is_restricted_path(path) || is_restricted_path(compressed_path)) + return false; + + /* We currently handle only one agg function per node */ + if (list_length(agg_path->path.pathtarget->exprs) != 1) + return false; + + /* Only sum on int 4 is supported at the moment */ + Node *expr_node = linitial(agg_path->path.pathtarget->exprs); + if (!IsA(expr_node, Aggref)) + return false; + + Aggref *aggref = castNode(Aggref, expr_node); + +#if PG14_LT + if (aggref->aggfnoid != F_INT4_SUM) +#else + if (aggref->aggfnoid != F_SUM_INT4) +#endif + return false; + + return true; +} + +bool +tsl_try_change_to_vector_path(PlannerInfo *root, AggPath *aggregation_path, Path *path) +{ + if (!ts_guc_enable_vectorized_aggregation) + return false; + + Assert(path != NULL); + Assert(aggregation_path->aggsplit == AGGSPLIT_INITIAL_SERIAL); + + if (is_vectorizable_agg_path(root, aggregation_path, path)) + { + Assert(ts_is_decompress_chunk_path(path)); + DecompressChunkPath *decompress_path = (DecompressChunkPath *) castNode(CustomPath, path); + + // TODO: Check why parallel paths are not selected + + /* + * It would be great if we could check decompress_path -> have_bulk_decompression_columns + * here (or ensure the column can be bulk decompressed). However, this information is only + * computed when the plan is created. + * + * In order to avoid wasting time and computing this information for all paths, we also + * enable vectorized decompression in case batch decompression is not possible (e.g. it is a + * segment_by column). In this case, the executor iterates over the uncompressed values and + * creates a partial aggregate as well. + */ + decompress_path->perform_vectorized_aggregation = true; + decompress_path->custom_path.path.pathtarget = aggregation_path->path.pathtarget; + + return true; + } + + return false; +} diff --git a/tsl/src/partialize_agg.h b/tsl/src/partialize_agg.h new file mode 100644 index 00000000000..e14ee076940 --- /dev/null +++ b/tsl/src/partialize_agg.h @@ -0,0 +1,10 @@ +/* + * This file and its contents are licensed under the Timescale License. + * Please see the included NOTICE for copyright information and + * LICENSE-TIMESCALE for a copy of the license. + */ + +#pragma once + +extern bool tsl_try_change_to_vector_path(PlannerInfo *root, AggPath *aggregation_path, + Path *subpath); diff --git a/tsl/test/expected/merge_append_partially_compressed-13.out b/tsl/test/expected/merge_append_partially_compressed-13.out index fd209572a6f..ae11ff788de 100644 --- a/tsl/test/expected/merge_append_partially_compressed-13.out +++ b/tsl/test/expected/merge_append_partially_compressed-13.out @@ -692,35 +692,31 @@ SELECT * FROM test1 ORDER BY time ASC NULLS FIRST, x3 DESC NULLS LAST, x4 ASC; :PREFIX SELECT x1, x2, max(time) FROM test1 GROUP BY x1, x2, time ORDER BY time limit 10; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Limit (actual rows=5 loops=1) - -> Sort (actual rows=5 loops=1) - Sort Key: test1."time" - Sort Method: quicksort - -> Finalize HashAggregate (actual rows=5 loops=1) - Group Key: test1."time", test1.x1, test1.x2 - Batches: 1 - -> Custom Scan (ChunkAppend) on test1 (actual rows=5 loops=1) - Order: test1."time", test1.x1, test1.x2 - -> Merge Append (actual rows=5 loops=1) + -> Finalize GroupAggregate (actual rows=5 loops=1) + Group Key: test1."time", test1.x1, test1.x2 + -> Custom Scan (ChunkAppend) on test1 (actual rows=5 loops=1) + Order: test1."time", test1.x1, test1.x2 + -> Merge Append (actual rows=5 loops=1) + Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + -> Sort (actual rows=4 loops=1) + Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Sort Method: quicksort + -> Partial HashAggregate (actual rows=4 loops=1) + Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Batches: 1 + -> Custom Scan (DecompressChunk) on _hyper_3_7_chunk (actual rows=4 loops=1) + -> Seq Scan on compress_hyper_4_8_chunk (actual rows=3 loops=1) + -> Sort (actual rows=1 loops=1) Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - -> Sort (actual rows=4 loops=1) - Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Sort Method: quicksort - -> Partial HashAggregate (actual rows=4 loops=1) - Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Batches: 1 - -> Custom Scan (DecompressChunk) on _hyper_3_7_chunk (actual rows=4 loops=1) - -> Seq Scan on compress_hyper_4_8_chunk (actual rows=3 loops=1) - -> Sort (actual rows=1 loops=1) - Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Sort Method: quicksort - -> Partial HashAggregate (actual rows=1 loops=1) - Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Batches: 1 - -> Seq Scan on _hyper_3_7_chunk (actual rows=1 loops=1) -(26 rows) + Sort Method: quicksort + -> Partial HashAggregate (actual rows=1 loops=1) + Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Batches: 1 + -> Seq Scan on _hyper_3_7_chunk (actual rows=1 loops=1) +(22 rows) :PREFIX SELECT * FROM test1 ORDER BY x1, x2, x5, x4, time LIMIT 10; diff --git a/tsl/test/expected/merge_append_partially_compressed-14.out b/tsl/test/expected/merge_append_partially_compressed-14.out index 581c40679dd..d7ead712181 100644 --- a/tsl/test/expected/merge_append_partially_compressed-14.out +++ b/tsl/test/expected/merge_append_partially_compressed-14.out @@ -692,35 +692,31 @@ SELECT * FROM test1 ORDER BY time ASC NULLS FIRST, x3 DESC NULLS LAST, x4 ASC; :PREFIX SELECT x1, x2, max(time) FROM test1 GROUP BY x1, x2, time ORDER BY time limit 10; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Limit (actual rows=5 loops=1) - -> Sort (actual rows=5 loops=1) - Sort Key: test1."time" - Sort Method: quicksort - -> Finalize HashAggregate (actual rows=5 loops=1) - Group Key: test1."time", test1.x1, test1.x2 - Batches: 1 - -> Custom Scan (ChunkAppend) on test1 (actual rows=5 loops=1) - Order: test1."time", test1.x1, test1.x2 - -> Merge Append (actual rows=5 loops=1) + -> Finalize GroupAggregate (actual rows=5 loops=1) + Group Key: test1."time", test1.x1, test1.x2 + -> Custom Scan (ChunkAppend) on test1 (actual rows=5 loops=1) + Order: test1."time", test1.x1, test1.x2 + -> Merge Append (actual rows=5 loops=1) + Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + -> Sort (actual rows=4 loops=1) + Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Sort Method: quicksort + -> Partial HashAggregate (actual rows=4 loops=1) + Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Batches: 1 + -> Custom Scan (DecompressChunk) on _hyper_3_7_chunk (actual rows=4 loops=1) + -> Seq Scan on compress_hyper_4_8_chunk (actual rows=3 loops=1) + -> Sort (actual rows=1 loops=1) Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - -> Sort (actual rows=4 loops=1) - Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Sort Method: quicksort - -> Partial HashAggregate (actual rows=4 loops=1) - Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Batches: 1 - -> Custom Scan (DecompressChunk) on _hyper_3_7_chunk (actual rows=4 loops=1) - -> Seq Scan on compress_hyper_4_8_chunk (actual rows=3 loops=1) - -> Sort (actual rows=1 loops=1) - Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Sort Method: quicksort - -> Partial HashAggregate (actual rows=1 loops=1) - Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Batches: 1 - -> Seq Scan on _hyper_3_7_chunk (actual rows=1 loops=1) -(26 rows) + Sort Method: quicksort + -> Partial HashAggregate (actual rows=1 loops=1) + Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Batches: 1 + -> Seq Scan on _hyper_3_7_chunk (actual rows=1 loops=1) +(22 rows) :PREFIX SELECT * FROM test1 ORDER BY x1, x2, x5, x4, time LIMIT 10; diff --git a/tsl/test/expected/merge_append_partially_compressed-15.out b/tsl/test/expected/merge_append_partially_compressed-15.out index 7d33ba0f2a1..76fdb218d66 100644 --- a/tsl/test/expected/merge_append_partially_compressed-15.out +++ b/tsl/test/expected/merge_append_partially_compressed-15.out @@ -698,35 +698,31 @@ SELECT * FROM test1 ORDER BY time ASC NULLS FIRST, x3 DESC NULLS LAST, x4 ASC; :PREFIX SELECT x1, x2, max(time) FROM test1 GROUP BY x1, x2, time ORDER BY time limit 10; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Limit (actual rows=5 loops=1) - -> Sort (actual rows=5 loops=1) - Sort Key: test1."time" - Sort Method: quicksort - -> Finalize HashAggregate (actual rows=5 loops=1) - Group Key: test1."time", test1.x1, test1.x2 - Batches: 1 - -> Custom Scan (ChunkAppend) on test1 (actual rows=5 loops=1) - Order: test1."time", test1.x1, test1.x2 - -> Merge Append (actual rows=5 loops=1) + -> Finalize GroupAggregate (actual rows=5 loops=1) + Group Key: test1."time", test1.x1, test1.x2 + -> Custom Scan (ChunkAppend) on test1 (actual rows=5 loops=1) + Order: test1."time", test1.x1, test1.x2 + -> Merge Append (actual rows=5 loops=1) + Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + -> Sort (actual rows=4 loops=1) + Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Sort Method: quicksort + -> Partial HashAggregate (actual rows=4 loops=1) + Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Batches: 1 + -> Custom Scan (DecompressChunk) on _hyper_3_7_chunk (actual rows=4 loops=1) + -> Seq Scan on compress_hyper_4_8_chunk (actual rows=3 loops=1) + -> Sort (actual rows=1 loops=1) Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - -> Sort (actual rows=4 loops=1) - Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Sort Method: quicksort - -> Partial HashAggregate (actual rows=4 loops=1) - Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Batches: 1 - -> Custom Scan (DecompressChunk) on _hyper_3_7_chunk (actual rows=4 loops=1) - -> Seq Scan on compress_hyper_4_8_chunk (actual rows=3 loops=1) - -> Sort (actual rows=1 loops=1) - Sort Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Sort Method: quicksort - -> Partial HashAggregate (actual rows=1 loops=1) - Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 - Batches: 1 - -> Seq Scan on _hyper_3_7_chunk (actual rows=1 loops=1) -(26 rows) + Sort Method: quicksort + -> Partial HashAggregate (actual rows=1 loops=1) + Group Key: _hyper_3_7_chunk."time", _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2 + Batches: 1 + -> Seq Scan on _hyper_3_7_chunk (actual rows=1 loops=1) +(22 rows) :PREFIX SELECT * FROM test1 ORDER BY x1, x2, x5, x4, time LIMIT 10; diff --git a/tsl/test/expected/vectorized_aggregation.out b/tsl/test/expected/vectorized_aggregation.out new file mode 100644 index 00000000000..c359c327d08 --- /dev/null +++ b/tsl/test/expected/vectorized_aggregation.out @@ -0,0 +1,724 @@ +-- This file and its contents are licensed under the Timescale License. +-- Please see the included NOTICE for copyright information and +-- LICENSE-TIMESCALE for a copy of the license. +\set EXPLAIN 'EXPLAIN (VERBOSE, COSTS OFF)' +CREATE TABLE testtable ( +time timestamptz NOT NULL, +segment_by_value integer NOT NULL, +int_value integer NOT NULL, +float_value double precision NOT NULL); +SELECT FROM create_hypertable(relation=>'testtable', time_column_name=> 'time'); +-- +(1 row) + +ALTER TABLE testtable SET (timescaledb.compress, timescaledb.compress_segmentby='segment_by_value'); +INSERT INTO testtable +SELECT time AS time, +value AS segment_by_value, +value AS int_value, +value AS float_value +FROM +generate_series('1980-01-01 00:00:00-00', '1980-03-01 00:00:00-00', INTERVAL '1 day') AS g1(time), +generate_series(1, 100, 1) AS g2(value) +ORDER BY time; +-- Aggregation result without any vectorization +SELECT sum(segment_by_value), sum(int_value), sum(float_value) FROM testtable; + sum | sum | sum +--------+--------+-------- + 308050 | 308050 | 308050 +(1 row) + +--- +-- Tests with some chunks compressed +--- +SELECT compress_chunk(ch) FROM show_chunks('testtable') ch LIMIT 3; + compress_chunk +---------------------------------------- + _timescaledb_internal._hyper_1_1_chunk + _timescaledb_internal._hyper_1_2_chunk + _timescaledb_internal._hyper_1_3_chunk +(3 rows) + +-- Vectorized aggregation possible +SELECT sum(segment_by_value) FROM testtable; + sum +-------- + 308050 +(1 row) + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.segment_by_value) + -> Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: (PARTIAL sum(_hyper_1_1_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: (PARTIAL sum(_hyper_1_2_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: (PARTIAL sum(_hyper_1_3_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_4_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_4_chunk + Output: _hyper_1_4_chunk.segment_by_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_5_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_5_chunk + Output: _hyper_1_5_chunk.segment_by_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_6_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_6_chunk + Output: _hyper_1_6_chunk.segment_by_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_7_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_7_chunk + Output: _hyper_1_7_chunk.segment_by_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_8_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_8_chunk + Output: _hyper_1_8_chunk.segment_by_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_9_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_9_chunk + Output: _hyper_1_9_chunk.segment_by_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_10_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_10_chunk + Output: _hyper_1_10_chunk.segment_by_value +(46 rows) + +-- Vectorization not possible due to a used filter +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable WHERE segment_by_value > 0; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.segment_by_value) + -> Append + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_1_chunk.segment_by_value) + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: _hyper_1_1_chunk.segment_by_value + -> Index Scan using compress_hyper_2_11_chunk__compressed_hypertable_2_segment_by_v on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + Index Cond: (compress_hyper_2_11_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_2_chunk.segment_by_value) + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: _hyper_1_2_chunk.segment_by_value + -> Index Scan using compress_hyper_2_12_chunk__compressed_hypertable_2_segment_by_v on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + Index Cond: (compress_hyper_2_12_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_3_chunk.segment_by_value) + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: _hyper_1_3_chunk.segment_by_value + -> Index Scan using compress_hyper_2_13_chunk__compressed_hypertable_2_segment_by_v on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + Index Cond: (compress_hyper_2_13_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_4_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_4_chunk + Output: _hyper_1_4_chunk.segment_by_value + Filter: (_hyper_1_4_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_5_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_5_chunk + Output: _hyper_1_5_chunk.segment_by_value + Filter: (_hyper_1_5_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_6_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_6_chunk + Output: _hyper_1_6_chunk.segment_by_value + Filter: (_hyper_1_6_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_7_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_7_chunk + Output: _hyper_1_7_chunk.segment_by_value + Filter: (_hyper_1_7_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_8_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_8_chunk + Output: _hyper_1_8_chunk.segment_by_value + Filter: (_hyper_1_8_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_9_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_9_chunk + Output: _hyper_1_9_chunk.segment_by_value + Filter: (_hyper_1_9_chunk.segment_by_value > 0) + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_10_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_10_chunk + Output: _hyper_1_10_chunk.segment_by_value + Filter: (_hyper_1_10_chunk.segment_by_value > 0) +(59 rows) + +-- Vectorization not possible due to segment_by +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable GROUP BY float_value; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize GroupAggregate + Output: sum(_hyper_1_1_chunk.segment_by_value), _hyper_1_1_chunk.float_value + Group Key: _hyper_1_1_chunk.float_value + -> Gather Merge + Output: _hyper_1_1_chunk.float_value, (PARTIAL sum(_hyper_1_1_chunk.segment_by_value)) + Workers Planned: 2 + -> Sort + Output: _hyper_1_1_chunk.float_value, (PARTIAL sum(_hyper_1_1_chunk.segment_by_value)) + Sort Key: _hyper_1_1_chunk.float_value + -> Partial HashAggregate + Output: _hyper_1_1_chunk.float_value, PARTIAL sum(_hyper_1_1_chunk.segment_by_value) + Group Key: _hyper_1_1_chunk.float_value + -> Parallel Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: _hyper_1_1_chunk.segment_by_value, _hyper_1_1_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: _hyper_1_2_chunk.segment_by_value, _hyper_1_2_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: _hyper_1_3_chunk.segment_by_value, _hyper_1_3_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_4_chunk + Output: _hyper_1_4_chunk.segment_by_value, _hyper_1_4_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_5_chunk + Output: _hyper_1_5_chunk.segment_by_value, _hyper_1_5_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_6_chunk + Output: _hyper_1_6_chunk.segment_by_value, _hyper_1_6_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_7_chunk + Output: _hyper_1_7_chunk.segment_by_value, _hyper_1_7_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_8_chunk + Output: _hyper_1_8_chunk.segment_by_value, _hyper_1_8_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_9_chunk + Output: _hyper_1_9_chunk.segment_by_value, _hyper_1_9_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_10_chunk + Output: _hyper_1_10_chunk.segment_by_value, _hyper_1_10_chunk.float_value +(39 rows) + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable GROUP BY int_value; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize GroupAggregate + Output: sum(_hyper_1_1_chunk.segment_by_value), _hyper_1_1_chunk.int_value + Group Key: _hyper_1_1_chunk.int_value + -> Gather Merge + Output: _hyper_1_1_chunk.int_value, (PARTIAL sum(_hyper_1_1_chunk.segment_by_value)) + Workers Planned: 2 + -> Sort + Output: _hyper_1_1_chunk.int_value, (PARTIAL sum(_hyper_1_1_chunk.segment_by_value)) + Sort Key: _hyper_1_1_chunk.int_value + -> Partial HashAggregate + Output: _hyper_1_1_chunk.int_value, PARTIAL sum(_hyper_1_1_chunk.segment_by_value) + Group Key: _hyper_1_1_chunk.int_value + -> Parallel Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: _hyper_1_1_chunk.segment_by_value, _hyper_1_1_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: _hyper_1_2_chunk.segment_by_value, _hyper_1_2_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: _hyper_1_3_chunk.segment_by_value, _hyper_1_3_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_4_chunk + Output: _hyper_1_4_chunk.segment_by_value, _hyper_1_4_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_5_chunk + Output: _hyper_1_5_chunk.segment_by_value, _hyper_1_5_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_6_chunk + Output: _hyper_1_6_chunk.segment_by_value, _hyper_1_6_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_7_chunk + Output: _hyper_1_7_chunk.segment_by_value, _hyper_1_7_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_8_chunk + Output: _hyper_1_8_chunk.segment_by_value, _hyper_1_8_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_9_chunk + Output: _hyper_1_9_chunk.segment_by_value, _hyper_1_9_chunk.int_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_10_chunk + Output: _hyper_1_10_chunk.segment_by_value, _hyper_1_10_chunk.int_value +(39 rows) + +:EXPLAIN +SELECT sum(int_value) FROM testtable GROUP BY segment_by_value; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize GroupAggregate + Output: sum(_hyper_1_1_chunk.int_value), _hyper_1_1_chunk.segment_by_value + Group Key: _hyper_1_1_chunk.segment_by_value + -> Gather Merge + Output: _hyper_1_1_chunk.segment_by_value, (PARTIAL sum(_hyper_1_1_chunk.int_value)) + Workers Planned: 2 + -> Sort + Output: _hyper_1_1_chunk.segment_by_value, (PARTIAL sum(_hyper_1_1_chunk.int_value)) + Sort Key: _hyper_1_1_chunk.segment_by_value + -> Partial HashAggregate + Output: _hyper_1_1_chunk.segment_by_value, PARTIAL sum(_hyper_1_1_chunk.int_value) + Group Key: _hyper_1_1_chunk.segment_by_value + -> Parallel Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: _hyper_1_1_chunk.int_value, _hyper_1_1_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: _hyper_1_2_chunk.int_value, _hyper_1_2_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: _hyper_1_3_chunk.int_value, _hyper_1_3_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_4_chunk + Output: _hyper_1_4_chunk.int_value, _hyper_1_4_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_5_chunk + Output: _hyper_1_5_chunk.int_value, _hyper_1_5_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_6_chunk + Output: _hyper_1_6_chunk.int_value, _hyper_1_6_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_7_chunk + Output: _hyper_1_7_chunk.int_value, _hyper_1_7_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_8_chunk + Output: _hyper_1_8_chunk.int_value, _hyper_1_8_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_9_chunk + Output: _hyper_1_9_chunk.int_value, _hyper_1_9_chunk.segment_by_value + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_10_chunk + Output: _hyper_1_10_chunk.int_value, _hyper_1_10_chunk.segment_by_value +(39 rows) + +-- Vectorized aggregation possible +SELECT sum(int_value) FROM testtable; + sum +-------- + 308050 +(1 row) + +:EXPLAIN +SELECT sum(int_value) FROM testtable; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.int_value) + -> Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: (PARTIAL sum(_hyper_1_1_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: (PARTIAL sum(_hyper_1_2_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: (PARTIAL sum(_hyper_1_3_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_4_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_4_chunk + Output: _hyper_1_4_chunk.int_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_5_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_5_chunk + Output: _hyper_1_5_chunk.int_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_6_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_6_chunk + Output: _hyper_1_6_chunk.int_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_7_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_7_chunk + Output: _hyper_1_7_chunk.int_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_8_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_8_chunk + Output: _hyper_1_8_chunk.int_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_9_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_9_chunk + Output: _hyper_1_9_chunk.int_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_10_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_10_chunk + Output: _hyper_1_10_chunk.int_value +(46 rows) + +-- Vectorized aggregation not possible +SELECT sum(float_value) FROM testtable; + sum +-------- + 308050 +(1 row) + +:EXPLAIN +SELECT sum(float_value) FROM testtable; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.float_value) + -> Gather + Output: (PARTIAL sum(_hyper_1_1_chunk.float_value)) + Workers Planned: 2 + -> Parallel Append + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_1_chunk.float_value) + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: _hyper_1_1_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_2_chunk.float_value) + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: _hyper_1_2_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_3_chunk.float_value) + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: _hyper_1_3_chunk.float_value + -> Parallel Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_4_chunk.float_value) + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_4_chunk + Output: _hyper_1_4_chunk.float_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_5_chunk.float_value) + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_5_chunk + Output: _hyper_1_5_chunk.float_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_6_chunk.float_value) + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_6_chunk + Output: _hyper_1_6_chunk.float_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_7_chunk.float_value) + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_7_chunk + Output: _hyper_1_7_chunk.float_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_8_chunk.float_value) + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_8_chunk + Output: _hyper_1_8_chunk.float_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_9_chunk.float_value) + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_9_chunk + Output: _hyper_1_9_chunk.float_value + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_10_chunk.float_value) + -> Parallel Seq Scan on _timescaledb_internal._hyper_1_10_chunk + Output: _hyper_1_10_chunk.float_value +(52 rows) + +--- +-- Tests with all chunks compressed +--- +SELECT compress_chunk(ch, if_not_compressed => true) FROM show_chunks('testtable') ch; +NOTICE: chunk "_hyper_1_1_chunk" is already compressed +NOTICE: chunk "_hyper_1_2_chunk" is already compressed +NOTICE: chunk "_hyper_1_3_chunk" is already compressed + compress_chunk +----------------------------------------- + _timescaledb_internal._hyper_1_1_chunk + _timescaledb_internal._hyper_1_2_chunk + _timescaledb_internal._hyper_1_3_chunk + _timescaledb_internal._hyper_1_4_chunk + _timescaledb_internal._hyper_1_5_chunk + _timescaledb_internal._hyper_1_6_chunk + _timescaledb_internal._hyper_1_7_chunk + _timescaledb_internal._hyper_1_8_chunk + _timescaledb_internal._hyper_1_9_chunk + _timescaledb_internal._hyper_1_10_chunk +(10 rows) + +-- Vectorized aggregation possible +SELECT sum(segment_by_value) FROM testtable; + sum +-------- + 308050 +(1 row) + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.segment_by_value) + -> Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: (PARTIAL sum(_hyper_1_1_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: (PARTIAL sum(_hyper_1_2_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: (PARTIAL sum(_hyper_1_3_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_4_chunk + Output: (PARTIAL sum(_hyper_1_4_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_14_chunk + Output: compress_hyper_2_14_chunk."time", compress_hyper_2_14_chunk.segment_by_value, compress_hyper_2_14_chunk.int_value, compress_hyper_2_14_chunk.float_value, compress_hyper_2_14_chunk._ts_meta_count, compress_hyper_2_14_chunk._ts_meta_sequence_num, compress_hyper_2_14_chunk._ts_meta_min_1, compress_hyper_2_14_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_5_chunk + Output: (PARTIAL sum(_hyper_1_5_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_15_chunk + Output: compress_hyper_2_15_chunk."time", compress_hyper_2_15_chunk.segment_by_value, compress_hyper_2_15_chunk.int_value, compress_hyper_2_15_chunk.float_value, compress_hyper_2_15_chunk._ts_meta_count, compress_hyper_2_15_chunk._ts_meta_sequence_num, compress_hyper_2_15_chunk._ts_meta_min_1, compress_hyper_2_15_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_6_chunk + Output: (PARTIAL sum(_hyper_1_6_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_16_chunk + Output: compress_hyper_2_16_chunk."time", compress_hyper_2_16_chunk.segment_by_value, compress_hyper_2_16_chunk.int_value, compress_hyper_2_16_chunk.float_value, compress_hyper_2_16_chunk._ts_meta_count, compress_hyper_2_16_chunk._ts_meta_sequence_num, compress_hyper_2_16_chunk._ts_meta_min_1, compress_hyper_2_16_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_7_chunk + Output: (PARTIAL sum(_hyper_1_7_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_17_chunk + Output: compress_hyper_2_17_chunk."time", compress_hyper_2_17_chunk.segment_by_value, compress_hyper_2_17_chunk.int_value, compress_hyper_2_17_chunk.float_value, compress_hyper_2_17_chunk._ts_meta_count, compress_hyper_2_17_chunk._ts_meta_sequence_num, compress_hyper_2_17_chunk._ts_meta_min_1, compress_hyper_2_17_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_8_chunk + Output: (PARTIAL sum(_hyper_1_8_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_18_chunk + Output: compress_hyper_2_18_chunk."time", compress_hyper_2_18_chunk.segment_by_value, compress_hyper_2_18_chunk.int_value, compress_hyper_2_18_chunk.float_value, compress_hyper_2_18_chunk._ts_meta_count, compress_hyper_2_18_chunk._ts_meta_sequence_num, compress_hyper_2_18_chunk._ts_meta_min_1, compress_hyper_2_18_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_9_chunk + Output: (PARTIAL sum(_hyper_1_9_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_19_chunk + Output: compress_hyper_2_19_chunk."time", compress_hyper_2_19_chunk.segment_by_value, compress_hyper_2_19_chunk.int_value, compress_hyper_2_19_chunk.float_value, compress_hyper_2_19_chunk._ts_meta_count, compress_hyper_2_19_chunk._ts_meta_sequence_num, compress_hyper_2_19_chunk._ts_meta_min_1, compress_hyper_2_19_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_10_chunk + Output: (PARTIAL sum(_hyper_1_10_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_20_chunk + Output: compress_hyper_2_20_chunk."time", compress_hyper_2_20_chunk.segment_by_value, compress_hyper_2_20_chunk.int_value, compress_hyper_2_20_chunk.float_value, compress_hyper_2_20_chunk._ts_meta_count, compress_hyper_2_20_chunk._ts_meta_sequence_num, compress_hyper_2_20_chunk._ts_meta_min_1, compress_hyper_2_20_chunk._ts_meta_max_1 +(53 rows) + +-- Vectorized aggregation possible +SELECT sum(int_value) FROM testtable; + sum +-------- + 308050 +(1 row) + +:EXPLAIN +SELECT sum(int_value) FROM testtable; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.int_value) + -> Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: (PARTIAL sum(_hyper_1_1_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: (PARTIAL sum(_hyper_1_2_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: (PARTIAL sum(_hyper_1_3_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_4_chunk + Output: (PARTIAL sum(_hyper_1_4_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_14_chunk + Output: compress_hyper_2_14_chunk."time", compress_hyper_2_14_chunk.segment_by_value, compress_hyper_2_14_chunk.int_value, compress_hyper_2_14_chunk.float_value, compress_hyper_2_14_chunk._ts_meta_count, compress_hyper_2_14_chunk._ts_meta_sequence_num, compress_hyper_2_14_chunk._ts_meta_min_1, compress_hyper_2_14_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_5_chunk + Output: (PARTIAL sum(_hyper_1_5_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_15_chunk + Output: compress_hyper_2_15_chunk."time", compress_hyper_2_15_chunk.segment_by_value, compress_hyper_2_15_chunk.int_value, compress_hyper_2_15_chunk.float_value, compress_hyper_2_15_chunk._ts_meta_count, compress_hyper_2_15_chunk._ts_meta_sequence_num, compress_hyper_2_15_chunk._ts_meta_min_1, compress_hyper_2_15_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_6_chunk + Output: (PARTIAL sum(_hyper_1_6_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_16_chunk + Output: compress_hyper_2_16_chunk."time", compress_hyper_2_16_chunk.segment_by_value, compress_hyper_2_16_chunk.int_value, compress_hyper_2_16_chunk.float_value, compress_hyper_2_16_chunk._ts_meta_count, compress_hyper_2_16_chunk._ts_meta_sequence_num, compress_hyper_2_16_chunk._ts_meta_min_1, compress_hyper_2_16_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_7_chunk + Output: (PARTIAL sum(_hyper_1_7_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_17_chunk + Output: compress_hyper_2_17_chunk."time", compress_hyper_2_17_chunk.segment_by_value, compress_hyper_2_17_chunk.int_value, compress_hyper_2_17_chunk.float_value, compress_hyper_2_17_chunk._ts_meta_count, compress_hyper_2_17_chunk._ts_meta_sequence_num, compress_hyper_2_17_chunk._ts_meta_min_1, compress_hyper_2_17_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_8_chunk + Output: (PARTIAL sum(_hyper_1_8_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_18_chunk + Output: compress_hyper_2_18_chunk."time", compress_hyper_2_18_chunk.segment_by_value, compress_hyper_2_18_chunk.int_value, compress_hyper_2_18_chunk.float_value, compress_hyper_2_18_chunk._ts_meta_count, compress_hyper_2_18_chunk._ts_meta_sequence_num, compress_hyper_2_18_chunk._ts_meta_min_1, compress_hyper_2_18_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_9_chunk + Output: (PARTIAL sum(_hyper_1_9_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_19_chunk + Output: compress_hyper_2_19_chunk."time", compress_hyper_2_19_chunk.segment_by_value, compress_hyper_2_19_chunk.int_value, compress_hyper_2_19_chunk.float_value, compress_hyper_2_19_chunk._ts_meta_count, compress_hyper_2_19_chunk._ts_meta_sequence_num, compress_hyper_2_19_chunk._ts_meta_min_1, compress_hyper_2_19_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_10_chunk + Output: (PARTIAL sum(_hyper_1_10_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_20_chunk + Output: compress_hyper_2_20_chunk."time", compress_hyper_2_20_chunk.segment_by_value, compress_hyper_2_20_chunk.int_value, compress_hyper_2_20_chunk.float_value, compress_hyper_2_20_chunk._ts_meta_count, compress_hyper_2_20_chunk._ts_meta_sequence_num, compress_hyper_2_20_chunk._ts_meta_min_1, compress_hyper_2_20_chunk._ts_meta_max_1 +(53 rows) + +--- +-- Tests with some chunks are partially compressed +--- +INSERT INTO testtable (time, segment_by_value, int_value, float_value) + VALUES ('1980-01-02 01:00:00-00', 0, 0, 0); +-- Vectorized aggregation possible +SELECT sum(segment_by_value) FROM testtable; + sum +-------- + 308050 +(1 row) + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.segment_by_value) + -> Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: (PARTIAL sum(_hyper_1_1_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_1_chunk.segment_by_value) + -> Seq Scan on _timescaledb_internal._hyper_1_1_chunk + Output: _hyper_1_1_chunk.segment_by_value + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: (PARTIAL sum(_hyper_1_2_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: (PARTIAL sum(_hyper_1_3_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_4_chunk + Output: (PARTIAL sum(_hyper_1_4_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_14_chunk + Output: compress_hyper_2_14_chunk."time", compress_hyper_2_14_chunk.segment_by_value, compress_hyper_2_14_chunk.int_value, compress_hyper_2_14_chunk.float_value, compress_hyper_2_14_chunk._ts_meta_count, compress_hyper_2_14_chunk._ts_meta_sequence_num, compress_hyper_2_14_chunk._ts_meta_min_1, compress_hyper_2_14_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_5_chunk + Output: (PARTIAL sum(_hyper_1_5_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_15_chunk + Output: compress_hyper_2_15_chunk."time", compress_hyper_2_15_chunk.segment_by_value, compress_hyper_2_15_chunk.int_value, compress_hyper_2_15_chunk.float_value, compress_hyper_2_15_chunk._ts_meta_count, compress_hyper_2_15_chunk._ts_meta_sequence_num, compress_hyper_2_15_chunk._ts_meta_min_1, compress_hyper_2_15_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_6_chunk + Output: (PARTIAL sum(_hyper_1_6_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_16_chunk + Output: compress_hyper_2_16_chunk."time", compress_hyper_2_16_chunk.segment_by_value, compress_hyper_2_16_chunk.int_value, compress_hyper_2_16_chunk.float_value, compress_hyper_2_16_chunk._ts_meta_count, compress_hyper_2_16_chunk._ts_meta_sequence_num, compress_hyper_2_16_chunk._ts_meta_min_1, compress_hyper_2_16_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_7_chunk + Output: (PARTIAL sum(_hyper_1_7_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_17_chunk + Output: compress_hyper_2_17_chunk."time", compress_hyper_2_17_chunk.segment_by_value, compress_hyper_2_17_chunk.int_value, compress_hyper_2_17_chunk.float_value, compress_hyper_2_17_chunk._ts_meta_count, compress_hyper_2_17_chunk._ts_meta_sequence_num, compress_hyper_2_17_chunk._ts_meta_min_1, compress_hyper_2_17_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_8_chunk + Output: (PARTIAL sum(_hyper_1_8_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_18_chunk + Output: compress_hyper_2_18_chunk."time", compress_hyper_2_18_chunk.segment_by_value, compress_hyper_2_18_chunk.int_value, compress_hyper_2_18_chunk.float_value, compress_hyper_2_18_chunk._ts_meta_count, compress_hyper_2_18_chunk._ts_meta_sequence_num, compress_hyper_2_18_chunk._ts_meta_min_1, compress_hyper_2_18_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_9_chunk + Output: (PARTIAL sum(_hyper_1_9_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_19_chunk + Output: compress_hyper_2_19_chunk."time", compress_hyper_2_19_chunk.segment_by_value, compress_hyper_2_19_chunk.int_value, compress_hyper_2_19_chunk.float_value, compress_hyper_2_19_chunk._ts_meta_count, compress_hyper_2_19_chunk._ts_meta_sequence_num, compress_hyper_2_19_chunk._ts_meta_min_1, compress_hyper_2_19_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_10_chunk + Output: (PARTIAL sum(_hyper_1_10_chunk.segment_by_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_20_chunk + Output: compress_hyper_2_20_chunk."time", compress_hyper_2_20_chunk.segment_by_value, compress_hyper_2_20_chunk.int_value, compress_hyper_2_20_chunk.float_value, compress_hyper_2_20_chunk._ts_meta_count, compress_hyper_2_20_chunk._ts_meta_sequence_num, compress_hyper_2_20_chunk._ts_meta_min_1, compress_hyper_2_20_chunk._ts_meta_max_1 +(57 rows) + +-- Vectorized aggregation possible +SELECT sum(int_value) FROM testtable; + sum +-------- + 308050 +(1 row) + +:EXPLAIN +SELECT sum(int_value) FROM testtable; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: sum(_hyper_1_1_chunk.int_value) + -> Append + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_1_chunk + Output: (PARTIAL sum(_hyper_1_1_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_11_chunk + Output: compress_hyper_2_11_chunk."time", compress_hyper_2_11_chunk.segment_by_value, compress_hyper_2_11_chunk.int_value, compress_hyper_2_11_chunk.float_value, compress_hyper_2_11_chunk._ts_meta_count, compress_hyper_2_11_chunk._ts_meta_sequence_num, compress_hyper_2_11_chunk._ts_meta_min_1, compress_hyper_2_11_chunk._ts_meta_max_1 + -> Partial Aggregate + Output: PARTIAL sum(_hyper_1_1_chunk.int_value) + -> Seq Scan on _timescaledb_internal._hyper_1_1_chunk + Output: _hyper_1_1_chunk.int_value + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_2_chunk + Output: (PARTIAL sum(_hyper_1_2_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_12_chunk + Output: compress_hyper_2_12_chunk."time", compress_hyper_2_12_chunk.segment_by_value, compress_hyper_2_12_chunk.int_value, compress_hyper_2_12_chunk.float_value, compress_hyper_2_12_chunk._ts_meta_count, compress_hyper_2_12_chunk._ts_meta_sequence_num, compress_hyper_2_12_chunk._ts_meta_min_1, compress_hyper_2_12_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_3_chunk + Output: (PARTIAL sum(_hyper_1_3_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_13_chunk + Output: compress_hyper_2_13_chunk."time", compress_hyper_2_13_chunk.segment_by_value, compress_hyper_2_13_chunk.int_value, compress_hyper_2_13_chunk.float_value, compress_hyper_2_13_chunk._ts_meta_count, compress_hyper_2_13_chunk._ts_meta_sequence_num, compress_hyper_2_13_chunk._ts_meta_min_1, compress_hyper_2_13_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_4_chunk + Output: (PARTIAL sum(_hyper_1_4_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_14_chunk + Output: compress_hyper_2_14_chunk."time", compress_hyper_2_14_chunk.segment_by_value, compress_hyper_2_14_chunk.int_value, compress_hyper_2_14_chunk.float_value, compress_hyper_2_14_chunk._ts_meta_count, compress_hyper_2_14_chunk._ts_meta_sequence_num, compress_hyper_2_14_chunk._ts_meta_min_1, compress_hyper_2_14_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_5_chunk + Output: (PARTIAL sum(_hyper_1_5_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_15_chunk + Output: compress_hyper_2_15_chunk."time", compress_hyper_2_15_chunk.segment_by_value, compress_hyper_2_15_chunk.int_value, compress_hyper_2_15_chunk.float_value, compress_hyper_2_15_chunk._ts_meta_count, compress_hyper_2_15_chunk._ts_meta_sequence_num, compress_hyper_2_15_chunk._ts_meta_min_1, compress_hyper_2_15_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_6_chunk + Output: (PARTIAL sum(_hyper_1_6_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_16_chunk + Output: compress_hyper_2_16_chunk."time", compress_hyper_2_16_chunk.segment_by_value, compress_hyper_2_16_chunk.int_value, compress_hyper_2_16_chunk.float_value, compress_hyper_2_16_chunk._ts_meta_count, compress_hyper_2_16_chunk._ts_meta_sequence_num, compress_hyper_2_16_chunk._ts_meta_min_1, compress_hyper_2_16_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_7_chunk + Output: (PARTIAL sum(_hyper_1_7_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_17_chunk + Output: compress_hyper_2_17_chunk."time", compress_hyper_2_17_chunk.segment_by_value, compress_hyper_2_17_chunk.int_value, compress_hyper_2_17_chunk.float_value, compress_hyper_2_17_chunk._ts_meta_count, compress_hyper_2_17_chunk._ts_meta_sequence_num, compress_hyper_2_17_chunk._ts_meta_min_1, compress_hyper_2_17_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_8_chunk + Output: (PARTIAL sum(_hyper_1_8_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_18_chunk + Output: compress_hyper_2_18_chunk."time", compress_hyper_2_18_chunk.segment_by_value, compress_hyper_2_18_chunk.int_value, compress_hyper_2_18_chunk.float_value, compress_hyper_2_18_chunk._ts_meta_count, compress_hyper_2_18_chunk._ts_meta_sequence_num, compress_hyper_2_18_chunk._ts_meta_min_1, compress_hyper_2_18_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_9_chunk + Output: (PARTIAL sum(_hyper_1_9_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_19_chunk + Output: compress_hyper_2_19_chunk."time", compress_hyper_2_19_chunk.segment_by_value, compress_hyper_2_19_chunk.int_value, compress_hyper_2_19_chunk.float_value, compress_hyper_2_19_chunk._ts_meta_count, compress_hyper_2_19_chunk._ts_meta_sequence_num, compress_hyper_2_19_chunk._ts_meta_min_1, compress_hyper_2_19_chunk._ts_meta_max_1 + -> Custom Scan (DecompressChunk) on _timescaledb_internal._hyper_1_10_chunk + Output: (PARTIAL sum(_hyper_1_10_chunk.int_value)) + Vectorized Aggregation: true + -> Seq Scan on _timescaledb_internal.compress_hyper_2_20_chunk + Output: compress_hyper_2_20_chunk."time", compress_hyper_2_20_chunk.segment_by_value, compress_hyper_2_20_chunk.int_value, compress_hyper_2_20_chunk.float_value, compress_hyper_2_20_chunk._ts_meta_count, compress_hyper_2_20_chunk._ts_meta_sequence_num, compress_hyper_2_20_chunk._ts_meta_min_1, compress_hyper_2_20_chunk._ts_meta_max_1 +(57 rows) + diff --git a/tsl/test/sql/CMakeLists.txt b/tsl/test/sql/CMakeLists.txt index 640e849f14a..76e6f7b337c 100644 --- a/tsl/test/sql/CMakeLists.txt +++ b/tsl/test/sql/CMakeLists.txt @@ -28,7 +28,8 @@ set(TEST_FILES partialize_finalize.sql reorder.sql skip_scan.sql - size_utils_tsl.sql) + size_utils_tsl.sql + vectorized_aggregation.sql) if(ENABLE_MULTINODE_TESTS) list(APPEND TEST_FILES dist_param.sql dist_views.sql) diff --git a/tsl/test/sql/vectorized_aggregation.sql b/tsl/test/sql/vectorized_aggregation.sql new file mode 100644 index 00000000000..0e98b8ae7cc --- /dev/null +++ b/tsl/test/sql/vectorized_aggregation.sql @@ -0,0 +1,100 @@ +-- This file and its contents are licensed under the Timescale License. +-- Please see the included NOTICE for copyright information and +-- LICENSE-TIMESCALE for a copy of the license. + +\set EXPLAIN 'EXPLAIN (VERBOSE, COSTS OFF)' + +CREATE TABLE testtable ( +time timestamptz NOT NULL, +segment_by_value integer NOT NULL, +int_value integer NOT NULL, +float_value double precision NOT NULL); + +SELECT FROM create_hypertable(relation=>'testtable', time_column_name=> 'time'); + +ALTER TABLE testtable SET (timescaledb.compress, timescaledb.compress_segmentby='segment_by_value'); + +INSERT INTO testtable +SELECT time AS time, +value AS segment_by_value, +value AS int_value, +value AS float_value +FROM +generate_series('1980-01-01 00:00:00-00', '1980-03-01 00:00:00-00', INTERVAL '1 day') AS g1(time), +generate_series(1, 100, 1) AS g2(value) +ORDER BY time; + +-- Aggregation result without any vectorization +SELECT sum(segment_by_value), sum(int_value), sum(float_value) FROM testtable; + +--- +-- Tests with some chunks compressed +--- +SELECT compress_chunk(ch) FROM show_chunks('testtable') ch LIMIT 3; + +-- Vectorized aggregation possible +SELECT sum(segment_by_value) FROM testtable; + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable; + +-- Vectorization not possible due to a used filter +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable WHERE segment_by_value > 0; + +-- Vectorization not possible due to segment_by +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable GROUP BY float_value; + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable GROUP BY int_value; + +:EXPLAIN +SELECT sum(int_value) FROM testtable GROUP BY segment_by_value; + +-- Vectorized aggregation possible +SELECT sum(int_value) FROM testtable; + +:EXPLAIN +SELECT sum(int_value) FROM testtable; + +-- Vectorized aggregation not possible +SELECT sum(float_value) FROM testtable; + +:EXPLAIN +SELECT sum(float_value) FROM testtable; + +--- +-- Tests with all chunks compressed +--- +SELECT compress_chunk(ch, if_not_compressed => true) FROM show_chunks('testtable') ch; + +-- Vectorized aggregation possible +SELECT sum(segment_by_value) FROM testtable; + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable; + +-- Vectorized aggregation possible +SELECT sum(int_value) FROM testtable; + +:EXPLAIN +SELECT sum(int_value) FROM testtable; + +--- +-- Tests with some chunks are partially compressed +--- +INSERT INTO testtable (time, segment_by_value, int_value, float_value) + VALUES ('1980-01-02 01:00:00-00', 0, 0, 0); + +-- Vectorized aggregation possible +SELECT sum(segment_by_value) FROM testtable; + +:EXPLAIN +SELECT sum(segment_by_value) FROM testtable; + +-- Vectorized aggregation possible +SELECT sum(int_value) FROM testtable; + +:EXPLAIN +SELECT sum(int_value) FROM testtable;