Chapter 11 · PL/SQL Fundamentals: Blocks, Types, Procedures, Functions, and Packages
Packages, Specification vs Body, State, Initialization, Encapsulation, and Editioning Awareness
Use package specifications as stable public APIs and package bodies as hidden implementation, then make session package state, initialization, invalidation, and editionability visible.
Learning outcomes
ServiceHub exposes many standalone routines. Clients depend on implementation details, and deployments frequently invalidate callers. A redesign moves the public contract into a package specification and hides implementation in the body. Then a long-lived connection pool reveals another Oracle-specific issue: package variables are session state, and replacing or recompiling a stateful package can cause existing sessions to discard state and surface ORA-04068.
Treat a package specification as the public API and the body as hidden implementation.
Use package initialization deliberately and understand that ordinary package state normally lives for the session.
Demonstrate package-state isolation across sessions and ORA-04068 after stateful invalidation/recompilation.
Inspect package/object status and dependencies instead of assuming CREATE OR REPLACE is invisible to running sessions.
Record EDITIONABLE/NONEDITIONABLE awareness without assuming Edition-Based Redefinition is configured.
Mandatory examples target a disposable ServiceHub schema in Oracle AI Database Free 26ai and were reviewed against RU 23.26.3, SQL Developer 26.2, and SQLcl 26.2.1. Free is limited to 2 foreground CPU cores, 2 GB combined SGA/PGA memory, and 12 GB user data and receives no Oracle patches or Support service requests. The chapter requires no Diagnostics Pack, Tuning Pack, RAC, Data Guard, Exadata, GoldenGate, OCI service, or COMPATIBLE change. Stored program creation in the course owner requires CREATE PROCEDURE; cross-schema execution requires explicit EXECUTE grants. Dynamic performance views used for optional session/PGA evidence should be read through a DBA observer or narrowly granted V_$ views in the disposable PDB.
1. Specification versus body is an API boundary
The package specification declares public types, constants, variables, exceptions, cursors, procedures, and functions. The body defines public subprograms and can contain private helpers and variables that callers cannot reference directly. Oracle stores the specification and body separately.
Callers that depend only on public declarations depend on the package specification, not on private implementation. Replacing the body without changing the specification therefore avoids many unnecessary caller invalidations—but an instantiated stateful package is a separate runtime concern.
CREATE OR REPLACE PACKAGE servicehub_case_api AUTHID DEFINER AS PROCEDURE set_default_status(p_status IN VARCHAR2); FUNCTION get_default_status RETURN VARCHAR2; FUNCTION normalize_status(p_status IN VARCHAR2) RETURN VARCHAR2;END servicehub_case_api;/
CREATE OR REPLACE PACKAGE BODY servicehub_case_api AS g_default_status VARCHAR2(12); FUNCTION normalize_status(p_status IN VARCHAR2) RETURN VARCHAR2 IS BEGIN RETURN UPPER(TRIM(p_status)); END; PROCEDURE set_default_status(p_status IN VARCHAR2) IS BEGIN g_default_status := normalize_status(p_status); END; FUNCTION get_default_status RETURN VARCHAR2 IS BEGIN RETURN g_default_status; END;BEGIN g_default_status := 'OPEN';END servicehub_case_api;/
2. Package initialization and state are session-scoped by default
The package body's initialization section executes when the package is instantiated in a session. Package variables can retain values across calls in that same session. Another session gets its own package instance/state. This makes package variables convenient for session-local caches/settings but dangerous as a substitute for durable business state.
SET SERVEROUTPUT ONEXEC servicehub_case_api.set_default_status('hold');BEGIN DBMS_OUTPUT.PUT_LINE( 'Session A=' || servicehub_case_api.get_default_status );END;/-- HOLD
SET SERVEROUTPUT ONBEGIN DBMS_OUTPUT.PUT_LINE( 'Session B=' || servicehub_case_api.get_default_status );END;/-- OPEN on this session's first instantiation
Connection pooling complicates this: a later request can inherit state from whatever database session the pool assigns. Prefer stateless APIs unless session state is intentional and the pool lifecycle explicitly resets it.
3. Changing package implementation can discard instantiated state
If a stateful package instance is invalidated/recompiled while a session has instantiated it, the session can lose package state. Oracle documents ORA-04068 as the signal that existing package state was discarded. The application should be designed to reinitialize or retry at a safe API boundary rather than assuming state survives deployments.
EXEC servicehub_case_api.set_default_status('closed');SELECT servicehub_case_api.get_default_status AS before_changeFROM dual;-- CLOSED
ALTER PACKAGE servicehub_case_api COMPILE BODY;
SELECT servicehub_case_api.get_default_status AS after_changeFROM dual;-- Stateful invalidation can surface ORA-04068.-- After safe re-instantiation, initialization state is OPEN.
Exact accompanying errors can vary with the invalidation/recompile path, but the mechanism is the same: session package state is not a deployment-stable persistence layer.
4. Deliberately wrong: mask package invalidation with an unrelated error
Replacing package-state errors with a generic server error can prevent clients from recognizing that session state was discarded and must be reinitialized. Oracle's guidance specifically warns that trapping package invalidation at the wrong layer can interfere with expected reinstantiation behavior.
Keep business operations idempotent at retry boundaries, reconnect or retry a well-defined package call when deployment invalidation is expected, and avoid relying on mutable package globals for durable workflow state.
5. Encapsulation reduces dependency blast radius
Put stable types and callable signatures in the specification. Keep helper routines and implementation variables private in the body. Changing a private algorithm often requires only a body replacement; changing a public parameter type or declaration changes the API and can invalidate dependents.
SELECT object_name, object_type, status, editionableFROM user_objectsWHERE object_name='SERVICEHUB_CASE_API'ORDER BY object_type;SELECT name, type, referenced_name, referenced_type, dependency_typeFROM user_dependenciesWHERE referenced_name='SERVICEHUB_CASE_API'ORDER BY name, type;
6. Editioning awareness: editionability is not the same as using EBR
Current 26ai CREATE PACKAGE syntax supports
EDITIONABLE and NONEDITIONABLE; when
editioning is enabled for the package object type, the default
is EDITIONABLE. A package body inherits the
specification's editionability unless explicitly specified to
match it.
This is awareness only. Edition-Based Redefinition (EBR) involves editions, editioning-enabled schemas/object types, editioning views, crossedition triggers, deployment routing, grants, and rollback design. The mandatory Chapter 11 lab does not enable editioning or create editions.
SELECT object_name, object_type, editionable, edition_name, statusFROM user_objectsWHERE object_name='SERVICEHUB_CASE_API'ORDER BY object_type;
7. Reproducible package lab
CREATE OR REPLACE PACKAGE servicehub_case_api AUTHID DEFINER AS PROCEDURE set_default_status(p_status IN VARCHAR2); FUNCTION get_default_status RETURN VARCHAR2;END servicehub_case_api;/CREATE OR REPLACE PACKAGE BODY servicehub_case_api AS g_default_status VARCHAR2(12); PROCEDURE set_default_status(p_status IN VARCHAR2) IS BEGIN g_default_status := UPPER(TRIM(p_status)); END; FUNCTION get_default_status RETURN VARCHAR2 IS BEGIN RETURN g_default_status; END;BEGIN g_default_status := 'OPEN';END servicehub_case_api;/SELECT object_name, object_type, status, editionableFROM user_objectsWHERE object_name='SERVICEHUB_CASE_API'ORDER BY object_type;
EXEC servicehub_case_api.set_default_status('hold');SELECT servicehub_case_api.get_default_status AS current_stateFROM dual;-- HOLD in this session
DROP PACKAGE servicehub_case_api;
8. Production judgment
Packages are ideal for stable database APIs, related types, encapsulated helpers, and carefully managed session-local state. Keep specs small and stable, bodies replaceable, privileges explicit, and deployment tests aware of long-lived sessions. Avoid package globals for durable business state and be cautious when connection pools reuse sessions across requests.
No edition configuration, special option, pack, restart, or
COMPATIBLE change is required. Lesson 4 focuses on
data-processing performance: when procedural row handling is
unavoidable, reduce PL/SQL-to-SQL boundary crossings with bulk
operations while controlling PGA consumption and error
semantics.
Check your understanding
- What belongs in the package specification?
- How long does ordinary package state typically live?
- Why can ORA-04068 appear after package recompilation?
- Does EDITIONABLE metadata mean EBR is already configured?
- Why does keeping private helpers in the body improve deployment isolation?
Review the answers
Public types, variables/constants/exceptions/cursors, and callable subprogram declarations that form the package API.
Normally for the life of the database session, subject to invalidation/recompilation and special package modes.
An instantiated stateful package can be invalidated; Oracle discards its existing session state and reports the discard on subsequent use.
No. Editionability is an object property; full EBR requires explicit edition/deployment architecture.
Callers depend on the specification, so changing body-only implementation can avoid invalidating code that depends only on public declarations.
Authoritative references
- What is a Package? — package specification, body, initialization and encapsulation
- CREATE PACKAGE — privileges and editionability
- CREATE PACKAGE BODY — body and initialization semantics
- Coding PL/SQL Subprograms and Packages — package invalidation/session-state guidance
- ORA-04068 — discarded package-state error