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;