From 39645d56da87521c3491efa97bafece00805d2b4 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Thu, 16 Dec 2021 23:16:34 +0100 Subject: [PATCH] Fix subtract_integer_from_now on 32-bit platforms This patch fixes subtract_integer_from_now on 32-bit platforms, improves error handling and adds some basic tests. subtract_integer_from_now would trigger an assert when called on a hypertable without integer time dimension (found by sqlsmith). Additionally subtract_integer_from_now would segfault when called on a hypertable without partitioning dimensions. --- src/hypertable.c | 5 + src/utils.c | 28 +++-- .../expected/subtract_integer_from_now.out | 118 ++++++++++++++++++ tsl/test/shared/sql/CMakeLists.txt | 3 +- .../shared/sql/subtract_integer_from_now.sql | 63 ++++++++++ 5 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 tsl/test/shared/expected/subtract_integer_from_now.out create mode 100644 tsl/test/shared/sql/subtract_integer_from_now.sql diff --git a/src/hypertable.c b/src/hypertable.c index 82d894072..ab39eb5e9 100644 --- a/src/hypertable.c +++ b/src/hypertable.c @@ -2391,6 +2391,11 @@ ts_hypertable_set_integer_now_func(PG_FUNCTION_ARGS) ts_hypertable_permissions_check(table_relid, GetUserId()); hypertable = ts_hypertable_cache_get_cache_and_entry(table_relid, CACHE_FLAG_NONE, &hcache); + if (TS_HYPERTABLE_IS_INTERNAL_COMPRESSION_TABLE(hypertable)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("custom time function not supported on internal compression table"))); + /* validate that the open dimension uses numeric type */ open_dim = hyperspace_get_open_dimension(hypertable->space, 0); diff --git a/src/utils.c b/src/utils.c index 7bdc93f5f..4947ba693 100644 --- a/src/utils.c +++ b/src/utils.c @@ -904,18 +904,24 @@ Datum ts_subtract_integer_from_now(PG_FUNCTION_ARGS) { Oid ht_relid = PG_GETARG_OID(0); - Datum lag = PG_GETARG_INT64(1); + int64 lag = PG_GETARG_INT64(1); Cache *hcache; - Hypertable *hypertable = - ts_hypertable_cache_get_cache_and_entry(ht_relid, CACHE_FLAG_NONE, &hcache); - - const Dimension *dim = hyperspace_get_open_dimension(hypertable->space, 0); - Oid partitioning_type = ts_dimension_get_partition_type(dim); - Oid now_func = ts_get_integer_now_func(dim); - if (now_func == InvalidOid) - elog(ERROR, "could not find valid integer_now function for hypertable"); - Assert(IS_INTEGER_TYPE(partitioning_type)); - int64 res = ts_sub_integer_from_now(lag, partitioning_type, now_func); + Hypertable *ht = ts_hypertable_cache_get_cache_and_entry(ht_relid, CACHE_FLAG_NONE, &hcache); + const Dimension *dim = hyperspace_get_open_dimension(ht->space, 0); ts_cache_release(hcache); + + if (!dim) + elog(ERROR, "hypertable has no open partitioning dimension"); + + Oid partitioning_type = ts_dimension_get_partition_type(dim); + + if (!IS_INTEGER_TYPE(partitioning_type)) + elog(ERROR, "hypertable has no integer partitioning dimension"); + + Oid now_func = ts_get_integer_now_func(dim); + if (!OidIsValid(now_func)) + elog(ERROR, "could not find valid integer_now function for hypertable"); + + int64 res = ts_sub_integer_from_now(lag, partitioning_type, now_func); return Int64GetDatum(res); } diff --git a/tsl/test/shared/expected/subtract_integer_from_now.out b/tsl/test/shared/expected/subtract_integer_from_now.out new file mode 100644 index 000000000..2f909b29c --- /dev/null +++ b/tsl/test/shared/expected/subtract_integer_from_now.out @@ -0,0 +1,118 @@ +-- 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. +-- test on normal table +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now('pg_class', 1); +ERROR: table "pg_class" is not a hypertable +\set ON_ERROR_STOP 1 +-- test on hypertable with non-int time dimension +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now('metrics', 1); +ERROR: hypertable has no integer partitioning dimension +\set ON_ERROR_STOP 1 +SELECT + format('%I.%I', ht.schema_name, ht.table_name) AS "TABLENAME" +FROM + _timescaledb_catalog.hypertable ht + INNER JOIN _timescaledb_catalog.hypertable uncompress ON (ht.id = uncompress.compressed_hypertable_id + AND uncompress.table_name = 'metrics_compressed') \gset +-- test on hypertable without dimensions +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now(:'TABLENAME', 1); +ERROR: hypertable has no open partitioning dimension +\set ON_ERROR_STOP 1 +-- test on hypertable without now function +CREATE TABLE subtract_int_no_func(time int NOT NULL); +SELECT table_name FROM create_hypertable('subtract_int_no_func','time',chunk_time_interval:=10); + table_name +---------------------- + subtract_int_no_func +(1 row) + +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int_no_func', 1); +ERROR: integer_now function not set +\set ON_ERROR_STOP 1 +CREATE OR REPLACE FUNCTION sub_int2_now() RETURNS int2 AS $$ SELECT 2::int2; $$ LANGUAGE SQL IMMUTABLE; +CREATE OR REPLACE FUNCTION sub_int4_now() RETURNS int4 AS $$ SELECT 4::int4; $$ LANGUAGE SQL IMMUTABLE; +CREATE OR REPLACE FUNCTION sub_int8_now() RETURNS int8 AS $$ SELECT 8::int8; $$ LANGUAGE SQL IMMUTABLE; +CREATE TABLE subtract_int2(time int2 NOT NULL); +CREATE TABLE subtract_int4(time int4 NOT NULL); +CREATE TABLE subtract_int8(time int8 NOT NULL); +SELECT table_name FROM create_hypertable('subtract_int2', 'time', chunk_time_interval:=10); + table_name +--------------- + subtract_int2 +(1 row) + +SELECT table_name FROM create_hypertable('subtract_int4', 'time', chunk_time_interval:=10); + table_name +--------------- + subtract_int4 +(1 row) + +SELECT table_name FROM create_hypertable('subtract_int8', 'time', chunk_time_interval:=10); + table_name +--------------- + subtract_int8 +(1 row) + +SELECT set_integer_now_func('subtract_int2', 'sub_int2_now'); + set_integer_now_func +---------------------- + +(1 row) + +SELECT set_integer_now_func('subtract_int4', 'sub_int4_now'); + set_integer_now_func +---------------------- + +(1 row) + +SELECT set_integer_now_func('subtract_int8', 'sub_int8_now'); + set_integer_now_func +---------------------- + +(1 row) + +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int2', lag) AS sub FROM (VALUES (-10),(0),(2),(4),(8)) v(lag); + sub +----- + 12 + 2 + 0 + -2 + -6 +(5 rows) + +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int4', lag) AS sub FROM (VALUES (-10),(0),(2),(4),(8)) v(lag); + sub +----- + 14 + 4 + 2 + 0 + -4 +(5 rows) + +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int8', lag) AS sub FROM (VALUES (-10),(0),(2),(4),(8)) v(lag); + sub +----- + 18 + 8 + 6 + 4 + 0 +(5 rows) + +-- test set_integer_now_func on internal table +\set ON_ERROR_STOP 0 +SELECT set_integer_now_func(:'TABLENAME', 'sub_int2_now'); +ERROR: custom time function not supported on internal compression table +\set ON_ERROR_STOP 1 +-- cleanup +DROP TABLE subtract_int_no_func; +DROP TABLE subtract_int2; +DROP TABLE subtract_int4; +DROP TABLE subtract_int8; diff --git a/tsl/test/shared/sql/CMakeLists.txt b/tsl/test/shared/sql/CMakeLists.txt index 714ae7f99..7c8c49f2b 100644 --- a/tsl/test/shared/sql/CMakeLists.txt +++ b/tsl/test/shared/sql/CMakeLists.txt @@ -9,7 +9,8 @@ set(TEST_FILES_SHARED dist_fetcher_type.sql dist_gapfill.sql dist_insert.sql - dist_queries.sql) + dist_queries.sql + subtract_integer_from_now.sql) if((${PG_VERSION_MAJOR} GREATER_EQUAL "14")) list(APPEND TEST_FILES_SHARED memoize.sql) diff --git a/tsl/test/shared/sql/subtract_integer_from_now.sql b/tsl/test/shared/sql/subtract_integer_from_now.sql new file mode 100644 index 000000000..9ac191bfa --- /dev/null +++ b/tsl/test/shared/sql/subtract_integer_from_now.sql @@ -0,0 +1,63 @@ +-- 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. + +-- test on normal table +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now('pg_class', 1); +\set ON_ERROR_STOP 1 + +-- test on hypertable with non-int time dimension +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now('metrics', 1); +\set ON_ERROR_STOP 1 + +SELECT + format('%I.%I', ht.schema_name, ht.table_name) AS "TABLENAME" +FROM + _timescaledb_catalog.hypertable ht + INNER JOIN _timescaledb_catalog.hypertable uncompress ON (ht.id = uncompress.compressed_hypertable_id + AND uncompress.table_name = 'metrics_compressed') \gset + +-- test on hypertable without dimensions +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now(:'TABLENAME', 1); +\set ON_ERROR_STOP 1 + +-- test on hypertable without now function +CREATE TABLE subtract_int_no_func(time int NOT NULL); +SELECT table_name FROM create_hypertable('subtract_int_no_func','time',chunk_time_interval:=10); +\set ON_ERROR_STOP 0 +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int_no_func', 1); +\set ON_ERROR_STOP 1 + +CREATE OR REPLACE FUNCTION sub_int2_now() RETURNS int2 AS $$ SELECT 2::int2; $$ LANGUAGE SQL IMMUTABLE; +CREATE OR REPLACE FUNCTION sub_int4_now() RETURNS int4 AS $$ SELECT 4::int4; $$ LANGUAGE SQL IMMUTABLE; +CREATE OR REPLACE FUNCTION sub_int8_now() RETURNS int8 AS $$ SELECT 8::int8; $$ LANGUAGE SQL IMMUTABLE; + +CREATE TABLE subtract_int2(time int2 NOT NULL); +CREATE TABLE subtract_int4(time int4 NOT NULL); +CREATE TABLE subtract_int8(time int8 NOT NULL); + +SELECT table_name FROM create_hypertable('subtract_int2', 'time', chunk_time_interval:=10); +SELECT table_name FROM create_hypertable('subtract_int4', 'time', chunk_time_interval:=10); +SELECT table_name FROM create_hypertable('subtract_int8', 'time', chunk_time_interval:=10); + +SELECT set_integer_now_func('subtract_int2', 'sub_int2_now'); +SELECT set_integer_now_func('subtract_int4', 'sub_int4_now'); +SELECT set_integer_now_func('subtract_int8', 'sub_int8_now'); + +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int2', lag) AS sub FROM (VALUES (-10),(0),(2),(4),(8)) v(lag); +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int4', lag) AS sub FROM (VALUES (-10),(0),(2),(4),(8)) v(lag); +SELECT _timescaledb_internal.subtract_integer_from_now('subtract_int8', lag) AS sub FROM (VALUES (-10),(0),(2),(4),(8)) v(lag); + +-- test set_integer_now_func on internal table +\set ON_ERROR_STOP 0 +SELECT set_integer_now_func(:'TABLENAME', 'sub_int2_now'); +\set ON_ERROR_STOP 1 + +-- cleanup +DROP TABLE subtract_int_no_func; +DROP TABLE subtract_int2; +DROP TABLE subtract_int4; +DROP TABLE subtract_int8;