Pular para conteúdo

Triggers

Introdução

De acordo com Sistemas de Banco de Dados de Elmasri e Navathe (2011), regras ativas podem ser disparadas automaticamente por eventos que ocorrem, como atualizações no banco de dados ou certos momentos sendo alcançados, e podem iniciar ações especificadas na declaração da regra, desde que determinadas condições sejam atendidas.

Diante da necessidade de garantir automação, integridade e consistência nos sistemas de gerenciamento de banco de dados, os triggers se tornaram um mecanismo essencial. Eles permitem a execução de ações automáticas em resposta a eventos específicos, reduzindo a necessidade de intervenção manual e garantindo a aplicação de regras de negócio de maneira eficiente.

Objetivo

Este documento apresenta a implementação e utilização de triggers no sistema, detalhando suas funcionalidades, benefícios e aplicabilidades dentro do contexto do gerenciamento de dados.

Triggers

Instanciar Parte do Corpo de Boss

O trigger trigger_gerar_partes_corpo_boss e sua função associada gerar_partes_corpo_boss são responsáveis pela criação automática das partes do corpo de um boss assim que ele é inserido na tabela boss.

CREATE OR REPLACE FUNCTION gerar_partes_corpo_boss()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO public.parte_corpo_boss (id_boss, parte_corpo, defesa_fisica, defesa_magica, chance_acerto, chance_acerto_critico)
    SELECT 
        NEW.id_boss,                     
        pc.id_parte_corpo,               
        pc.defesa_fisica * 2,           
        pc.defesa_magica * 2,           
        pc.chance_acerto * 2,            
        pc.chance_acerto_critico * 2     
    FROM public.parte_corpo pc;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


CREATE TRIGGER trigger_gerar_partes_corpo_boss
AFTER INSERT ON public.boss
FOR EACH ROW
EXECUTE FUNCTION gerar_partes_corpo_boss();

Instanciar Parte do Corpo de Cavaleiro

O trigger trigger_gerar_partes_corpo_cavaleiro e sua função associada gerar_partes_corpo_cavaleiro são responsáveis por criar automaticamente as partes do corpo de um cavaleiro assim que ele é inserido na tabela instancia_cavaleiro.

CREATE OR REPLACE FUNCTION gerar_partes_corpo_cavaleiro()
RETURNS TRIGGER AS $$
BEGIN

    INSERT INTO public.parte_corpo_cavaleiro (
        id_cavaleiro, 
        parte_corpo,  
        id_player, 
        defesa_fisica, 
        defesa_magica, 
        chance_acerto, 
        chance_acerto_critico
    )
    SELECT 
        NEW.id_cavaleiro,        
        pc.id_parte_corpo,        
        NEW.id_player,            
        pc.defesa_fisica,          
        pc.defesa_magica,         
        pc.chance_acerto,          
        pc.chance_acerto_critico   
    FROM public.parte_corpo pc;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_gerar_partes_corpo_cavaleiro
AFTER INSERT ON public.instancia_cavaleiro
FOR EACH ROW
EXECUTE FUNCTION gerar_partes_corpo_cavaleiro();

Instanciar Parte do Corpo de Player

O trigger trigger_gerar_partes_corpo_player e sua função associada gerar_partes_corpo_player têm a função de criar automaticamente as partes do corpo do jogador assim que ele é inserido na tabela player.

CREATE OR REPLACE FUNCTION gerar_partes_corpo_player()
RETURNS TRIGGER AS $$
BEGIN

    INSERT INTO public.parte_corpo_player (
        id_player, 
        parte_corpo, 
        defesa_fisica, 
        defesa_magica, 
        chance_acerto, 
        chance_acerto_critico
    )
    SELECT 
        NEW.id_player,   
        pc.id_parte_corpo, 
        pc.defesa_fisica,          
        pc.defesa_magica,         
        pc.chance_acerto,          
        pc.chance_acerto_critico 
    FROM public.parte_corpo pc;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_gerar_partes_corpo_player
AFTER INSERT ON public.player
FOR EACH ROW
EXECUTE FUNCTION gerar_partes_corpo_player();

Instanciar Parte do Corpo de Inimigo

O trigger trigger_gerar_partes_corpo_inimigo e sua função associada gerar_partes_corpo_inimigo são responsáveis por criar automaticamente as partes do corpo de um inimigo assim que ele é inserido na tabela instancia_inimigo.

CREATE OR REPLACE FUNCTION gerar_partes_corpo_inimigo()
RETURNS TRIGGER AS $$
BEGIN

    INSERT INTO public.parte_corpo_inimigo (
        id_instancia, 
        id_inimigo,
        parte_corpo, 
        defesa_fisica, 
        defesa_magica, 
        chance_acerto, 
        chance_acerto_critico
    )
    SELECT 
        NEW.id_instancia,      
        NEW.id_inimigo,        
        pc.id_parte_corpo,     
        pc.defesa_fisica,      
        pc.defesa_magica,     
        pc.chance_acerto,      
        pc.chance_acerto_critico  
    FROM public.parte_corpo pc;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


CREATE TRIGGER trigger_gerar_partes_corpo_inimigo
AFTER INSERT ON public.instancia_inimigo
FOR EACH ROW
EXECUTE FUNCTION gerar_partes_corpo_inimigo();

Integridade de Personagem

O trigger de integridade trigger_inserir_tipo_personagem e sua função associada inserir_tipo_personagem são responsáveis por registrar automaticamente o tipo de personagem ao inserir um novo player, cavaleiro, inimigo ou boss no banco de dados.

CREATE OR REPLACE FUNCTION inserir_tipo_personagem()
RETURNS TRIGGER AS $$
DECLARE
    v_id_personagem INTEGER;
BEGIN
    IF TG_TABLE_NAME = 'player' THEN
        INSERT INTO Tipo_Personagem (tipo_personagem)
        VALUES ('p')
        RETURNING id_personagem INTO v_id_personagem;
        NEW.id_player := v_id_personagem;

    ELSIF TG_TABLE_NAME = 'cavaleiro' THEN
        INSERT INTO Tipo_Personagem (tipo_personagem)
        VALUES ('c')
        RETURNING id_personagem INTO v_id_personagem;
        NEW.id_cavaleiro := v_id_personagem;

    ELSIF TG_TABLE_NAME = 'inimigo' THEN
        INSERT INTO Tipo_Personagem (tipo_personagem)
        VALUES ('i')
        RETURNING id_personagem INTO v_id_personagem;
        NEW.id_inimigo := v_id_personagem;

    ELSIF TG_TABLE_NAME = 'boss' THEN
        INSERT INTO Tipo_Personagem (tipo_personagem)
        VALUES ('b')
        RETURNING id_personagem INTO v_id_personagem;
        NEW.id_boss:= v_id_personagem;
    ELSE
        RAISE EXCEPTION 'Tabela desconhecida para a trigger inserir_tipo_personagem';
    END IF;


    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


CREATE TRIGGER trigger_inserir_tipo_personagem_personagem
BEFORE INSERT ON Player
FOR EACH ROW
EXECUTE FUNCTION inserir_tipo_personagem();

CREATE TRIGGER trigger_inserir_tipo_personagem_cavaleiro
BEFORE INSERT ON Cavaleiro
FOR EACH ROW
EXECUTE FUNCTION inserir_tipo_personagem();

CREATE TRIGGER trigger_inserir_tipo_personagem_inimigo
BEFORE INSERT ON Inimigo
FOR EACH ROW
EXECUTE FUNCTION inserir_tipo_personagem();

CREATE TRIGGER trigger_inserir_tipo_personagem_boss
BEFORE INSERT ON Boss
FOR EACH ROW
EXECUTE FUNCTION inserir_tipo_personagem();

Integridade de NPC

O trigger de integridade trigger_inserir_tipo_npc e sua função associada inserir_tipo_npc têm a função de registrar automaticamente o tipo de NPC ao inserir um novo ferreiro, mercador ou NPC de missão no banco de dados.

CREATE OR REPLACE FUNCTION inserir_tipo_npc()
RETURNS TRIGGER AS $$
DECLARE
    v_id_npc INTEGER;
    v_tipo_npc enum_tipo_npc;
BEGIN
    -- Determinar o tipo do NPC com base na tabela de origem
    IF TG_TABLE_NAME = 'ferreiro' THEN
        v_tipo_npc := 'f';
    ELSIF TG_TABLE_NAME = 'mercador' THEN
        v_tipo_npc := 'm';
    ELSIF TG_TABLE_NAME = 'quest' THEN
        v_tipo_npc := 'q';
    ELSE
        RAISE EXCEPTION 'Tipo de NPC desconhecido';
    END IF;

    -- Inserir na tabela Tipo_NPC e capturar o id_npc gerado
    INSERT INTO Tipo_NPC (tipo_npc)
    VALUES (v_tipo_npc)
    RETURNING id_npc INTO v_id_npc;

    -- Atribuir o ID gerado ao campo id_npc da tabela específica
    NEW.id_npc := v_id_npc;

    -- Retornar a nova linha para completar a inserção na tabela de destino
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Criar triggers para garantir a integridade referencial em cada tabela
CREATE TRIGGER trigger_inserir_tipo_npc_ferreiro
BEFORE INSERT ON Ferreiro
FOR EACH ROW
EXECUTE FUNCTION inserir_tipo_npc();

CREATE TRIGGER trigger_inserir_tipo_npc_mercador
BEFORE INSERT ON Mercador
FOR EACH ROW
EXECUTE FUNCTION inserir_tipo_npc();

CREATE TRIGGER trigger_inserir_tipo_npc_quest
BEFORE INSERT ON Quest
FOR EACH ROW
EXECUTE FUNCTION inserir_tipo_npc();

Integridade de Itens

O trigger de interidade before_insert_generic e sua função associada são responsáveis por classificar automaticamente os itens inseridos no banco de dados, garantindo que cada um seja corretamente categorizado como craftável ou não-craftável antes da inserção.

CREATE OR REPLACE FUNCTION before_insert_generic()
RETURNS TRIGGER AS $$
DECLARE
    new_id_item INTEGER;
    table_name TEXT := TG_TABLE_NAME; -- Obtém o nome da tabela
BEGIN

    IF table_name = 'livro' THEN
        INSERT INTO tipo_item (tipo_item)
        VALUES ('nc')  
        RETURNING id_item INTO new_id_item;

        INSERT INTO Nao_Craftavel (id_nao_craftavel, tipo_nao_craftavel)
        VALUES (new_id_item, 'l');
        NEW.id_item := new_id_item;

    ELSIF table_name = 'item_missao' THEN
        INSERT INTO tipo_item (tipo_item)
        VALUES ('nc')  
        RETURNING id_item INTO new_id_item;

        INSERT INTO Nao_Craftavel (id_nao_craftavel, tipo_nao_craftavel)
        VALUES (new_id_item, 'i');
        NEW.id_item := new_id_item;

    ELSIF table_name = 'consumivel' THEN
        INSERT INTO tipo_item (tipo_item)
        VALUES ('nc')  
        RETURNING id_item INTO new_id_item;

        INSERT INTO Nao_Craftavel (id_nao_craftavel, tipo_nao_craftavel)
        VALUES (new_id_item, 'c');
        NEW.id_item := new_id_item;

    ELSIF table_name = 'armadura' THEN
        INSERT INTO tipo_item (tipo_item)
        VALUES ('c')  
        RETURNING id_item INTO new_id_item;

        INSERT INTO Craftavel (id_craftavel, tipo_craftavel)
        VALUES (new_id_item, 'a');
        NEW.id_armadura := new_id_item;

    ELSIF table_name = 'material' THEN
        INSERT INTO tipo_item (tipo_item)
        VALUES ('c')  
        RETURNING id_item INTO new_id_item;

        INSERT INTO Craftavel (id_craftavel, tipo_craftavel)
        VALUES (new_id_item, 'm');
        NEW.id_material := new_id_item;

    END IF;



    RETURN NEW;
END;
$$ LANGUAGE plpgsql;



CREATE TRIGGER before_insert_consumivel_trigger
BEFORE INSERT ON consumivel
FOR EACH ROW
EXECUTE FUNCTION before_insert_generic();

CREATE TRIGGER before_insert_livro_trigger
BEFORE INSERT ON livro
FOR EACH ROW
EXECUTE FUNCTION before_insert_generic();

CREATE TRIGGER before_insert_item_missao_trigger
BEFORE INSERT ON item_missao
FOR EACH ROW
EXECUTE FUNCTION before_insert_generic();

CREATE TRIGGER before_insert_material_trigger
BEFORE INSERT ON material
FOR EACH ROW
EXECUTE FUNCTION before_insert_generic();

CREATE TRIGGER before_insert_armadura_trigger
BEFORE INSERT ON armadura
FOR EACH ROW
EXECUTE FUNCTION before_insert_generic();

Criar Party

O trigger de trigger_insert_party e sua função associada insert_party_trigger_function são responsáveis por criar automaticamente um registro na tabela Party sempre que um novo jogador (Player) for inserido no banco de dados.

CREATE OR REPLACE FUNCTION insert_party_trigger_function()
RETURNS TRIGGER AS $$
BEGIN

    INSERT INTO Party (id_player, id_sala) VALUES (NEW.id_player, listar_sala_segura());
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_insert_party
AFTER INSERT ON Player
FOR EACH ROW
EXECUTE FUNCTION insert_party_trigger_function();

Instanciar Player Missao

O trigger instanciar_player_missao e sua função associada instanciar_player_missao_procedure são responsáveis por associar automaticamente todas as missões disponíveis a um novo jogador assim que ele é inserido no banco de dados.

CREATE OR REPLACE FUNCTION instanciar_player_missao_procedure()
RETURNS TRIGGER AS $$
DECLARE
    missao RECORD;
BEGIN

    FOR missao IN SELECT id_missao FROM Missao LOOP
        INSERT INTO Player_missao (id_player, id_missao, status_missao)
        VALUES (NEW.id_player, missao.id_missao, 'ni');
    END LOOP;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


CREATE TRIGGER instanciar_player_missao
AFTER INSERT ON Player
FOR EACH ROW
EXECUTE FUNCTION instanciar_player_missao_procedure();

Instanciar Player Missao 2

O trigger instancia_player_missao_new_missoes e sua função associada instanciar_player_missao_new_missao são responsáveis por garantir que toda nova missão adicionada ao banco de dados seja automaticamente associada a todos os jogadores existentes.

CREATE OR REPLACE FUNCTION instanciar_player_missao_new_missao()
RETURNS TRIGGER AS $$
DECLARE
    player RECORD;
BEGIN
    FOR player IN SELECT id_player FROM Player LOOP
        INSERT INTO Player_missao (id_player, id_missao, status_missao)
        VALUES (player.id_player, NEW.id_missao, 'ni');
    END LOOP;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER instancia_player_missao_new_missoes
AFTER INSERT ON Missao
FOR EACH ROW
EXECUTE FUNCTION instanciar_player_missao_new_missao();

Criar Inventário

O trigger after_player_insert e sua função associada after_player_insert_function têm a função de criar automaticamente um inventário inicial para cada novo jogador que é criado no jogo.

CREATE OR REPLACE FUNCTION after_player_insert_function()
RETURNS TRIGGER AS $$
BEGIN

    INSERT INTO inventario (id_player, dinheiro, alma_armadura)
    VALUES (NEW.id_player, 200, 200);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER after_player_insert
AFTER INSERT ON player
FOR EACH ROW
EXECUTE FUNCTION after_player_insert_function();

Check dinheiro suficiente

O trigger trg_verificar_dinheiro e sua função associada verificar_dinheiro garantem que um jogador não possa ter um saldo negativo de dinheiro no inventário.

CREATE OR REPLACE FUNCTION verificar_dinheiro()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
    IF NEW.dinheiro < 0 THEN
        RAISE EXCEPTION 'O jogador não pode ter dinheiro negativo.';
    END IF;
    RETURN NEW;
END $$;

CREATE TRIGGER trg_verificar_dinheiro
BEFORE UPDATE ON inventario
FOR EACH ROW
EXECUTE FUNCTION verificar_dinheiro();

Subir de nível

Os triggers trigger_subir_de_nivel_player e trigger_subir_de_nivel_instancia_cavaleiro, juntamente com a função subir_de_nivel, são responsáveis por gerenciar automaticamente a progressão de nível tanto do jogador (player) quanto dos cavaleiros (instancia_cavaleiro) quando acumulam experiência suficiente.

CREATE OR REPLACE FUNCTION subir_de_nivel()
RETURNS TRIGGER AS $$
DECLARE
    xp_prox_nivel INTEGER;
BEGIN
    LOOP
        SELECT xp_necessaria INTO xp_prox_nivel
        FROM xp_necessaria 
        WHERE nivel = NEW.nivel + 1;

        IF xp_prox_nivel IS NULL OR NEW.xp_atual < xp_prox_nivel THEN
            EXIT;
        END IF;

        NEW.xp_atual := NEW.xp_atual - xp_prox_nivel;
        NEW.nivel := NEW.nivel + 1;
    END LOOP;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_subir_de_nivel_player
BEFORE UPDATE OF xp_atual ON player
FOR EACH ROW
WHEN (OLD.xp_atual <> NEW.xp_atual) 
EXECUTE FUNCTION subir_de_nivel();

CREATE TRIGGER trigger_subir_de_nivel_instancia_cavaleiro
BEFORE UPDATE OF xp_atual ON instancia_cavaleiro
FOR EACH ROW
WHEN (OLD.xp_atual <> NEW.xp_atual) 
EXECUTE FUNCTION subir_de_nivel();

Missão Concluida

O trigger trigger_atualizar_status_missao e sua função associada atualizar_status_missao_ao_drop_item são responsáveis por atualizar automaticamente o status de missões do jogador quando ele conclui uma missão adquirindo o item da missão.

CREATE OR REPLACE FUNCTION atualizar_status_missao_ao_drop_item()
RETURNS TRIGGER AS $$
DECLARE
    tipo_do_item TEXT;
    missao_id INTEGER;
BEGIN
    -- Verificar o tipo do item usando a tabela Tipo_item
    SELECT nc.tipo_nao_craftavel INTO tipo_do_item 
    FROM tipo_item ti
    JOIN nao_craftavel nc on  ti.id_item = nc.id_nao_craftavel
    WHERE id_item = NEW.id_item;

    -- Verificar se o tipo do item é "i" (item missao)
    IF tipo_do_item = 'i' THEN
        -- Verifica se o item é necessário para alguma missão
        SELECT id_missao INTO missao_id
        FROM missao
        WHERE item_necessario = NEW.id_item;

        -- Se o item for necessário para uma missão, atualiza o status da missão do jogador
        IF missao_id IS NOT NULL THEN
            UPDATE player_missao
            SET status_missao = 'c'
            WHERE id_player = NEW.id_inventario
            AND id_missao = missao_id
            AND status_missao = 'i'; -- Apenas atualiza se não estiver concluída
        END IF;
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_atualizar_status_missao
AFTER INSERT ON item_armazenado
FOR EACH ROW
EXECUTE FUNCTION atualizar_status_missao_ao_drop_item();

Remove Item do inventario

O trigger trg_remover_item_se_zero e sua função associada remover_item_se_zero são responsáveis por remover automaticamente um item do inventário do jogador quando sua quantidade chega a zero.

CREATE OR REPLACE FUNCTION remover_item_se_zero()
RETURNS TRIGGER AS $$
BEGIN

    IF NEW.quantidade = 0 THEN
        DELETE FROM item_armazenado
        WHERE id_inventario = NEW.id_inventario
        AND id_item = NEW.id_item;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_remover_item_se_zero
AFTER UPDATE OF quantidade
ON item_armazenado
FOR EACH ROW
WHEN (NEW.quantidade = 0) 
EXECUTE FUNCTION remover_item_se_zero();

Desbloquear Cavaleiro

O trigger trigger_liberar_cavaleiro e sua função associada instanciar_cavaleiro são responsáveis por desbloquear e instanciar automaticamente um cavaleiro para um jogador quando ele conclui uma missão específica que concede um cavaleiro como recompensa.

CREATE OR REPLACE FUNCTION instanciar_cavaleiro()
RETURNS TRIGGER AS $$
DECLARE
    p_id_cavaleiro INTEGER;
    p_id_player INTEGER := NEW.id_player;
    p_id_party INTEGER := NEW.ID_player; -- Defina a lógica correta para a party, se necessário
    p_id_classe INTEGER;
    p_id_elemento INTEGER;
    p_nome TEXT;
    p_nivel INTEGER;
    p_hp_max INTEGER;
    p_magia_max INTEGER;
    p_velocidade INTEGER;
    p_ataque_fisico INTEGER;
    p_ataque_magico INTEGER;
BEGIN
    -- Obtém o cavaleiro desbloqueado pela missão concluída
    SELECT m.id_cavaleiro_desbloqueado
    INTO p_id_cavaleiro
    FROM missao m
    WHERE m.id_missao = NEW.id_missao;

    -- Se não houver cavaleiro a ser desbloqueado, não faz nada
    IF p_id_cavaleiro IS NULL THEN
        RETURN NEW;
    END IF;

    -- Obtém as informações do cavaleiro
    SELECT c.id_classe, c.id_elemento, c.nome, c.nivel, c.hp_max, c.magia_max, 
        c.velocidade, c.ataque_fisico, c.ataque_magico
    INTO p_id_classe, p_id_elemento, p_nome, p_nivel, p_hp_max, p_magia_max, 
        p_velocidade, p_ataque_fisico, p_ataque_magico
    FROM cavaleiro c
    WHERE c.id_cavaleiro = p_id_cavaleiro;

    -- Verifica se o jogador já tem este cavaleiro para evitar duplicatas
    IF NOT EXISTS (
        SELECT 1 FROM instancia_cavaleiro 
        WHERE id_player = p_id_player AND id_cavaleiro = p_id_cavaleiro
    ) THEN
        -- Insere o cavaleiro na instância do jogador
        INSERT INTO instancia_cavaleiro (
            id_cavaleiro, id_player, nivel, tipo_armadura, xp_atual, 
            hp_max, magia_max, hp_atual, magia_atual, velocidade, 
            ataque_fisico, ataque_magico
        ) VALUES (
            p_id_cavaleiro, p_id_player, p_nivel, 0, 0, 
            p_hp_max, p_magia_max, p_hp_max, p_magia_max, p_velocidade, 
            p_ataque_fisico, p_ataque_magico
        );
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_liberar_cavaleiro
AFTER UPDATE ON player_missao
FOR EACH ROW
WHEN (NEW.status_missao = 'c' AND old.status_missao != 'c')
EXECUTE FUNCTION instanciar_cavaleiro();

Mover de Sala ao Morrer

O trigger trigger_player_morreu e sua função associada mover_sala_segura_pos_morte são responsáveis por mover automaticamente o jogador para uma sala segura ao ser derrotado, reduzindo seu dinheiro como penalidade e restaurando parcialmente sua vida.

CREATE OR REPLACE FUNCTION mover_sala_segura_pos_morte()
RETURNS TRIGGER AS $$
BEGIN

    UPDATE inventario
    SET dinheiro = dinheiro / 2
    WHERE inventario.id_player = OLD.id_player;

    RAISE NOTICE '% foi derrotado, Saori Kido o resgata mas com um custo...', OLD.nome;


    UPDATE party
    SET id_sala = (SELECT id_sala FROM public.sala_segura LIMIT 1)  -- Usar parênteses no SELECT
    WHERE party.id_player = NEW.id_player;

    NEW.hp_atual := NEW.hp_max / 2;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


CREATE TRIGGER trigger_player_morreu
BEFORE UPDATE ON player
FOR EACH ROW
WHEN (NEW.hp_atual <= 0) 
EXECUTE FUNCTION mover_sala_segura_pos_morte();

Verificar Slot Único

O trigger impedir_slots_repetidos e sua função associada verificar_slots_unicos garantem que um jogador não possa atribuir duas habilidades ao mesmo slot na tabela Habilidade_Player.

CREATE OR REPLACE FUNCTION verificar_slots_unicos() 
RETURNS TRIGGER AS $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM Habilidade_Player
        WHERE id_player = NEW.id_player 
        AND slot = NEW.slot
    ) THEN
        RAISE EXCEPTION '❌ Esse slot já está ocupado! Escolha outro.';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER impedir_slots_repetidos
BEFORE INSERT OR UPDATE ON Habilidade_Player
FOR EACH ROW
EXECUTE FUNCTION verificar_slots_unicos();

Remover cavaleiro da party

O trigger trigger_remover_cavaleiro_party e sua função associada remover_cavaleiro_da_party são responsáveis por remover automaticamente um cavaleiro da party do jogador quando seu HP chega a 0.

CREATE OR REPLACE FUNCTION remover_cavaleiro_da_party()
RETURNS TRIGGER AS $$
BEGIN

    IF NEW.hp_atual <= 0 AND OLD.hp_atual > 0 AND OLD.id_party IS NOT NULL THEN

        UPDATE instancia_cavaleiro 
        SET id_party = NULL
        WHERE id_cavaleiro = NEW.id_cavaleiro AND id_player = NEW.id_player;


        RAISE NOTICE 'O cavaleiro % foi removido da party do player % porque seu HP chegou a 0.', NEW.id_cavaleiro, NEW.id_player;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_remover_cavaleiro_party
BEFORE UPDATE OF hp_atual
ON public.instancia_cavaleiro
FOR EACH ROW
EXECUTE FUNCTION remover_cavaleiro_da_party();

Remover livro apos aprender habilidade

Os triggers trigger_reduzir_item_habilidade_player e trigger_reduzir_item_habilidade_cavaleiro, juntamente com a função reduzir_item_armazenado, são responsáveis por remover automaticamente um livro do inventário do jogador quando uma habilidade é aprendida.

CREATE OR REPLACE FUNCTION reduzir_item_armazenado()
RETURNS TRIGGER AS $$
DECLARE
    id_livro INT;
    player_id INT;
BEGIN
    -- Obtém o ID do livro relacionado à habilidade aprendida
    SELECT id_item INTO id_livro 
    FROM livro 
    WHERE id_habilidade = NEW.id_habilidade;

    -- Obtém o ID do player (caso seja um cavaleiro, pegamos do cavaleiro correspondente)
    IF TG_TABLE_NAME = 'habilidade_player' THEN
        player_id := NEW.id_player;
    ELSIF TG_TABLE_NAME = 'habilidade_cavaleiro' THEN
        SELECT id_player INTO player_id
        FROM instancia_cavaleiro
        WHERE id_cavaleiro = NEW.id_cavaleiro;
    END IF;

    -- Atualiza a quantidade do item no inventário do jogador
    UPDATE item_armazenado
    SET quantidade = quantidade - 1
    WHERE id_inventario = player_id
    AND id_item = id_livro;

    -- Remove o item do inventário se a quantidade for 0
    DELETE FROM item_armazenado
    WHERE id_inventario= player_id
    AND id_item = id_livro
    AND quantidade <= 0;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;



CREATE TRIGGER trigger_reduzir_item_habilidade_player
AFTER INSERT ON habilidade_player
FOR EACH ROW
EXECUTE FUNCTION reduzir_item_armazenado();


CREATE TRIGGER trigger_reduzir_item_habilidade_cavaleiro
AFTER INSERT ON habilidade_cavaleiro
FOR EACH ROW
EXECUTE FUNCTION reduzir_item_armazenado();

Triggers não utilizados

Abaixo se encontram triggers que não foram utilizadas quanto ao controle de integralidade pois faltou refatoramento:

    CREATE OR REPLACE FUNCTION enforce_tipo_item_exclusivo()
    RETURNS TRIGGER AS $$
    DECLARE
        v_id_item INT;
        total_subclasses INT;
    BEGIN
        CASE TG_TABLE_NAME
            WHEN 'material' THEN v_id_item := NEW.id_material;
            WHEN 'armadura' THEN v_id_item := NEW.id_armadura;
            WHEN 'item_missao' THEN v_id_item := NEW.id_item;
            WHEN 'consumivel' THEN v_id_item := NEW.id_item;
            WHEN 'livro' THEN v_id_item := NEW.id_item;
            ELSE 
                RAISE EXCEPTION 'Trigger chamada em uma tabela desconhecida!';
        END CASE;

        SELECT COUNT(*) INTO total_subclasses FROM (
            SELECT id_material FROM material WHERE id_material = v_id_item
            UNION ALL
            SELECT id_armadura FROM armadura WHERE id_armadura = v_id_item
            UNION ALL
            SELECT id_item FROM item_missao WHERE id_item = v_id_item
            UNION ALL
            SELECT id_item FROM consumivel WHERE id_item = v_id_item
            UNION ALL
            SELECT id_item FROM livro WHERE id_item = v_id_item
        ) AS sub_tabelas;

        IF total_subclasses > 1 THEN
            RAISE EXCEPTION 'O item deve pertencer a apenas uma subclasse!';
        END IF;

        RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;

    CREATE TRIGGER check_tipo_item_exclusivo_material
    BEFORE INSERT OR UPDATE ON material
    FOR EACH ROW EXECUTE FUNCTION enforce_tipo_item_exclusivo();

    CREATE TRIGGER check_tipo_item_exclusivo_armadura
    BEFORE INSERT OR UPDATE ON armadura
    FOR EACH ROW EXECUTE FUNCTION enforce_tipo_item_exclusivo();

    CREATE TRIGGER check_tipo_item_exclusivo_item_missao
    BEFORE INSERT OR UPDATE ON item_missao
    FOR EACH ROW EXECUTE FUNCTION enforce_tipo_item_exclusivo();

    CREATE TRIGGER check_tipo_item_exclusivo_consumivel
    BEFORE INSERT OR UPDATE ON consumivel
    FOR EACH ROW EXECUTE FUNCTION enforce_tipo_item_exclusivo();

    CREATE TRIGGER check_tipo_item_exclusivo_livro
    BEFORE INSERT OR UPDATE ON livro
    FOR EACH ROW EXECUTE FUNCTION enforce_tipo_item_exclusivo();

Resumo dos Triggers

Utilizados

Trigger Tabela Afetada Evento Função
trigger_gerar_partes_corpo_boss boss AFTER INSERT Cria automaticamente as partes do corpo do boss ao ser inserido.
trigger_gerar_partes_corpo_cavaleiro instancia_cavaleiro AFTER INSERT Gera automaticamente as partes do corpo dos cavaleiros ao serem instanciados.
trigger_gerar_partes_corpo_player player AFTER INSERT Cria automaticamente as partes do corpo do jogador ao ser inserido.
trigger_gerar_partes_corpo_inimigo instancia_inimigo AFTER INSERT Gera automaticamente as partes do corpo dos inimigos ao serem instanciados.
trigger_inserir_tipo_personagem_player player BEFORE INSERT Registra automaticamente o tipo de personagem ao inserir um novo jogador.
trigger_inserir_tipo_personagem_cavaleiro cavaleiro BEFORE INSERT Registra automaticamente o tipo de personagem ao inserir um novo cavaleiro.
trigger_inserir_tipo_personagem_inimigo inimigo BEFORE INSERT Registra automaticamente o tipo de personagem ao inserir um novo inimigo.
trigger_inserir_tipo_personagem_boss boss BEFORE INSERT Registra automaticamente o tipo de personagem ao inserir um novo boss.
trigger_inserir_tipo_npc_ferreiro ferreiro BEFORE INSERT Registra automaticamente o tipo do NPC ao inserir um ferreiro.
trigger_inserir_tipo_npc_mercador mercador BEFORE INSERT Registra automaticamente o tipo do NPC ao inserir um mercador.
trigger_inserir_tipo_npc_quest quest BEFORE INSERT Registra automaticamente o tipo do NPC ao inserir um NPC de missão.
trigger_before_insert_consumivel consumivel BEFORE INSERT Classifica automaticamente o item como não-craftável antes da inserção.
trigger_before_insert_livro livro BEFORE INSERT Classifica automaticamente o item como não-craftável antes da inserção.
trigger_before_insert_item_missao item_missao BEFORE INSERT Classifica automaticamente o item como não-craftável antes da inserção.
trigger_before_insert_material material BEFORE INSERT Classifica automaticamente o item como craftável antes da inserção.
trigger_before_insert_armadura armadura BEFORE INSERT Classifica automaticamente o item como craftável antes da inserção.
trigger_insert_party player AFTER INSERT Cria automaticamente uma party para o jogador ao ser inserido.
trigger_instanciar_player_missao player AFTER INSERT Associa todas as missões ao jogador quando ele é criado.
trigger_instanciar_player_missao_new_missao missao AFTER INSERT Associa uma nova missão a todos os jogadores existentes.
trigger_after_player_insert player AFTER INSERT Cria automaticamente um inventário inicial para o jogador.
trigger_verificar_dinheiro inventario BEFORE UPDATE Impede que o jogador tenha dinheiro negativo.
trigger_subir_de_nivel_player player BEFORE UPDATE Gerencia a progressão de nível do jogador com base no XP acumulado.
trigger_subir_de_nivel_instancia_cavaleiro instancia_cavaleiro BEFORE UPDATE Gerencia a progressão de nível dos cavaleiros com base no XP acumulado.
trigger_atualizar_status_missao item_armazenado AFTER INSERT Atualiza automaticamente o status da missão ao coletar um item necessário.
trigger_remover_item_se_zero item_armazenado AFTER UPDATE Remove um item do inventário se sua quantidade chegar a zero.
trigger_liberar_cavaleiro player_missao AFTER UPDATE Adiciona um cavaleiro ao jogador ao completar uma missão específica.
trigger_player_morreu player BEFORE UPDATE Move o jogador para uma sala segura e reduz seu dinheiro ao morrer.
impedir_slots_repetidos habilidade_player BEFORE INSERT/UPDATE Impede que o jogador atribua duas habilidades ao mesmo slot.
trigger_remover_cavaleiro_party instancia_cavaleiro BEFORE UPDATE Remove um cavaleiro da party se seu HP chegar a zero.
trigger_reduzir_item_habilidade_player habilidade_player AFTER INSERT Reduz a quantidade do livro de habilidade no inventário ao aprender uma nova.
trigger_reduzir_item_habilidade_cavaleiro habilidade_cavaleiro AFTER INSERT Reduz a quantidade do livro de habilidade ao ensinar uma a um cavaleiro.

Triggers não utilizados

Trigger Tabela Afetada Evento Função
check_tipo_item_exclusivo_material material BEFORE INSERT/UPDATE Impede que um item pertença a mais de uma categoria de tipo de item.
check_tipo_item_exclusivo_armadura armadura BEFORE INSERT/UPDATE Impede que um item pertença a mais de uma categoria de tipo de item.
check_tipo_item_exclusivo_item_missao item_missao BEFORE INSERT/UPDATE Impede que um item pertença a mais de uma categoria de tipo de item.
check_tipo_item_exclusivo_consumivel consumivel BEFORE INSERT/UPDATE Impede que um item pertença a mais de uma categoria de tipo de item.
check_tipo_item_exclusivo_livro livro BEFORE INSERT/UPDATE Impede que um item pertença a mais de uma categoria de tipo de item.

Referência Bibliográfica

[1] ELMASRI, Ramez; NAVATHE, Shamkant B. Sistemas de banco de dados. Tradução: Daniel Vieira. Revisão técnica: Enzo Seraphim; Thatyana de Faria Piola Seraphim. 6. ed. São Paulo: Pearson Addison Wesley, 2011. Capítulo 26. Modelos de dados avançados para aplicações avançada, página 626.

[2] SILBERSCHATZ, Abraham; KORTH, Henry F.; SUDARSHAN, S. Database system concepts. 6. ed. New York: McGraw-Hill, 2011. Capítulo 5 Advanced SQL, tópico 5.3 Triggers, página 180

Bibliografia

Triggers e Stored Procedure Prison Trading. Disponível em: https://sbd1.github.io/2024.1-Prison-Trading/#/Modulo-3/Triggers Acesso em 13 de fevereiro de 2025.

Triggers e Stored Procedure Ben 10. Disponível em: https://sbd1.github.io/2024.1-Ben10/modulo3/triggers/ Acesso em 13 de fevereiro de 2025.

Historico de Versões

Versão Data Modificação Autor
0.1 02/02/2025 Criação do Documento Vinícius Rufino
1.0 03/02/2025 Atualização dos Triggers Vinícius Rufino
1.1 10/02/2025 Atualização dos Triggers e Adição da Toggle List Vinícius Rufino
2.0 13/02/2025 Atualização e refatoração do documento para versão final Pedro Lucas