-- This file and its contents are licensed under the Apache License 2.0. -- Please see the included NOTICE for copyright information and -- LICENSE-APACHE for a copy of the license. CREATE TABLE part_legacy(time timestamptz, temp float, device int); SELECT create_hypertable('part_legacy', 'time', 'device', 2, partitioning_func => '_timescaledb_internal.get_partition_for_key'); NOTICE: adding not-null constraint to column "time" create_hypertable -------------------------- (1,public,part_legacy,t) (1 row) -- Show legacy partitioning function is used SELECT * FROM _timescaledb_catalog.dimension; id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length | integer_now_func_schema | integer_now_func ----+---------------+-------------+--------------------------+---------+------------+--------------------------+-----------------------+-----------------+-------------------------+------------------ 1 | 1 | time | timestamp with time zone | t | | | | 604800000000 | | 2 | 1 | device | integer | f | 2 | _timescaledb_internal | get_partition_for_key | | | (2 rows) INSERT INTO part_legacy VALUES ('2017-03-22T09:18:23', 23.4, 1); INSERT INTO part_legacy VALUES ('2017-03-22T09:18:23', 23.4, 76); VACUUM part_legacy; -- Show two chunks and CHECK constraint with cast SELECT * FROM test.show_constraintsp('_timescaledb_internal._hyper_1_%_chunk'); Table | Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ----------------------------------------+--------------+------+----------+-------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- _timescaledb_internal._hyper_1_1_chunk | constraint_1 | c | {time} | - | (("time" >= 'Wed Mar 15 17:00:00 2017 PDT'::timestamp with time zone) AND ("time" < 'Wed Mar 22 17:00:00 2017 PDT'::timestamp with time zone)) | f | f | t _timescaledb_internal._hyper_1_1_chunk | constraint_2 | c | {device} | - | (_timescaledb_internal.get_partition_for_key(device) >= 1073741823) | f | f | t _timescaledb_internal._hyper_1_2_chunk | constraint_1 | c | {time} | - | (("time" >= 'Wed Mar 15 17:00:00 2017 PDT'::timestamp with time zone) AND ("time" < 'Wed Mar 22 17:00:00 2017 PDT'::timestamp with time zone)) | f | f | t _timescaledb_internal._hyper_1_2_chunk | constraint_3 | c | {device} | - | (_timescaledb_internal.get_partition_for_key(device) < 1073741823) | f | f | t (4 rows) -- Make sure constraint exclusion works on device column BEGIN; -- For plan stability between versions SET LOCAL enable_bitmapscan = false; SET LOCAL enable_indexscan = false; EXPLAIN (verbose, costs off) SELECT * FROM part_legacy WHERE device = 1; QUERY PLAN ----------------------------------------------------------------------------------------- Append -> Seq Scan on _timescaledb_internal._hyper_1_1_chunk Output: _hyper_1_1_chunk."time", _hyper_1_1_chunk.temp, _hyper_1_1_chunk.device Filter: (_hyper_1_1_chunk.device = 1) (4 rows) COMMIT; CREATE TABLE part_new(time timestamptz, temp float, device int); SELECT create_hypertable('part_new', 'time', 'device', 2); NOTICE: adding not-null constraint to column "time" create_hypertable ----------------------- (2,public,part_new,t) (1 row) SELECT * FROM _timescaledb_catalog.dimension; id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length | integer_now_func_schema | integer_now_func ----+---------------+-------------+--------------------------+---------+------------+--------------------------+-----------------------+-----------------+-------------------------+------------------ 1 | 1 | time | timestamp with time zone | t | | | | 604800000000 | | 2 | 1 | device | integer | f | 2 | _timescaledb_internal | get_partition_for_key | | | 3 | 2 | time | timestamp with time zone | t | | | | 604800000000 | | 4 | 2 | device | integer | f | 2 | _timescaledb_internal | get_partition_hash | | | (4 rows) INSERT INTO part_new VALUES ('2017-03-22T09:18:23', 23.4, 1); INSERT INTO part_new VALUES ('2017-03-22T09:18:23', 23.4, 2); VACUUM part_new; -- Show two chunks and CHECK constraint without cast SELECT * FROM test.show_constraintsp('_timescaledb_internal._hyper_2_%_chunk'); Table | Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ----------------------------------------+--------------+------+----------+-------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- _timescaledb_internal._hyper_2_3_chunk | constraint_4 | c | {time} | - | (("time" >= 'Wed Mar 15 17:00:00 2017 PDT'::timestamp with time zone) AND ("time" < 'Wed Mar 22 17:00:00 2017 PDT'::timestamp with time zone)) | f | f | t _timescaledb_internal._hyper_2_3_chunk | constraint_5 | c | {device} | - | (_timescaledb_internal.get_partition_hash(device) < 1073741823) | f | f | t _timescaledb_internal._hyper_2_4_chunk | constraint_4 | c | {time} | - | (("time" >= 'Wed Mar 15 17:00:00 2017 PDT'::timestamp with time zone) AND ("time" < 'Wed Mar 22 17:00:00 2017 PDT'::timestamp with time zone)) | f | f | t _timescaledb_internal._hyper_2_4_chunk | constraint_6 | c | {device} | - | (_timescaledb_internal.get_partition_hash(device) >= 1073741823) | f | f | t (4 rows) -- Make sure constraint exclusion works on device column BEGIN; -- For plan stability between versions SET LOCAL enable_bitmapscan = false; SET LOCAL enable_indexscan = false; EXPLAIN (verbose, costs off) SELECT * FROM part_new WHERE device = 1; QUERY PLAN ----------------------------------------------------------------------------------------- Append -> Seq Scan on _timescaledb_internal._hyper_2_3_chunk Output: _hyper_2_3_chunk."time", _hyper_2_3_chunk.temp, _hyper_2_3_chunk.device Filter: (_hyper_2_3_chunk.device = 1) (4 rows) COMMIT; CREATE TABLE part_new_convert1(time timestamptz, temp float8, device int); SELECT create_hypertable('part_new_convert1', 'time', 'temp', 2); NOTICE: adding not-null constraint to column "time" create_hypertable -------------------------------- (3,public,part_new_convert1,t) (1 row) INSERT INTO part_new_convert1 VALUES ('2017-03-22T09:18:23', 1.0, 2); \set ON_ERROR_STOP 0 -- Changing the type of a hash-partitioned column should not be supported ALTER TABLE part_new_convert1 ALTER COLUMN temp TYPE numeric; ERROR: cannot change the type of a hash-partitioned column \set ON_ERROR_STOP 1 -- Should be able to change if not hash partitioned though ALTER TABLE part_new_convert1 ALTER COLUMN time TYPE timestamp; SELECT * FROM test.show_columnsp('_timescaledb_internal._hyper_3_%_chunk'); Relation | Kind | Column | Column type | NotNull ----------------------------------------+------+--------+-----------------------------+--------- _timescaledb_internal._hyper_3_5_chunk | r | time | timestamp without time zone | t _timescaledb_internal._hyper_3_5_chunk | r | temp | double precision | f _timescaledb_internal._hyper_3_5_chunk | r | device | integer | f (3 rows) CREATE TABLE part_add_dim(time timestamptz, temp float8, device int, location int); SELECT create_hypertable('part_add_dim', 'time', 'temp', 2); NOTICE: adding not-null constraint to column "time" create_hypertable --------------------------- (4,public,part_add_dim,t) (1 row) \set ON_ERROR_STOP 0 SELECT add_dimension('part_add_dim', 'location', 2, partitioning_func => 'bad_func'); ERROR: function "bad_func" does not exist at character 74 \set ON_ERROR_STOP 1 SELECT add_dimension('part_add_dim', 'location', 2, partitioning_func => '_timescaledb_internal.get_partition_for_key'); add_dimension ------------------------------------ (9,public,part_add_dim,location,t) (1 row) SELECT * FROM _timescaledb_catalog.dimension; id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length | integer_now_func_schema | integer_now_func ----+---------------+-------------+-----------------------------+---------+------------+--------------------------+-----------------------+-----------------+-------------------------+------------------ 1 | 1 | time | timestamp with time zone | t | | | | 604800000000 | | 2 | 1 | device | integer | f | 2 | _timescaledb_internal | get_partition_for_key | | | 3 | 2 | time | timestamp with time zone | t | | | | 604800000000 | | 4 | 2 | device | integer | f | 2 | _timescaledb_internal | get_partition_hash | | | 6 | 3 | temp | double precision | f | 2 | _timescaledb_internal | get_partition_hash | | | 5 | 3 | time | timestamp without time zone | t | | | | 604800000000 | | 7 | 4 | time | timestamp with time zone | t | | | | 604800000000 | | 8 | 4 | temp | double precision | f | 2 | _timescaledb_internal | get_partition_hash | | | 9 | 4 | location | integer | f | 2 | _timescaledb_internal | get_partition_for_key | | | (9 rows) -- Test that we support custom SQL-based partitioning functions and -- that our native partitioning function handles function expressions -- as argument CREATE OR REPLACE FUNCTION custom_partfunc(source anyelement) RETURNS INTEGER LANGUAGE PLPGSQL IMMUTABLE AS $BODY$ DECLARE retval INTEGER; BEGIN retval = _timescaledb_internal.get_partition_hash(substring(source::text FROM '[A-za-z0-9 ]+')); RAISE NOTICE 'hash value for % is %', source, retval; RETURN retval; END $BODY$; CREATE TABLE part_custom_func(time timestamptz, temp float8, device text); SELECT create_hypertable('part_custom_func', 'time', 'device', 2, partitioning_func => 'custom_partfunc'); NOTICE: adding not-null constraint to column "time" create_hypertable ------------------------------- (5,public,part_custom_func,t) (1 row) SELECT _timescaledb_internal.get_partition_hash(substring('dev1' FROM '[A-za-z0-9 ]+')); get_partition_hash -------------------- 1129986420 (1 row) SELECT _timescaledb_internal.get_partition_hash('dev1'::text); get_partition_hash -------------------- 1129986420 (1 row) SELECT _timescaledb_internal.get_partition_hash('dev7'::text); get_partition_hash -------------------- 449729092 (1 row) INSERT INTO part_custom_func VALUES ('2017-03-22T09:18:23', 23.4, 'dev1'), ('2017-03-22T09:18:23', 23.4, 'dev7'); NOTICE: hash value for dev1 is 1129986420 NOTICE: hash value for dev1 is 1129986420 NOTICE: hash value for dev7 is 449729092 NOTICE: hash value for dev7 is 449729092 SELECT * FROM test.show_subtables('part_custom_func'); Child | Tablespace ----------------------------------------+------------ _timescaledb_internal._hyper_5_6_chunk | _timescaledb_internal._hyper_5_7_chunk | (2 rows) -- This first test is slightly trivial, but segfaulted in old versions CREATE TYPE simpl AS (val1 int4); CREATE OR REPLACE FUNCTION simpl_type_hash(ANYELEMENT) RETURNS int4 AS $$ SELECT $1.val1; $$ LANGUAGE SQL IMMUTABLE; CREATE TABLE simpl_partition ("timestamp" TIMESTAMPTZ, object simpl); SELECT create_hypertable( 'simpl_partition', 'timestamp', 'object', 1000, chunk_time_interval => interval '1 day', partitioning_func=>'simpl_type_hash'); NOTICE: adding not-null constraint to column "timestamp" create_hypertable ------------------------------ (6,public,simpl_partition,t) (1 row) INSERT INTO simpl_partition VALUES ('2017-03-22T09:18:23', ROW(1)::simpl); SELECT * from simpl_partition; timestamp | object ------------------------------+-------- Wed Mar 22 09:18:23 2017 PDT | (1) (1 row) -- Also test that the fix works when we have more chunks than allowed at once SET timescaledb.max_open_chunks_per_insert=1; INSERT INTO simpl_partition VALUES ('2017-03-22T10:18:23', ROW(0)::simpl), ('2017-03-22T10:18:23', ROW(1)::simpl), ('2017-03-22T10:18:23', ROW(2)::simpl), ('2017-03-22T10:18:23', ROW(3)::simpl), ('2017-03-22T10:18:23', ROW(4)::simpl), ('2017-03-22T10:18:23', ROW(5)::simpl); SET timescaledb.max_open_chunks_per_insert=default; SELECT * from simpl_partition; timestamp | object ------------------------------+-------- Wed Mar 22 09:18:23 2017 PDT | (1) Wed Mar 22 10:18:23 2017 PDT | (0) Wed Mar 22 10:18:23 2017 PDT | (1) Wed Mar 22 10:18:23 2017 PDT | (2) Wed Mar 22 10:18:23 2017 PDT | (3) Wed Mar 22 10:18:23 2017 PDT | (4) Wed Mar 22 10:18:23 2017 PDT | (5) (7 rows) -- Test that index creation is handled correctly. CREATE TABLE hyper_with_index(time timestamptz, temp float, device int); CREATE UNIQUE INDEX temp_index ON hyper_with_index(temp); \set ON_ERROR_STOP 0 SELECT create_hypertable('hyper_with_index', 'time'); NOTICE: adding not-null constraint to column "time" ERROR: cannot create a unique index without the column "time" (used in partitioning) SELECT create_hypertable('hyper_with_index', 'time', 'device', 2); NOTICE: adding not-null constraint to column "time" ERROR: cannot create a unique index without the column "time" (used in partitioning) SELECT create_hypertable('hyper_with_index', 'time', 'temp', 2); NOTICE: adding not-null constraint to column "time" ERROR: cannot create a unique index without the column "time" (used in partitioning) \set ON_ERROR_STOP 1 DROP INDEX temp_index; CREATE UNIQUE INDEX time_index ON hyper_with_index(time); \set ON_ERROR_STOP 0 -- should error because device not in index SELECT create_hypertable('hyper_with_index', 'time', 'device', 4); NOTICE: adding not-null constraint to column "time" ERROR: cannot create a unique index without the column "device" (used in partitioning) \set ON_ERROR_STOP 1 SELECT create_hypertable('hyper_with_index', 'time'); NOTICE: adding not-null constraint to column "time" create_hypertable -------------------------------- (11,public,hyper_with_index,t) (1 row) -- make sure user created index is used. -- not using \d or \d+ because output syntax differs -- between postgres 9 and postgres 10. SELECT indexname FROM pg_indexes WHERE tablename = 'hyper_with_index'; indexname ------------ time_index (1 row) \set ON_ERROR_STOP 0 SELECT add_dimension('hyper_with_index', 'device', 4); ERROR: cannot create a unique index without the column "device" (used in partitioning) \set ON_ERROR_STOP 1 DROP INDEX time_index; CREATE UNIQUE INDEX time_space_index ON hyper_with_index(time, device); SELECT add_dimension('hyper_with_index', 'device', 4); add_dimension --------------------------------------- (23,public,hyper_with_index,device,t) (1 row) CREATE TABLE hyper_with_primary(time TIMESTAMPTZ PRIMARY KEY, temp float, device int); \set ON_ERROR_STOP 0 SELECT create_hypertable('hyper_with_primary', 'time', 'device', 4); ERROR: cannot create a unique index without the column "device" (used in partitioning) \set ON_ERROR_STOP 1 SELECT create_hypertable('hyper_with_primary', 'time'); create_hypertable ---------------------------------- (13,public,hyper_with_primary,t) (1 row) \set ON_ERROR_STOP 0 SELECT add_dimension('hyper_with_primary', 'device', 4); ERROR: cannot create a unique index without the column "device" (used in partitioning) \set ON_ERROR_STOP 1 -- NON-unique indexes can still be created CREATE INDEX temp_index ON hyper_with_index(temp); -- Make sure custom composite types are supported as dimensions CREATE TYPE TUPLE as (val1 int4, val2 int4); CREATE FUNCTION tuple_hash(value ANYELEMENT) RETURNS INT4 LANGUAGE PLPGSQL IMMUTABLE AS $BODY$ BEGIN RAISE NOTICE 'custom hash value is: %', value.val1+value.val2; RETURN value.val1+value.val2; END $BODY$; CREATE TABLE part_custom_dim (time TIMESTAMPTZ, combo TUPLE, device TEXT); \set ON_ERROR_STOP 0 -- should fail because no partitioning function supplied and the given custom type -- has no default hash function SELECT create_hypertable('part_custom_dim', 'time', 'combo', 4); NOTICE: adding not-null constraint to column "time" ERROR: could not find hash function for type tuple \set ON_ERROR_STOP 1 SELECT create_hypertable('part_custom_dim', 'time', 'combo', 4, partitioning_func=>'tuple_hash'); NOTICE: adding not-null constraint to column "time" create_hypertable ------------------------------- (15,public,part_custom_dim,t) (1 row) INSERT INTO part_custom_dim(time, combo) VALUES (now(), (1,2)); NOTICE: custom hash value is: 3 NOTICE: custom hash value is: 3 DROP TABLE part_custom_dim; -- Now make sure that renaming partitioning_func_schema will get updated properly \c :TEST_DBNAME :ROLE_SUPERUSER CREATE SCHEMA IF NOT EXISTS my_partitioning_schema; CREATE FUNCTION my_partitioning_schema.tuple_hash(value ANYELEMENT) RETURNS INT4 LANGUAGE PLPGSQL IMMUTABLE AS $BODY$ BEGIN RAISE NOTICE 'custom hash value is: %', value.val1+value.val2; RETURN value.val1+value.val2; END $BODY$; CREATE TABLE part_custom_dim (time TIMESTAMPTZ, combo TUPLE, device TEXT); SELECT create_hypertable('part_custom_dim', 'time', 'combo', 4, partitioning_func=>'my_partitioning_schema.tuple_hash'); NOTICE: adding not-null constraint to column "time" create_hypertable ------------------------------- (16,public,part_custom_dim,t) (1 row) INSERT INTO part_custom_dim(time, combo) VALUES (now(), (1,2)); NOTICE: custom hash value is: 3 NOTICE: custom hash value is: 3 ALTER SCHEMA my_partitioning_schema RENAME TO new_partitioning_schema; -- Inserts should work even after we rename the schema INSERT INTO part_custom_dim(time, combo) VALUES (now(), (3,4)); NOTICE: custom hash value is: 7 NOTICE: custom hash value is: 7 -- Test partitioning function on an open (time) dimension CREATE OR REPLACE FUNCTION time_partfunc(unixtime float8) RETURNS TIMESTAMPTZ LANGUAGE PLPGSQL IMMUTABLE AS $BODY$ DECLARE retval TIMESTAMPTZ; BEGIN retval := to_timestamp(unixtime); RAISE NOTICE 'time value for % is %', unixtime, timezone('UTC', retval); RETURN retval; END $BODY$; CREATE OR REPLACE FUNCTION time_partfunc_bad_parameters(unixtime float8, extra text) RETURNS TIMESTAMPTZ LANGUAGE SQL IMMUTABLE AS $BODY$ SELECT to_timestamp(unixtime); $BODY$; CREATE OR REPLACE FUNCTION time_partfunc_bad_return_type(unixtime float8) RETURNS FLOAT8 LANGUAGE SQL IMMUTABLE AS $BODY$ SELECT unixtime; $BODY$; CREATE TABLE part_time_func(time float8, temp float8, device text); \set ON_ERROR_STOP 0 -- Should fail due to invalid time column SELECT create_hypertable('part_time_func', 'time'); ERROR: invalid dimension type: "time" must be an integer, date or timestamp -- Should fail due to bad signature of time partitioning function SELECT create_hypertable('part_time_func', 'time', time_partitioning_func => 'time_partfunc_bad_parameters'); ERROR: invalid partitioning function SELECT create_hypertable('part_time_func', 'time', time_partitioning_func => 'time_partfunc_bad_return_type'); ERROR: invalid partitioning function \set ON_ERROR_STOP 1 -- Should work with time partitioning function that returns a valid time type SELECT create_hypertable('part_time_func', 'time', time_partitioning_func => 'time_partfunc'); NOTICE: adding not-null constraint to column "time" create_hypertable ------------------------------ (17,public,part_time_func,t) (1 row) INSERT INTO part_time_func VALUES (1530214157.134, 23.4, 'dev1'), (1533214157.8734, 22.3, 'dev7'); NOTICE: time value for 1530214157.134 is Thu Jun 28 19:29:17.134 2018 NOTICE: time value for 1530214157.134 is Thu Jun 28 19:29:17.134 2018 NOTICE: time value for 1530214157.134 is Thu Jun 28 19:29:17.134 2018 NOTICE: time value for 1530214157.134 is Thu Jun 28 19:29:17.134 2018 NOTICE: time value for 1533214157.8734 is Thu Aug 02 12:49:17.8734 2018 NOTICE: time value for 1533214157.8734 is Thu Aug 02 12:49:17.8734 2018 NOTICE: time value for 1533214157.8734 is Thu Aug 02 12:49:17.8734 2018 NOTICE: time value for 1533214157.8734 is Thu Aug 02 12:49:17.8734 2018 SELECT time, temp, device FROM part_time_func; time | temp | device -----------------+------+-------- 1530214157.134 | 23.4 | dev1 1533214157.8734 | 22.3 | dev7 (2 rows) SELECT time_partfunc(time) at time zone 'UTC', temp, device FROM part_time_func; NOTICE: time value for 1530214157.134 is Thu Jun 28 19:29:17.134 2018 NOTICE: time value for 1533214157.8734 is Thu Aug 02 12:49:17.8734 2018 timezone | temp | device -------------------------------+------+-------- Thu Jun 28 19:29:17.134 2018 | 23.4 | dev1 Thu Aug 02 12:49:17.8734 2018 | 22.3 | dev7 (2 rows) SELECT * FROM test.show_subtables('part_time_func'); Child | Tablespace ------------------------------------------+------------ _timescaledb_internal._hyper_17_11_chunk | _timescaledb_internal._hyper_17_12_chunk | (2 rows) SELECT (test.show_constraints("Child")).* FROM test.show_subtables('part_time_func'); Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ---------------+------+---------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- constraint_18 | c | {time} | - | ((time_partfunc("time") >= 'Wed Jun 27 17:00:00 2018 PDT'::timestamp with time zone) AND (time_partfunc("time") < 'Wed Jul 04 17:00:00 2018 PDT'::timestamp with time zone)) | f | f | t constraint_19 | c | {time} | - | ((time_partfunc("time") >= 'Wed Aug 01 17:00:00 2018 PDT'::timestamp with time zone) AND (time_partfunc("time") < 'Wed Aug 08 17:00:00 2018 PDT'::timestamp with time zone)) | f | f | t (2 rows) SELECT (test.show_indexes("Child")).* FROM test.show_subtables('part_time_func'); Index | Columns | Expr | Unique | Primary | Exclusion | Tablespace ------------------------------------------------------------------+---------+---------------------+--------+---------+-----------+------------ _timescaledb_internal._hyper_17_11_chunk_part_time_func_expr_idx | {expr} | time_partfunc(expr) | f | f | f | _timescaledb_internal._hyper_17_12_chunk_part_time_func_expr_idx | {expr} | time_partfunc(expr) | f | f | f | (2 rows) -- Check that constraint exclusion works with time partitioning -- function (scan only one chunk) -- No exclusion EXPLAIN (verbose, costs off) SELECT * FROM part_time_func; QUERY PLAN ----------------------------------------------------------------------------------------------- Append -> Seq Scan on _timescaledb_internal._hyper_17_11_chunk Output: _hyper_17_11_chunk."time", _hyper_17_11_chunk.temp, _hyper_17_11_chunk.device -> Seq Scan on _timescaledb_internal._hyper_17_12_chunk Output: _hyper_17_12_chunk."time", _hyper_17_12_chunk.temp, _hyper_17_12_chunk.device (5 rows) -- Exclude using the function on time EXPLAIN (verbose, costs off) SELECT * FROM part_time_func WHERE time_partfunc(time) < '2018-07-01'; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------- Append -> Index Scan using _hyper_17_11_chunk_part_time_func_expr_idx on _timescaledb_internal._hyper_17_11_chunk Output: _hyper_17_11_chunk."time", _hyper_17_11_chunk.temp, _hyper_17_11_chunk.device Index Cond: (time_partfunc(_hyper_17_11_chunk."time") < 'Sun Jul 01 00:00:00 2018 PDT'::timestamp with time zone) (4 rows) -- Exclude using the same date but as a UNIX timestamp. Won't do an -- index scan since the index is on the time function expression EXPLAIN (verbose, costs off) SELECT * FROM part_time_func WHERE time < 1530403200.0; NOTICE: time value for 1530403200 is Sun Jul 01 00:00:00 2018 QUERY PLAN ----------------------------------------------------------------------------------------------- Append -> Seq Scan on _timescaledb_internal._hyper_17_11_chunk Output: _hyper_17_11_chunk."time", _hyper_17_11_chunk.temp, _hyper_17_11_chunk.device Filter: (_hyper_17_11_chunk."time" < '1530403200'::double precision) (4 rows) -- Check that inserts will fail if we use a time partitioning function -- that returns NULL CREATE OR REPLACE FUNCTION time_partfunc_null_ret(unixtime float8) RETURNS TIMESTAMPTZ LANGUAGE PLPGSQL IMMUTABLE AS $BODY$ BEGIN RETURN NULL; END $BODY$; CREATE TABLE part_time_func_null_ret(time float8, temp float8, device text); SELECT create_hypertable('part_time_func_null_ret', 'time', time_partitioning_func => 'time_partfunc_null_ret'); NOTICE: adding not-null constraint to column "time" create_hypertable --------------------------------------- (18,public,part_time_func_null_ret,t) (1 row) \set ON_ERROR_STOP 0 INSERT INTO part_time_func_null_ret VALUES (1530214157.134, 23.4, 'dev1'), (1533214157.8734, 22.3, 'dev7'); ERROR: partitioning function "public.time_partfunc_null_ret" returned NULL \set ON_ERROR_STOP 1