Skip to content

Instantly share code, notes, and snippets.

@hmarr
Last active March 30, 2020 13:45
Show Gist options
  • Save hmarr/0b5cdf8700876c56ae7e to your computer and use it in GitHub Desktop.
Save hmarr/0b5cdf8700876c56ae7e to your computer and use it in GitHub Desktop.
Random-looking id generation in Postgres
CREATE OR REPLACE FUNCTION base32_encode(n bigint, min_width int DEFAULT 8) RETURNS text AS $$
DECLARE
alphabet CONSTANT text := '0123456789abcdefghjkmnpqrstvwxyz';
output text := '';
BEGIN
IF n < 0 THEN
RAISE EXCEPTION 'base32_encode called with negative value';
END IF;
WHILE n != 0 LOOP
output := substr(alphabet, 1 + (n % 32)::int, 1) || output;
n := n / 32;
END LOOP;
IF char_length(output) < min_width THEN
output := lpad(output, min_width, '0');
END IF;
RETURN output;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION feistel_cipher(value bigint, key int DEFAULT 1065940451) returns bigint AS $$
DECLARE
-- Constants that control the size of the input and output.
num_bits CONSTANT int := 38;
max_piece_val CONSTANT bigint := power(2, num_bits / 2)::bigint - 1;
-- Constants for the round function (a linear congruential generator)
round_f_inc CONSTANT bigint := key % max_piece_val;
round_f_mul CONSTANT bigint := (power(2, 31) - key)::bigint % max_piece_val;
l1 bigint;
l2 bigint;
r1 bigint;
r2 bigint;
round int := 0;
BEGIN
-- Pull out the n/2 most significant bits by shifting and masking
l1 := (value >> (num_bits / 2)) & max_piece_val;
-- Pull out the n/2 least significant bits by masking
r1 := value & max_piece_val;
WHILE round < 3 LOOP
l2 := r1;
r2 := l1 # ((round_f_mul * r1 + round_f_inc) % max_piece_val)::bigint;
l1 := l2;
r1 := r2;
round := round + 1;
END LOOP;
RETURN ((l1::bigint << (num_bits / 2)) + r1);
END;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;
CREATE OR REPLACE FUNCTION generate_id(seqname text) RETURNS text AS $$
DECLARE
seqval bigint;
BEGIN
SELECT nextval(seqname) INTO seqval;
RETURN base32_encode(feistel_cipher(seqval, hashtext(seqname)));
END;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment