External Subprograms

If a C procedure, Java method, or JavaScript function is stored in the database, you can publish it as an external subprogram and then invoke it from PL/SQL.

To publish an external subprogram, define a stored PL/SQL subprogram with a call specification. The call specification maps the name, parameter types, and return type of the external subprogram to PL/SQL equivalents. Invoke the published external subprogram by its PL/SQL name.

For example, suppose that this Java class, Adjuster, is stored in the database:

import java.sql.*;
import oracle.jdbc.driver.*;
public class Adjuster {
  public static void raiseSalary (int empNo, float percent)
  throws SQLException {
    Connection conn = new OracleDriver().defaultConnection();
    String sql = "UPDATE employees SET salary = salary * ?
                    WHERE employee_id = ?";
    try {
      PreparedStatement pstmt = conn.prepareStatement(sql);
      pstmt.setFloat(1, (1 + percent / 100));
      pstmt.setInt(2, empNo);
      pstmt.executeUpdate();
      pstmt.close();
    } catch (SQLException e)
          {System.err.println(e.getMessage());}
    }
}

The Java class Adjuster has one method, raiseSalary, which raises the salary of a specified employee by a specified percentage. Because raiseSalary is a void method, you publish it as a PL/SQL procedure (rather than a function).

Example 9-46 publishes the stored Java method Adjuster.raiseSalary as a PL/SQL standalone procedure, mapping the Java method name Adjuster.raiseSalary to the PL/SQL procedure name raise_salary and the Java data types int and float to the PL/SQL data type NUMBER. Then the anonymous block invokes raise_salary.

Example 9-47 publishes the stored Java method java.lang.Thread.sleep as a PL/SQL standalone procedure, mapping the Java method name to the PL/SQL procedure name java_sleep and the Java data type long to the PL/SQL data type NUMBER. The PL/SQL standalone procedure sleep invokes java_sleep.

Example 9-48 implements the functionality of the Java adjuster example in JavaScript. The JavaScript function raiseSal is mapped to the PL/SQL procedure js_raise_sal, which is then invoked using an anonymous PL/SQL block.

See Also:

Example 9-46 PL/SQL Anonymous Block Invokes External Procedure

-- Publish Adjuster.raiseSalary as standalone PL/SQL procedure:

CREATE OR REPLACE PROCEDURE raise_salary (
  empid NUMBER,
  pct   NUMBER
) AS
  LANGUAGE JAVA NAME 'Adjuster.raiseSalary (int, float)';  -- call specification
/

BEGIN
  raise_salary(120, 10);  -- invoke Adjuster.raiseSalary by PL/SQL name
END;
/

Example 9-47 PL/SQL Standalone Procedure Invokes External Procedure

-- Java call specification:

CREATE PROCEDURE java_sleep (
  milli_seconds IN NUMBER
) AS LANGUAGE JAVA NAME 'java.lang.Thread.sleep(long)';
/

CREATE OR REPLACE PROCEDURE sleep (
  milli_seconds IN NUMBER
) AUTHID DEFINER IS
BEGIN
  DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.get_time());
  java_sleep (milli_seconds);
  DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.get_time());
END;
/

Example 9-48 Implement JavaScript External Procedure

CREATE OR REPLACE MLE MODULE js_adjuster LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

/**
* Give an employee a raise
* @param {number} empNo - ID of the employee to give a raise
* @param {number} percent - the raise in percent (0 - 100)
* @returns {number} the new salary
*/

export function raiseSal(empNo, percent) {
  if (empNo === undefined || percent === undefined) {
    throw "provide the employee ID and the raise percentage";
  }

  if(percent < 0 || percent > 100){
    throw new Error("raise must be greater than 0 and less than 100");
  }

  const result = session.execute(
    `UPDATE hr.employees
       SET salary = salary * (1 + (:percent / 100))
     WHERE employee_id = :empNo
     RETURNING new salary into :newSal`,
    {
      percent: {
        type: oracledb.NUMBER,
        val: percent,
        dir: oracledb.BIND_IN,
      },
      empNo: {
        type: oracledb.NUMBER,
        val: empNo,
        dir: oracledb.BIND_IN,
      },
      newSal: {
        type: oracledb.NUMBER,
        dir: oracledb.BIND_OUT,
      },
    },
  );

  //report an error in case the update did not affect any rows
  if(result.rowsAffected !== 1){
    throw new Error(`error updating the salary for employee ${empNo}`);
  }

  //outBinds contain the new salary returned by the RETURNING clause
  //the first element indicates the first new salary (there is only 1)
  return result.outBinds.newSal[0];
}
/

The following call specification publishes the JavaScript function raiseSal as a standalone PL/SQL function.

CREATE OR REPLACE FUNCTION js_raise_sal(
  p_empno NUMBER, 
  p_percent NUMBER
) RETURN NUMBER 
AS MLE MODULE js_adjuster
SIGNATURE 'raiseSal';
/

The PL/SQL procedure js_raise_sal is invoked by the following anonymous block.

SET SERVEROUTPUT ON;
DECLARE
  l_new_sal NUMBER;
  l_old_sal NUMBER;
  l_empNo NUMBER := 100;
BEGIN
  SELECT salary
  INTO l_old_sal
  FROM hr.employees
  WHERE employee_id = l_empNo;

DBMS_OUTPUT.PUT_LINE('Current salary for employee ' || l_empNo
                          || ' amounts to ' || l_old_sal);

l_new_sal := js_raise_sal(
  p_empno => l_empNo,
  p_percent => 10
);
 
DBMS_OUTPUT.PUT_LINE('New salary for employee ' || l_empNo
                          || ' increased to ' || l_new_sal);
END;
/

Result:

Current salary for employee 100 amounts to 24000
New salary for employee 100 increased to 26400