diff --git a/api/schema.sql b/api/schema.sql index 82455437..d0008678 100644 --- a/api/schema.sql +++ b/api/schema.sql @@ -91,6 +91,39 @@ CREATE TABLE IF NOT EXISTS clans ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); +ALTER TABLE characters + ADD COLUMN IF NOT EXISTS clan_id UUID REFERENCES clans(id) ON DELETE SET NULL; + +CREATE TABLE IF NOT EXISTS clan_members ( + clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, + character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, + role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('leader', 'co_leader', 'member')), + joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (clan_id, character_id), + UNIQUE (character_id) +); + +ALTER TABLE clan_members + DROP CONSTRAINT IF EXISTS clan_members_role_check; + +ALTER TABLE clan_members + ADD CONSTRAINT clan_members_role_check + CHECK (role IN ('leader', 'co_leader', 'member')); + +CREATE TABLE IF NOT EXISTS clan_requests ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, + character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, + message TEXT NOT NULL DEFAULT '', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + UNIQUE (character_id) +); + +CREATE INDEX IF NOT EXISTS idx_clans_leader_character_id ON clans(leader_character_id); +CREATE INDEX IF NOT EXISTS idx_characters_clan_id ON characters(clan_id); +CREATE INDEX IF NOT EXISTS idx_clan_members_clan_id ON clan_members(clan_id); +CREATE INDEX IF NOT EXISTS idx_clan_requests_clan_id ON clan_requests(clan_id); + ALTER TABLE clans DROP CONSTRAINT IF EXISTS clans_alignment_check; @@ -141,38 +174,7 @@ ALTER TABLE clans ADD CONSTRAINT clans_alignment_check CHECK (alignment IN ('citizen', 'criminal')); -ALTER TABLE characters - ADD COLUMN IF NOT EXISTS clan_id UUID REFERENCES clans(id) ON DELETE SET NULL; - -CREATE TABLE IF NOT EXISTS clan_members ( - clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, - character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, - role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('leader', 'co_leader', 'member')), - joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - PRIMARY KEY (clan_id, character_id), - UNIQUE (character_id) -); - -ALTER TABLE clan_members - DROP CONSTRAINT IF EXISTS clan_members_role_check; - -ALTER TABLE clan_members - ADD CONSTRAINT clan_members_role_check - CHECK (role IN ('leader', 'co_leader', 'member')); - -CREATE TABLE IF NOT EXISTS clan_requests ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, - character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, - message TEXT NOT NULL DEFAULT '', - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - UNIQUE (character_id) -); -CREATE INDEX IF NOT EXISTS idx_clans_leader_character_id ON clans(leader_character_id); -CREATE INDEX IF NOT EXISTS idx_characters_clan_id ON characters(clan_id); -CREATE INDEX IF NOT EXISTS idx_clan_members_clan_id ON clan_members(clan_id); -CREATE INDEX IF NOT EXISTS idx_clan_requests_clan_id ON clan_requests(clan_id); ALTER TABLE characters ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; diff --git a/api/src/migrate.ts b/api/src/migrate.ts index 3e9425ea..ab0b7a66 100644 --- a/api/src/migrate.ts +++ b/api/src/migrate.ts @@ -6,18 +6,28 @@ async function migrate(): Promise { const schemaPath = path.resolve(__dirname, "..", "schema.sql"); const schemaSql = fs.readFileSync(schemaPath, "utf8"); - await pool.query(schemaSql); - await pool.query(` - CREATE INDEX IF NOT EXISTS idx_market_listings_active_price_created - ON market_listings(price ASC, created_at ASC) - WHERE status = 'active' - `); - await pool.query(` - CREATE INDEX IF NOT EXISTS idx_market_listings_seller_active_created - ON market_listings(seller_character_id, created_at DESC) - WHERE status = 'active' - `); - console.log("Database schema applied successfully"); + const client = await pool.connect(); + try { + await client.query("BEGIN"); + await client.query(schemaSql); + await client.query(` + CREATE INDEX IF NOT EXISTS idx_market_listings_active_price_created + ON market_listings(price ASC, created_at ASC) + WHERE status = 'active' + `); + await client.query(` + CREATE INDEX IF NOT EXISTS idx_market_listings_seller_active_created + ON market_listings(seller_character_id, created_at DESC) + WHERE status = 'active' + `); + await client.query("COMMIT"); + console.log("Database schema applied successfully"); + } catch (error) { + await client.query("ROLLBACK"); + throw error; + } finally { + client.release(); + } } void migrate()