Last active
February 3, 2025 01:27
-
-
Save lavellotron/159f192856d9f5b73ed6f1700953acc4 to your computer and use it in GitHub Desktop.
Nano ID for PostgreSQL on Supabase
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 characters
/* | |
* -------------------------- | |
* | |
* NANO ID on SUPABASE | |
* Modified the original (link below) to work as Supabase function instead of adding to pgcrypto extension. | |
* - remove statements for modifying pgcrypto extension | |
* - commented LEAKPROOF as it requires superadmin which we do not have | |
* - change gen_random_bytes(step); to extensions.gen_random_bytes(step); | |
* | |
* https://github.com/viascom/nanoid-postgres/blob/main/nanoid.sql | |
* | |
* -------------------------- | |
*/ | |
/* | |
* Copyright 2023 Viascom Ltd liab. Co | |
* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
-- The `nanoid()` function generates a compact, URL-friendly unique identifier. | |
-- Based on the given size and alphabet, it creates a randomized string that's ideal for | |
-- use-cases requiring small, unpredictable IDs (e.g., URL shorteners, generated file names, etc.). | |
-- While it comes with a default configuration, the function is designed to be flexible, | |
-- allowing for customization to meet specific needs. | |
CREATE OR REPLACE FUNCTION nanoid( | |
size int DEFAULT 21, -- The number of symbols in the NanoId String. Must be greater than 0. | |
alphabet text DEFAULT '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', -- The symbols used in the NanoId String. Must contain between 1 and 255 symbols. | |
additionalBytesFactor float DEFAULT 1.6 -- The additional bytes factor used for calculating the step size. Must be equal or greater then 1. | |
) | |
RETURNS text -- A randomly generated NanoId String | |
LANGUAGE plpgsql | |
VOLATILE | |
-- LEAKPROOF | |
PARALLEL SAFE | |
AS | |
$$ | |
DECLARE | |
alphabetArray text[]; | |
alphabetLength int := 64; | |
mask int := 63; | |
step int := 34; | |
BEGIN | |
IF size IS NULL OR size < 1 THEN | |
RAISE EXCEPTION 'The size must be defined and greater than 0!'; | |
END IF; | |
IF alphabet IS NULL OR length(alphabet) = 0 OR length(alphabet) > 255 THEN | |
RAISE EXCEPTION 'The alphabet can''t be undefined, zero or bigger than 255 symbols!'; | |
END IF; | |
IF additionalBytesFactor IS NULL OR additionalBytesFactor < 1 THEN | |
RAISE EXCEPTION 'The additional bytes factor can''t be less than 1!'; | |
END IF; | |
alphabetArray := regexp_split_to_array(alphabet, ''); | |
alphabetLength := array_length(alphabetArray, 1); | |
mask := (2 << cast(floor(log(alphabetLength - 1) / log(2)) as int)) - 1; | |
step := cast(ceil(additionalBytesFactor * mask * size / alphabetLength) AS int); | |
IF step > 1024 THEN | |
step := 1024; -- The step size % can''t be bigger then 1024! | |
END IF; | |
RETURN nanoid_optimized(size, alphabet, mask, step); | |
END | |
$$; | |
-- Generates an optimized random string of a specified size using the given alphabet, mask, and step. | |
-- This optimized version is designed for higher performance and lower memory overhead. | |
-- No checks are performed! Use it only if you really know what you are doing. | |
CREATE OR REPLACE FUNCTION nanoid_optimized( | |
size int, -- The desired length of the generated string. | |
alphabet text, -- The set of characters to choose from for generating the string. | |
mask int, -- The mask used for mapping random bytes to alphabet indices. Should be `(2^n) - 1` where `n` is a power of 2 less than or equal to the alphabet size. | |
step int -- The number of random bytes to generate in each iteration. A larger value may speed up the function but increase memory usage. | |
) | |
RETURNS text -- A randomly generated NanoId String | |
LANGUAGE plpgsql | |
VOLATILE | |
-- LEAKPROOF | |
PARALLEL SAFE | |
AS | |
$$ | |
DECLARE | |
idBuilder text := ''; | |
counter int := 0; | |
bytes bytea; | |
alphabetIndex int; | |
alphabetArray text[]; | |
alphabetLength int := 64; | |
BEGIN | |
alphabetArray := regexp_split_to_array(alphabet, ''); | |
alphabetLength := array_length(alphabetArray, 1); | |
LOOP | |
bytes := extensions.gen_random_bytes(step); | |
FOR counter IN 0..step - 1 | |
LOOP | |
alphabetIndex := (get_byte(bytes, counter) & mask) + 1; | |
IF alphabetIndex <= alphabetLength THEN | |
idBuilder := idBuilder || alphabetArray[alphabetIndex]; | |
IF length(idBuilder) = size THEN | |
RETURN idBuilder; | |
END IF; | |
END IF; | |
END LOOP; | |
END LOOP; | |
END | |
$$; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment