Roles, definer’s rights, and why your Data Pump procedure suddenly breaks
I ran into this while testing what should have been a pretty simple setup.
A procedure that calls DBMS_DATAPUMP, executed by another user.
Nothing fancy.
And yet it failed with:
ORA-31631: privileges are required
Which didn’t make sense.
The procedure owner had the right roles.
The grants were there.
And this exact setup used to work in 19c.
Same code. Same grants. Different result.
So what changed?
The Setup
Two users:
dpump
Has full Data Pump privileges:
- DATAPUMP_EXP_FULL_DATABASE
- DATAPUMP_IMP_FULL_DATABASE
dpump_user
Just enough to run the procedure:
grant connect to dpump_user;grant execute on expdp_schema to dpump_user;
The procedure is owned by dpump and internally calls DBMS_DATAPUMP on different schemas.
The idea is straightforward:
Let a low-privilege user run exports without giving them full access.
What I Expected
This is a definer’s rights procedure.
So the mental model is:
“Use the privileges of the procedure owner, not the caller.”
That’s how it behaved in 19c and 21c.
What Actually Happens
On 23ai / 26ai:
ORA-31631: privileges are required
Even though:
- The owner has the role
- The role is correctly granted
That’s the part that’s confusing.
What Tripped Me Up
The assumption is that roles “just work” inside PL/SQL.
They don’t. At least not here.
Something changed in newer releases, and you really notice it when you hit this.
When it’s called from inside a stored procedure:
- Role-based privileges aren’t used the way you expect
- Having the role is not enough
- Definer’s rights alone don’t save you here
So even though everything looks correct, it still fails.
First Thing I Tried
Switching to invoker’s rights:
CREATE OR REPLACE PROCEDURE expdp_schemaAUTHID CURRENT_USERAS...
This actually does work, just not for this use case.
With AUTHID CURRENT_USER, you’re saying:
“Only run this if the caller already has the required privileges.”
So:
dpumpworksdpump_userfails
Which is expected.
And to be fair, this is a good pattern when you want to enforce that.
If your goal is:
“Only allow users who already have Data Pump privileges to run this”
Then AUTHID CURRENT_USER is exactly what you want.
But That Wasn’t the Goal Here
In this case, the goal was different:
Let a low-privilege user run a very specific Data Pump operation, without giving them full access.
So instead of checking the user’s privileges, the idea is:
Hide the complexity behind a controlled interface.
What Actually Worked
The fix ended up being simpler than expected.
Grant the role directly to the procedure:
grant DATAPUMP_EXP_FULL_DATABASE to PROCEDURE dpump.expdp_schema;
Why This Works
Now the procedure itself has the required privilege.
Which means:
- The caller doesn’t need Data Pump roles
- The logic stays controlled inside the procedure
- The user can only do exactly what the procedure allows
It basically turns the procedure into a black box: The user can run it. They just can’t go beyond it.
Takeaway
There are really two valid approaches here, depending on what you want:
Use AUTHID CURRENT_USER when:
- You want to enforce privilege checks on the caller
- Users should already have the required roles
Grant the role to the procedure when:
You’re building a “black box” interface
You want to expose a controlled operation
You don’t want to give broad privileges to users



Leave a comment