CREATE FUNCTION @extschema@.show_partition_info(
    p_child_table text
    , p_partition_interval text DEFAULT NULL
    , p_parent_table text DEFAULT NULL
    , p_table_exists boolean DEFAULT true
    , OUT child_start_time timestamptz
    , OUT child_end_time timestamptz
    , OUT child_start_id bigint
    , OUT child_end_id bigint
    , OUT suffix text
)
    RETURNS record
    LANGUAGE plpgsql STABLE
    SET search_path = @extschema@, pg_catalog, pg_temp
    AS $$
DECLARE

v_child_schemaname          text;
v_child_tablename       text;
v_control               text;
v_control_type          text;
v_end_string            text;
v_epoch                 text;
v_exact_control_type    text;
v_parent_schemaname     text;
v_parent_table          text;
v_parent_tablename      text;
v_partstrat             char;
v_partition_interval    text;
v_start_string          text;
v_suffix_position       int;
v_time_decoder_safe     text;
v_time_decoder          text;

BEGIN
/*
 * Show the data boundaries for a given child table as well as the suffix that will be used.
 * Passing the parent table argument slightly improves performance by avoiding a catalog lookup.
 * Passing an interval lets you set one different than the default configured one if desired.
 */

IF p_parent_table IS NULL THEN
    IF p_table_exists = FALSE THEN
        RAISE EXCEPTION 'If given child table does not exist (p_table_exists = false), then the p_parent_table parameter must be set';
    END IF;
    SELECT n.nspname||'.'|| c.relname INTO v_parent_table
    FROM pg_catalog.pg_inherits h
    JOIN pg_catalog.pg_class c ON c.oid = h.inhparent
    JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
    WHERE h.inhrelid::regclass = p_child_table::regclass;
ELSE
    v_parent_table := p_parent_table;
END IF;

SELECT n.nspname, c.relname INTO v_parent_schemaname, v_parent_tablename
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
WHERE n.nspname = split_part(v_parent_table, '.', 1)::name
AND c.relname = split_part(v_parent_table, '.', 2)::name;
    IF v_parent_tablename IS NULL THEN
        RAISE EXCEPTION 'Unable to find given parent table in system catalogs. Ensure it is schema qualified: %', p_parent_table;
    END IF;

SELECT time_decoder
INTO v_time_decoder
FROM @extschema@.part_config
WHERE parent_table = v_parent_table;

IF v_time_decoder IS NOT NULL THEN
    v_time_decoder_safe := partman_safe_obj_name(v_time_decoder);
END IF;

IF p_partition_interval IS NULL THEN
    SELECT control, partition_interval, epoch
    INTO v_control, v_partition_interval, v_epoch
    FROM @extschema@.part_config WHERE parent_table = v_parent_table;
ELSE
    v_partition_interval := p_partition_interval;
    SELECT control, epoch
    INTO v_control, v_epoch
    FROM @extschema@.part_config WHERE parent_table = v_parent_table;
END IF;

IF v_control IS NULL THEN
    RAISE EXCEPTION 'Parent table of given child not managed by pg_partman: %', v_parent_table;
END IF;

SELECT p.partstrat INTO v_partstrat
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
JOIN pg_catalog.pg_partitioned_table p ON c.oid = p.partrelid
WHERE n.nspname = v_parent_schemaname::name
AND c.relname = v_parent_tablename::name;

IF p_table_exists THEN
    SELECT n.nspname, c.relname INTO v_child_schemaname, v_child_tablename
    FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
    WHERE n.nspname = split_part(p_child_table, '.', 1)::name
    AND c.relname = split_part(p_child_table, '.', 2)::name;

    IF v_child_tablename IS NULL THEN
        IF p_parent_table IS NOT NULL THEN
            RAISE EXCEPTION 'Child table given does not exist (%) for given parent table (%)', p_child_table, p_parent_table;
        ELSE
            RAISE EXCEPTION 'Child table given does not exist (%)', p_child_table;
        END IF;
    END IF;

    -- Look at actual partition bounds in catalog and pull values from there (when possible).
    IF v_partstrat = 'r' THEN
        SELECT (regexp_match(pg_get_expr(c.relpartbound, c.oid, true)
            , $REGEX$\(([^)]+)\) TO \(([^)]+)\)$REGEX$))[1]::text,
(regexp_match(pg_get_expr(c.relpartbound, c.oid, true)
            , $REGEX$\(([^)]+)\) TO \(([^)]+)\)$REGEX$))[2]::text
        INTO v_start_string, v_end_string
        FROM pg_catalog.pg_class c
        JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
        WHERE c.relname = v_child_tablename
        AND n.nspname = v_child_schemaname;

    ELSIF v_partstrat = 'l' THEN
        SELECT (regexp_match(pg_get_expr(c.relpartbound, c.oid, true)
            , $REGEX$FOR VALUES IN \(([^)]+)\)$REGEX$))[1]::text
        INTO v_start_string
        FROM pg_catalog.pg_class c
        JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
        WHERE c.relname = v_child_tablename
        AND n.nspname = v_child_schemaname;
    ELSE
        RAISE EXCEPTION 'partman functions only work with list partitioning with integers and ranged partitioning with time or integers. Found partition strategy "%" for given partition set', v_partstrat;
    END IF;

ELSE

    v_child_tablename := split_part(p_child_table, '.', 1);
    v_child_schemaname := split_part(p_child_table, '.', 2);
    v_suffix_position := (length(v_child_tablename) - position('p_' in reverse(v_child_tablename))) + 2;
    v_start_string := substring(v_child_tablename from v_suffix_position);

END IF;


SELECT general_type, exact_type INTO v_control_type, v_exact_control_type FROM @extschema@.check_control_type(v_parent_schemaname, v_parent_tablename, v_control);

RAISE DEBUG 'show_partition_info: v_child_schemaname: %, v_child_tablename: %, v_control_type: %, v_exact_control_type: %',
            v_child_schemaname, v_child_tablename, v_control_type, v_exact_control_type;

IF v_control_type IN ('time', 'text', 'uuid') OR (v_control_type = 'id' AND v_epoch <> 'none') THEN

    IF v_control_type = 'time' THEN
        child_start_time := v_start_string::timestamptz;
        child_end_time := v_end_string::timestamptz;
    ELSIF v_control_type IN ('text', 'uuid') THEN
        EXECUTE format('SELECT %s(%s)', v_time_decoder_safe, v_start_string) INTO child_start_time;
        EXECUTE format('SELECT %s(%s)', v_time_decoder_safe, v_end_string) INTO child_end_time;
    ELSIF (v_control_type = 'id' AND v_epoch <> 'none') THEN
        -- bigint data type is stored as a single-quoted string in the partition expression. Must strip quotes for valid type-cast.
        v_start_string := trim(BOTH '''' FROM v_start_string);
        v_end_string := trim(BOTH '''' FROM v_start_string);
        IF v_epoch = 'seconds' THEN
            child_start_time := to_timestamp(v_start_string::double precision);
            child_end_time := to_timestamp(v_end_string::double precision);
        ELSIF v_epoch = 'milliseconds' THEN
            child_start_time := to_timestamp((v_start_string::double precision) / 1000);
            child_end_time := to_timestamp((v_end_string::double precision) / 1000);
        ELSIF v_epoch = 'microseconds' THEN
            child_start_time := to_timestamp((v_start_string::double precision) / 1000000);
            child_end_time := to_timestamp((v_end_string::double precision) / 1000000);
        ELSIF v_epoch = 'nanoseconds' THEN
            child_start_time := to_timestamp((v_start_string::double precision) / 1000000000);
            child_end_time := to_timestamp((v_end_string::double precision) / 1000000000);
        END IF;
    ELSE
        RAISE EXCEPTION 'Unexpected code path in show_partition_info(). Please report this bug with the configuration that lead to it.';
    END IF;

    SELECT to_char(base_timestamp, datetime_string)
    INTO suffix
    FROM @extschema@.calculate_time_partition_info(v_partition_interval::interval, child_start_time);

ELSIF v_control_type = 'id' THEN

    IF v_exact_control_type IN ('int8', 'int4', 'int2') THEN
        -- Have to do a trim here because of inconsistency in quoting different integer types. Ex: bigint boundary values are quoted but int values are not
        child_start_id := trim(BOTH $QUOTE$''$QUOTE$ FROM v_start_string)::bigint;
        child_end_id := trim(BOTH $QUOTE$''$QUOTE$ FROM v_end_string)::bigint;
    ELSIF v_exact_control_type = 'numeric' THEN
        -- cast to numeric then trunc to get rid of decimal without rounding
        child_start_id := trunc(trim(BOTH $QUOTE$''$QUOTE$ FROM v_start_string)::numeric)::bigint;
        child_end_id := trunc(trim(BOTH $QUOTE$''$QUOTE$ FROM v_end_string)::numeric)::bigint;
    END IF;

ELSE
    RAISE EXCEPTION 'Invalid partition type encountered in show_partition_info()';
END IF;

RETURN;

END
$$;

