Oracle AI Database 26ai can send a prompt to Google Gemini straight from SQL and get the answer back as a CLOB. No middle tier is involved. The only secret is a Gemini API key, and it should never appear in your code.
This guide shows how to give a schema the right to call Gemini, store the API key as a database credential, make the first call with DBMS_VECTOR_CHAIN.UTL_TO_GENERATE_TEXT, and fix the errors you are most likely to meet.
Code for This Guide
The scripts used here are in the Oracle AI code repository on GitHub:
| File | What it does |
|---|---|
| setup/atlas/create-user.sql | Creates the sample schema ATLAS with the privileges and network access below |
| examples/ch02 | The credential, the first call, and a privilege check, 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.
The examples run as the user ATLAS in Oracle AI Database 26ai Free. Any schema works if it has the same privileges.
What You Need
| Requirement | Why |
|---|---|
| A Gemini API key | Identifies your Google account to the Gemini API. |
| The CREATE CREDENTIAL privilege | Lets the schema store the key. Without it, creating the credential fails with ORA-27486: insufficient privileges. |
| EXECUTE on UTL_HTTP | The vector packages make their HTTPS calls through it. |
| A network access control entry | Oracle blocks every outbound connection unless an ACE allows the host. |
Get a Gemini API Key
- Open Google AI Studio at aistudio.google.com and sign in with a Google account.
- Choose Get API key, then Create API key. AI Studio creates the key in a Google Cloud project: pick an existing project or let it create one.
- Copy the key.
A new key is on the free tier. It costs nothing but allows a limited number of requests per minute and per day, and Google may use the prompts you send to improve its products. Send only test data on a free key. A key on a paid project raises the limits and keeps your prompts out of training; you pay per token.
Treat the key like a password. Anyone who has it can use your quota, or spend your money on a paid project. Keep it out of code, shared scripts, screenshots, and version control.
Grant the Privileges
As a database administrator, grant the schema the right to store credentials and to make HTTP calls, then allow the Gemini host in an access control entry. These are the relevant lines of the setup script.
Example (run as an administrator in the pluggable database):
grant create credential to atlas; -- keys of AI providers, stored in the database
-- calling AI providers over HTTPS (the host list is in the ACL below)
grant execute on sys.utl_http to atlas;
begin
dbms_network_acl_admin.append_host_ace(
host => 'generativelanguage.googleapis.com',
ace => xs$ace_type(privilege_list => xs$name_list('http'),
principal_name => 'ATLAS',
principal_type => xs_acl.ptype_db));
end;
/APPEND_HOST_ACE grants the http privilege on one host, here generativelanguage.googleapis.com, to one user. The http privilege covers HTTPS too. A wildcard such as *.googleapis.com would allow every Google API host, which is broader than you need.
Connected as the schema, you can check what it ended up with.
Example (run as ATLAS):
select role from session_roles order by role;
select privilege from session_privs
where privilege in ('CREATE MINING MODEL', 'CREATE CREDENTIAL')
order by privilege;
select host, privilege from user_host_aces;Output:
ROLE ____________________ CTXAPP DB_DEVELOPER_ROLE RESOURCE SODA_APP PRIVILEGE ______________________ CREATE CREDENTIAL CREATE MINING MODEL HOST PRIVILEGE ____________________________________ ____________ generativelanguage.googleapis.com HTTP
Store the Key as a Credential
A credential is a schema object that holds a secret, encrypted, under a name. Code refers to the credential by its name and never sees the key itself. DBMS_VECTOR_CHAIN creates and drops them.
Syntax:
dbms_vector_chain.create_credential(credential_name varchar2, params json) dbms_vector_chain.drop_credential(credential_name varchar2)
PARAMS is a JSON object whose fields depend on the provider. For the Gemini API, the key goes in the field access_token. Replace the placeholder with your own key when you run this, and do not save the edited script anywhere it could be shared.
Example (run as ATLAS):
declare
params json_object_t := json_object_t();
begin
params.put('access_token', '<your-gemini-api-key>');
dbms_vector_chain.create_credential(
credential_name => 'GEMINI_CRED',
params => json(params.to_string));
end;
/Output:
PL/SQL procedure successfully completed.
Three things to know about credentials:
- The field name is not checked. With any other name, such as api_key, the credential is still created, but the whole JSON object is sent to Google as the key, and every call fails with API key not valid.
- A credential belongs to the schema that created it. Create it in each schema that calls Gemini.
- To change the key, drop the credential and create it again with the new key.
Make the First Call
DBMS_VECTOR_CHAIN.UTL_TO_GENERATE_TEXT sends a prompt to a provider and returns the answer as a CLOB. Its second argument is a JSON object with four fields:
| Field | Value for Gemini |
|---|---|
| provider | googleai |
| credential_name | The credential that holds the key, GEMINI_CRED |
| url | https://generativelanguage.googleapis.com/v1beta/models/ |
| model | The model name followed by :generateContent |
Example:
select dbms_vector_chain.utl_to_generate_text(
'In one sentence, what does a help desk do?',
json('{"provider": "googleai",
"credential_name": "GEMINI_CRED",
"url": "https://generativelanguage.googleapis.com/v1beta/models/",
"model": "gemini-flash-latest:generateContent"}')) as answer
from dual;Output:
ANSWER __________________________________________________________________________________________________ A help desk serves as a centralized point of contact to troubleshoot technical issues, answer questions, and provide ongoing support to users.
Language models do not answer the same way twice, so your sentence will differ. The model gemini-flash-latest is a name that Google points at its current fast, inexpensive model. Older model names are retired over time, and the -latest name keeps working code from breaking when that happens.
Fix Common Errors
| Error | Cause | Fix |
|---|---|---|
| ORA-24247: network access denied by access control list | No ACE for the host | Run APPEND_HOST_ACE for generativelanguage.googleapis.com |
| ORA-20003: missing URL parameter | The url field is missing | Add the url field shown above |
| ORA-20002: The provider returned an error: (with no text) | The model name is wrong or lacks :generateContent | Use "model": "gemini-flash-latest:generateContent" |
| API key not valid | The credential's field is not access_token, or the key is wrong | Drop and create the credential again |
| This model is currently experiencing high demand | Google's servers are busy | Wait a minute and try again |
| This model is no longer available to new users | The model has been retired | Use gemini-flash-latest |
Gemini in Oracle APEX
APEX does not use the database credential. It calls Gemini through a Generative AI Service of the workspace, which stores the key as a web credential of its own. Setting that up is covered in generative AI in Oracle APEX: services, agents, and tools.
Conclusion
To call Gemini from Oracle Database, grant the schema CREATE CREDENTIAL and EXECUTE on UTL_HTTP, allow generativelanguage.googleapis.com with APPEND_HOST_ACE, and store the API key with DBMS_VECTOR_CHAIN.CREATE_CREDENTIAL in the field access_token. UTL_TO_GENERATE_TEXT then calls the model by the credential's name, so the key never appears in your SQL, your logs, or your repository.
