Функция для шестнадцатеричных чисел

Сначала для удобства определим функцию для одной цифры.

=> CREATE FUNCTION digit(d text) RETURNS integer
AS $$
SELECT ascii(d) - CASE
        WHEN d BETWEEN '0' AND '9' THEN ascii('0')
        ELSE ascii('A') - 10
    END;
$$ IMMUTABLE LANGUAGE sql;
CREATE FUNCTION

Теперь основная функция:

=> CREATE FUNCTION convert(hex text) RETURNS integer
AS $$
WITH s(d,ord) AS (
    SELECT *
    FROM regexp_split_to_table(reverse(upper(hex)),'') WITH ORDINALITY
)
SELECT sum(digit(d) * 16^(ord-1))::integer
FROM s;
$$ IMMUTABLE LANGUAGE sql;
CREATE FUNCTION
=> SELECT convert('0FE'), convert('0FF'), convert('100');
 convert | convert | convert 
---------+---------+---------
     254 |     255 |     256
(1 row)

Функция для любой системы счисления

Предполагаем, что основание системы счисления от 2 до 36, то есть число записывается цифрами от 0 до 9, либо буквами от A до Z. В этом случае изменения минимальные.

=> DROP FUNCTION convert(text);
DROP FUNCTION
=> CREATE FUNCTION convert(num text, radix integer DEFAULT 16)
RETURNS integer
AS $$
WITH s(d,ord) AS (
    SELECT *
    FROM regexp_split_to_table(reverse(upper(num)),'') WITH ORDINALITY
)
SELECT sum(digit(d) * radix^(ord-1))::integer
FROM s;
$$ IMMUTABLE LANGUAGE sql;
CREATE FUNCTION
=> SELECT convert('0110',2), convert('0FF'), convert('Z',36);
 convert | convert | convert 
---------+---------+---------
       6 |     255 |      35
(1 row)

Функция generate_series для строк

Сначала напишем вспомогательные функции, переводящие строку в числовое представление и обратно.

Первая очень похожа на функцию из предыдущего задания:

=> CREATE FUNCTION text2num(s text) RETURNS integer
AS $$
WITH s(d,ord) AS (
    SELECT *
    FROM regexp_split_to_table(reverse(s),'') WITH ORDINALITY
)
SELECT sum( (ascii(d)-ascii('A')) * 26^(ord-1))::integer
FROM s;
$$ IMMUTABLE LANGUAGE sql;
CREATE FUNCTION

Обратную функцию напишем с помощью рекурсивного запроса:

=> CREATE FUNCTION num2text(n integer, digits integer) RETURNS text
AS $$
WITH RECURSIVE r(num,txt, level) AS (
    SELECT n/26, chr( n%26 + ascii('A') )::text, 1
    UNION ALL
    SELECT r.num/26, chr( r.num%26 + ascii('A') ) || r.txt, r.level+1
    FROM r
    WHERE r.level < digits
)
SELECT r.txt FROM r WHERE r.level = digits;
$$ IMMUTABLE LANGUAGE sql;
CREATE FUNCTION
=> SELECT num2text( text2num('ABC'), length('ABC') );
 num2text 
----------
 ABC
(1 row)

Теперь функцию generate_series для строк можно переписать, используя generate_series для целых чисел.

=> CREATE FUNCTION generate_series(start text, stop text) RETURNS SETOF text
AS $$
SELECT num2text( g.n, length(start) )
FROM generate_series(text2num(start), text2num(stop)) g(n);
$$ IMMUTABLE LANGUAGE sql;
CREATE FUNCTION
=> SELECT generate_series('AZ','BC');
 generate_series 
-----------------
 AZ
 BA
 BB
 BC
(4 rows)