CodeGym /Cursos /SQL SELF /Llamada de procedimientos y funciones dentro de transacci...

Llamada de procedimientos y funciones dentro de transacciones

SQL SELF
Nivel 53 , Lección 1
Disponible

En los sistemas modernos de bases de datos, la lógica de negocio muchas veces se implementa del lado del servidor — usando procedimientos y funciones. Si trabajas con PostgreSQL, es clave entender la diferencia entre funciones y procedimientos (sobre todo desde que existen los procedimientos desde la versión 11+) y cómo interactúan con las transacciones.

Aquí te cuento los puntos clave sobre la mecánica de transacciones, llamadas anidadas y rollback parcial de cambios en procedimientos/funciones de PostgreSQL 17, según la documentación oficial y las limitaciones actuales.

Conceptos clave: funciones vs procedimientos

Función (CREATE FUNCTION) — siempre se ejecuta dentro de una sola transacción externa; dentro de funciones no puedes usar comandos transaccionales explícitos (BEGIN, COMMIT, ROLLBACK, SAVEPOINT).

  • Cualquier cambio se confirma o se revierte solo a nivel de la transacción externa.
  • Para hacer un "rollback parcial" dentro de funciones se usa BEGIN ... EXCEPTION ... END, pero esto no permite hacer commits dentro de la función.

Procedimiento (CREATE PROCEDURE) — apareció para poder manejar transacciones directamente en el servidor (por ejemplo, hacer commits parciales, rollback por etapas, etc).

  • En procedimientos (PL/pgSQL) puedes usar COMMIT, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT.
  • IMPORTANTE: no puedes usar ROLLBACK TO SAVEPOINT en un procedimiento PL/pgSQL (te dará un error de sintaxis).
  • Los procedimientos solo se pueden llamar con el comando SQL CALL ..., no con SELECT ni dentro de otras funciones.

¿Cómo llamar un procedimiento/función desde otro?

Las funciones llaman a otras funciones de forma "transparente" usando el nombre normalmente:

-- Ejemplo: función para calcular descuento
CREATE OR REPLACE FUNCTION calcular_descuento(order_total NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
    IF order_total >= 100 THEN
        RETURN order_total * 0.1;
    ELSE
        RETURN 0;
    END IF;
END;
$$ LANGUAGE plpgsql;

-- Función de procesamiento de pedido llama a otra función
CREATE OR REPLACE FUNCTION procesar_pedido(order_id INT, order_total NUMERIC)
RETURNS VOID AS $$
DECLARE
    descuento NUMERIC;
BEGIN
    descuento := calcular_descuento(order_total);
    RAISE NOTICE 'Descuento: %', descuento;
    INSERT INTO orders_log (order_id, order_total, descuento)
    VALUES (order_id, order_total, descuento);
END;
$$ LANGUAGE plpgsql;

¡Todo se ejecuta dentro de una sola transacción externa! Un error en cualquier función hará rollback de todos los cambios.

Llamada de procedimientos y transacciones anidadas

Los procedimientos se pueden llamar dentro de otros procedimientos usando el comando CALL ... (en PostgreSQL 17 se permite una pila de llamadas CALL proc1() -> CALL proc2()), pero las reglas de transacciones siguen igual:

  • Los comandos de transacción (COMMIT, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT) solo están disponibles en el nivel superior de los procedimientos.
  • Si un procedimiento con manejo de transacciones se llama dentro de una transacción explícita ya activa (por ejemplo, desde un cliente sin autocommit), intentar hacer COMMIT/SAVEPOINT dará error.
IMPORTANTE:

no puedes ejecutar procedimientos dentro de funciones o bloques anónimos (DO ...). Solo con el comando CALL

Ejemplo de procedimiento con manejo de transacciones

-- Procedimiento con commit por etapas (solo funciona en modo autocommit de la conexión)
CREATE PROCEDURE procesar_pedidos_lote()
LANGUAGE plpgsql
AS $$
DECLARE
    rec RECORD;
BEGIN
    FOR rec IN SELECT order_id, order_total FROM incoming_orders LOOP
        BEGIN
            -- Guardamos cada lote de datos por separado
            INSERT INTO orders (order_id, total) VALUES (rec.order_id, rec.order_total);
        EXCEPTION WHEN OTHERS THEN
            INSERT INTO order_errors(order_id, err_text) VALUES (rec.order_id, SQLERRM);
        END;
        COMMIT;
    END LOOP;
END;
$$;

-- Llamada al procedimiento
CALL procesar_pedidos_lote();

Después de cada COMMIT empieza automáticamente una nueva transacción.

Rollback parcial (comportamiento tipo savepoint) en PL/pgSQL

PL/pgSQL (tanto en funciones como en procedimientos) no soporta el comando ROLLBACK TO SAVEPOINT.

Para revertir cambios de parte del código solo se usa el bloque BEGIN ... EXCEPTION ... END:

BEGIN
    -- algunas acciones
    BEGIN
        -- operación que puede fallar
    EXCEPTION WHEN OTHERS THEN
        -- todos los cambios de este bloque se revierten
        RAISE NOTICE '¡Rollback dentro del bloque!';
    END;
END;

En procedimientos también puedes usar SAVEPOINT y RELEASE SAVEPOINT, pero no ROLLBACK TO SAVEPOINT. Su sentido es separar etapas, pero solo puedes manejarlas con manejo de excepciones.

Limitaciones y buenas prácticas

  1. Funciones — solo operaciones atómicas: todo o nada. Si algo falla — se revierte todo.
  2. Procedimientos — solo con CALL: y solo con comando SQL aparte, no desde SELECT/funciones. El manejo anidado de transacciones es posible, pero solo cumpliendo las limitaciones de PL/pgSQL.
  3. Rollback parcial — solo con EXCEPTION: es la forma recomendada y soportada oficialmente para rollback parcial (como un SAVEPOINT).
  4. Procedimientos anidados solo pueden manejar transacciones si se llaman con CALL: si no, habrá error.

Preguntas sobre la interacción de lógica y transacciones

¿Puedo hacer una "transacción anidada" dentro de una función?

No. Todo se ejecuta en una sola transacción. Para rollback parcial — solo bloques EXCEPTION.

¿Puedo hacer COMMIT/ROLLBACK dentro de una función o bloque anónimo?

No, eso es un error de sintaxis. Usa procedimientos.

¿Se puede llamar un procedimiento desde una función?

No, solo con el comando CALL. Desde función/SELECT — no se puede.

¿Se puede hacer ROLLBACK TO SAVEPOINT en un procedimiento?

¡No! En PL/pgSQL está prohibido. Usa bloques EXCEPTION.

2
Tarea
SQL SELF, nivel 53, lección 1
Bloqueada
Creación y llamada de procedimientos para calcular y registrar logs
Creación y llamada de procedimientos para calcular y registrar logs
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION