PL/SQL by Example / Savepoints

Savepoints are a mechanism within a PL/SQL block that allows you to divide the transaction being executed into partial units, or milestones. You can then easily return to these milestones without losing the entire transaction.

First, let’s define a helper function that returns the salary of a specific employee.

CREATE OR REPLACE FUNCTION sf_ReturnSalary(
    p_Employee_ID NUMBER
) RETURN NUMBER
IS
  l_Salary NUMBER;
BEGIN
  SELECT salary
    INTO l_Salary
    FROM employees
    WHERE employee_id=p_Employee_ID
  ;
  RETURN l_Salary;
END;
/

And one more that updates the salary.

CREATE OR REPLACE PROCEDURE sp_SetSalary(
    p_Employee_ID NUMBER
  , p_Salary NUMBER
)
IS
BEGIN
  UPDATE employees
    SET salary=p_Salary
    WHERE (employee_id=p_Employee_ID)
  ;
END;
/

In the first step, we set the initial salary to 24000, which starts the transaction. We don’t want to lose this value, no matter what happens later in the code. Therefore, we create a recovery point and name it outer_savepoint.

The second step sets the salary again, this time to 25500.

For illustration, an anonymous block follows, which creates “its own” milestone named inner_savepoint before making changes, sets the salary to 26000, and raises an exception. This is caught, and a partial rollback of changes is performed to the state before the anonymous block began.

In the final phase, an exception is raised again, and a rollback to the savepoint occurs, which also nullifies the salary update from the second step.

DECLARE
  L_EMPLOYEE_ID CONSTANT NUMBER:=100; -- test employee
  CUSTOM_EXCEPTION EXCEPTION;
BEGIN
  sp_SetSalary(L_EMPLOYEE_ID, 24000);
  dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 24000

  SAVEPOINT outer_savepoint;
  sp_SetSalary(L_EMPLOYEE_ID, 25500);
  dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 25500

  /* START OF INNER BLOCK */
  BEGIN
    SAVEPOINT inner_savepoint;
    sp_SetSalary(L_EMPLOYEE_ID, 26000);
    dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 26000
    RAISE CUSTOM_EXCEPTION;
  EXCEPTION
    WHEN CUSTOM_EXCEPTION THEN
      ROLLBACK TO SAVEPOINT inner_savepoint;
  END;
  /* END OF INNER BLOCK */

  dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 25500
  RAISE CUSTOM_EXCEPTION;
EXCEPTION
  WHEN CUSTOM_EXCEPTION THEN
    ROLLBACK TO SAVEPOINT outer_savepoint;
    dbms_output.put_line(sf_ReturnSalary(L_EMPLOYEE_ID)); -- 24000
END;
/

Leave a reply

Your email address will not be published. Required fields are marked *