Skip to content

Commit

Permalink
Add encoder/decoder checks and fix julian date test
Browse files Browse the repository at this point in the history
  • Loading branch information
calebj committed Jan 8, 2025
1 parent e44919b commit c592b2b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 3 deletions.
81 changes: 81 additions & 0 deletions sql/functions/check_time_encoder_decoder.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
CREATE FUNCTION @[email protected]_time_encoder_decoder(
p_time_encoder TEXT
, p_time_decoder TEXT
, p_control_type TEXT
, p_start_timestamp TIMESTAMPTZ
)
RETURNS VOID
LANGUAGE plpgsql STABLE
AS $$
DECLARE

v_control_type_oid REGTYPE;
v_i_check_rec RECORD;
v_i_fname_parsed TEXT[];
v_i_found_proc_row pg_catalog.pg_proc%ROWTYPE;
v_i_null_returned_null BOOLEAN;
v_roundtrip_result TIMESTAMPTZ;

BEGIN
/*
* Performs sanity checks on provided time encoder and decoder functions.
*/

SELECT p_control_type::regtype INTO STRICT v_control_type_oid;

FOR v_i_check_rec IN
WITH loops(fname, ftypin, ftypout) AS (VALUES
(p_time_encoder, 'TIMESTAMPTZ'::regtype, v_control_type_oid),
(p_time_decoder, v_control_type_oid, 'TIMESTAMPTZ'::regtype)
)
SELECT * FROM loops
LOOP
-- Make sure the function name is valid and schema qualified
v_i_fname_parsed := parse_ident(v_i_check_rec.fname);
IF cardinality(v_i_fname_parsed) <> 2 THEN
RAISE EXCEPTION 'The function name % is not a valid fully qualified name.', v_i_check_rec.fname;
END IF;

-- Check the functions exist with exact parameter/return types
BEGIN
SELECT * INTO STRICT v_i_found_proc_row
FROM pg_catalog.pg_proc p
JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = v_i_fname_parsed[1] AND proname = v_i_fname_parsed[2]
AND pronargs >= 1 and proargtypes[0] = v_i_check_rec.ftypin
AND prorettype = v_i_check_rec.ftypout;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'No function named % matching argument % and returning %.', v_i_check_rec.fname, v_i_check_rec.ftypin, ftypout;
WHEN TOO_MANY_ROWS THEN
RAISE EXCEPTION 'Function %(%) -> % is ambiguous, this should not happen.', v_i_check_rec.fname, v_i_check_rec.ftypin, ftypout;
END;

-- Check that the function is declared IMMUTABLE
IF v_i_found_proc_row.provolatile <> 'i' THEN
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;
END IF;

-- Check that the functions return NULL when passed a NULL
EXECUTE FORMAT('SELECT %s(NULL) IS NULL', v_i_check_rec.fname) INTO v_i_null_returned_null;

IF NOT v_i_null_returned_null THEN
RAISE EXCEPTION 'Function % does not return NULL when called on NULL.', v_i_check_rec.fname;
END IF;

-- Show performance warning for default of PARALLEL UNSAFE
IF v_i_found_proc_row.proparallel <> 's' THEN
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;
END IF;

END LOOP;

-- test roundtrip
EXECUTE FORMAT('SELECT %s(%s(%L))', p_time_decoder, p_time_encoder, p_start_timestamp) INTO v_roundtrip_result;

IF p_start_timestamp <> v_roundtrip_result THEN
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;
END IF;

END
$$;
4 changes: 4 additions & 0 deletions sql/functions/create_parent.sql
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ IF v_control_type IN ('time', 'text', 'uuid') OR (v_control_type = 'id' AND p_ep
INTO v_base_timestamp, v_datetime_string
FROM @[email protected]_time_partition_info(v_time_interval, v_start_time, p_date_trunc_interval);

IF p_epoch = 'func' THEN
PERFORM @[email protected]_time_encoder_decoder(p_time_encoder, p_time_decoder, v_control_exact_type, v_base_timestamp);
END IF;

RAISE DEBUG 'create_parent(): parent_table: %, v_base_timestamp: %', p_parent_table, v_base_timestamp;

v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp);
Expand Down
6 changes: 3 additions & 3 deletions test/test-id-func-julian-monthly.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ SELECT plan(121);
CREATE SCHEMA partman_test;
CREATE SCHEMA partman_retention_test;

-- Convert to and from Julian days using Postgres builtin functions, aligned to noon UTC
-- Convert to and from Julian days using Postgres builtin functions
CREATE FUNCTION partman_test.timestamp_to_julian_day(ts TIMESTAMPTZ)
RETURNS INTEGER LANGUAGE SQL IMMUTABLE PARALLEL SAFE AS
$$SELECT EXTRACT(julian FROM ts at time zone 'UTC+12')::INTEGER$$;
$$SELECT EXTRACT(julian FROM ts at time zone 'UTC')::INTEGER$$;

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

CREATE TABLE partman_test.time_taptest_table
(col1 int
Expand Down

0 comments on commit c592b2b

Please sign in to comment.