Files
food-ai/backend/migrations/001_create_users.sql
dbastrikin ea4a6301ea feat: replace UUID v4 with UUID v7 across backend
Define uuid_generate_v7() in the first migration and switch all table
primary key defaults to it. Remove the uuid-ossp extension dependency.
Update refresh token and request ID generation in Go code to use
uuid.NewV7() from the existing google/uuid v1.6.0 library.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 13:19:55 +02:00

81 lines
2.6 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- +goose Up
-- Generate UUID v7 (time-ordered, millisecond precision).
-- Structure: 48-bit unix_ts_ms | 4-bit version (0111) | 12-bit rand_a | 2-bit variant (10) | 62-bit rand_b
CREATE OR REPLACE FUNCTION uuid_generate_v7()
RETURNS uuid
AS $$
DECLARE
unix_ts_ms bytea;
uuid_bytes bytea;
BEGIN
-- 48-bit Unix timestamp in milliseconds.
-- int8send produces 8 big-endian bytes; skip the first 2 zero bytes to get 6.
unix_ts_ms = substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3);
-- Use a random v4 UUID as the source of random bits for rand_a and rand_b.
uuid_bytes = uuid_send(gen_random_uuid());
-- Overwrite bytes 05 with the timestamp (positions 16 in 1-indexed bytea).
uuid_bytes = overlay(uuid_bytes placing unix_ts_ms from 1 for 6);
-- Set version nibble (bits 4851) to 0111 (7).
uuid_bytes = set_bit(uuid_bytes, 48, 0);
uuid_bytes = set_bit(uuid_bytes, 49, 1);
uuid_bytes = set_bit(uuid_bytes, 50, 1);
uuid_bytes = set_bit(uuid_bytes, 51, 1);
-- Variant bits (6465) stay at 10 as inherited from gen_random_uuid().
RETURN encode(uuid_bytes, 'hex')::uuid;
END
$$ LANGUAGE plpgsql VOLATILE;
CREATE TYPE user_plan AS ENUM ('free', 'paid');
CREATE TYPE user_gender AS ENUM ('male', 'female');
CREATE TYPE user_goal AS ENUM ('lose', 'maintain', 'gain');
CREATE TYPE activity_level AS ENUM ('low', 'moderate', 'high');
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
firebase_uid VARCHAR(128) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL DEFAULT '',
avatar_url TEXT,
-- Body parameters
height_cm SMALLINT,
weight_kg DECIMAL(5,2),
age SMALLINT,
gender user_gender,
activity activity_level,
-- Goal and calculated daily norm
goal user_goal,
daily_calories INTEGER,
-- Plan
plan user_plan NOT NULL DEFAULT 'free',
-- Preferences (JSONB for flexibility)
preferences JSONB NOT NULL DEFAULT '{}'::jsonb,
-- Refresh token
refresh_token TEXT,
token_expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_users_firebase_uid ON users (firebase_uid);
CREATE INDEX idx_users_email ON users (email);
-- +goose Down
DROP TABLE IF EXISTS users;
DROP TYPE IF EXISTS activity_level;
DROP TYPE IF EXISTS user_goal;
DROP TYPE IF EXISTS user_gender;
DROP TYPE IF EXISTS user_plan;
DROP FUNCTION IF EXISTS uuid_generate_v7();