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.
This commit is contained in:
Sven Klemm 2021-12-16 23:16:34 +01:00 committed by Sven Klemm
parent a760887145
commit 39645d56da
5 changed files with 205 additions and 12 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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)

View File

@ -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;