Chapter 12 · Triggers, Dynamic SQL, Scheduler, Advanced PL/SQL, and Database APIs

Design Server-Side APIs with Packages, Privileges, Transaction Boundaries, and Deployment Discipline

Expose base-table behavior through a narrow package API with intentional definer/invoker rights, caller-owned transactions, explicit grants, deployment validation, versioning, and tested rollback artifacts.

Advanced125–145 minutesLeast-privilege package API + deployment labAUTHID DEFINER/CURRENT_USER boundaries explicitSERVICEHUB_OWNER + SERVICEHUB_APP course identitiesLast reviewed: August 2026

Learning outcomes

ServiceHub's runtime account currently has direct SELECT/INSERT/UPDATE/DELETE privileges on owner tables because “the application needs the data.” As a result, every code path can bypass validation, audit context, and state transitions. The safer database API pattern keeps base tables private and grants the application EXECUTE on a narrow package whose privilege model, transaction ownership, version contract, and deployment rollback are explicit.

01

Design a stable package specification that exposes business operations rather than base-table CRUD.

02

Choose AUTHID DEFINER versus AUTHID CURRENT_USER intentionally and account for direct grants and INHERIT PRIVILEGES behavior.

03

Grant the runtime account EXECUTE without unrestricted table privileges and verify direct table access fails.

04

Keep transaction ownership with the application caller unless a background/autonomous contract explicitly says otherwise.

05

Validate compiled objects, privileges, dependencies and API version during deployment and roll back by redeploying a tested prior artifact rather than assuming DDL rollback.

Version, tooling, scope, privilege, and licensing baseline

Mandatory work targets a disposable ServiceHub schema in Oracle AI Database Free 26ai and was reviewed against RU 23.26.3 (July 2026), SQL Developer 26.2, and SQLcl 26.2.1. Free is capped at 2 foreground CPU cores, 2 GB combined SGA/PGA memory, and 12 GB user data and receives no Oracle patches or Oracle Support service requests. The mandatory Chapter 12 path uses local database features only: no Diagnostics/Tuning Pack, RAC, Data Guard, GoldenGate, Exadata, OCI service, remote Scheduler agent, or COMPATIBLE change is required. Stored program/trigger creation requires CREATE PROCEDURE/CREATE TRIGGER as appropriate; Scheduler job creation requires CREATE JOB. Cross-schema API tests use the course identities SERVICEHUB_OWNER and SERVICEHUB_APP with narrow grants.

1. A package API is a privilege boundary

With definer's-rights code, a runtime account can execute a package operation without receiving direct privileges on the underlying table. The package owner becomes responsible for restricting which columns/rows/actions are exposed. Oracle's Security Guide specifically presents definer-rights procedures as a way to give users controlled access to private objects through EXECUTE.

sql · owner table
CREATE TABLE servicehub_api_case(  case_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,  external_ref VARCHAR2(40) NOT NULL UNIQUE,  status_code VARCHAR2(12) NOT NULL,  amount NUMBER(10,2) NOT NULL,  created_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,  CONSTRAINT sh12_api_status_ck    CHECK(status_code IN ('OPEN','HOLD','CLOSED')));

2. Expose business verbs, not unrestricted CRUD

sql · stable package contract
CREATE OR REPLACE PACKAGE servicehub_work_apiAUTHID DEFINERAS  FUNCTION api_version RETURN VARCHAR2;  PROCEDURE create_case(    p_external_ref IN VARCHAR2,    p_amount       IN NUMBER,    p_case_id      OUT NUMBER  );  PROCEDURE set_status(    p_case_id IN NUMBER,    p_status  IN VARCHAR2  );  FUNCTION get_status(    p_case_id IN NUMBER  ) RETURN VARCHAR2;END servicehub_work_api;/
sql · hidden implementation — no COMMIT
CREATE OR REPLACE PACKAGE BODY servicehub_work_api AS  FUNCTION api_version RETURN VARCHAR2 IS  BEGIN    RETURN '1.0.0';  END;  PROCEDURE create_case(    p_external_ref IN VARCHAR2,    p_amount       IN NUMBER,    p_case_id      OUT NUMBER  ) IS  BEGIN    IF p_amount IS NULL OR p_amount <= 0 THEN      RAISE_APPLICATION_ERROR(-20410,'amount must be positive');    END IF;    INSERT INTO servicehub_api_case(external_ref,status_code,amount)    VALUES(UPPER(TRIM(p_external_ref)),'OPEN',p_amount)    RETURNING case_id INTO p_case_id;  END;  PROCEDURE set_status(p_case_id IN NUMBER,p_status IN VARCHAR2) IS    l_status VARCHAR2(12) := UPPER(TRIM(p_status));  BEGIN    IF l_status NOT IN ('OPEN','HOLD','CLOSED') THEN      RAISE_APPLICATION_ERROR(-20411,'invalid status transition value');    END IF;    UPDATE servicehub_api_case    SET status_code=l_status    WHERE case_id=p_case_id;    IF SQL%ROWCOUNT=0 THEN      RAISE_APPLICATION_ERROR(-20412,'unknown case_id');    END IF;  END;  FUNCTION get_status(p_case_id IN NUMBER) RETURN VARCHAR2 IS    l_status servicehub_api_case.status_code%TYPE;  BEGIN    SELECT status_code INTO l_status    FROM servicehub_api_case    WHERE case_id=p_case_id;    RETURN l_status;  EXCEPTION    WHEN NO_DATA_FOUND THEN      RAISE_APPLICATION_ERROR(-20412,'unknown case_id');  END;END servicehub_work_api;/

The specification is the client contract; body changes can preserve that contract. The version function is implemented in the body so changing the reported implementation version does not require changing a public constant declaration.

3. Definer rights and invoker rights solve different security problems

AUTHID DEFINER is the default. It is appropriate for a narrow API that intentionally lets callers perform controlled operations using the owner's privileges. If definer-rights code references objects in another schema, required object privileges must be granted directly to the owner rather than only through roles.

AUTHID CURRENT_USER is appropriate when the operation should use the caller's object resolution/privileges. Oracle also protects invoker-rights execution with INHERIT PRIVILEGES/INHERIT ANY PRIVILEGES: a hardened site can revoke default inheritance and explicitly allow only trusted invoker-rights owners.

Do not use invoker rights by reflex

Changing an API to AUTHID CURRENT_USER can make its behavior depend on the caller schema, active roles, object names, and grants. Choose it for an explicit security model, not as a generic “more secure” setting.

4. Remove direct table access and grant only EXECUTE

The course already established SERVICEHUB_APP as a runtime identity. For this new table, first inspect whether any legacy direct grants actually exist; revoke only grants that are present as part of a deliberate migration, then grant the package capability.

sql · inspect then grant the API
-- As SERVICEHUB_OWNER (or an administrator where required):SELECT grantee,owner,table_name,privilegeFROM all_tab_privsWHERE grantee='SERVICEHUB_APP'  AND owner=USER  AND table_name='SERVICEHUB_API_CASE'ORDER BY privilege;-- If legacy direct grants are returned above, revoke exactly those grants.-- Example migration only when SELECT/INSERT/UPDATE/DELETE are actually present:-- REVOKE SELECT,INSERT,UPDATE,DELETE-- ON servicehub_api_case FROM servicehub_app;GRANT EXECUTE ON servicehub_work_api TO servicehub_app;

This keeps the mandatory lab rerunnable: a REVOKE for a privilege that was never granted can fail. Production deployment scripts should query current grants and make privilege migrations explicit rather than suppressing every error.

sql · verify effective object grants
SELECT grantee,owner,table_name,privilegeFROM all_tab_privsWHERE grantee='SERVICEHUB_APP'  AND owner=USER  AND table_name IN ('SERVICEHUB_API_CASE','SERVICEHUB_WORK_API')ORDER BY table_name,privilege;

5. Caller-owned transactions make composition possible

The API body deliberately does not commit. A web request may need to call two packages and either commit both or roll back both. Hidden commits make that composition impossible.

sql · runtime user calls API then rolls back
-- Connect as SERVICEHUB_APP.VARIABLE new_case NUMBEREXEC servicehub_owner.servicehub_work_api.create_case('EXT-9001',250,:new_case);PRINT new_caseSELECT servicehub_owner.servicehub_work_api.get_status(:new_case)AS status_before_rollbackFROM dual;-- OPENROLLBACK;BEGIN  DBMS_OUTPUT.PUT_LINE(    servicehub_owner.servicehub_work_api.get_status(:new_case)  );END;/-- Expected application error -20412 because the insert was rolled back.

Background Scheduler jobs can own their transaction; request APIs usually should not. State the transaction owner in the API documentation.

6. Deliberately wrong: grant table CRUD and hope clients voluntarily use the package

If SERVICEHUB_APP keeps direct table DML, it can bypass the package validation and future policy. The package becomes a convenience library rather than a security boundary.

sql · negative test as runtime user
SELECT COUNT(*)FROM servicehub_owner.servicehub_api_case;-- With no SELECT grant, direct access should fail (commonly ORA-00942).UPDATE servicehub_owner.servicehub_api_caseSET status_code='CLOSED';-- Direct DML should fail without the object privilege.

The package call can still succeed because definer-rights code accesses the owner's table through its controlled program unit.

7. Deployment is DDL: validate before routing traffic

CREATE OR REPLACE PACKAGE is DDL and is not transactional deployment in the application sense. Capture the previous tested artifact before deployment. After compiling, verify status/errors, signatures, dependent objects, and grants before directing traffic to the new code.

sql · post-deployment checks
SELECT object_name,object_type,status,editionable,last_ddl_timeFROM user_objectsWHERE object_name='SERVICEHUB_WORK_API'ORDER BY object_type;SELECT name,type,line,position,textFROM user_errorsWHERE name='SERVICEHUB_WORK_API'ORDER BY type,sequence;SELECT package_name,object_name,argument_name,position,       in_out,data_type,defaultedFROM user_argumentsWHERE package_name='SERVICEHUB_WORK_API'ORDER BY object_name,overload,sequence;SELECT referenced_name,referenced_type,dependency_typeFROM user_dependenciesWHERE name='SERVICEHUB_WORK_API'ORDER BY referenced_type,referenced_name;

A rollback is normally “redeploy the previously tested package spec/body and grants,” not ROLLBACK. Edition-Based Redefinition can provide richer coexistence/rollback architecture, but it is not assumed or configured in this mandatory lab.

8. Versioning and compatibility discipline

Treat the package specification like an API schema. Additive overloads can still create ambiguity; removing/renaming parameters breaks callers; changing default behavior can be a semantic breaking change even if compilation succeeds. Keep API tests in source control and validate both old and new application callers during staged deployment.

sql · runtime contract evidence
SELECT servicehub_owner.servicehub_work_api.api_version AS api_versionFROM dual;SELECT owner,object_name,procedure_name,authidFROM all_proceduresWHERE owner='SERVICEHUB_OWNER'  AND object_name='SERVICEHUB_WORK_API'ORDER BY procedure_name;

9. Reproducible owner cleanup

sql · cleanup after runtime session disconnects/finishes
-- As SERVICEHUB_OWNER:REVOKE EXECUTE ON servicehub_work_api FROM servicehub_app;DROP PACKAGE servicehub_work_api;DROP TABLE servicehub_api_case PURGE;

Do not drop the shared course runtime account. It belongs to the cross-chapter environment established earlier.

10. Production judgment and chapter bridge

A package API is appropriate when the database should own validated business operations, privilege mediation, or reusable transactional behavior. Keep the spec stable, grants narrow, body side effects explicit, and transaction control caller-owned unless the unit is independently scheduled/autonomous by design. Audit privilege changes and test negative access paths.

No paid option, management pack, restart, or COMPATIBLE change is required. This closes the advanced PL/SQL/API chapter. Chapter 13 moves to multitenant architecture—CDB root, PDBs, common/local identities, services, cloning, portability, resource/isolation boundaries, and lifecycle operations—where package/API privileges and Scheduler state must be understood in container scope.

Check your understanding

  1. Why can EXECUTE on a definer-rights package replace direct table DML grants?
  2. Why should a request-oriented package usually avoid COMMIT?
  3. What extra security concept matters for invoker-rights code?
  4. Can ROLLBACK undo CREATE OR REPLACE PACKAGE?
  5. What should a deployment validate besides object STATUS=VALID?
Review the answers

The package runs its controlled operations under the definer security domain, letting the caller perform only the exposed verbs without unrestricted base-table privileges.

The caller may need to compose several operations into one transaction and decide whether all of them commit or roll back together.

INHERIT PRIVILEGES/INHERIT ANY PRIVILEGES controls whether the program owner may inherit an invoking user’s privileges.

No. Package replacement is DDL; restore a prior tested artifact or use a designed edition/deployment mechanism.

Compiler errors, public signatures, dependencies, grants, negative privilege tests, behavior tests, API version, and application compatibility.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.