Created
November 27, 2019 03:49
-
-
Save srfrog/ef8c9a3a4282b865fb93f429d671d63b to your computer and use it in GitHub Desktop.
Revisions
-
srfrog created this gist
Nov 27, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,134 @@ -- Collision-resistant ids optimized for horizontal scaling and performance, for PL/PgSQL. -- Based on https://github.com/ericelliott/cuid -- Version 1.0.0 -- Usage: SELECT cuid(); -- BEGIN CONFIG --- -- Put a unique host ID (int) here per server instance. -- Once set, this value should not be changed. SET plpgsql.cuid_hostid = "897892"; -- Create the sequence used to track ID counts. Used in _safeCounter() CREATE SEQUENCE IF NOT EXISTS "cuid_seq"; -- END OF CONFIG --- -- Get the next ID in counter. The sequence will reset after max_id is reached. CREATE OR REPLACE FUNCTION _safeCounter() RETURNS VARCHAR AS $$ DECLARE seq_id bigint; max_id int := 1679616; BEGIN SELECT nextval('cuid_seq') INTO seq_id; IF seq_id > max_id THEN SELECT setval('cuid_seq', 0) INTO seq_id; END IF; RETURN pad(base36enc(seq_id), 4); END; $$ LANGUAGE PLPGSQL; -- Uses the backend PID and a unique host ID to generate a fingerprint. -- Unfortunately getting the hostname of a Pg server is rather expensive so we opt for -- a static unique host ID. CREATE OR REPLACE FUNCTION _fingerprint() RETURNS VARCHAR AS $$ DECLARE padding int := 2; ret varchar; BEGIN ret := pad(base36enc(pg_backend_pid()), padding); ret := ret || pad(base36enc(current_setting('plpgsql.cuid_hostid')::bigint), padding); RETURN ret; END; $$ LANGUAGE PLPGSQL; -- Encodes a base-10 bigint to a base-36 string representation. CREATE OR REPLACE FUNCTION base36enc(num bigint) RETURNS VARCHAR AS $$ DECLARE base36 char[]; ret varchar; val bigint; tmp bigint; BEGIN base36 := ARRAY[ '0','1','2','3','4','5','6','7','8','9','a','b', 'c','d','e','f','g','h','i','j','k','l','m','n', 'o','p','q','r','s','t','u','v','w','x','y','z' ]; IF num ISNULL THEN RETURN NULL; END IF; val := num; ret := ''; LOOP tmp := val % 36 + 1; ret := base36[tmp] || ret; val := val / 36; IF val = 0 THEN exit; END IF; END LOOP; IF num < 0 THEN ret := '-' || ret; END IF; RETURN ret; END; $$ LANGUAGE PLPGSQL; -- Adds zero-padding of a size to a num string. If the string is longer than size -- it gets truncated to the left. CREATE OR REPLACE FUNCTION pad(num varchar, size int) RETURNS VARCHAR AS $$ BEGIN RETURN lpad(right(num, size), size, '0'); END; $$ LANGUAGE PLPGSQL; -- Get value of current timestamp encoded as base-36 CREATE OR REPLACE FUNCTION _timestamp() RETURNS VARCHAR AS $$ DECLARE now_ts bigint; BEGIN SELECT floor(extract(EPOCH FROM clock_timestamp())) INTO now_ts; RETURN base36enc(now_ts); END; $$ LANGUAGE PLPGSQL; -- Get a random base-36 valid string of size block_size. CREATE OR REPLACE FUNCTION _randomBlock() RETURNS VARCHAR AS $$ DECLARE base36 varchar := '0123456789abcdefghijklmnopqrstuvwxyz'; block_size int := 4; val varchar; BEGIN SELECT array_to_string(ARRAY( SELECT substring(base36 FROM (random() * 36)::int FOR 1) FROM generate_series(1, block_size)), '', '0') INTO val; RETURN val; END; $$ LANGUAGE PLPGSQL; -- Generates a new Collision-resistant ID (CUID) CREATE OR REPLACE FUNCTION cuid() RETURNS VARCHAR AS $$ DECLARE ret varchar; BEGIN ret := 'c'; ret := ret || _timestamp(); ret := ret || _safeCounter(); ret := ret || _fingerprint(); ret := ret || _randomBlock(); ret := ret || _randomBlock(); RETURN ret; END; $$ LANGUAGE PLPGSQL;