Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

PL/SQL & T-SQL (Oracle and SQL Server)  Module 11 – Subprograms, packages and robust error logging

PL/SQL packages, session state and ORA-04068

A package specification declares what callers may use, its body hides the rest, and its state lives once per session until a recompiled body discards it.

  • Intermediate
  • 20 minutes
  • Examples run with oracle-free 23.26.3.0.0, mssql 17.0.5005.3, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 7 of 20 minutes free
Free: A package has a public specification and a private body · Package state lives once per session, and the first call starts it · What takes that state away, and what keeps callers out. With a pass: 2 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Build packages with a public specification and a private body
  • Explain why package state and its initialisation belong to one session
  • Handle ORA-04068 after a package body is recompiled, with the RESETTABLE clause
  • Use ACCESSIBLE BY to restrict which units may call a helper

Before you start

What you’ll learn · Before you start

What you will learn

  • Build packages with a public specification and a private body
  • Explain why package state and its initialisation belong to one session
  • Handle ORA-04068 after a package body is recompiled, with the RESETTABLE clause
  • Use ACCESSIBLE BY to restrict which units may call a helper

Before you start

On this page

Three calls in, a session’s package counter reads 3. A colleague recompiles that package body, and that session’s next call returns no number at all: it fails with ORA-04068, and the counter and everything else the package held for that session are gone.

PL/SQL packages group related code and the data it keeps into one stored unit, in two pieces. A specification declares what callers may use, and a body hides how the work is done. You will build PL/SQL packages in both pieces, follow their state from its first call to the recompilation that discards it, and decide what a specification should expose.

A package has a public specification and a private body

The package specification: The public face of a PL/SQL package: the declarations other code may name, such as subprogram headings, types, constants, exceptions and cursors, each complete enough to be used without reading the code behind it. of account_api declares three items: balance_of, lookups_so_far and forget_cache. Each declaration carries everything a caller needs and nothing about how it works. The package body: The piece of a PL/SQL package that defines everything its specification declares, and may add private items and an initialisation part that nothing outside the package can reach. defines all three, and its headings must repeat the declarations word for word apart from white space, or the body compiles with PLS-00323.

The body is also where the private parts live. account_api keeps a helper, read_balance, and three variables no caller can name: a call of account_api.read_balance does not compile, and neither does a read of account_api.g_lookups. Oracle’s writing guidelines ask for that, declaring only the items invoking programs must see. Step through one caller reaching the package:

Interactive · step by step What a caller sees of a package, and what the body keeps Step through one caller reaching account_api, and watch which parts it can name.
What a caller sees of a package, and what the body keeps, step 6 of 6. Showing A caller (must recompile), Specification (one more parameter), Package state (g_lookups, g_cached_id), Package body (changed on its own), Private items. In focus: Specification, A caller.calls balance_ofruns the body's codereads and writesA callermust recompileSpecificationone more parameterPackage stateg_lookups, g_cached_idPackage bodychanged on its ownPrivate itemsread_balance and more

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. The caller reads the spec. account_api declares three items: the function balance_of, the function lookups_so_far and the procedure forget_cache. Each declaration carries everything a caller needs, which is the parameters, the types and the return type.
  2. The body answers the call. The body defines every item the specification declared, and the two headings must match word for word apart from white space. A mismatch gives PLS-00323 when the body compiles.
  3. The body keeps its own items. read_balance is declared only in the body, so no caller can name it. A call of account_api.read_balance fails to compile with PLS-00302, which is what hiding the lookup is for.
  4. Variables make the state. The body also declares g_lookups, g_cached_id and g_cached_balance. Those three values are the package state, and each session that calls the package gets its own copy of them.
  5. A new body, the same callers. Rewriting the cache inside the body changes nothing a caller named, so units that call account_api need no recompilation. This is why the guidelines put only what callers must see in the specification.
  6. A new spec, new work. Adding a parameter to balance_of changes the specification, so every unit that calls the public items has to be recompiled. The body stays a black box; the specification is the promise.

The script creates both pieces and calls balance_of twice for account 3; its last two blocks fail on purpose:

account_pkg.sql: a specification, a body and two refused calls SQL · packages/account_pkg.sql
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- One package in two pieces: a specification that declares three public items, and a body that hides a private
-- lookup, two state variables and an initialisation part. Needs the practice schema (run the schema_oracle.sql of
-- practice-schema-and-dialect-basics first). examples/model/session_state_model.py prints the same counters.
-- The script drops the package at the end, so it can run again and leaves the schema as it found it.
SET SERVEROUTPUT ON

-- The specification: everything a caller needs, and nothing about how it works.
CREATE OR REPLACE PACKAGE account_api AUTHID DEFINER IS
  FUNCTION balance_of (p_account_id IN NUMBER) RETURN NUMBER;
  FUNCTION lookups_so_far RETURN PLS_INTEGER;
  PROCEDURE forget_cache;
END account_api;
/

-- The body: the private helper and the two variables are invisible from outside.
CREATE OR REPLACE PACKAGE BODY account_api IS
  g_lookups        PLS_INTEGER;                   -- package state, one copy per session
  g_cached_id      accounts.account_id%TYPE;
  g_cached_balance accounts.balance%TYPE;

  FUNCTION read_balance (p_account_id IN NUMBER)  -- private: declared here, not in the specification
    RETURN NUMBER
  IS
    v_balance accounts.balance%TYPE;
  BEGIN
    SELECT balance INTO v_balance
      FROM accounts
     WHERE account_id = p_account_id;
    g_lookups := g_lookups + 1;
    RETURN v_balance;
  END read_balance;

  FUNCTION balance_of (p_account_id IN NUMBER)
    RETURN NUMBER
  IS
  BEGIN
    IF g_cached_id IS NULL OR g_cached_id <> p_account_id THEN
      g_cached_balance := read_balance(p_account_id);
      g_cached_id      := p_account_id;
      DBMS_OUTPUT.PUT_LINE('read from the table');
    ELSE
      DBMS_OUTPUT.PUT_LINE('answered from the cache');
    END IF;
    RETURN g_cached_balance;
  END balance_of;

  FUNCTION lookups_so_far RETURN PLS_INTEGER IS
  BEGIN
    RETURN g_lookups;
  END lookups_so_far;

  PROCEDURE forget_cache IS
  BEGIN
    g_cached_id      := NULL;
    g_cached_balance := NULL;
  END forget_cache;
BEGIN                                             -- the initialisation part: the first reference runs it
  g_lookups   := 0;
  g_cached_id := NULL;
  DBMS_OUTPUT.PUT_LINE('account_api: initialised for this session');
END account_api;
/

-- Two calls for the same account, then the counter the specification exposes.
BEGIN
  DBMS_OUTPUT.PUT_LINE('balance_of(3) = ' || TO_CHAR(account_api.balance_of(3), 'FM999990.00'));
  DBMS_OUTPUT.PUT_LINE('balance_of(3) = ' || TO_CHAR(account_api.balance_of(3), 'FM999990.00'));
  DBMS_OUTPUT.PUT_LINE('lookups_so_far = ' || account_api.lookups_so_far);
END;
/
-- By the documented behaviour this prints, in order:
--   account_api: initialised for this session   (the initialisation part, once in this session)
--   read from the table
--   balance_of(3) = 760.25
--   answered from the cache
--   balance_of(3) = 760.25
--   lookups_so_far = 1                          (one table read, two calls)

-- A caller cannot reach the private parts: both of these fail to compile with PLS-00302.
BEGIN
  DBMS_OUTPUT.PUT_LINE(account_api.read_balance(3));
END;
/
BEGIN
  DBMS_OUTPUT.PUT_LINE(account_api.g_lookups);
END;
/
-- Expected for each block: PLS-00302: component 'READ_BALANCE' (then 'G_LOOKUPS') must be declared.

DROP PACKAGE account_api;
account_pkg.sql, run in the lab Python · account_pkg_session.py
"""account_pkg.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""

from oracle_session import Lab

with Lab() as lab:
    lab.sqlplus("packages/account_pkg.sql", errors=2)

Output

$ sqlplus -s lab@//localhost/FREEPDB1 @packages/account_pkg.sql
Package created.


Package body created.

account_api: initialised for this session
read from the table
balance_of(3) = 760.25
answered from the cache
balance_of(3) = 760.25
lookups_so_far = 1

PL/SQL procedure successfully completed.

  DBMS_OUTPUT.PUT_LINE(account_api.read_balance(3));
                                   *
ERROR at line 2:
ORA-06550: line 2, column 36:
PLS-00302: component 'READ_BALANCE' must be declared
ORA-06550: line 2, column 3:
PL/SQL: Statement ignored
Help: https://docs.oracle.com/error-help/db/ora-06550/


  DBMS_OUTPUT.PUT_LINE(account_api.g_lookups);
                                   *
ERROR at line 2:
ORA-06550: line 2, column 36:
PLS-00302: component 'G_LOOKUPS' must be declared
ORA-06550: line 2, column 3:
PL/SQL: Statement ignored
Help: https://docs.oracle.com/error-help/db/ora-06550/



Package dropped.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 account_pkg_session.py

Checkpoint

Check: The specification and the body

  1. Question 1 of 2 account_api declares balance_of in its specification and read_balance only in its body. Which call compiles in another unit of the same schema?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: account_api.balance_of(3)

    The specification is the public face of a package. Items declared only in the body are invisible outside it, which is what lets the body change without warning callers.

  2. Question 2 of 2 You rewrite the lookup inside a package body, leaving every declaration in the specification untouched. What must be recompiled?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The body alone

    Separating the two pieces keeps dependencies small: callers depend on the parameters and return types in the specification, so the body can be rewritten freely.

Package state lives once per session, and the first call starts it

The values of the variables, constants and cursors a package declares, in either piece, are its package state: The values of the variables, constants and cursors a package declares in either of its pieces. One variable is enough to make a package stateful, and each session keeps its own copy of the values., and one variable is enough to make a package stateful. Package state is session state: every session that references a package item gets its own instantiation, carrying its own copy of those values.

Instantiating a package also package initialisation: What Oracle Database does when a session first references a package: it assigns the initial values of the public constants and variables, then runs the initialisation part at the foot of the body. it. Oracle assigns the initial values of the public constants, then of the public variables that declare one, then runs the body’s initialisation part, the block between BEGIN and END at its foot. That happens on a session’s first reference, not on every call, so account_pkg.sql prints its one line once. This session state lives as long as the session does, and two clients calling one stored package keep two counters:

Interactive · message by message Two sessions calling one package, each with its own state Step through the six calls and watch each session's counter follow only its own work.
Two sessions calling one package, each with its own state, step 7 of 7. Messages so far: Session A to Oracle Database: balance_of(3); Oracle Database to Session A: 760.25; Session B to Oracle Database: balance_of(3); Oracle Database to Session B: 760.25; Session A to Oracle Database: balance_of(4).Session Alookups 2Oracle DatabaseA: 2, B: 1Session Blookups 1balance_of(3)760.25balance_of(3)760.25balance_of(4)

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. A calls the package first. Session A calls account_api.balance_of(3). It is the first reference to a package item in this session, so Oracle instantiates account_api for session A.
  2. Instantiating means initialising. Initialising sets the initial values of the public constants and variables that declare one, then runs the initialisation part of the body: g_lookups becomes 0, g_cached_id NULL, and one line is printed.
  3. The first read, then the cache. The function reads 760.25 from the accounts table, counts the read and keeps it. A's second call for the same account answers from the cache, so lookups_so_far stays 1.
  4. B calls the same package. Session B calls balance_of(3) too. Every session that references a package has its own instantiation, so B gets a second copy of the state and its own initialisation runs.
  5. B reads the table again. B's cache is empty, so its call reads the table even though A read the same row a moment ago. Package state is per session, never shared, so B's counter goes to 1 on its own.
  6. A asks for another account. A calls balance_of(4), which is not the cached account, so the body reads 41250.50 and replaces the cached pair. A has now made two table reads.
  7. The counters have parted. lookups_so_far returns 2 in session A and 1 in session B, from the same stored package. Both copies live for as long as their session does.

The model applies the same rules to those six calls. Press Edit, give session B a call for account 4, and run it again: only B’s counter moves.

Two sessions, one package, two sets of state Python · model/session_state_model.py
"""One package, two sessions: Oracle's instantiation rules, modelled.

The model replays account_pkg.sql in two database sessions over the practice
accounts (3 holds 760.25 and 4 holds 41250.50). It applies the documented
rules, so nothing here is a recorded database run.

The rules (PL/SQL Language Reference 26ai, Package Instantiation and
Initialization, and Package State):
1. The first time a session references a package item, Oracle instantiates
   the package for that session. Every session gets its own instantiation.
2. Instantiating it initialises it: initial values for public constants,
   then for public variables that declare one, then the initialisation part
   of the body.
3. The values of the variables, constants and cursors a package declares
   are its package state, and a stateful instantiation keeps its own copy
   for the life of the session.

Edit CALLS to give the sessions other accounts, or call forget_cache, and
run it again: each session's counter follows only its own calls.
"""
from decimal import Decimal

BALANCE = {              # the practice accounts, account_id -> balance
    1: Decimal("2500.00"),
    2: Decimal("18000.00"),
    3: Decimal("760.25"),
    4: Decimal("41250.50"),
}
CALLS = [                # (session, call) in the order the two clients run them
    ("session A", "balance_of(3)"),
    ("session A", "balance_of(3)"),
    ("session B", "balance_of(3)"),
    ("session A", "balance_of(4)"),
    ("session B", "lookups_so_far"),
    ("session A", "lookups_so_far"),
]


class Instantiation:
    """One session's copy of the package state, as the body declares it."""

    def __init__(self, session):
        self.session = session
        self.lookups = 0            # g_lookups
        self.cached_id = None       # g_cached_id
        self.cached_balance = None  # g_cached_balance

    def balance_of(self, account_id):
        """The public function: a table read, or the cached answer."""
        if self.cached_id != account_id:
            self.cached_balance = BALANCE[account_id]
            self.cached_id = account_id
            self.lookups += 1
            return self.cached_balance, "read from the table"
        return self.cached_balance, "answered from the cache"


def reference(sessions, session):
    """Returns the session's instantiation, creating it on first reference."""
    if session in sessions:
        return sessions[session]
    print(f"  {session}: no instantiation yet, so Oracle makes one")
    print("    initialisation part: g_lookups 0, g_cached_id NULL")
    print("    prints: account_api: initialised for this session")
    sessions[session] = Instantiation(session)
    return sessions[session]


def run(calls):
    """Runs the calls in order and reports what each session sees."""
    sessions = {}
    for session, call in calls:
        print(f"{session} calls account_api.{call}")
        state = reference(sessions, session)
        if call == "lookups_so_far":
            print(f"  returns {state.lookups}")
            continue
        account_id = int(call[len("balance_of("):-1])
        balance, how = state.balance_of(account_id)
        print(f"  prints: {how}")
        print(f"  returns {balance}")
    print()
    print("At the end of the two clients' work:")
    for session, state in sessions.items():
        print(f"  {session}: lookups {state.lookups}, cached account "
              f"{state.cached_id}")


run(CALLS)
total = sum(1 for session, _ in CALLS if session == "session A")
print(f"session A made {total} of the {len(CALLS)} calls; neither session "
      "touched the other's state.")

Output

session A calls account_api.balance_of(3)
  session A: no instantiation yet, so Oracle makes one
    initialisation part: g_lookups 0, g_cached_id NULL
    prints: account_api: initialised for this session
  prints: read from the table
  returns 760.25
session A calls account_api.balance_of(3)
  prints: answered from the cache
  returns 760.25
session B calls account_api.balance_of(3)
  session B: no instantiation yet, so Oracle makes one
    initialisation part: g_lookups 0, g_cached_id NULL
    prints: account_api: initialised for this session
  prints: read from the table
  returns 760.25
session A calls account_api.balance_of(4)
  prints: read from the table
  returns 41250.50
session B calls account_api.lookups_so_far
  returns 1
session A calls account_api.lookups_so_far
  returns 2

At the end of the two clients' work:
  session A: lookups 2, cached account 4
  session B: lookups 1, cached account 3
session A made 4 of the 6 calls; neither session touched the other's state.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 session_state_model.py

Checkpoint

Check: State and initialisation per session

  1. Question 1 of 2 A package body ends with an initialisation part that prints one line. A session calls three of the package's procedures, one after the other. How many times is that line printed?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Once, before the first of the three calls does its work

    Instantiation assigns the initial values of the public constants and variables, then runs the initialisation part, once per session.

  2. Question 2 of 2 Session A calls bump three times and session B calls bump twice, on a package body whose initialisation part sets g_count to 0. What does count_so_far return in session B?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 2

    Each session has its own instantiation and its own g_count, so B counts only its own two calls. A reads 3 at the same moment.

What takes that state away, and what keeps callers out

Session state outlives every call in its session, with three exceptions, and the first is this lesson’s problem. When the body of an instantiated, stateful package is recompiled, that package’s next call in each holding session discards the session state and raises ORA-04068, existing state of packages has been discarded. The call does no work, and a second, identical call runs normally against a freshly initialised package, so ORA-04068 is the price a release charges the sessions that were holding state. The other two exceptions are the pragma in the table below and the invalidation that dependencies and compilation covers.

Oracle offers four more tools, and the table says what each does:

Tool What it does
RESETTABLE package: A package marked with the RESETTABLE clause, from Oracle AI Database 26ai Release Update 23.26.0, whose state a session may discard by itself, running the initialisation again at the next reference instead of meeting ORA-04068. A clause of 26ai Release Update 23.26.0: a session may discard such a unit’s state itself, without ORA-04068.
ACCESSIBLE BY: A clause that lists the PL/SQL units allowed to call a package or one of its subprograms; any other caller is refused with PLS-00904, whatever privileges it holds. A clause listing the units allowed to call a package or one subprogram; any other caller is refused with PLS-00904.
SERIALLY_REUSABLE package: A package marked with the SERIALLY_REUSABLE pragma, whose state lives for one server call in a pool the database shares, instead of for the whole session. A pragma that keeps state for one server call rather than the session, in a shared pool.
The writing guidelines Oracle’s advice on what a specification holds: what callers must see, with procedures in place of variables.

Which fits depends on whether the value can be rebuilt, who else needs it and who may ask for it.

Checkpoint

Check: What each named tool is for

  1. Question 1 of 2 Which of the four tools decides which units may call a package or one of its subprograms?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: ACCESSIBLE BY

    Each tool answers its own question: who may call the unit, how long its state lives and what happens to that state when the body changes.

  2. Question 2 of 2 A package keeps a list it reads from a table whenever it initialises, and the next release changes its body. Which tool spares the holding sessions a failed call without moving the value anywhere?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Marking the package RESETTABLE

    The clause fits state that initialisation can rebuild, which is exactly what a list re-read from a table is.

Reproducing ORA-04068 and the RESETTABLE fix Pro: opens with a Pro, Premium or Ultimate pass

Two clients, one stateful package and one recompilation are enough to produce the error on purpose, and watching it happen tells you exactly which sessions pay for a release. The same timeline then runs twice more, with a replacement that discards nothing and with a package that re-initialises itself.

In this part:

  • A two-session timeline of a discarded state
  • The errors the next call reports, and the output it lost
  • When a replacement discards nothing, and what RESETTABLE changes

1 diagram · 5 runnable examples (Python, SQL) · about 10 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Restricting callers with ACCESSIBLE BY and keeping state minimal Pro: opens with a Pro, Premium or Ultimate pass

A package specification is a contract, and ACCESSIBLE BY is how you keep part of the implementation out of it while still splitting the code across units. The same thinking decides how much state a package should hold at all.

In this part:

  • A helper only the API package may call
  • Choosing what a specification exposes, with the trade-offs

1 diagram · 2 runnable examples (Python, SQL) · about 12 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: decide what each session holds

Each question gives a package and a moment in a session. Work out what its state holds first:

Practice that adapts to you

Practise this lesson

8 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 What does a package body add that the specification cannot?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: Private items and an initialisation part

    A body is required as soon as the specification declares a cursor or a subprogram, and it is the only place private items can live.

  2. Question 2 of 8 Which change forces every unit that calls a package to be recompiled?

    Choose one answer.

    Show the answer: Question 2 of 8

    Answer: Adding a parameter to a public procedure

    Dependencies follow the specification. Keeping it small is what stops a change rippling through a schema.

  3. Question 3 of 8 A package specification declares two constants and a type, and nothing else. What does the package need?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: No body at all

    A package of types and constants needs only its specification, and if every item is a compile-time constant Oracle treats it as stateless.

  4. Question 4 of 8 Two sessions call the same stored package, which keeps a cached balance in a body variable. What do they share?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: The compiled package, but not the values in its variables

    This is why a package cache answers quickly for one session and does nothing for a session that has just connected.

  5. Question 5 of 8 When does Oracle run the initialisation part of a package body?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: When a session first references an item of that package

    Initialisation assigns the initial values of public constants and variables, then runs the body's initialisation part, in that order.

  6. Question 6 of 8 A package counts the table reads of a session. Session A reads account 3, then account 3 again from its cache, then account 4. What does its counter show?

    Type a number.

    Show the answer: Question 6 of 8

    Answer: 2

    Only the two calls that went to the table counted; the cached answer did not. A second session doing the same work would keep its own count of 2.

  7. Question 7 of 8 Which tool stops a value from surviving past the server call that set it?

    Choose one answer.

    Show the answer: Question 7 of 8

    Answer: The SERIALLY_REUSABLE pragma

    Each tool answers a different question: how long the state lives, what happens when the body changes, and who may call the unit.

  8. Question 8 of 8 What does ORA-04068 report?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: That a session's existing package state has been discarded

    The documented action is to try again once the application has re-initialised whatever state it keeps itself.

Interview questions

Warm-up (fresher): what goes in a package specification, and what goes only in its body?

The specification holds the declarations callers need, which are the types, constants, exceptions, cursors and subprogram headings they name. The body holds the code behind them, and may add private items of its own, such as a helper procedure or a variable. It can end with an initialisation part that runs on a session’s first reference, and it is required as soon as the specification declares a cursor or a subprogram. The split pays off when code changes: rewriting the body leaves the calling units compiled, while a change to the specification makes them recompile.

Three longer questions follow: a release that broke a connection pool, a cache that outlived its data, and a helper other teams called:

  1. Mid to senior

    A release recompiles one package body at the start of a shift. Users of a web application then see errors from actions that worked all week, and the same action succeeds when they try it again. The database team reports no failed statements of its own. What happened, and how would you release that package next time?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to staff

    A package reads the day's interest rate on its first use and keeps it in a body variable. Operations change the rate during the day, and some sessions go on charging the old one until they disconnect. Where would you keep that rate instead, and how would you prove the fix?

    Model answer, Pro. First step and follow-ups

  3. Senior to staff

    Two other teams found a helper package of yours and now call it directly, skipping the checks your API package does first. Renaming it is out of the question this quarter. How do you stop the direct calls, and what will the other teams see when you do?

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What does each of a package's two pieces hold?

    The specification declares what callers may use, carrying everything a caller needs and nothing about how it works; the body defines those items and hides the private helpers and variables. The body's headings must repeat the declarations word for word apart from white space, or it compiles with PLS-00323.

  2. What happens when a caller names an item declared only in the body?

    It does not compile: a private helper or variable cannot be reached from outside. Oracle's writing guidelines ask for exactly that, declaring only the items invoking programs must see, with procedures in place of variables.

  3. What is a package's state, and how much of it does one session see?

    The values of the variables, constants and cursors it declares in either piece, and one variable is enough to make a package stateful. It is session state: every session that references a package item gets its own instantiation with its own copy, so two clients keep two counters.

  4. What does instantiating a package do, and how often?

    Oracle assigns the initial values of the public constants, then of the public variables that declare one, then runs the body's initialisation part, the block between BEGIN and END at its foot. That happens on the session's first reference, not on every call.

  5. What is ORA-04068, and what did the call that reported it do?

    Existing state of packages has been discarded: the body of an instantiated, stateful package was recompiled, so that package's next call in each holding session throws away the session state. That call does no work, and a second identical call runs normally against a freshly initialised package.

  6. What does the RESETTABLE clause offer?

    From 26ai Release Update 23.26.0, a session may discard such a unit's state itself, without ORA-04068.

  7. What does ACCESSIBLE BY do?

    It lists the units allowed to call a package or one subprogram; any other caller is refused with PLS-00904.

  8. What does PRAGMA SERIALLY_REUSABLE change about state?

    It keeps the state for one server call rather than for the session, in a shared pool.

Key takeaways

  • A specification declares the public items and the body defines them and hides the rest, so only a change to the specification makes the calling units recompile.
  • Package state is the values of the variables, constants and cursors a package declares, and every session gets its own copy of them.
  • Instantiation initialises a package once per session, before its first call does any work.
  • ORA-04068 reports that a session’s package state has been discarded after its body was recompiled, and the call that reports it did no work.

Terms in this lesson

ACCESSIBLE BY
A clause that lists the PL/SQL units allowed to call a package or one of its subprograms; any other caller is refused with PLS-00904, whatever privileges it holds.
package body
The piece of a PL/SQL package that defines everything its specification declares, and may add private items and an initialisation part that nothing outside the package can reach.
package initialisation
What Oracle Database does when a session first references a package: it assigns the initial values of the public constants and variables, then runs the initialisation part at the foot of the body.
package specification
The public face of a PL/SQL package: the declarations other code may name, such as subprogram headings, types, constants, exceptions and cursors, each complete enough to be used without reading the code behind it.
package state
The values of the variables, constants and cursors a package declares in either of its pieces. One variable is enough to make a package stateful, and each session keeps its own copy of the values.
RESETTABLE package
A package marked with the RESETTABLE clause, from Oracle AI Database 26ai Release Update 23.26.0, whose state a session may discard by itself, running the initialisation again at the next reference instead of meeting ORA-04068.
SERIALLY_REUSABLE package
A package marked with the SERIALLY_REUSABLE pragma, whose state lives for one server call in a pool the database shares, instead of for the whole session.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.