Every call to an AI provider sends part of your data across the line between your database and someone else's systems. Much of what a prompt carries is not needed: to classify a ticket or summarize a conversation, the model does not need the customer's phone number. Removing such details before the call keeps them out of the provider's systems and out of your own logs.
This guide shows how to see exactly what left the database, redact email addresses, card numbers, and phone numbers with a PL/SQL function, and check that the API key and network access are locked down, all in Oracle AI Database 26ai.
Code for This Guide
The examples are files 01 to 03 in the examples/ch27 folder of the Oracle AI code repository on GitHub, each with its output.
These examples come from AI Applications with Oracle Database 26ai and APEX 26.1, a book of 237 tested examples of AI in Oracle Database and APEX.
They read the LLM_CALLS log written by the GENERATE function from how to make LLM calls reliable in PL/SQL.
See What Left the Database
The first question of any review of an AI feature: what was sent, to whom, and how much? When every model call goes through one logging function, the log answers it.
Example:
-- everything GENERATE sent to a provider is in the log: how much, to which model
select model, count(*) as calls,
round(sum(dbms_lob.getlength(prompt)) / 1024) as prompt_kb,
min(called_at) as first_call
from llm_calls
group by model
order by calls desc;Output:
MODEL CALLS PROMPT_KB FIRST_CALL ______________ ________ ____________ _______________________ GEMINI 237 481 02-OCT-2026 07:41:52 LLAMA_LOCAL 134 90 02-OCT-2026 08:22:15 GEMINI_LITE 13 5 02-OCT-2026 07:49:25
Every prompt that went to Google is in the log, with its time: hundreds of calls and about half a megabyte of text for Gemini, plus the calls to a local model, which went nowhere. One central function for all model calls is a security measure, not only a convenience: it is the one place to log, to redact, and to block.
The log holds whatever the prompts held, ticket texts and customer names included, so it needs the same protection as the data: access limited to the people who investigate calls, and a retention period, such as a nightly DELETE FROM llm_calls WHERE called_at < SYSTIMESTAMP - INTERVAL '30' DAY.
Providers' terms differ and change. At the time of writing, Google's terms for the Gemini API say content sent on the paid tier is not used to improve its products, while content on the free tier may be. Read your provider's terms for your tier before sending real data.
Redact Before the Call
REDACT replaces email addresses, card numbers, and phone numbers with placeholders.
Example:
-- replaces e-mail addresses, card numbers, and phone numbers with placeholders
create or replace function redact (p_text in clob) return clob
is
l_text clob := p_text;
begin
l_text := regexp_replace(l_text,
'[[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}', '[e-mail]');
l_text := regexp_replace(l_text,
'\d{4}[ -]?\d{4}[ -]?\d{4}[ -]?\d{1,4}', '[card number]');
l_text := regexp_replace(l_text,
'\+?\d{1,3}[ .-]?\(?\d{2,4}\)?[ .-]?\d{3,4}[ .-]?\d{3,4}', '[phone]');
return l_text;
end;
/
select redact('Please call me at +44 20 7946 0958 or write to '
|| 'olivia.walker@northwind.example. The charge was on card '
|| '4111 1111 1111 1111, on 3 March 2026.') as redacted
from dual;Output:
Function REDACT compiled REDACTED __________________________________________________________________________________________________ Please call me at [phone] or write to [e-mail]. The charge was on card [card number], on 3 March 2026.
The placeholders keep the sentence readable for the model, which still knows a phone number and a card were mentioned, and the date survives.
| Where to call REDACT | Effect |
|---|---|
| Inside the central GENERATE function | No feature built on it can forget redaction. |
| In an APEX request handler | Every AI call of the application is redacted, including assistants and agents. |
| Around individual prompts | Works, but each new feature must remember it. |
Applying it in an APEX request handler is shown in how to intercept AI calls with request handlers in APEX.
Regular expressions catch only the patterns they are written for. Names and addresses need more, such as a list of the customer's own details to replace, and no method is complete. Send the least data a task needs. For masking data in query results shown to users, see mask sensitive data in Oracle APEX with Oracle Data Redaction (DBMS_REDACT).
Lock Down the Key and the Network
Two database features do quiet, important work:
- The credential holds the API key encrypted. Code refers to it by name, and nobody can read the key back, not even the schema that owns it.
- The network ACL allows connections only to the hosts the schema needs. Without an entry for a host, the schema cannot reach it, whatever its code tries.
Example:
-- the key is stored in the credential, encrypted; the dictionary shows its name only select credential_name, username, enabled from user_credentials; -- and the network access of the schema: one host for Gemini, one port for Ollama select host, lower_port, upper_port, privilege from user_host_aces order by host;
Output:
CREDENTIAL_NAME USERNAME ENABLED __________________ ___________ __________ GEMINI_CRED NA TRUE HOST LOWER_PORT UPPER_PORT PRIVILEGE ____________________________________ _____________ _____________ ____________ generativelanguage.googleapis.com HTTP host.docker.internal 11434 11434 HTTP
The credential shows its name and nothing else. The ACL entries allow Gemini's host and Ollama's port, and code that tried to send data anywhere else would fail with ORA-24247. Keep ACLs to the hosts you use, change keys by dropping and recreating the credential, and give each application schema its own key so one can be revoked without the others. Setting both up is covered in how to call Gemini from Oracle Database with a stored credential.
Conclusion
Control what leaves Oracle Database by routing every model call through one logging function, protecting and expiring that log, and redacting email addresses, card numbers, and phone numbers before each prompt, ideally inside the central function or an APEX request handler. Keep the API key in an encrypted credential and limit network ACLs to the providers you use, and send only the data each task needs.
