Scope and Visibility of Identifiers
The scope of an identifier is the region of a PL/SQL unit from which you can reference the identifier. The visibility of an identifier is the region of a PL/SQL unit from which you can reference the identifier without qualifying it. An identifier is local to the PL/SQL unit that declares it. If that unit has subunits, the identifier is global to them.
If a subunit redeclares a global identifier, then inside the subunit, both identifiers are in scope, but only the local identifier is visible. To reference the global identifier, the subunit must qualify it with the name of the unit that declared it. If that unit has no name, then the subunit cannot reference the global identifier.
A PL/SQL unit cannot reference identifiers declared in other units at the same level, because those identifiers are neither local nor global to the block.
You cannot declare the same identifier twice in the same PL/SQL unit. If you do, an error occurs when you reference the duplicate identifier.
You can declare the same identifier in two different units. The two objects represented by the identifier are distinct. Changing one does not affect the other.
In the same scope, give labels and subprograms unique names to avoid confusion and unexpected results.
Examples
Example 3-17 Scope and Visibility of Identifiers
This example shows the scope and visibility of several identifiers. The first sub-block redeclares the global identifier a
. To reference the global variable a
, the first sub-block would have to qualify it with the name of the outer blockābut the outer block has no name. Therefore, the first sub-block cannot reference the global variable a
; it can reference only its local variable a
. Because the sub-blocks are at the same level, the first sub-block cannot reference d
, and the second sub-block cannot reference c
.
-- Outer block: DECLARE a CHAR; -- Scope of a (CHAR) begins b REAL; -- Scope of b begins BEGIN -- Visible: a (CHAR), b -- First sub-block: DECLARE a INTEGER; -- Scope of a (INTEGER) begins c REAL; -- Scope of c begins BEGIN -- Visible: a (INTEGER), b, c NULL; END; -- Scopes of a (INTEGER) and c end -- Second sub-block: DECLARE d REAL; -- Scope of d begins BEGIN -- Visible: a (CHAR), b, d NULL; END; -- Scope of d ends -- Visible: a (CHAR), b END; -- Scopes of a (CHAR) and b end /
Example 3-18 Qualifying Redeclared Global Identifier with Block Label
This example labels the outer block with the name outer
. Therefore, after the sub-block redeclares the global variable birthdate
, it can reference that global variable by qualifying its name with the block label. The sub-block can also reference its local variable birthdate
, by its simple name.
<<outer>> -- label DECLARE birthdate DATE := TO_DATE('09-AUG-70', 'DD-MON-YY'); BEGIN DECLARE birthdate DATE := TO_DATE('29-SEP-70', 'DD-MON-YY'); BEGIN IF birthdate = outer.birthdate THEN DBMS_OUTPUT.PUT_LINE ('Same Birthday'); ELSE DBMS_OUTPUT.PUT_LINE ('Different Birthday'); END IF; END; END; /
Result:
Different Birthday
Example 3-19 Qualifying Identifier with Subprogram Name
In this example, the procedure check_credit
declares a variable, rating
, and a function, check_rating
. The function redeclares the variable. Then the function references the global variable by qualifying it with the procedure name.
CREATE OR REPLACE PROCEDURE check_credit (credit_limit NUMBER) AS rating NUMBER := 3; FUNCTION check_rating RETURN BOOLEAN IS rating NUMBER := 1; over_limit BOOLEAN; BEGIN IF check_credit.rating <= credit_limit THEN -- reference global variable over_limit := FALSE; ELSE over_limit := TRUE; rating := credit_limit; -- reference local variable END IF; RETURN over_limit; END check_rating; BEGIN IF check_rating THEN DBMS_OUTPUT.PUT_LINE ('Credit rating over limit (' || TO_CHAR(credit_limit) || '). ' || 'Rating: ' || TO_CHAR(rating)); ELSE DBMS_OUTPUT.PUT_LINE ('Credit rating OK. ' || 'Rating: ' || TO_CHAR(rating)); END IF; END; / BEGIN check_credit(1); END; /
Result:
Credit rating over limit (1). Rating: 3
Example 3-20 Duplicate Identifiers in Same Scope
You cannot declare the same identifier twice in the same PL/SQL unit. If you do, an error occurs when you reference the duplicate identifier, as this example shows.
DECLARE
id BOOLEAN;
id VARCHAR2(5); -- duplicate identifier
BEGIN
id := FALSE;
END;
/
Result:
id := FALSE; * ERROR at line 5: ORA-06550: line 5, column 3: PLS-00371: at most one declaration for 'ID' is permitted ORA-06550: line 5, column 3: PL/SQL: Statement ignored
Example 3-21 Declaring Same Identifier in Different Units
You can declare the same identifier in two different units. The two objects represented by the identifier are distinct. Changing one does not affect the other, as this example shows. In the same scope, give labels and subprograms unique names to avoid confusion and unexpected results.
DECLARE PROCEDURE p IS x VARCHAR2(1); BEGIN x := 'a'; -- Assign the value 'a' to x DBMS_OUTPUT.PUT_LINE('In procedure p, x = ' || x); END; PROCEDURE q IS x VARCHAR2(1); BEGIN x := 'b'; -- Assign the value 'b' to x DBMS_OUTPUT.PUT_LINE('In procedure q, x = ' || x); END; BEGIN p; q; END; /
Result:
In procedure p, x = a In procedure q, x = b
Example 3-22 Label and Subprogram with Same Name in Same Scope
In this example, echo
is the name of both a block and a subprogram. Both the block and the subprogram declare a variable named x
. In the subprogram, echo
.x
refers to the local variable x
, not to the global variable x
.
<<echo>> DECLARE x NUMBER := 5; PROCEDURE echo AS x NUMBER := 0; BEGIN DBMS_OUTPUT.PUT_LINE('x = ' || x); DBMS_OUTPUT.PUT_LINE('echo.x = ' || echo.x); END; BEGIN echo; END; /
Result:
x = 0 echo.x = 0
Example 3-23 Block with Multiple and Duplicate Labels
This example has two labels for the outer block, compute_ratio
and another_label
. The second label appears again in the inner block. In the inner block, another_label
.denominator
refers to the local variable denominator
, not to the global variable denominator
, which results in the error ZERO_DIVIDE
.
<<compute_ratio>> <<another_label>> DECLARE numerator NUMBER := 22; denominator NUMBER := 7; BEGIN <<another_label>> DECLARE denominator NUMBER := 0; BEGIN DBMS_OUTPUT.PUT_LINE('Ratio with compute_ratio.denominator = '); DBMS_OUTPUT.PUT_LINE(numerator/compute_ratio.denominator); DBMS_OUTPUT.PUT_LINE('Ratio with another_label.denominator = '); DBMS_OUTPUT.PUT_LINE(numerator/another_label.denominator); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Divide-by-zero error: can''t divide ' || numerator || ' by ' || denominator); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Unexpected error.'); END another_label; END compute_ratio; /
Result:
Ratio with compute_ratio.denominator = 3.14285714285714285714285714285714285714 Ratio with another_label.denominator = Divide-by-zero error: cannot divide 22 by 0