From e6fb6dd8cd9f9b1ab84ab368b9e5fb1d7492051d Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Wed, 24 Dec 2025 11:56:31 -0300 Subject: [PATCH] fix: uuid_generate_v7 integer overflow and seeder role constraint Migration 009: - Simplified uuid_generate_v7() to avoid integer overflow on bit shifts - Uses double precision for timestamp then converts to hex Seeder: - Changed roles from 'admin','company' to 'companyAdmin' - Matches users table CHECK constraint: superadmin, companyAdmin, recruiter, jobSeeker --- .../009_create_uuid_v7_function.sql | 58 ++++++++----------- seeder-api/src/seeders/users.js | 6 +- 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/backend/migrations/009_create_uuid_v7_function.sql b/backend/migrations/009_create_uuid_v7_function.sql index 75c9c57..d623e0c 100644 --- a/backend/migrations/009_create_uuid_v7_function.sql +++ b/backend/migrations/009_create_uuid_v7_function.sql @@ -1,52 +1,42 @@ -- Migration: Create UUID v7 generation function -- Description: PostgreSQL function to generate UUID v7 (time-ordered UUIDs) --- UUID v7 format: tttttttt-tttt-7xxx-yxxx-xxxxxxxxxxxx --- Where: t = timestamp, 7 = version, y = variant, x = random +-- Uses gen_random_uuid() as base, then overrides first 6 bytes with timestamp -- Enable pgcrypto extension for gen_random_bytes() CREATE EXTENSION IF NOT EXISTS pgcrypto; --- Create or replace the uuid_generate_v7 function +-- Simple UUID v7 function that works with PostgreSQL's type system CREATE OR REPLACE FUNCTION uuid_generate_v7() RETURNS uuid AS $$ DECLARE - unix_ts_ms bigint; - uuid_bytes bytea; + v_time double precision; + v_secs bigint; + v_msec int; + v_random bytea; + v_uuid bytea; + v_timestamp bytea; BEGIN - -- Get current Unix timestamp in milliseconds - unix_ts_ms := (EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint; + -- Get current time + v_time := extract(epoch from clock_timestamp()); + v_secs := floor(v_time); + v_msec := floor((v_time - v_secs) * 1000); - -- Build the UUID bytes: - -- First 6 bytes: timestamp (48 bits) - -- Next 2 bytes: version (4 bits) + random (12 bits) - -- Last 8 bytes: variant (2 bits) + random (62 bits) - uuid_bytes := set_byte( - set_byte( - set_byte( - set_byte( - set_byte( - set_byte( - gen_random_bytes(16), - 0, (unix_ts_ms >> 40)::int - ), - 1, (unix_ts_ms >> 32)::int - ), - 2, (unix_ts_ms >> 24)::int - ), - 3, (unix_ts_ms >> 16)::int - ), - 4, (unix_ts_ms >> 8)::int - ), - 5, unix_ts_ms::int - ); + -- Generate random bytes for the rest + v_random := gen_random_bytes(10); - -- Set version 7 (0111) in byte 6 - uuid_bytes := set_byte(uuid_bytes, 6, (get_byte(uuid_bytes, 6) & 15) | 112); + -- Build timestamp bytes (6 bytes = 48 bits) + v_timestamp := decode(lpad(to_hex(v_secs * 1000 + v_msec), 12, '0'), 'hex'); + + -- Combine: timestamp (6 bytes) + random (10 bytes) + v_uuid := v_timestamp || v_random; + + -- Set version 7 (0111) in byte 6 (bits 4-7) + v_uuid := set_byte(v_uuid, 6, (get_byte(v_uuid, 6) & 15) | 112); -- Set variant RFC 4122 (10xx) in byte 8 - uuid_bytes := set_byte(uuid_bytes, 8, (get_byte(uuid_bytes, 8) & 63) | 128); + v_uuid := set_byte(v_uuid, 8, (get_byte(v_uuid, 8) & 63) | 128); - RETURN encode(uuid_bytes, 'hex')::uuid; + RETURN encode(v_uuid, 'hex')::uuid; END; $$ LANGUAGE plpgsql VOLATILE; diff --git a/seeder-api/src/seeders/users.js b/seeder-api/src/seeders/users.js index 901ea15..21f8268 100644 --- a/seeder-api/src/seeders/users.js +++ b/seeder-api/src/seeders/users.js @@ -41,9 +41,9 @@ export async function seedUsers() { // 2. Create Company Admins const companyAdmins = [ - { identifier: 'takeshi_yamamoto', fullName: 'Takeshi Yamamoto', company: 'TechCorp', email: 'takeshi@techcorp.com', pass: 'Takeshi@2025', roles: ['admin', 'company'] }, - { identifier: 'kenji', fullName: 'Kenji Tanaka', company: 'AppMakers', email: 'kenji@appmakers.mobile', pass: 'Takeshi@2025', roles: ['admin', 'company'] }, - { identifier: 'maria_santos', fullName: 'Maria Santos', company: 'DesignHub', email: 'maria@designhub.com', pass: 'User@2025', roles: ['recruiter', 'company'] } + { identifier: 'takeshi_yamamoto', fullName: 'Takeshi Yamamoto', company: 'TechCorp', email: 'takeshi@techcorp.com', pass: 'Takeshi@2025', roles: ['companyAdmin'] }, + { identifier: 'kenji', fullName: 'Kenji Tanaka', company: 'AppMakers', email: 'kenji@appmakers.mobile', pass: 'Takeshi@2025', roles: ['companyAdmin'] }, + { identifier: 'maria_santos', fullName: 'Maria Santos', company: 'DesignHub', email: 'maria@designhub.com', pass: 'User@2025', roles: ['recruiter'] } ]; for (const admin of companyAdmins) {