Skip to content

Commit 5ba740e

Browse files
svenklemmJLockerman
authored andcommitted
Add gapfill query support
This patch adds first level support for gap fill queries, including support for LOCF (last observation carried forward) and interpolation, without requiring to join against `generate_series`. This makes it easier to join timeseries with different or irregular sampling intervals.
1 parent be7c74c commit 5ba740e

35 files changed

+3674
-4
lines changed

sql/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ set(SOURCE_FILES
4444
bgw_scheduler.sql
4545
installation_metadata.sql
4646
views.sql
47+
gapfill.sql
4748
)
4849

4950
# These files should be pre-pended to update scripts so that they are

sql/gapfill.sql

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
-- Copyright (c) 2016-2018 Timescale, Inc. All Rights Reserved.
2+
--
3+
-- This file is licensed under the Apache License, see LICENSE-APACHE
4+
-- at the top level directory of the TimescaleDB distribution.
5+
6+
CREATE OR REPLACE FUNCTION time_bucket_gapfill(bucket_width SMALLINT, ts SMALLINT, "start" SMALLINT, "end" SMALLINT) RETURNS SMALLINT
7+
AS '@MODULE_PATHNAME@', 'ts_gapfill_int16_bucket' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED STRICT;
8+
9+
CREATE OR REPLACE FUNCTION time_bucket_gapfill(bucket_width INT, ts INT, "start" INT, "end" INT) RETURNS INT
10+
AS '@MODULE_PATHNAME@', 'ts_gapfill_int32_bucket' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED STRICT;
11+
12+
CREATE OR REPLACE FUNCTION time_bucket_gapfill(bucket_width BIGINT, ts BIGINT, "start" BIGINT, "end" BIGINT) RETURNS BIGINT
13+
AS '@MODULE_PATHNAME@', 'ts_gapfill_int64_bucket' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED STRICT;
14+
15+
CREATE OR REPLACE FUNCTION time_bucket_gapfill(bucket_width INTERVAL, ts DATE, "start" DATE, "end" DATE) RETURNS DATE
16+
AS '@MODULE_PATHNAME@', 'ts_gapfill_date_bucket' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED STRICT;
17+
18+
CREATE OR REPLACE FUNCTION time_bucket_gapfill(bucket_width INTERVAL, ts TIMESTAMP, "start" TIMESTAMP, "end" TIMESTAMP) RETURNS TIMESTAMP
19+
AS '@MODULE_PATHNAME@', 'ts_gapfill_timestamp_bucket' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED STRICT;
20+
21+
CREATE OR REPLACE FUNCTION time_bucket_gapfill(bucket_width INTERVAL, ts TIMESTAMPTZ, "start" TIMESTAMPTZ, "end" TIMESTAMPTZ) RETURNS TIMESTAMPTZ
22+
AS '@MODULE_PATHNAME@', 'ts_gapfill_timestamptz_bucket' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED STRICT;
23+
24+
-- locf function
25+
CREATE OR REPLACE FUNCTION locf(value ANYELEMENT, prev ANYELEMENT=NULL) RETURNS ANYELEMENT
26+
AS '@MODULE_PATHNAME@', 'ts_gapfill_marker' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED;
27+
28+
-- interpolate functions
29+
CREATE OR REPLACE FUNCTION interpolate(value SMALLINT,prev RECORD=NULL,next RECORD=NULL) RETURNS SMALLINT
30+
AS '@MODULE_PATHNAME@', 'ts_gapfill_marker' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED;
31+
32+
CREATE OR REPLACE FUNCTION interpolate(value INT,prev RECORD=NULL,next RECORD=NULL) RETURNS INT
33+
AS '@MODULE_PATHNAME@', 'ts_gapfill_marker' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED;
34+
35+
CREATE OR REPLACE FUNCTION interpolate(value BIGINT,prev RECORD=NULL,next RECORD=NULL) RETURNS BIGINT
36+
AS '@MODULE_PATHNAME@', 'ts_gapfill_marker' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED;
37+
38+
CREATE OR REPLACE FUNCTION interpolate(value REAL,prev RECORD=NULL,next RECORD=NULL) RETURNS REAL
39+
AS '@MODULE_PATHNAME@', 'ts_gapfill_marker' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED;
40+
41+
CREATE OR REPLACE FUNCTION interpolate(value FLOAT,prev RECORD=NULL,next RECORD=NULL) RETURNS FLOAT
42+
AS '@MODULE_PATHNAME@', 'ts_gapfill_marker' LANGUAGE C IMMUTABLE PARALLEL RESTRICTED;
43+

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ set(SOURCES
2020
dimension_vector.c
2121
event_trigger.c
2222
extension.c
23+
gapfill.c
2324
guc.c
2425
histogram.c
2526
hypercube.c

src/cross_module_fn.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <postgres.h>
88
#include <fmgr.h>
99
#include <utils/timestamp.h>
10+
#include <utils/lsyscache.h>
1011

1112
#include "export.h"
1213
#include "cross_module_fn.h"
@@ -90,6 +91,18 @@ bgw_policy_job_execute_default_fn(BgwJob *job)
9091
return false;
9192
}
9293

94+
static Datum
95+
error_no_default_fn_pg_function(PG_FUNCTION_ARGS)
96+
{
97+
ereport(ERROR,
98+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
99+
errmsg("function \"%s\" is not supported under the current license \"%s\"",
100+
get_func_name(fcinfo->flinfo->fn_oid),
101+
ts_guc_license_key),
102+
errhint("Buy a Timescale license to enable the functionality")));
103+
PG_RETURN_NULL();
104+
}
105+
93106
/*
94107
* Define cross-module functions' default values:
95108
* If the submodule isn't activated, using one of the cm functions will throw an
@@ -106,6 +119,14 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
106119
.add_recluster_policy = error_no_default_fn_pg,
107120
.remove_drop_chunks_policy = error_no_default_fn_pg,
108121
.remove_recluster_policy = error_no_default_fn_pg,
122+
.create_upper_paths_hook = NULL,
123+
.gapfill_marker = error_no_default_fn_pg_function,
124+
.gapfill_int16_time_bucket = error_no_default_fn_pg_function,
125+
.gapfill_int32_time_bucket = error_no_default_fn_pg_function,
126+
.gapfill_int64_time_bucket = error_no_default_fn_pg_function,
127+
.gapfill_date_time_bucket = error_no_default_fn_pg_function,
128+
.gapfill_timestamp_time_bucket = error_no_default_fn_pg_function,
129+
.gapfill_timestamptz_time_bucket = error_no_default_fn_pg_function,
109130
};
110131

111132
TSDLLEXPORT CrossModuleFunctions *ts_cm_functions = &ts_cm_functions_default;

src/cross_module_fn.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <c.h>
1111
#include <utils/timestamp.h>
12+
#include <optimizer/planner.h>
1213

1314
#include "export.h"
1415
#include "bgw/job.h"
@@ -35,6 +36,14 @@ typedef struct CrossModuleFunctions
3536
Datum (*add_recluster_policy) (PG_FUNCTION_ARGS);
3637
Datum (*remove_drop_chunks_policy) (PG_FUNCTION_ARGS);
3738
Datum (*remove_recluster_policy) (PG_FUNCTION_ARGS);
39+
void (*create_upper_paths_hook) (PlannerInfo *, UpperRelationKind, RelOptInfo *, RelOptInfo *);
40+
PGFunction gapfill_marker;
41+
PGFunction gapfill_int16_time_bucket;
42+
PGFunction gapfill_int32_time_bucket;
43+
PGFunction gapfill_int64_time_bucket;
44+
PGFunction gapfill_date_time_bucket;
45+
PGFunction gapfill_timestamp_time_bucket;
46+
PGFunction gapfill_timestamptz_time_bucket;
3847
} CrossModuleFunctions;
3948

4049
extern TSDLLEXPORT CrossModuleFunctions *ts_cm_functions;

src/export.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
#endif
5959

6060
#define TS_FUNCTION_INFO_V1(fn) \
61-
PGDLLEXPORT Datum fn(PG_FUNCTION_ARGS); \
61+
TSDLLEXPORT Datum fn(PG_FUNCTION_ARGS); \
6262
PG_FUNCTION_INFO_V1(fn)
6363

6464
#endif /* TIMESCALEDB_EXPORT_H */

src/gapfill.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2016-2018 Timescale, Inc. All Rights Reserved.
3+
*
4+
* This file is licensed under the Apache License,
5+
* see LICENSE-APACHE at the top level directory.
6+
*/
7+
#include <postgres.h>
8+
#include <fmgr.h>
9+
10+
#include "license_guc.h"
11+
#include "cross_module_fn.h"
12+
#include "compat.h"
13+
#include "export.h"
14+
15+
/*
16+
* stub function to trigger locf and interpolate in gapfill node
17+
* the calls to these functions will be removed from the final plan
18+
* for a valid gapfill query. This function will only be called if
19+
* no timescale license is available or when they are used outside of
20+
* a valid gapfill query.
21+
*/
22+
TS_FUNCTION_INFO_V1(ts_gapfill_marker);
23+
Datum
24+
ts_gapfill_marker(PG_FUNCTION_ARGS)
25+
{
26+
PG_RETURN_DATUM(ts_cm_functions->gapfill_marker(fcinfo));
27+
}
28+
29+
#define GAPFILL_TIMEBUCKET_WRAPPER(datatype) \
30+
TS_FUNCTION_INFO_V1(ts_gapfill_ ## datatype ## _bucket); \
31+
Datum \
32+
ts_gapfill_ ## datatype ## _bucket(PG_FUNCTION_ARGS) \
33+
{ \
34+
return ts_cm_functions->gapfill_ ## datatype ## _time_bucket(fcinfo); \
35+
}
36+
37+
GAPFILL_TIMEBUCKET_WRAPPER(int16);
38+
GAPFILL_TIMEBUCKET_WRAPPER(int32);
39+
GAPFILL_TIMEBUCKET_WRAPPER(int64);
40+
GAPFILL_TIMEBUCKET_WRAPPER(date);
41+
GAPFILL_TIMEBUCKET_WRAPPER(timestamp);
42+
GAPFILL_TIMEBUCKET_WRAPPER(timestamptz);

src/planner.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
#endif
3838
#include "compat-msvc-exit.h"
3939

40+
#include "cross_module_fn.h"
41+
#include "license_guc.h"
4042
#include "hypertable_cache.h"
4143
#include "extension.h"
4244
#include "utils.h"
@@ -515,6 +517,9 @@ timescale_create_upper_paths_hook(PlannerInfo *root,
515517
if (!ts_extension_is_loaded())
516518
return;
517519

520+
if (ts_cm_functions->create_upper_paths_hook != NULL)
521+
ts_cm_functions->create_upper_paths_hook(root, stage, input_rel, output_rel);
522+
518523
/* Modify for INSERTs on a hypertable */
519524
if (output_rel != NULL && output_rel->pathlist != NIL)
520525
output_rel->pathlist = replace_hypertable_insert_paths(root, output_rel->pathlist);

src/time_bucket.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) 2016-2018 Timescale, Inc. All Rights Reserved.
3+
*
4+
* This file is licensed under the Apache License,
5+
* see LICENSE-APACHE at the top level directory.
6+
*/
7+
#ifndef TIMESCALEDB_TIME_BUCKET_H
8+
#define TIMESCALEDB_TIME_BUCKET_H
9+
10+
#include <postgres.h>
11+
#include <fmgr.h>
12+
13+
extern Datum ts_int16_bucket(PG_FUNCTION_ARGS);
14+
extern Datum ts_int32_bucket(PG_FUNCTION_ARGS);
15+
extern Datum ts_int64_bucket(PG_FUNCTION_ARGS);
16+
extern Datum ts_date_bucket(PG_FUNCTION_ARGS);
17+
extern Datum ts_timestamp_bucket(PG_FUNCTION_ARGS);
18+
extern Datum ts_timestamptz_bucket(PG_FUNCTION_ARGS);
19+
20+
#endif /* TIMESCALEDB_TIME_BUCKET_H */

test/expected/extension.out

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ ORDER BY proname;
3333
hypertable_relation_size_pretty
3434
indexes_relation_size
3535
indexes_relation_size_pretty
36+
interpolate
3637
last
38+
locf
3739
remove_drop_chunks_policy
3840
remove_recluster_policy
3941
set_adaptive_chunking
@@ -42,5 +44,6 @@ ORDER BY proname;
4244
show_chunks
4345
show_tablespaces
4446
time_bucket
45-
(27 rows)
47+
time_bucket_gapfill
48+
(30 rows)
4649

0 commit comments

Comments
 (0)