TRIGGERS
Introdução
Stored Procedures são blocos de código SQL armazenados no banco de dados, permitindo a execução de várias instruções em uma única unidade. Elas melhoram o desempenho ao reduzir a comunicação entre a aplicação e o banco, além de oferecerem mais segurança ao restringir o acesso direto a tabelas. Outra vantagem é a manutenção simplificada, já que a lógica pode ser centralizada no banco de dados, facilitando atualizações sem a necessidade de alterar a aplicação.
Triggers
-- PostGreSQL:
-- `incrementar_peso_acessorio`:
-- when character gets a new `acessorio`, it will increment the `peso` of the `mochila`, and if it surpasses `peso_total`, do not allow.
-- PostGreSQL:
CREATE OR REPLACE FUNCTION incrementar_peso_acessorio()
RETURNS TRIGGER AS $$
DECLARE
item_peso INT;
BEGIN
-- get item weight
SELECT COALESCE(a.peso, po.peso, pe.peso)
FROM acessorio a
LEFT JOIN pocao po ON po.id = NEW.item_id
LEFT JOIN pergaminho pe ON pe.id = NEW.item_id
INTO item_peso;
UPDATE mochila
SET peso = peso + item_peso
WHERE id = NEW.mochila_id;
IF (SELECT peso FROM mochila WHERE id = NEW.mochila_id) > (SELECT peso_total FROM mochila WHERE id = NEW.mochila_id) THEN
RAISE EXCEPTION 'Peso total da mochila excedido';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Link the trigger `incrementar_peso_acessorio` to the table `item_instancia` when a new row is created.
CREATE TRIGGER trigger_incrementar_peso_acessorio
AFTER INSERT ON item_instancia
FOR EACH ROW
EXECUTE FUNCTION incrementar_peso_acessorio();
-- PostGreSQL:
-- `decrementar_peso_acessorio`:
-- when one `item_instancia` is deleted, it will decrement the `peso` of the `mochila`.
CREATE OR REPLACE FUNCTION decrementar_peso_acessorio()
RETURNS TRIGGER AS $$
BEGIN
UPDATE mochila
SET peso = peso - OLD.peso
WHERE id = OLD.inventario_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- PostGreSQL:
-- When a quest_instancia is created, check if the character has an acessory that is in the armazenamento of the quest.
CREATE OR REPLACE FUNCTION check_acessory()
RETURNS TRIGGER AS $$
BEGIN
-- Check if the character already has the item from the quest's armazenamento
IF EXISTS (SELECT 1
FROM item_instancia
WHERE item_id IN (SELECT item_id
FROM armazenamento
WHERE quest_id = NEW.quest_id)
AND inventario_id = NEW.inventario_id) THEN
RAISE EXCEPTION 'Personagem already has the item';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- PostGreSQL
-- When the character reaches maximum xp, increase the level and total xp
CREATE OR REPLACE FUNCTION update_level()
RETURNS TRIGGER AS $$
BEGIN
WHILE NEW.xp >= NEW.xp_total LOOP
NEW.nivel := NEW.nivel + 1;
NEW.xp_total := NEW.xp_total + 100;
END LOOP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- PostGreSQL
-- Buff character of the same element in the region
CREATE OR REPLACE FUNCTION aplicar_buff()
RETURNS TRIGGER AS $$
DECLARE
regiao_elemento TIPO_ELEMENTO;
BEGIN
SELECT r.elemento INTO regiao_elemento
FROM sub_regiao sr
JOIN regiao r ON sr.regiao_id = r.id
WHERE sr.id = NEW.sub_regiao_id;
IF regiao_elemento = NEW.elemento THEN
NEW.vida_maxima := CEIL(NEW.vida_maxima * 1.2);
NEW.energia_arcana_maxima := CEIL(NEW.energia_arcana_maxima * 1.1);
ELSE
NEW.vida_maxima := OLD.vida_maxima;
NEW.energia_arcana_maxima := OLD.energia_arcana_maxima;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_aplicar_buff
BEFORE UPDATE ON personagem
FOR EACH ROW
WHEN (NEW.sub_regiao_id IS DISTINCT FROM OLD.sub_regiao_id)
EXECUTE FUNCTION aplicar_buff();
-- PostGreSQL
-- When adding transaction, create item instance in merchant storage
CREATE OR REPLACE FUNCTION criar_instancia_item()
RETURNS TRIGGER AS $$
BEGIN
IF EXISTS (SELECT 1 FROM armazenamento_mercador WHERE mercador_id = NEW.mercador_id AND armazenamento_id = NEW.item_id) THEN
UPDATE armazenamento
SET quantidade = quantidade + 1
WHERE id = NEW.item_id;
ELSE
INSERT INTO armazenamento_mercador (mercador_id, armazenamento_id)
VALUES (NEW.mercador_id, NEW.item_id);
UPDATE armazenamento
SET quantidade = 1
WHERE id = NEW.item_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_criar_instancia_item
AFTER INSERT ON transacao
FOR EACH ROW
EXECUTE FUNCTION criar_instancia_item();
CREATE OR REPLACE FUNCTION check_conclusion_quest()
RETURNS TRIGGER AS $$
DECLARE
v_inimigos_restantes INT;
v_personagem_id INT; -- Variable to store the personagem_id
BEGIN
-- Count how many enemies are still alive in the quest subregion
SELECT COUNT(*)
INTO v_inimigos_restantes
FROM inimigo_instancia
WHERE sub_regiao_id = NEW.sub_regiao_id AND vida > 0;
-- If there are no enemies remaining, mark the quest as completed
IF v_inimigos_restantes = 0 THEN
-- Mark the quest as completed
UPDATE quest_instancia
SET completed = TRUE
WHERE quest_id = (SELECT id FROM quest WHERE sub_regiao_id = NEW.sub_regiao_id);
-- Retrieve the personagem_id associated with the quest
SELECT personagem_id
INTO v_personagem_id
FROM quest_instancia
WHERE quest_id = (SELECT quest_id FROM quest WHERE sub_regiao_id = NEW.sub_regiao_id);
-- Update character coins
UPDATE personagem
SET moedas = moedas + 100, xp = xp + 2
WHERE id = v_personagem_id;
-- Give to character the quest armazenamento items
INSERT INTO item_instancia (item_id, mochila_id, usado)
SELECT a.item_id, m.id, FALSE
FROM armazenamento a
JOIN quest q ON q.armazenamento_id = a.id
JOIN quest_instancia qi ON qi.quest_id = q.id AND qi.personagem_id = v_personagem_id
JOIN inventario i ON i.personagem_id = v_personagem_id
JOIN mochila m ON m.id = i.id
WHERE q.sub_regiao_id = NEW.sub_regiao_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_check_conclusion_quest
AFTER UPDATE ON inimigo_instancia
FOR EACH ROW
WHEN (NEW.vida <= 0)
EXECUTE FUNCTION check_conclusion_quest();
-- Potion:
-- When an "instance of item" that is an "item" of type "potion" is marked as used:
-- Update character's fields multiplying by each potion's effect field multiplier.
CREATE OR REPLACE FUNCTION use_potion()
RETURNS TRIGGER AS $$
DECLARE
v_personagem_id INT;
BEGIN
-- If not potion, return.
IF NOT EXISTS (SELECT 1 FROM pocao WHERE id = NEW.item_id) THEN
RETURN NEW;
END IF;
-- Get `´personagem_id` from the `item_instancia` table.
SELECT
inventario.personagem_id INTO v_personagem_id
FROM item_instancia
JOIN mochila ON mochila.id = item_instancia.mochila_id
JOIN inventario ON inventario.id = mochila.id
WHERE item_instancia.id = NEW.id;
UPDATE personagem
SET
inteligencia = inteligencia * (SELECT inteligencia FROM efeito WHERE id IN (SELECT efeito_id FROM pocao_efeito WHERE pocao_id = NEW.item_id)),
vida = vida * (SELECT vida FROM efeito WHERE id IN (SELECT efeito_id FROM pocao_efeito WHERE pocao_id = NEW.item_id)),
vida_maxima = vida_maxima * (SELECT vida FROM efeito WHERE id IN (SELECT efeito_id FROM pocao_efeito WHERE pocao_id = NEW.item_id)),
energia_arcana_maxima = energia_arcana_maxima * (SELECT energia_arcana FROM efeito WHERE id IN (SELECT efeito_id FROM pocao_efeito WHERE pocao_id = NEW.item_id)),
energia_arcana = energia_arcana * (SELECT energia_arcana FROM efeito WHERE id IN (SELECT efeito_id FROM pocao_efeito WHERE pocao_id = NEW.item_id))
WHERE id = v_personagem_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- After updating "item_instancia" column value "usado", execute "use_potion" function.
CREATE TRIGGER trigger_use_potion
AFTER UPDATE ON item_instancia
FOR EACH ROW
WHEN (NEW.usado = TRUE)
EXECUTE FUNCTION use_potion();
Histórico de Versão
Versão | Data | Descrição | Autor |
---|---|---|---|
1.0 |
03/02/2025 | Criação | Grupo |
2.0 |
10/02/2025 | Atualização | Grupo |