Subprogram dependencies and privileges.

Subprogram dependencies

When we compile a new stored procedure all information about this procedure is stored in the Oracle dictionary. We can refer to user views
  • user_objects
  • user_procedures
  • user_source.
    For example, to look at the names of all stored procedures we can execute a query like
    select object_name 
      from user_objects
     where object_type='PROCEDURE';
    
    Information about procedures stored by the Oracle also includes references to all objects the procedures reference. Oracle needs that information because the procedures depend on these objects. As probably you remember we got compiling errors when referenced inside a procedure a table that didn't exist. Thus, Oracle wants to keep all these information in case that the reference objects will be changed after a procedure is successfully compiled. For example, our stored procedure ShowTestTable
    create or replace procedure ShowTestTable as
    begin
      for v_row in (select name, id from pl_test) loop
        dbms_output.put_line('name: ' || v_row.name ||  '   id: ' || v_row.id);
      end loop;
    end;
    
    uses table pl_test. Once the procedure successfully created we can take a look at its status:
    SQL> select object_name, status
      2    from user_objects
      3   where object_type='PROCEDURE' or object_type='FUNCTION';
    
    OBJECT_NAME          STATUS
    --------------------------
    ADDUSER              VALID
    ADD_TEST_VALUE       VALID
    GET_SUM              VALID
    INSERT_IN_TEST       VALID
    PRINTREQUIRED        VALID
    SHOWTESTTABLE        VALID
    
    Now we will modify the table pl_test.
    SQL> alter table pl_test add phone number(7);
    
    Table altered.
    
    Now, when the table got changed what happened to the stored procedure that depends on that table?
    SQL> select object_name, status
      2    from user_objects
      3   where object_type='PROCEDURE' or object_type='FUNCTION';
    
    OBJECT_NAME          STATUS
    --------------------------
    ADDUSER              VALID
    ADD_TEST_VALUE       VALID
    GET_SUM              VALID
    INSERT_IN_TEST       VALID
    PRINTREQUIRED        VALID
    SHOWTESTTABLE        INVALID
    
    As we can see the procedure is marked as INVALID. If an object is invalidated, the PL/SQL engine will automatically try to recompile it the next time it is called. Since all we did was just adding a new column to the table and our procedure doesn't use that column, Oracle will recompile it with no errors. Thus, after
    SQL> exec ShowTestTable;
    
    this procedure will be VALID again.

    The kind of the dependency we just discussed is called direct dependency. Oracle also takes care of indirect dependencies, when a procedure doesn't directly refer to a changed object, but it refers to another object which becomes invalid upon those changes. We can illustrate this kind of dependency by creating another procedure that doesn't do anything to the pl_test table directly, but calls the ShowTestTable function.

    The situations with packages is different, however. This difference is caused by the fact that a package consists of two parts the header and the body. The body of a package depends on the header, but the header does not depend on the body. All outside procedures that call procedures from this package depend only on the header. Thus, if a package body becomes invalid, it doesn't influence the header or outside procedures. This situation is very well illustrated by the example from Scott Urman book.

    Privileges and stored subprograms

    Oracle considers procedures, functions, and packages as usual database objects. That means, they are owned by a user, or a schema. Other users can access those objects if they are granted the correct privileges on them.

    execute privilege

    For stored subprograms and packages a privilege to grant is execute. Now we will discuss three different situations to illustrate how subprogram privileges work. We will consider this on the example from the great Scott Urman book. In all situations we will deal with two users UserA and UserB, two tables temp_table and classes, and two subprograms - procedure RecordFullClass that invokes function AlmostFull

    Case 1: In the first case we'll assume that UserA owns all the objects listed above, and the UserB was only granted the execute privilege on the RecordFullClass

    SQL> grant execute on RecordFullClass to UserB;
    

    Please note that UserB has not been granted any privileges neither on the tables, nor on the AlmostFull function. This user does not need those privileges, because all the job will be done by the RecordFullClass procedure and this procedure has all the privileges on these objects since it belongs to the same schema. Thus, this procedure will update the temp_table belonging to the UserA.

    Case 2: The second case is very similar to the first one, except this time we assume that UserB also has a table temp_table.

    Although, despite this fact, the same UseA,temp_table will be modified. The reason for this is that by default all procedures and functions work with the objects from their own schema. Since procedure RecordFullClasses belongs to UserA, it works with the temp_table from the schema UserA.

    Case 3: In the last case we assume that UserB owns the RecordFullClass procedure. Of course, in this case this procedure needs to be a little bit different. We need to manually specify that it should read from the table classes from the UseA schema and should call the function AlmostFull that belongs to the UserA:

    create or replace procedure RecordFullClass as
      cursor c_Classes is select department, course from UserA.classes;
    begin
      for c_ClassRec in c_Classes loop
        if UserA.AlmostFull(v_ClassRec.department, v_ClassRec) then
          insert into temp_table (char_col)
            values (v_ClassRec.department||' '||v_ClassRec||' is almost full');
        end if;
      end loop;
    end RecordFullClass;
    
    Since now the RecordFullClass dosn't belong to the schema UserA, UserB needs privileges to execute the AlmostFull function and select from the classes table.
    SQL> grant execute on AlmostFull to UserB;
    SQL> grant select on classes to UserB;
    

    And just like we mentioned before, due to the fact that procedures by default work with objects from their own schema and we did not specify a schema for temp_table, the RecordFullClass procedure will update the UserB.temp_table this time.

    There is one more thing we would like to point out.
    If a subprogram needs some privileges these privileges must be granted explicitly and not through a role.

    Invoker's versus definer's rights

    In the last two cases discussed in the previous section the procedure RecordFullClass worked with the table from its own schema; that is, the procedure worked under the definer rights. In other words, all unqualified references within the subprogram were resolved under the privilege set of its owner, or definer.

    This default behavior can be changed (starting from Oracle8i). In an invoker's rights subprogram, external references are resolved under the privilege set of the caller, not the owner. An invoker's rights routine is created by using authid clause. It is valid for stand-alone subprograms, package specifications, and object type specifications only. The general syntax of using this clause is

    create [or replace] function function_name [(parameter_list)] return return_type 
      [authid {current user | definer}] 
      {as | is}
      function_body;
    
    create [or replace] function procedure_name [(parameter_list)]  
      [authid {current user | definer}] 
      {as | is}
      procedure_body;
    
    create or replace package package_name
      [authid {current user | definer}]
      {as | is}
      package_specification; 
    
    If the current user option is specified in the authid clause, the object will have invoker's rights. If definer is specified or the authid clause is not present, then the object will have the definer's right. Please see Scot Urman's example for more details.

    Using stored functions in SQL statements

    Functions that meet certain criteria can be called from a SQL statements. These criteria are defined in term of purity levels. There are four different purity levels for functions:
    Level Meaning Description
    WNDS Writes No Database Set The function does not modify any database tables (using DML statements)
    RNDS Reads No Database Set The function does not read any database table (using select statement)
    WNPS Writes No Package Set The function does not modify any packaged variables (no package variables are used on the left side of an assignment or in a fetch statement)
    RNPS Reads No Package State The function does not examine any packaged variables (no package variables appear on the right side of an assignment operator or as a part of a procedural or SQL expression)

    Depending of the purity level of a function, it is subject to the following restrictions:

    In addition to these restrictions, a user-defined functions must also meet the following requirements to be callable from a SQL statement:

    Here is an example of a function that can be used in a SQL statement

    create or replace function Balance(end_date in date) return number as
      res BankAccount.amount%type;
    begin
      select sum(amount) 
        into res 
        from BankAccount 
       where tdate <= end_date;
      return res;
    end Balance;
    

    In class assignment:

    1. Write a function that checks if the student with given ID took all prerequisite courses for the given course.
    2. Write a function that returns the number of students that may take a particular class; that is, they have taken all prerequisites.
    3. Create a package with these two functions. Make one of them global and the other local.
    4. Use the second function to print all the classes offered next semester and the number of students that may take these courses.


    References:

    For more information see Oracle9i PL/SQL Programming by Scott Urman. Chapter 10.