CREATE TYPE range AS (start int8, stop int8);

CREATE FUNCTION range(start int8, stop int8)
RETURNS range
LANGUAGE sql
AS
$$
SELECT ROW(start,stop)::range
$$;

CREATE FUNCTION range(int8range)
RETURNS range
LANGUAGE sql
AS
$$
SELECT
  ROW
  (
    lower($1) + CASE lower_inc($1) WHEN TRUE THEN 0 ELSE 1 END,
    upper($1) - CASE upper_inc($1) WHEN TRUE THEN 0 ELSE 1 END
  )::range
$$;

CREATE FUNCTION range(int4range)
RETURNS range
LANGUAGE sql
AS
$$
SELECT
  ROW
  (
    lower($1) + CASE lower_inc($1) WHEN TRUE THEN 0 ELSE 1 END,
    upper($1) - CASE upper_inc($1) WHEN TRUE THEN 0 ELSE 1 END
  )::range
$$;

CREATE FUNCTION range(int8range[])
RETURNS range[]
LANGUAGE sql
AS
$$
SELECT array_agg(range(unnest) ORDER BY ORDINALITY) FROM unnest($1) WITH ORDINALITY
$$;

CREATE FUNCTION range(int4range[])
RETURNS range[]
LANGUAGE sql
AS
$$
SELECT array_agg(range(unnest) ORDER BY ORDINALITY) FROM unnest($1) WITH ORDINALITY
$$;

SELECT * FROM range(10,20);
/*
 start | stop
-------+------
    10 |   20
(1 row)
*/

SELECT * FROM range(int8range(10,20,'[]'));
/*
 start | stop
-------+------
    10 |   20
(1 row)
*/

SELECT * FROM range(int4range(10,20,'[]'));
/*
 start | stop
-------+------
    10 |   20
(1 row)
*/

SELECT * FROM range(ARRAY[
  int8range(10,20,'[]'),
  int8range(20,30,'[]')
]);

/*
         range
-----------------------
 {"(10,20)","(20,30)"}
(1 row)
*/

SELECT * FROM range(ARRAY[
  int4range(10,20,'[]'),
  int4range(20,30,'[]')
]);

/*
         range
-----------------------
 {"(10,20)","(20,30)"}
(1 row)
*/