Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add timezone support to time_bucket_gapfill #4670

Merged
merged 2 commits into from Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 10 additions & 2 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,16 @@ accidentally triggering the load of a previous DB version.**

## Unreleased

This version adds time_bucket_gapfill function that allows
specifying the timezone to bucket for. Unfortunately this introduces
an ambiguity with some previous call variations when an untyped
start/finish argument is passed to time_bucket_gapfill. Some queries
might need to be adjusted and either explicitly name the positional
argument or resolve the type ambiguity by casting to the intended type.

**Features**
* #4670 Add timezone support to time_bucket_gapfill

**Bugfixes**
* #4619 Improve handling enum columns in compressed hypertables

Expand Down Expand Up @@ -2563,5 +2573,3 @@ the next release.
* [39f4c0f] Remove sample data instructions and point to docs site
* [9015314] Revised the `get_general_index_definition` function to handle cases where indexes have definitions other than just `CREATE INDEX` (thanks @bricklen)

**Bugfixes**
* #4619 Improve handling enum columns in compressed hypertables
3 changes: 3 additions & 0 deletions sql/gapfill.sql
Expand Up @@ -20,6 +20,9 @@ CREATE OR REPLACE FUNCTION @extschema@.time_bucket_gapfill(bucket_width INTERVAL
CREATE OR REPLACE FUNCTION @extschema@.time_bucket_gapfill(bucket_width INTERVAL, ts TIMESTAMPTZ, start TIMESTAMPTZ=NULL, finish TIMESTAMPTZ=NULL) RETURNS TIMESTAMPTZ
AS '@MODULE_PATHNAME@', 'ts_gapfill_timestamptz_bucket' LANGUAGE C VOLATILE PARALLEL SAFE;

CREATE OR REPLACE FUNCTION @extschema@.time_bucket_gapfill(bucket_width INTERVAL, ts TIMESTAMPTZ, timezone TEXT, start TIMESTAMPTZ=NULL, finish TIMESTAMPTZ=NULL) RETURNS TIMESTAMPTZ
AS '@MODULE_PATHNAME@', 'ts_gapfill_timestamptz_timezone_bucket' LANGUAGE C VOLATILE PARALLEL SAFE;

-- locf function
CREATE OR REPLACE FUNCTION @extschema@.locf(value ANYELEMENT, prev ANYELEMENT=NULL, treat_null_as_missing BOOL=false) RETURNS ANYELEMENT
AS '@MODULE_PATHNAME@', 'ts_gapfill_marker' LANGUAGE C VOLATILE PARALLEL SAFE;
Expand Down
5 changes: 5 additions & 0 deletions sql/updates/latest-dev.sql
@@ -0,0 +1,5 @@

-- gapfill with timezone support
CREATE FUNCTION @extschema@.time_bucket_gapfill(bucket_width INTERVAL, ts TIMESTAMPTZ, timezone TEXT, start TIMESTAMPTZ=NULL, finish TIMESTAMPTZ=NULL) RETURNS TIMESTAMPTZ
AS '@MODULE_PATHNAME@', 'ts_gapfill_timestamptz_timezone_bucket' LANGUAGE C VOLATILE PARALLEL SAFE;

4 changes: 4 additions & 0 deletions sql/updates/reverse-dev.sql
@@ -0,0 +1,4 @@

-- gapfill with timezone support
DROP FUNCTION @extschema@.time_bucket_gapfill(INTERVAL,TIMESTAMPTZ,TEXT,TIMESTAMPTZ,TIMESTAMPTZ);

1 change: 1 addition & 0 deletions src/cross_module_fn.c
Expand Up @@ -417,6 +417,7 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
.gapfill_date_time_bucket = error_no_default_fn_pg_community,
.gapfill_timestamp_time_bucket = error_no_default_fn_pg_community,
.gapfill_timestamptz_time_bucket = error_no_default_fn_pg_community,
.gapfill_timestamptz_timezone_time_bucket = error_no_default_fn_pg_community,

/* bgw policies */
.policy_compression_add = error_no_default_fn_pg_community,
Expand Down
1 change: 1 addition & 0 deletions src/cross_module_fn.h
Expand Up @@ -86,6 +86,7 @@ typedef struct CrossModuleFunctions
PGFunction gapfill_date_time_bucket;
PGFunction gapfill_timestamp_time_bucket;
PGFunction gapfill_timestamptz_time_bucket;
PGFunction gapfill_timestamptz_timezone_time_bucket;

PGFunction reorder_chunk;
PGFunction move_chunk;
Expand Down
16 changes: 14 additions & 2 deletions src/func_cache.c
Expand Up @@ -64,6 +64,7 @@ date_trunc_sort_transform(FuncExpr *func)
(list_length((func)->args) == 2 || IsA(lthird((func)->args), Const))

#define time_bucket_has_const_period(func) IsA(linitial((func)->args), Const)
#define time_bucket_has_const_timezone(func) IsA(lthird((func)->args), Const)

static Expr *
do_sort_transform(FuncExpr *func)
Expand All @@ -85,9 +86,10 @@ time_bucket_gapfill_sort_transform(FuncExpr *func)
* proof: time_bucket(const1, time1) >= time_bucket(const1,time2) iff time1
* > time2
*/
Assert(list_length(func->args) == 4);
Assert(list_length(func->args) == 4 || list_length(func->args) == 5);

if (!time_bucket_has_const_period(func))
if (!time_bucket_has_const_period(func) ||
(list_length(func->args) == 5 && !time_bucket_has_const_timezone(func)))
return (Expr *) func;

return do_sort_transform(func);
Expand Down Expand Up @@ -402,6 +404,16 @@ static FuncInfo funcinfo[] = {
.group_estimate = time_bucket_group_estimate,
.sort_transform = time_bucket_gapfill_sort_transform,
},
{
.origin = ORIGIN_TIMESCALE,
.is_bucketing_func = true,
.allowed_in_cagg_definition = false,
.funcname = "time_bucket_gapfill",
.nargs = 5,
.arg_types = { INTERVALOID, TIMESTAMPTZOID, TEXTOID, TIMESTAMPTZOID, TIMESTAMPTZOID },
.group_estimate = time_bucket_group_estimate,
.sort_transform = time_bucket_gapfill_sort_transform,
},
{
.origin = ORIGIN_TIMESCALE,
.is_bucketing_func = true,
Expand Down
1 change: 1 addition & 0 deletions src/gapfill.c
Expand Up @@ -34,3 +34,4 @@ GAPFILL_TIMEBUCKET_WRAPPER(int64);
GAPFILL_TIMEBUCKET_WRAPPER(date);
GAPFILL_TIMEBUCKET_WRAPPER(timestamp);
GAPFILL_TIMEBUCKET_WRAPPER(timestamptz);
GAPFILL_TIMEBUCKET_WRAPPER(timestamptz_timezone);
2 changes: 1 addition & 1 deletion tsl/src/fdw/deparse.c
Expand Up @@ -78,7 +78,7 @@
#include "scan_plan.h"
#include "extension_constants.h"
#include "partialize_finalize.h"
#include "nodes/gapfill/planner.h"
#include "nodes/gapfill/gapfill.h"
#include "planner/planner.h"

/*
Expand Down
3 changes: 2 additions & 1 deletion tsl/src/init.c
Expand Up @@ -40,7 +40,7 @@
#include "license_guc.h"
#include "nodes/decompress_chunk/planner.h"
#include "nodes/skip_scan/skip_scan.h"
#include "nodes/gapfill/gapfill.h"
#include "nodes/gapfill/gapfill_functions.h"
#include "partialize_finalize.h"
#include "planner.h"
#include "process_utility.h"
Expand Down Expand Up @@ -126,6 +126,7 @@ CrossModuleFunctions tsl_cm_functions = {
.gapfill_date_time_bucket = gapfill_date_time_bucket,
.gapfill_timestamp_time_bucket = gapfill_timestamp_time_bucket,
.gapfill_timestamptz_time_bucket = gapfill_timestamptz_time_bucket,
.gapfill_timestamptz_timezone_time_bucket = gapfill_timestamptz_timezone_time_bucket,

.reorder_chunk = tsl_reorder_chunk,
.move_chunk = tsl_move_chunk,
Expand Down
6 changes: 4 additions & 2 deletions tsl/src/nodes/gapfill/CMakeLists.txt
@@ -1,5 +1,7 @@
set(SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/gapfill.c ${CMAKE_CURRENT_SOURCE_DIR}/planner.c
${CMAKE_CURRENT_SOURCE_DIR}/exec.c ${CMAKE_CURRENT_SOURCE_DIR}/locf.c
${CMAKE_CURRENT_SOURCE_DIR}/gapfill_functions.c
${CMAKE_CURRENT_SOURCE_DIR}/gapfill_plan.c
${CMAKE_CURRENT_SOURCE_DIR}/gapfill_exec.c
${CMAKE_CURRENT_SOURCE_DIR}/locf.c
${CMAKE_CURRENT_SOURCE_DIR}/interpolate.c)
target_sources(${TSL_LIBRARY_NAME} PRIVATE ${SOURCES})
2 changes: 1 addition & 1 deletion tsl/src/nodes/gapfill/README.md
Expand Up @@ -21,7 +21,7 @@ The locf and interpolate function calls serve as markers in the plan to
trigger locf or interpolate behaviour. In the targetlist of the gapfill node
those functions will be toplevel function calls.

The gapfill state transitions are described in exec.h
The gapfill state transitions are described in gapfill_internal.h

## Usage

Expand Down
20 changes: 12 additions & 8 deletions tsl/src/nodes/gapfill/gapfill.h
Expand Up @@ -8,18 +8,22 @@
#define TIMESCALEDB_TSL_NODES_GAPFILL_H

#include <postgres.h>
#include <fmgr.h>
#include <nodes/pathnodes.h>
#include <nodes/primnodes.h>

#define GAPFILL_FUNCTION "time_bucket_gapfill"
#define GAPFILL_LOCF_FUNCTION "locf"
#define GAPFILL_INTERPOLATE_FUNCTION "interpolate"

extern Datum gapfill_marker(PG_FUNCTION_ARGS);
extern Datum gapfill_int16_time_bucket(PG_FUNCTION_ARGS);
extern Datum gapfill_int32_time_bucket(PG_FUNCTION_ARGS);
extern Datum gapfill_int64_time_bucket(PG_FUNCTION_ARGS);
extern Datum gapfill_timestamp_time_bucket(PG_FUNCTION_ARGS);
extern Datum gapfill_timestamptz_time_bucket(PG_FUNCTION_ARGS);
extern Datum gapfill_date_time_bucket(PG_FUNCTION_ARGS);
bool gapfill_in_expression(Expr *node);
void plan_add_gapfill(PlannerInfo *root, RelOptInfo *group_rel);
void gapfill_adjust_window_targetlist(PlannerInfo *root, RelOptInfo *input_rel,
RelOptInfo *output_rel);

typedef struct GapFillPath
{
CustomPath cpath;
FuncExpr *func; /* time_bucket_gapfill function call */
} GapFillPath;

#endif /* TIMESCALEDB_TSL_NODES_GAPFILL_H */