-- 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_functions.get_partition_for_key');
    create_hypertable     
--------------------------
 (1,public,part_legacy,t)

-- 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 | compress_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_functions   | get_partition_for_key |                 |                          |                         | 

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_functions.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_functions.get_partition_for_key(device) < 1073741823)                                                                            | f          | f        | t

-- 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, buffers off, costs off)
SELECT * FROM part_legacy WHERE device = 1;
--- QUERY PLAN ---
 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)

COMMIT;
CREATE TABLE part_new(time timestamptz, temp float, device int);
SELECT create_hypertable('part_new', 'time', 'device', 2);
   create_hypertable   
-----------------------
 (2,public,part_new,t)

SELECT * FROM _timescaledb_catalog.dimension;
 id | hypertable_id | column_name |       column_type        | aligned | num_slices | partitioning_func_schema |   partitioning_func   | interval_length | compress_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_functions   | get_partition_for_key |                 |                          |                         | 
  3 |             2 | time        | timestamp with time zone | t       |            |                          |                       |    604800000000 |                          |                         | 
  4 |             2 | device      | integer                  | f       |          2 | _timescaledb_functions   | get_partition_hash    |                 |                          |                         | 

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_functions.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_functions.get_partition_hash(device) >= 1073741823)                                                                              | f          | f        | t

-- 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, buffers off, costs off)
SELECT * FROM part_new WHERE device = 1;
--- QUERY PLAN ---
 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)

COMMIT;
CREATE TABLE part_new_convert1(time timestamptz, temp float8, device int);
SELECT create_hypertable('part_new_convert1', 'time', 'temp', 2);
       create_hypertable        
--------------------------------
 (3,public,part_new_convert1,t)

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

CREATE TABLE part_add_dim(time timestamptz, temp float8, device int, location int);
SELECT create_hypertable('part_add_dim', 'time', 'temp', 2);
     create_hypertable     
---------------------------
 (4,public,part_add_dim,t)

\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_functions.get_partition_for_key');
           add_dimension            
------------------------------------
 (9,public,part_add_dim,location,t)

SELECT * FROM _timescaledb_catalog.dimension;
 id | hypertable_id | column_name |         column_type         | aligned | num_slices | partitioning_func_schema |   partitioning_func   | interval_length | compress_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_functions   | get_partition_for_key |                 |                          |                         | 
  3 |             2 | time        | timestamp with time zone    | t       |            |                          |                       |    604800000000 |                          |                         | 
  4 |             2 | device      | integer                     | f       |          2 | _timescaledb_functions   | get_partition_hash    |                 |                          |                         | 
  6 |             3 | temp        | double precision            | f       |          2 | _timescaledb_functions   | 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_functions   | get_partition_hash    |                 |                          |                         | 
  9 |             4 | location    | integer                     | f       |          2 | _timescaledb_functions   | get_partition_for_key |                 |                          |                         | 

-- 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_functions.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');
       create_hypertable       
-------------------------------
 (5,public,part_custom_func,t)

SELECT _timescaledb_functions.get_partition_hash(substring('dev1' FROM '[A-za-z0-9 ]+'));
 get_partition_hash 
--------------------
         1129986420

SELECT _timescaledb_functions.get_partition_hash('dev1'::text);
 get_partition_hash 
--------------------
         1129986420

SELECT _timescaledb_functions.get_partition_hash('dev7'::text);
 get_partition_hash 
--------------------
          449729092

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 | 

-- 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');
      create_hypertable       
------------------------------
 (6,public,simpl_partition,t)

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)

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

-- 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');
ERROR:  cannot create a unique index without the column "time" (used in partitioning)
SELECT create_hypertable('hyper_with_index', 'time', 'device', 2);
ERROR:  cannot create a unique index without the column "time" (used in partitioning)
SELECT create_hypertable('hyper_with_index', 'time', 'temp', 2);
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);
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');
       create_hypertable        
--------------------------------
 (11,public,hyper_with_index,t)

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

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

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)

\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);
SELECT create_hypertable('part_custom_dim', 'time', 'combo', 4, partitioning_func=>'tuple_hash');
       create_hypertable       
-------------------------------
 (14,public,part_custom_dim,t)

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');
       create_hypertable       
-------------------------------
 (15,public,part_custom_dim,t)

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 type for dimension "time"
-- 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');
      create_hypertable       
------------------------------
 (16,public,part_time_func,t)

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

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

SELECT * FROM test.show_subtables('part_time_func');
                  Child                   | Tablespace 
------------------------------------------+------------
 _timescaledb_internal._hyper_16_11_chunk | 
 _timescaledb_internal._hyper_16_12_chunk | 

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

SELECT (test.show_indexes("Child")).*
FROM test.show_subtables('part_time_func');
                              Index                               | Columns |        Expr         | Unique | Primary | Exclusion | Tablespace 
------------------------------------------------------------------+---------+---------------------+--------+---------+-----------+------------
 _timescaledb_internal._hyper_16_11_chunk_part_time_func_expr_idx | {expr}  | time_partfunc(expr) | f      | f       | f         | 
 _timescaledb_internal._hyper_16_12_chunk_part_time_func_expr_idx | {expr}  | time_partfunc(expr) | f      | f       | f         | 

-- Check that constraint exclusion works with time partitioning
-- function (scan only one chunk)
-- No exclusion
EXPLAIN (verbose, buffers off, costs off)
SELECT * FROM part_time_func;
--- QUERY PLAN ---
 Append
   ->  Seq Scan on _timescaledb_internal._hyper_16_11_chunk
         Output: _hyper_16_11_chunk."time", _hyper_16_11_chunk.temp, _hyper_16_11_chunk.device
   ->  Seq Scan on _timescaledb_internal._hyper_16_12_chunk
         Output: _hyper_16_12_chunk."time", _hyper_16_12_chunk.temp, _hyper_16_12_chunk.device

-- Exclude using the function on time
EXPLAIN (verbose, buffers off, costs off)
SELECT * FROM part_time_func WHERE time_partfunc(time) < '2018-07-01';
--- QUERY PLAN ---
 Index Scan using _hyper_16_11_chunk_part_time_func_expr_idx on _timescaledb_internal._hyper_16_11_chunk
   Output: _hyper_16_11_chunk."time", _hyper_16_11_chunk.temp, _hyper_16_11_chunk.device
   Index Cond: (time_partfunc(_hyper_16_11_chunk."time") < 'Sun Jul 01 00:00:00 2018 PDT'::timestamp with time zone)

-- 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, buffers off, 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 ---
 Seq Scan on _timescaledb_internal._hyper_16_11_chunk
   Output: _hyper_16_11_chunk."time", _hyper_16_11_chunk.temp, _hyper_16_11_chunk.device
   Filter: (_hyper_16_11_chunk."time" < '1530403200'::double precision)

-- 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');
           create_hypertable           
---------------------------------------
 (17,public,part_time_func_null_ret,t)

\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
