PostgreSQL 中的 Base58 编码器函数
Base58 Encoder function in PostgreSQL
我为 Base58 Encoder in a Github Gist 找到了这个 MySQL 函数。
DELIMITER $$
CREATE FUNCTION base58_encode (num int) RETURNS varchar(255)
DETERMINISTIC
BEGIN
DECLARE alphabet varchar(255);
DECLARE base_count int DEFAULT 0;
DECLARE encoded varchar(255);
DECLARE divisor DECIMAL(10,4);
DECLARE mode int DEFAULT 0;
SET alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
SET base_count = CHAR_LENGTH(alphabet);
SET encoded = "";
WHILE num >= base_count DO
SET divisor = num / base_count;
SET mode = (num - (base_count* TRUNCATE(divisor,0)));
SET encoded = CONCAT(SUBSTRING(alphabet FROM mode+1 FOR 1), encoded);
SET num = TRUNCATE(divisor,0);
END WHILE;
SET encoded = CONCAT(SUBSTRING(alphabet FROM num+1 FOR 1), encoded);
RETURN (encoded);
END
我是 PostgreSQL
的新手,很难将上述函数转换为 PostgreSQL 函数。
上述 SQL 片段的等价 PostgreSQL
函数如何用于 Base58 编码器?
我想出的 PostgreSQL 中的等效函数如下。
CREATE FUNCTION base58_encode(num INT)
RETURNS VARCHAR(255) AS $encoded$
DECLARE
alphabet VARCHAR(255);
base_count INT DEFAULT 0;
encoded VARCHAR(255);
divisor DECIMAL(10, 4);
mod INT DEFAULT 0;
BEGIN
alphabet := '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
base_count := char_length(alphabet);
encoded := '';
WHILE num >= base_count LOOP
divisor := num / base_count;
mod := (num - (base_count * trunc(divisor, 0)));
encoded := concat(substring(alphabet FROM mod + 1 FOR 1), encoded);
num := trunc(divisor, 0);
END LOOP;
encoded = concat(substring(alphabet FROM num + 1 FOR 1), encoded);
RETURN (encoded);
END; $encoded$
LANGUAGE PLPGSQL;
Postgres 9.x
CREATE OR REPLACE FUNCTION base58_encode (num bigint)
RETURNS text AS
$body$
declare
--alphabet text = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
alphabet text[] = array[
'1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f','g','h','i','j','k','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z'
];
cnt integer = 58;
dst text = '';
mod integer;
begin
while (num >= cnt) loop
num = num / cnt;
mod = num % cnt + 1;
dst = alphabet[mod] || dst;
end loop;
return alphabet[num] || dst;
end;
$body$
LANGUAGE 'plpgsql'
IMMUTABLE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
为了完整起见,这里有一个快速而肮脏的反 base58_decode()
函数:
CREATE OR REPLACE FUNCTION base58_decode(str VARCHAR(255))
RETURNS BIGINT AS $$
DECLARE
alphabet VARCHAR(255);
c CHAR(1);
p INT;
v BIGINT;
BEGIN
alphabet := '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
v := 0;
FOR i IN 1..char_length(str) LOOP
c := substring(str FROM i FOR 1);
-- This is probably wildly inefficient, but we're just using this function for diagnostics...
p := position(c IN alphabet);
IF p = 0 THEN
RAISE 'Illegal base58 character ''%'' in ''%''', c, str;
END IF;
v := (v * 58) + (p - 1);
END LOOP;
RETURN v;
END;$$
LANGUAGE PLPGSQL;
为了继续完整性,如果您需要解码为 UUID (Int16),我会根据 @mike-blackwell 的答案进行解码。 Gist
我希望得到一些反馈和改进。我不得不想象从 128 位数字到 UUID
有更好的方法
代码:
CREATE OR REPLACE FUNCTION base58_decode(encoded_id VARCHAR(22))
RETURNS UUID AS $$
DECLARE
-- Bitcoin base58 alphabet
alphabet CHAR(58) := '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
c CHAR(1) := null;
p INT := null;
raw_num NUMERIC := 0;
uuid_str VARCHAR(32);
BEGIN
/*
Parses a UUID encoded with the Bitcoin base58 standard
Use sparingly, any application connecting to the database should handle decoding the ID itself
*/
-- Decode id to numeric
FOR i IN 1..CHAR_LENGTH(encoded_id) LOOP
c = SUBSTRING(encoded_id FROM i FOR 1);
p = POSITION(c IN alphabet);
raw_num = (raw_num * 58) + (p - 1);
END LOOP;
-- Parse NUMERIC into bytes
-- There must be a better way to go from a NUMERIC -> UUID
uuid_str := '';
FOR i IN 0..31 LOOP
uuid_str = CONCAT(uuid_str, TO_HEX(MOD(raw_num, 16)::INT));
raw_num = DIV(raw_num, 16);
END LOOP;
return REVERSE(uuid_str)::UUID;
END;$$
LANGUAGE PLPGSQL;
我为 Base58 Encoder in a Github Gist 找到了这个 MySQL 函数。
DELIMITER $$
CREATE FUNCTION base58_encode (num int) RETURNS varchar(255)
DETERMINISTIC
BEGIN
DECLARE alphabet varchar(255);
DECLARE base_count int DEFAULT 0;
DECLARE encoded varchar(255);
DECLARE divisor DECIMAL(10,4);
DECLARE mode int DEFAULT 0;
SET alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
SET base_count = CHAR_LENGTH(alphabet);
SET encoded = "";
WHILE num >= base_count DO
SET divisor = num / base_count;
SET mode = (num - (base_count* TRUNCATE(divisor,0)));
SET encoded = CONCAT(SUBSTRING(alphabet FROM mode+1 FOR 1), encoded);
SET num = TRUNCATE(divisor,0);
END WHILE;
SET encoded = CONCAT(SUBSTRING(alphabet FROM num+1 FOR 1), encoded);
RETURN (encoded);
END
我是 PostgreSQL
的新手,很难将上述函数转换为 PostgreSQL 函数。
上述 SQL 片段的等价 PostgreSQL
函数如何用于 Base58 编码器?
我想出的 PostgreSQL 中的等效函数如下。
CREATE FUNCTION base58_encode(num INT)
RETURNS VARCHAR(255) AS $encoded$
DECLARE
alphabet VARCHAR(255);
base_count INT DEFAULT 0;
encoded VARCHAR(255);
divisor DECIMAL(10, 4);
mod INT DEFAULT 0;
BEGIN
alphabet := '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
base_count := char_length(alphabet);
encoded := '';
WHILE num >= base_count LOOP
divisor := num / base_count;
mod := (num - (base_count * trunc(divisor, 0)));
encoded := concat(substring(alphabet FROM mod + 1 FOR 1), encoded);
num := trunc(divisor, 0);
END LOOP;
encoded = concat(substring(alphabet FROM num + 1 FOR 1), encoded);
RETURN (encoded);
END; $encoded$
LANGUAGE PLPGSQL;
Postgres 9.x
CREATE OR REPLACE FUNCTION base58_encode (num bigint)
RETURNS text AS
$body$
declare
--alphabet text = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
alphabet text[] = array[
'1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f','g','h','i','j','k','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z'
];
cnt integer = 58;
dst text = '';
mod integer;
begin
while (num >= cnt) loop
num = num / cnt;
mod = num % cnt + 1;
dst = alphabet[mod] || dst;
end loop;
return alphabet[num] || dst;
end;
$body$
LANGUAGE 'plpgsql'
IMMUTABLE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
为了完整起见,这里有一个快速而肮脏的反 base58_decode()
函数:
CREATE OR REPLACE FUNCTION base58_decode(str VARCHAR(255))
RETURNS BIGINT AS $$
DECLARE
alphabet VARCHAR(255);
c CHAR(1);
p INT;
v BIGINT;
BEGIN
alphabet := '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
v := 0;
FOR i IN 1..char_length(str) LOOP
c := substring(str FROM i FOR 1);
-- This is probably wildly inefficient, but we're just using this function for diagnostics...
p := position(c IN alphabet);
IF p = 0 THEN
RAISE 'Illegal base58 character ''%'' in ''%''', c, str;
END IF;
v := (v * 58) + (p - 1);
END LOOP;
RETURN v;
END;$$
LANGUAGE PLPGSQL;
为了继续完整性,如果您需要解码为 UUID (Int16),我会根据 @mike-blackwell 的答案进行解码。 Gist
我希望得到一些反馈和改进。我不得不想象从 128 位数字到 UUID
有更好的方法代码:
CREATE OR REPLACE FUNCTION base58_decode(encoded_id VARCHAR(22))
RETURNS UUID AS $$
DECLARE
-- Bitcoin base58 alphabet
alphabet CHAR(58) := '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
c CHAR(1) := null;
p INT := null;
raw_num NUMERIC := 0;
uuid_str VARCHAR(32);
BEGIN
/*
Parses a UUID encoded with the Bitcoin base58 standard
Use sparingly, any application connecting to the database should handle decoding the ID itself
*/
-- Decode id to numeric
FOR i IN 1..CHAR_LENGTH(encoded_id) LOOP
c = SUBSTRING(encoded_id FROM i FOR 1);
p = POSITION(c IN alphabet);
raw_num = (raw_num * 58) + (p - 1);
END LOOP;
-- Parse NUMERIC into bytes
-- There must be a better way to go from a NUMERIC -> UUID
uuid_str := '';
FOR i IN 0..31 LOOP
uuid_str = CONCAT(uuid_str, TO_HEX(MOD(raw_num, 16)::INT));
raw_num = DIV(raw_num, 16);
END LOOP;
return REVERSE(uuid_str)::UUID;
END;$$
LANGUAGE PLPGSQL;