Tuesday, 14 January 2014

PL/SQL - Sub Programs--1

#=======================================================================
# Writer          : Lokanadham Thandlam
#=======================================================================
--Example 8–1 Declaring, Defining, AND Invoking A Simple PL/SQL PROCEDURE

DECLARE
   first_name   employees.first_name%TYPE;
   last_name    employees.last_name%TYPE;
   email        employees.email%TYPE;
   employer     VARCHAR2 (8) := 'AcmeCorp';

   -- Declare and define procedure
   PROCEDURE create_email (                       -- Subprogram heading begins
                           name1 VARCHAR2, name2 VARCHAR2, company VARCHAR2) -- Subprogram heading ends
   IS
      -- Declarative part begins
      error_message   VARCHAR2 (30) := 'Email address is too long.';
   BEGIN                                             -- Executable part begins
      email := name1 || '.' || name2 || '@' || company;
   EXCEPTION                                 -- Exception-handling part begins
      WHEN VALUE_ERROR
      THEN
         DBMS_OUTPUT.PUT_LINE (error_message);
   END create_email;
BEGIN
   first_name := 'John';
   last_name := 'Doe';
   create_email (first_name, last_name, employer);               -- invocation
   DBMS_OUTPUT.PUT_LINE ('With first name first, email is: ' || email);
   create_email (last_name, first_name, employer);               -- invocation
   DBMS_OUTPUT.PUT_LINE ('With last name first, email is: ' || email);
   first_name := 'Elizabeth';
   last_name := 'MacDonald';
   create_email (first_name, last_name, employer);               -- invocation
END;
/

--Example 8–2 Declaring, Defining, AND Invoking A Simple PL/SQL FUNCTION

DECLARE
   -- Declare and define function
   FUNCTION square (original NUMBER)                         -- parameter list
      RETURN NUMBER                                           -- RETURN clause
   AS
      -- Declarative part begins
      original_squared   NUMBER;
   BEGIN                                             -- Executable part begins
      original_squared := original * original;
      RETURN original_squared;                             -- RETURN statement
   END;
BEGIN
   DBMS_OUTPUT.PUT_LINE (square (100));                          -- invocation
END;
/

--Example 8–3 Execution Resumes AFTER RETURN STATEMENT IN FUNCTION

DECLARE
   x   INTEGER;

   FUNCTION f (n INTEGER)
      RETURN INTEGER
   IS
   BEGIN
      RETURN (n * n);
   END;
BEGIN
   DBMS_OUTPUT.PUT_LINE (
      'f returns ' || f (2) || '. Execution returns here (1).');
   x := f (2);
   DBMS_OUTPUT.PUT_LINE ('Execution returns here (2).');
END;
/

--Example 8–4 FUNCTION WHERE NOT Every Execution PATH Leads TO RETURN STATEMENT

CREATE OR REPLACE FUNCTION f (n INTEGER)
   RETURN INTEGER
   AUTHID DEFINER
IS
BEGIN
   IF n = 0
   THEN
      RETURN 1;
   ELSIF n = 1
   THEN
      RETURN n;
   END IF;
END;
/

--Example 8–5 Function Where Every Execution Path Leads to RETURN Statement

CREATE OR REPLACE FUNCTION f (n INTEGER)
   RETURN INTEGER
   AUTHID DEFINER
IS
BEGIN
   IF n = 0
   THEN
      RETURN 1;
   ELSIF n = 1
   THEN
      RETURN n;
   ELSE
      RETURN n * n;
   END IF;
END;
/

BEGIN
   FOR i IN 0 .. 3
   LOOP
      DBMS_OUTPUT.PUT_LINE ('f(' || i || ') = ' || f (i));
   END LOOP;
END;
/


--Example 8–6 Execution Resumes AFTER RETURN STATEMENT IN PROCEDURE

DECLARE
   PROCEDURE p
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('Inside p');
      RETURN;
      DBMS_OUTPUT.PUT_LINE ('Unreachable statement.');
   END;
BEGIN
   p;
   DBMS_OUTPUT.PUT_LINE ('Control returns here.');
END;
/


--Example 8–7 Execution Resumes AFTER RETURN STATEMENT IN Anonymous BLOCK

BEGIN
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('Inside inner block.');
      RETURN;
      DBMS_OUTPUT.PUT_LINE ('Unreachable statement.');
   END;

   DBMS_OUTPUT.PUT_LINE ('Inside outer block. Unreachable statement.');
END;
/

--Example 8–8 NESTED Subprograms Invoke EACH OTHER

DECLARE
   -- Declare proc1 (forward declaration):
   PROCEDURE proc1 (number1 NUMBER);

   -- Declare and define proc2:
   PROCEDURE proc2 (number2 NUMBER)
   IS
   BEGIN
      proc1 (number2);
   END;

   -- Define proc 1:
   PROCEDURE proc1 (number1 NUMBER)
   IS
   BEGIN
      proc2 (number1);
   END;
BEGIN
   NULL;
END;
/

--Example 8–9 Formal PARAMETERS AND Actual PARAMETERS

DECLARE
   emp_num   NUMBER (6) := 120;
   bonus     NUMBER (6) := 100;
   merit     NUMBER (4) := 50;

   PROCEDURE raise_salary (emp_id NUMBER,                  -- formal parameter
                                         amount NUMBER     -- formal parameter
                                                      )
   IS
   BEGIN
      UPDATE employees
         SET salary = salary + amount         -- reference to formal parameter
       WHERE employee_id = emp_id;            -- reference to formal parameter
   END raise_salary;
BEGIN
   raise_salary (emp_num, bonus);                         -- actual parameters
   /* raise_salary runs this statement:
   UPDATE employees
   SET salary = salary + 100
   WHERE employee_id = 120; */
   raise_salary (emp_num, merit + bonus);                 -- actual parameters
/* raise_salary runs this statement:
UPDATE employees
SET salary = salary + 150
WHERE employee_id = 120; */
END;
/

--Example 8–10 Actual Parameter Inherits ONLY NOT NULL FROM SUBTYPE

DECLARE
   SUBTYPE License IS VARCHAR2 (7) NOT NULL;

   n   License := 'DLLLDDD';

   PROCEDURE p (x License)
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE (x);
   END;
BEGIN
   p ('1ABC123456789');                     -- Succeeds; size is not inherited
   p (NULL);                            -- Raises error; NOT NULL is inherited
END;
/


--Example 8–11 Actual Parameter AND RETURN VALUE Inherit ONLY RANGE FROM SUBTYPE

DECLARE
   FUNCTION test (p INTEGER)
      RETURN INTEGER
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('p = ' || p);
      RETURN p;
   END test;
BEGIN
   DBMS_OUTPUT.PUT_LINE ('test(p) = ' || test (0.66));
END;
/

--Example 8–12 FUNCTION Implicitly Converts Formal Parameter TO Constrained SUBTYPE

DECLARE
   FUNCTION test (p NUMBER)
      RETURN NUMBER
   IS
      q   INTEGER := p;                    -- Implicitly converts p to INTEGER
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('p = ' || q);                  -- Display q, not p
      RETURN q;                                             -- Return q, not p
   END test;
BEGIN
   DBMS_OUTPUT.PUT_LINE ('test(p) = ' || test (0.66));
END;
/


--Example 8–13 Avoiding Implicit Conversion OF Actual PARAMETERS

CREATE OR REPLACE PROCEDURE p (n NUMBER)
   AUTHID DEFINER
IS
BEGIN
   NULL;
END;
/

DECLARE
   x   NUMBER := 1;
   y   VARCHAR2 (1) := '1';
BEGIN
   p (x);                                              -- No conversion needed
   p (y);                    -- z implicitly converted from VARCHAR2 to NUMBER
   p (TO_NUMBER (y));        -- z explicitly converted from VARCHAR2 to NUMBER
END;
/


--Example 8–14 Parameter VALUES BEFORE, During, AND AFTER PROCEDURE Invocation

CREATE OR REPLACE PROCEDURE p (a          PLS_INTEGER,        -- IN by default
                               b   IN     PLS_INTEGER,
                               c      OUT PLS_INTEGER,
                               d   IN OUT BINARY_FLOAT)
   AUTHID DEFINER
IS
BEGIN
   -- Print values of parameters:
   DBMS_OUTPUT.PUT_LINE ('Inside procedure p:');
   DBMS_OUTPUT.PUT ('IN a = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (a), 'NULL'));
   DBMS_OUTPUT.PUT ('IN b = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (b), 'NULL'));
   DBMS_OUTPUT.PUT ('OUT c = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (c), 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('IN OUT d = ' || TO_CHAR (d));
   -- Can reference IN parameters a and b,
   -- but cannot assign values to them.
   c := a + 10;                               -- Assign value to OUT parameter
   d := 10 / b;                            -- Assign value to IN OUT parameter
END;
/

DECLARE
   aa   CONSTANT PLS_INTEGER := 1;
   bb            PLS_INTEGER := 2;
   cc            PLS_INTEGER := 3;
   dd            BINARY_FLOAT := 4;
   ee            PLS_INTEGER;
   ff            BINARY_FLOAT := 5;
BEGIN
   DBMS_OUTPUT.PUT_LINE ('Before invoking procedure p:');
   DBMS_OUTPUT.PUT ('aa = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (aa), 'NULL'));
   DBMS_OUTPUT.PUT ('bb = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (bb), 'NULL'));
   DBMS_OUTPUT.PUT ('cc = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (cc), 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('dd = ' || TO_CHAR (dd));
   p (aa,                                                          -- constant
      bb,                                              -- initialized variable
      cc,                                              -- initialized variable
      dd                                               -- initialized variable
        );
   DBMS_OUTPUT.PUT_LINE ('After invoking procedure p:');
   DBMS_OUTPUT.PUT ('aa = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (aa), 'NULL'));
   DBMS_OUTPUT.PUT ('bb = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (bb), 'NULL'));
   DBMS_OUTPUT.PUT ('cc = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (cc), 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('dd = ' || TO_CHAR (dd));
   DBMS_OUTPUT.PUT_LINE ('Before invoking procedure p:');
   DBMS_OUTPUT.PUT ('ee = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (ee), 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('ff = ' || TO_CHAR (ff));
   p (1,                                                            -- literal
      (bb + 3) * 4,                                              -- expression
      ee,                                            -- uninitialized variable
      ff                                               -- initialized variable
        );
   DBMS_OUTPUT.PUT_LINE ('After invoking procedure p:');
   DBMS_OUTPUT.PUT ('ee = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (ee), 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('ff = ' || TO_CHAR (ff));
END;
/

--Example 8–15 OUT AND IN OUT Parameter VALUES AFTER Unhandled EXCEPTION

DECLARE
   j   PLS_INTEGER := 10;
   k   BINARY_FLOAT := 15;
BEGIN
   DBMS_OUTPUT.PUT_LINE ('Before invoking procedure p:');
   DBMS_OUTPUT.PUT ('j = ');
   DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (j), 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('k = ' || TO_CHAR (k));
   p (4,
      0,
      j,
      k);                       -- causes p to exit with exception ZERO_DIVIDE
EXCEPTION
   WHEN ZERO_DIVIDE
   THEN
      DBMS_OUTPUT.PUT_LINE ('After invoking procedure p:');
      DBMS_OUTPUT.PUT ('j = ');
      DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (j), 'NULL'));
      DBMS_OUTPUT.PUT_LINE ('k = ' || TO_CHAR (k));
END;
/


--Example 8–16 OUT Formal Parameter OF RECORD TYPE WITH Non-NULL DEFAULT VALUE

CREATE OR REPLACE PACKAGE r_types
   AUTHID DEFINER
IS
   TYPE r_type_1 IS RECORD (f VARCHAR2 (5):= 'abcde');

   TYPE r_type_2 IS RECORD (f VARCHAR2 (5));
END;
/

CREATE OR REPLACE PROCEDURE p (x   OUT r_types.r_type_1,
                               y   OUT r_types.r_type_2,
                               z   OUT VARCHAR2)
   AUTHID CURRENT_USER
IS
BEGIN
   DBMS_OUTPUT.PUT_LINE ('x.f is ' || NVL (x.f, 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('y.f is ' || NVL (y.f, 'NULL'));
   DBMS_OUTPUT.PUT_LINE ('z is ' || NVL (z, 'NULL'));
END;
/

DECLARE
   r1   r_types.r_type_1;
   r2   r_types.r_type_2;
   s    VARCHAR2 (5) := 'fghij';
BEGIN
   p (r1, r2, s);
END;
/


--Example 8–17 Aliasing FROM GLOBAL VARIABLE AS Actual Parameter

DECLARE
   TYPE Definition IS RECORD
   (
      word      VARCHAR2 (20),
      meaning   VARCHAR2 (200)
   );

   TYPE Dictionary IS VARRAY (2000) OF Definition;

   lexicon   Dictionary := Dictionary ();                   -- global variable

   PROCEDURE add_entry (word_list IN OUT NOCOPY Dictionary -- formal NOCOPY parameter
                                                          )
   IS
   BEGIN
      word_list (1).word := 'aardvark';
   END;
BEGIN
   lexicon.EXTEND;
   lexicon (1).word := 'aardwolf';
   add_entry (lexicon);                 -- global variable is actual parameter
   DBMS_OUTPUT.PUT_LINE (lexicon (1).word);
END;
/


--Example 8–18 Aliasing FROM Same Actual Parameter FOR Multiple Formal PARAMETERS

DECLARE
   n   NUMBER := 10;

   PROCEDURE p (n1 IN NUMBER, n2 IN OUT NUMBER, n3 IN OUT NOCOPY NUMBER)
   IS
   BEGIN
      n2 := 20;        -- actual parameter is 20 only after procedure succeeds
      DBMS_OUTPUT.put_line (n1);         -- actual parameter value is still 10
      n3 := 30;                   -- might change actual parameter immediately
      DBMS_OUTPUT.put_line (n1);  -- actual parameter value is either 10 or 30
   END;
BEGIN
   p (n, n, n);
   DBMS_OUTPUT.put_line (n);
END;
/


--Example 8–19 Aliasing FROM CURSOR VARIABLE Subprogram PARAMETERS

DECLARE
   TYPE EmpCurTyp IS REF CURSOR;

   c1   EmpCurTyp;
   c2   EmpCurTyp;

   PROCEDURE get_emp_data (emp_cv1   IN OUT EmpCurTyp,
                           emp_cv2   IN OUT EmpCurTyp)
   IS
      emp_rec   employees%ROWTYPE;
   BEGIN
      OPEN emp_cv1 FOR SELECT * FROM employees;

      emp_cv2 := emp_cv1;         -- now both variables refer to same location

      FETCH emp_cv1 INTO emp_rec;            -- fetches first row of employees

      FETCH emp_cv1 INTO emp_rec;           -- fetches second row of employees

      FETCH emp_cv2 INTO emp_rec;            -- fetches third row of employees

      CLOSE emp_cv1;                                  -- closes both variables

      FETCH emp_cv2 INTO emp_rec; -- causes error when get_emp_data is invoked
   END;
BEGIN
   get_emp_data (c1, c2);
END;
/


--Example 8–20 PROCEDURE WITH DEFAULT Parameter VALUES

DECLARE
   PROCEDURE raise_salary (emp_id   IN employees.employee_id%TYPE,
                           amount   IN employees.salary%TYPE := 100,
                           extra    IN employees.salary%TYPE := 50)
   IS
   BEGIN
      UPDATE employees
         SET salary = salary + amount + extra
       WHERE employee_id = emp_id;
   END raise_salary;
BEGIN
   raise_salary (120);                   -- same as raise_salary(120, 100, 50)
   raise_salary (121, 200);              -- same as raise_salary(121, 200, 50)
END;
/


--Example 8–21 FUNCTION Provides DEFAULT Parameter VALUE

DECLARE
   global   PLS_INTEGER := 0;

   FUNCTION f
      RETURN PLS_INTEGER
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('Inside f.');
      global := global + 1;
      RETURN global * 2;
   END f;

   PROCEDURE p (x IN PLS_INTEGER := f ())
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE (
         'Inside p. ' || ' global = ' || global || ', x = ' || x || '.');
      DBMS_OUTPUT.PUT_LINE ('--------------------------------');
   END p;

   PROCEDURE pre_p
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('Before invoking p, global = ' || global || '.');
      DBMS_OUTPUT.PUT_LINE ('Invoking p.');
   END pre_p;
BEGIN
   pre_p;
   p ();                                    -- default expression is evaluated
   pre_p;
   p (100);                             -- default expression is not evaluated
   pre_p;
   p ();                                    -- default expression is evaluated
END;
/

--Example 8–22 Adding Subprogram Parameter WITHOUT Changing Existing Invocations

--CREATE PROCEDURE :

CREATE OR REPLACE PROCEDURE print_name (FIRST VARCHAR2, LAST VARCHAR2)
   AUTHID DEFINER
IS
BEGIN
   DBMS_OUTPUT.PUT_LINE (FIRST || ' ' || LAST);
END print_name;
/

--Invoke PROCEDURE:

BEGIN
   print_name ('John', 'Doe');
END;
/

--RESULT:
--John Doe
--ADD third parameter WITH DEFAULT VALUE:

CREATE OR REPLACE PROCEDURE print_name (FIRST    VARCHAR2,
                                        LAST     VARCHAR2,
                                        mi       VARCHAR2 := NULL)
   AUTHID DEFINER
IS
BEGIN
   IF mi IS NULL
   THEN
      DBMS_OUTPUT.PUT_LINE (FIRST || ' ' || LAST);
   ELSE
      DBMS_OUTPUT.PUT_LINE (FIRST || ' ' || mi || '. ' || LAST);
   END IF;
END print_name;
/

--Invoke PROCEDURE:

BEGIN
   print_name ('John', 'Doe');                          -- original invocation
   print_name ('John', 'Public', 'Q');                       -- new invocation
END;

/