Skip to content

Commit 9f8dfa2

Browse files
committed
Add encoder/decoder checks and fix julian date test
1 parent e44919b commit 9f8dfa2

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
CREATE FUNCTION @[email protected]_time_encoder_decoder(
2+
p_time_encoder TEXT
3+
, p_time_decoder TEXT
4+
, p_control_type TEXT
5+
, p_start_timestamp TIMESTAMPTZ
6+
)
7+
RETURNS VOID
8+
LANGUAGE plpgsql STABLE
9+
AS $$
10+
DECLARE
11+
12+
v_control_type_oid REGTYPE;
13+
v_i_check_rec RECORD;
14+
v_i_fname_parsed TEXT[];
15+
v_i_found_proc_row pg_catalog.pg_proc%ROWTYPE;
16+
v_i_null_returned_null BOOLEAN;
17+
v_roundtrip_result TIMESTAMPTZ;
18+
19+
BEGIN
20+
/*
21+
* Performs sanity checks on provided time encoder and decoder functions.
22+
*/
23+
24+
SELECT p_control_type::regtype INTO STRICT v_control_type_oid;
25+
26+
FOR v_i_check_rec IN
27+
WITH loops(fname, ftypin, ftypout) AS (VALUES
28+
(p_time_encoder, 'TIMESTAMPTZ'::regtype, v_control_type_oid),
29+
(p_time_decoder, v_control_type_oid, 'TIMESTAMPTZ'::regtype)
30+
)
31+
SELECT * FROM loops
32+
LOOP
33+
-- Make sure the function name is valid and schema qualified
34+
v_i_fname_parsed := parse_ident(v_i_check_rec.fname);
35+
IF cardinality(v_i_fname_parsed) <> 2 THEN
36+
RAISE EXCEPTION 'The function name % is not a valid fully qualified name.', v_i_check_rec.fname;
37+
END IF;
38+
39+
-- Check the functions exist with exact parameter/return types
40+
BEGIN
41+
SELECT * INTO STRICT v_i_found_proc_row
42+
FROM pg_catalog.pg_proc p
43+
JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
44+
WHERE n.nspname = v_i_fname_parsed[1] AND proname = v_i_fname_parsed[2]
45+
AND pronargs >= 1 and proargtypes[0] = v_i_check_rec.ftypin
46+
AND prorettype = v_i_check_rec.ftypout;
47+
EXCEPTION
48+
WHEN NO_DATA_FOUND THEN
49+
RAISE EXCEPTION 'No function named % matching argument % and returning %.', v_i_check_rec.fname, v_i_check_rec.ftypin, ftypout;
50+
WHEN TOO_MANY_ROWS THEN
51+
RAISE EXCEPTION 'Function %(%) -> % is ambiguous, this should not happen.', v_i_check_rec.fname, v_i_check_rec.ftypin, ftypout;
52+
END;
53+
54+
-- Check that the function is declared IMMUTABLE
55+
IF v_i_found_proc_row.provolatile <> 'i' THEN
56+
RAISE EXCEPTION 'Function % must be declared IMMUTABLE (got %).', v_i_check_rec.fname, CASE v_i_found_proc_row.provolatile WHEN 's' THEN 'STABLE' WHEN 'v' THEN 'VOLATILE' END;
57+
END IF;
58+
59+
-- Check that the functions return NULL when passed a NULL
60+
EXECUTE FORMAT('SELECT %s(NULL) IS NULL', v_i_check_rec.fname) INTO v_i_null_returned_null;
61+
62+
IF NOT v_i_null_returned_null THEN
63+
RAISE EXCEPTION 'Function % does not return NULL when called on NULL.', v_i_check_rec.fname;
64+
END IF;
65+
66+
-- Show performance warning for default of PARALLEL UNSAFE
67+
IF v_i_found_proc_row.proparallel <> 's' THEN
68+
RAISE NOTICE 'Function % is not declared parallel safe, this may affect performance if you use it for predicates. See the documentation for CREATE FUNCTION.', v_i_check_rec.fname;
69+
END IF;
70+
71+
END LOOP;
72+
73+
-- test roundtrip
74+
EXECUTE FORMAT('SELECT %s(%s(%L))', p_time_decoder, p_time_encoder, p_start_timestamp) INTO v_roundtrip_result;
75+
76+
IF p_start_timestamp <> v_roundtrip_result THEN
77+
RAISE EXCEPTION 'Encoding and then decoding the start of the first partition range (%) got a different value (%). Make sure the encoding is correct and aligns with the partition interval.', p_start_timestamp, v_roundtrip_result;
78+
END IF;
79+
80+
END
81+
$$;

sql/functions/create_parent.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ IF v_control_type IN ('time', 'text', 'uuid') OR (v_control_type = 'id' AND p_ep
348348
INTO v_base_timestamp, v_datetime_string
349349
FROM @[email protected]_time_partition_info(v_time_interval, v_start_time, p_date_trunc_interval);
350350

351+
IF p_epoch = 'func' THEN
352+
PERFORM @[email protected]_time_encoder_decoder(p_time_encoder, p_time_decoder, v_control_exact_type, v_base_timestamp);
353+
END IF;
354+
351355
RAISE DEBUG 'create_parent(): parent_table: %, v_base_timestamp: %', p_parent_table, v_base_timestamp;
352356

353357
v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp);

test/test-id-func-julian-monthly.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ CREATE SCHEMA partman_retention_test;
1515
-- Convert to and from Julian days using Postgres builtin functions, aligned to noon UTC
1616
CREATE FUNCTION partman_test.timestamp_to_julian_day(ts TIMESTAMPTZ)
1717
RETURNS INTEGER LANGUAGE SQL IMMUTABLE PARALLEL SAFE AS
18-
$$SELECT EXTRACT(julian FROM ts at time zone 'UTC+12')::INTEGER$$;
18+
$$SELECT EXTRACT(julian FROM ts at time zone 'UTC')::INTEGER$$;
1919

2020
CREATE FUNCTION partman_test.julian_day_to_timestamp(jul_day integer)
2121
RETURNS TIMESTAMPTZ LANGUAGE SQL IMMUTABLE PARALLEL SAFE AS
22-
$$SELECT ('J' || jul_day)::TIMESTAMP AT TIME ZONE 'UTC+12'$$;
22+
$$SELECT ('J' || jul_day)::TIMESTAMP AT TIME ZONE 'UTC'$$;
2323

2424
CREATE TABLE partman_test.time_taptest_table
2525
(col1 int

0 commit comments

Comments
 (0)