79 lines
2.7 KiB
JavaScript
79 lines
2.7 KiB
JavaScript
// To make the migration parser happy, we add the customary up and down of knex.
|
|
// They're ignored. A better way would be to conventionally exclude these types of files.
|
|
up = async knex => {}
|
|
down = async knex => {}
|
|
|
|
let hashTriggerGenerator = ( tableName, hostField, hashByField ) => `
|
|
CREATE OR REPLACE FUNCTION ${tableName}_hash_${hostField}_update_tg() RETURNS trigger AS $$
|
|
BEGIN
|
|
IF ( tg_op = 'INSERT' OR tg_op = 'UPDATE') AND (( NEW.${hostField} = '') IS NOT FALSE) THEN
|
|
NEW.${hostField} = md5( ${ hashByField ? "NEW." + hashByField : "NEW" }::text );
|
|
RETURN NEW;
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER ${tableName}_hash_${hostField}_update
|
|
BEFORE INSERT OR UPDATE ON ${tableName}
|
|
FOR EACH ROW EXECUTE PROCEDURE ${tableName}_hash_${hostField}_update_tg();
|
|
`
|
|
|
|
// ref: https://blog.andyet.com/2016/02/23/generating-shortids-in-postgres/
|
|
let shortIdTriggerGenerator = ( tableName, hostField ) => `
|
|
CREATE OR REPLACE FUNCTION ${tableName}_shortid_update() RETURNS trigger AS $$
|
|
|
|
DECLARE
|
|
key TEXT;
|
|
qry TEXT;
|
|
found TEXT;
|
|
|
|
BEGIN
|
|
qry := 'SELECT ${hostField} FROM ' || quote_ident(TG_TABLE_NAME) || ' WHERE ${hostField}=';
|
|
|
|
LOOP
|
|
|
|
-- Generate our string bytes and re-encode as a base64 string.
|
|
key := encode(gen_random_bytes(6), 'base64');
|
|
|
|
-- Base64 encoding contains 2 URL unsafe characters by default.
|
|
-- The URL-safe version has these replacements.
|
|
key := replace(key, '/', '_'); -- url safe replacement
|
|
key := replace(key, '+', '-'); -- url safe replacement
|
|
|
|
-- Concat the generated key (safely quoted) with the generated query
|
|
-- and run it.
|
|
-- SELECT id FROM "test" WHERE id='blahblah' INTO found
|
|
-- Now "found" will be the duplicated id or NULL.
|
|
EXECUTE qry || quote_literal(key) INTO found;
|
|
|
|
-- Check to see if found is NULL.
|
|
-- If we checked to see if found = NULL it would always be FALSE
|
|
-- because (NULL = NULL) is always FALSE.
|
|
IF found IS NULL THEN
|
|
-- If we didn't find a collision then leave the LOOP.
|
|
EXIT;
|
|
END IF;
|
|
|
|
-- We haven't EXITed yet, so return to the top of the LOOP
|
|
-- and try again.
|
|
END LOOP;
|
|
|
|
-- NEW and OLD are available in TRIGGER PROCEDURES.
|
|
-- NEW is the mutated row that will actually be INSERTed.
|
|
-- We're replacing id, regardless of what it was before
|
|
-- with our key variable.
|
|
NEW.${hostField} = key;
|
|
|
|
-- The RECORD returned here is what will actually be INSERTed,
|
|
-- or what the next trigger will get if there is one.
|
|
RETURN NEW;
|
|
END;
|
|
$$ language 'plpgsql';
|
|
|
|
CREATE TRIGGER ${tableName}_shortid_update
|
|
BEFORE INSERT ON ${tableName}
|
|
FOR EACH ROW EXECUTE PROCEDURE ${tableName}_shortid_update();
|
|
`
|
|
|
|
module.exports = { hashTriggerGenerator, shortIdTriggerGenerator, up, down } |