Files
FlockPal/backend/src/db/schema.ts
T
2026-05-30 22:46:31 -04:00

493 lines
20 KiB
TypeScript

import { db, type DatabaseClient } from './client.js';
export const ensureSchema = async (database: DatabaseClient = db) => {
await database.query(`
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255),
name VARCHAR(160) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS workspaces (
id INTEGER PRIMARY KEY,
name VARCHAR(160) NOT NULL DEFAULT 'My Flock',
workspace_type VARCHAR(16) NOT NULL DEFAULT 'standard',
billing_email VARCHAR(255),
billing_plan VARCHAR(32) NOT NULL DEFAULT 'household_basic',
billing_interval VARCHAR(16) NOT NULL DEFAULT 'monthly',
subscription_status VARCHAR(32) NOT NULL DEFAULT 'none',
stripe_customer_id VARCHAR(255),
stripe_subscription_id VARCHAR(255),
rescue_verification_status VARCHAR(32) NOT NULL DEFAULT 'not_required',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE workspaces
DROP CONSTRAINT IF EXISTS workspaces_id_check;
ALTER TABLE workspaces
ADD COLUMN IF NOT EXISTS billing_email VARCHAR(255),
ADD COLUMN IF NOT EXISTS billing_plan VARCHAR(32) NOT NULL DEFAULT 'household_basic',
ADD COLUMN IF NOT EXISTS billing_interval VARCHAR(16) NOT NULL DEFAULT 'monthly',
ADD COLUMN IF NOT EXISTS subscription_status VARCHAR(32) NOT NULL DEFAULT 'none',
ADD COLUMN IF NOT EXISTS stripe_customer_id VARCHAR(255),
ADD COLUMN IF NOT EXISTS stripe_subscription_id VARCHAR(255),
ADD COLUMN IF NOT EXISTS rescue_verification_status VARCHAR(32) NOT NULL DEFAULT 'not_required';
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspaces_stripe_subscription_id
ON workspaces (stripe_subscription_id)
WHERE stripe_subscription_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_workspaces_stripe_customer_id
ON workspaces (stripe_customer_id)
WHERE stripe_customer_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_workspaces_rescue_status
ON workspaces (workspace_type, rescue_verification_status, created_at DESC);
UPDATE workspaces
SET subscription_status = 'none'
WHERE workspace_type = 'standard'
AND stripe_subscription_id IS NULL
AND subscription_status = 'active';
UPDATE workspaces
SET rescue_verification_status = 'pending'
WHERE workspace_type = 'rescue'
AND rescue_verification_status = 'not_required';
CREATE TABLE IF NOT EXISTS workspace_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
invite_email VARCHAR(255) NOT NULL,
name VARCHAR(160) NOT NULL,
role VARCHAR(16) NOT NULL DEFAULT 'caregiver',
accepted_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE workspace_members
ADD COLUMN IF NOT EXISTS email VARCHAR(255),
ADD COLUMN IF NOT EXISTS user_id UUID REFERENCES users(id) ON DELETE CASCADE,
ADD COLUMN IF NOT EXISTS invite_email VARCHAR(255),
ADD COLUMN IF NOT EXISTS accepted_at TIMESTAMPTZ;
UPDATE workspace_members
SET role = CASE
WHEN role = 'manager' THEN 'assistant'
WHEN role = 'staff' THEN 'caregiver'
ELSE role
END
WHERE role IN ('manager', 'staff');
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'workspace_members'
AND column_name = 'email'
) THEN
UPDATE workspace_members
SET invite_email = COALESCE(invite_email, email)
WHERE invite_email IS NULL;
END IF;
END $$;
UPDATE workspace_members
SET invite_email = ''
WHERE invite_email IS NULL;
UPDATE workspace_members
SET email = invite_email
WHERE email IS NULL;
ALTER TABLE workspace_members
ALTER COLUMN invite_email SET NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_members_workspace_email
ON workspace_members (workspace_id, invite_email);
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_members_workspace_user
ON workspace_members (workspace_id, user_id)
WHERE user_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_workspace_members_user_accepted
ON workspace_members (user_id, accepted_at, workspace_id)
WHERE user_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_workspace_members_owner_email
ON workspace_members (LOWER(COALESCE(invite_email, email)), workspace_id)
WHERE role = 'owner'
AND accepted_at IS NOT NULL;
CREATE TABLE IF NOT EXISTS auth_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
active_workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
token_hash VARCHAR(255) NOT NULL UNIQUE,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_auth_sessions_created_user
ON auth_sessions (created_at DESC, user_id);
CREATE TABLE IF NOT EXISTS integration_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
name VARCHAR(160) NOT NULL,
token_hash VARCHAR(255) NOT NULL UNIQUE,
token_prefix VARCHAR(32) NOT NULL,
scope VARCHAR(16) NOT NULL DEFAULT 'read_write',
last_used_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE integration_tokens
ADD COLUMN IF NOT EXISTS workspace_id INTEGER REFERENCES workspaces(id) ON DELETE CASCADE,
ADD COLUMN IF NOT EXISTS name VARCHAR(160) NOT NULL DEFAULT 'Integration token',
ADD COLUMN IF NOT EXISTS token_prefix VARCHAR(32),
ADD COLUMN IF NOT EXISTS scope VARCHAR(16) NOT NULL DEFAULT 'read_write',
ADD COLUMN IF NOT EXISTS last_used_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS revoked_at TIMESTAMPTZ;
UPDATE integration_tokens
SET token_prefix = LEFT(token_hash, 12)
WHERE token_prefix IS NULL;
CREATE INDEX IF NOT EXISTS idx_integration_tokens_user_workspace
ON integration_tokens (user_id, workspace_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_integration_tokens_lookup
ON integration_tokens (token_hash)
WHERE revoked_at IS NULL;
CREATE TABLE IF NOT EXISTS auth_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
provider_key VARCHAR(32) NOT NULL,
provider_subject VARCHAR(255) NOT NULL,
provider_email VARCHAR(255),
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_auth_accounts_provider_subject
ON auth_accounts (provider_key, provider_subject);
CREATE TABLE IF NOT EXISTS oauth_states (
id UUID PRIMARY KEY,
provider_key VARCHAR(32) NOT NULL,
code_verifier VARCHAR(255) NOT NULL,
redirect_to TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS magic_link_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL,
name VARCHAR(160),
token_hash VARCHAR(255) NOT NULL UNIQUE,
redirect_to TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_magic_link_tokens_email
ON magic_link_tokens (email, created_at DESC);
CREATE TABLE IF NOT EXISTS birds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id INTEGER NOT NULL DEFAULT 1,
name VARCHAR(120) NOT NULL,
tag_id VARCHAR(80),
species VARCHAR(120) NOT NULL,
motivators VARCHAR(1000),
demotivators VARCHAR(1000),
favorite_snack VARCHAR(160),
gender VARCHAR(16) NOT NULL DEFAULT 'unknown',
date_of_birth DATE,
gotcha_day DATE,
chart_color VARCHAR(7) NOT NULL DEFAULT '#cb3a35',
photo_data_url TEXT,
photo_object_key TEXT,
photo_content_type VARCHAR(80),
photo_updated_at TIMESTAMPTZ,
notify_on_dob BOOLEAN NOT NULL DEFAULT FALSE,
notify_on_gotcha_day BOOLEAN NOT NULL DEFAULT FALSE,
public_profile_code VARCHAR(32),
public_profile_enabled BOOLEAN NOT NULL DEFAULT FALSE,
memorialized_at TIMESTAMPTZ,
memorialized_on DATE,
memorial_note VARCHAR(1000),
notify_on_memorial_day BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE birds
ADD COLUMN IF NOT EXISTS workspace_id INTEGER NOT NULL DEFAULT 1,
ADD COLUMN IF NOT EXISTS motivators VARCHAR(1000),
ADD COLUMN IF NOT EXISTS demotivators VARCHAR(1000),
ADD COLUMN IF NOT EXISTS favorite_snack VARCHAR(160),
ADD COLUMN IF NOT EXISTS gender VARCHAR(16) NOT NULL DEFAULT 'unknown',
ADD COLUMN IF NOT EXISTS date_of_birth DATE,
ADD COLUMN IF NOT EXISTS gotcha_day DATE,
ADD COLUMN IF NOT EXISTS chart_color VARCHAR(7) NOT NULL DEFAULT '#cb3a35',
ADD COLUMN IF NOT EXISTS photo_data_url TEXT,
ADD COLUMN IF NOT EXISTS photo_object_key TEXT,
ADD COLUMN IF NOT EXISTS photo_content_type VARCHAR(80),
ADD COLUMN IF NOT EXISTS photo_updated_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS notify_on_dob BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS notify_on_gotcha_day BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS public_profile_code VARCHAR(32),
ADD COLUMN IF NOT EXISTS public_profile_enabled BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS memorialized_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS memorialized_on DATE,
ADD COLUMN IF NOT EXISTS memorial_note VARCHAR(1000),
ADD COLUMN IF NOT EXISTS notify_on_memorial_day BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE birds
ALTER COLUMN tag_id DROP NOT NULL;
UPDATE birds
SET tag_id = NULL
WHERE tag_id IS NOT NULL
AND LOWER(BTRIM(tag_id)) IN ('unknown', 'not recorded', 'n/a', 'na', 'none');
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'birds_workspace_fk') THEN
ALTER TABLE birds
ADD CONSTRAINT birds_workspace_fk
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
END IF;
END $$;
DELETE FROM workspaces
WHERE id = 1
AND name = 'My Flock'
AND NOT EXISTS (SELECT 1 FROM workspace_members WHERE workspace_members.workspace_id = workspaces.id)
AND NOT EXISTS (SELECT 1 FROM birds WHERE birds.workspace_id = workspaces.id);
ALTER TABLE birds
DROP CONSTRAINT IF EXISTS birds_tag_id_key;
DROP INDEX IF EXISTS idx_birds_workspace_tag_id;
CREATE UNIQUE INDEX IF NOT EXISTS idx_birds_workspace_tag_id
ON birds (workspace_id, LOWER(tag_id))
WHERE tag_id IS NOT NULL
AND BTRIM(tag_id) <> ''
AND LOWER(BTRIM(tag_id)) NOT IN ('unknown', 'not recorded', 'n/a', 'na', 'none');
CREATE INDEX IF NOT EXISTS idx_birds_workspace_active_name
ON birds (workspace_id, name)
WHERE memorialized_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_birds_workspace_memorialized
ON birds (workspace_id, memorialized_on DESC, name)
WHERE memorialized_at IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_birds_tag_lookup_active
ON birds (LOWER(tag_id), created_at)
WHERE tag_id IS NOT NULL
AND BTRIM(tag_id) <> ''
AND LOWER(BTRIM(tag_id)) NOT IN ('unknown', 'not recorded', 'n/a', 'na', 'none')
AND memorialized_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_birds_photo_object_key
ON birds (photo_object_key)
WHERE photo_object_key IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_birds_public_profile_code
ON birds (public_profile_code)
WHERE public_profile_code IS NOT NULL;
CREATE TABLE IF NOT EXISTS pending_bird_transfers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bird_id UUID NOT NULL REFERENCES birds(id) ON DELETE CASCADE,
source_workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
destination_owner_email VARCHAR(255) NOT NULL,
requested_by_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
completed_at TIMESTAMPTZ,
completed_workspace_id INTEGER REFERENCES workspaces(id) ON DELETE SET NULL,
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE pending_bird_transfers
ADD COLUMN IF NOT EXISTS completed_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS completed_workspace_id INTEGER REFERENCES workspaces(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS last_error TEXT;
CREATE INDEX IF NOT EXISTS idx_pending_bird_transfers_destination_email
ON pending_bird_transfers (LOWER(destination_owner_email), created_at DESC)
WHERE completed_at IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_pending_bird_transfers_open_bird
ON pending_bird_transfers (bird_id)
WHERE completed_at IS NULL;
CREATE TABLE IF NOT EXISTS flock_notes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
bird_id UUID REFERENCES birds(id) ON DELETE SET NULL,
title VARCHAR(160) NOT NULL,
body TEXT NOT NULL,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_flock_notes_workspace_updated
ON flock_notes (workspace_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_flock_notes_bird_updated
ON flock_notes (bird_id, updated_at DESC)
WHERE bird_id IS NOT NULL;
CREATE TABLE IF NOT EXISTS audit_log_entries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
actor_name VARCHAR(160),
actor_email VARCHAR(255),
action VARCHAR(80) NOT NULL,
entity_type VARCHAR(80) NOT NULL,
entity_id VARCHAR(120),
entity_name VARCHAR(255),
details JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_audit_log_entries_workspace_created
ON audit_log_entries (workspace_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_log_entries_entity
ON audit_log_entries (workspace_id, entity_type, entity_id, created_at DESC);
CREATE TABLE IF NOT EXISTS weight_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bird_id UUID NOT NULL REFERENCES birds(id) ON DELETE CASCADE,
weight_grams NUMERIC(8, 2) NOT NULL CHECK (weight_grams > 0),
recorded_on DATE NOT NULL,
notes VARCHAR(280),
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (bird_id, recorded_on)
);
CREATE TABLE IF NOT EXISTS vet_visits (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bird_id UUID NOT NULL REFERENCES birds(id) ON DELETE CASCADE,
visited_on DATE NOT NULL,
clinic_name VARCHAR(160) NOT NULL,
reason VARCHAR(160) NOT NULL,
notes VARCHAR(1000),
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS medications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bird_id UUID NOT NULL REFERENCES birds(id) ON DELETE CASCADE,
name VARCHAR(160) NOT NULL,
dosage VARCHAR(160) NOT NULL,
frequency VARCHAR(160) NOT NULL,
dose_schedule JSONB NOT NULL DEFAULT '[{"key":"dose-1","label":"Dose","time":""}]'::jsonb,
route VARCHAR(80),
start_date DATE NOT NULL,
end_date DATE,
notes VARCHAR(1000),
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CHECK (end_date IS NULL OR end_date >= start_date)
);
ALTER TABLE medications
ADD COLUMN IF NOT EXISTS dose_schedule JSONB NOT NULL DEFAULT '[{"key":"dose-1","label":"Dose","time":""}]'::jsonb;
CREATE TABLE IF NOT EXISTS bird_milestone_reminder_deliveries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bird_id UUID NOT NULL REFERENCES birds(id) ON DELETE CASCADE,
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
reminder_type VARCHAR(24) NOT NULL CHECK (reminder_type IN ('hatch_day', 'gotcha_day', 'memorial_day')),
reminder_year INTEGER NOT NULL,
delivered_on DATE NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (bird_id, reminder_type, reminder_year)
);
ALTER TABLE bird_milestone_reminder_deliveries
DROP CONSTRAINT IF EXISTS bird_milestone_reminder_deliveries_reminder_type_check;
ALTER TABLE bird_milestone_reminder_deliveries
ADD CONSTRAINT bird_milestone_reminder_deliveries_reminder_type_check
CHECK (reminder_type IN ('hatch_day', 'gotcha_day', 'memorial_day'));
CREATE TABLE IF NOT EXISTS medication_administrations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
medication_id UUID NOT NULL REFERENCES medications(id) ON DELETE CASCADE,
bird_id UUID NOT NULL REFERENCES birds(id) ON DELETE CASCADE,
administered_on DATE NOT NULL,
administration_slot VARCHAR(80) NOT NULL DEFAULT 'dose-1',
status VARCHAR(20) NOT NULL CHECK (status IN ('administered', 'missed')),
notes VARCHAR(500),
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE medication_administrations
ADD COLUMN IF NOT EXISTS administration_slot VARCHAR(80) NOT NULL DEFAULT 'dose-1';
ALTER TABLE medication_administrations
DROP CONSTRAINT IF EXISTS medication_administrations_medication_id_administered_on_key;
CREATE UNIQUE INDEX IF NOT EXISTS idx_medication_administrations_unique_slot
ON medication_administrations (medication_id, administered_on, administration_slot);
CREATE INDEX IF NOT EXISTS idx_weight_records_bird_recorded_on
ON weight_records (bird_id, recorded_on DESC);
CREATE INDEX IF NOT EXISTS idx_vet_visits_bird_visited_on
ON vet_visits (bird_id, visited_on DESC);
CREATE INDEX IF NOT EXISTS idx_medications_bird_start_date
ON medications (bird_id, start_date DESC);
CREATE INDEX IF NOT EXISTS idx_bird_milestone_reminder_deliveries_workspace
ON bird_milestone_reminder_deliveries (workspace_id, delivered_on DESC);
CREATE INDEX IF NOT EXISTS idx_medication_administrations_bird_administered_on
ON medication_administrations (bird_id, administered_on DESC);
CREATE INDEX IF NOT EXISTS idx_medication_administrations_medication_date
ON medication_administrations (medication_id, administered_on DESC, created_at DESC);
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'birds'
AND column_name = 'is_female'
) THEN
UPDATE birds
SET gender = CASE
WHEN is_female IS TRUE THEN 'female'
WHEN is_female IS FALSE THEN 'male'
ELSE gender
END
WHERE gender = 'unknown';
END IF;
END $$;
`);
};