4. Run your first SQL query¶
This walkthrough creates a temporary table with three rows, stores an OpenAI API key in a temporary database credential, creates a Select AI profile, and queries the table using natural language. It removes its table, credential, and profile when finished. Use a development database account.
Requests use the API key in OPENAI_API_KEY on each run. Existing database
credentials are not used or replaced. OpenAI API usage is billed separately
from ChatGPT and Codex subscriptions.
4.1. Install and configure Python¶
Use Python 3.11 through 3.14 and an Oracle Database environment with
DBMS_CLOUD_AI available. See Compatibility for
feature-specific requirements.
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install 'select_ai==1.5.0'
On Windows, use the virtual-environment activation command in Installation.
Set the following values in your shell. Choose an OpenAI model supported by your database integration and available to your API project:
export SELECT_AI_USER='your_database_user'
export SELECT_AI_PASSWORD='your_database_password'
export SELECT_AI_DB_CONNECT_STRING='your_database_connect_string'
export OPENAI_API_KEY='your_api_key'
export SELECT_AI_MODEL='gpt-4.1'
To try a different key, replace OPENAI_API_KEY and rerun the script. Keep
keys out of source files and version control. OCI-specific environment
variables are not read by this sample.
For wallet connections, add the parameters described in
Wallet connections to the
sample’s connect() call. Do not commit secrets to the repository.
4.2. Prepare the database account¶
A database administrator must grant the application user Select AI package
privileges and outbound HTTP access to api.openai.com. Pass --setup to
perform both steps before running the example. Set the administrator variables
in addition to the application and OpenAI variables above:
export SELECT_AI_ADMIN_USER='your_database_admin_user'
export SELECT_AI_ADMIN_PASSWORD='your_database_admin_password'
python samples/quickstart.py --setup
The script connects as the administrator, calls grant_privileges() and
grant_network_access(users=..., host="api.openai.com", privileges="http"),
commits the setup, and disconnects. It then connects as the application user
and runs the example. The administrator must be allowed to grant package
privileges and manage network ACLs. Administrator and application connections
use the same DSN; add required wallet arguments to both connection calls.
Run setup once for each application user/database. For later runs, omit the flag; administrator credentials are then unnecessary:
python samples/quickstart.py
The grants remain after the temporary sample objects are removed. See Privileges to revoke package or network privileges later.
The application account must also have CREATE SESSION, CREATE TABLE,
and sufficient tablespace quota to create the temporary table. Have the
administrator assign an appropriate quota in the account’s default tablespace.
The helper does not grant these privileges or quota. The sample does not
create a public credential synonym and does not require CREATE PUBLIC
SYNONYM.
4.3. Run the example¶
Save the following as quickstart.py, or run
python samples/quickstart.py from a repository checkout:
import argparse
import os
import uuid
import select_ai
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--setup",
action="store_true",
help="Grant package privileges and OpenAI HTTP access as administrator",
)
args = parser.parse_args()
user = os.environ["SELECT_AI_USER"]
password = os.environ["SELECT_AI_PASSWORD"]
dsn = os.environ["SELECT_AI_DB_CONNECT_STRING"]
api_key = os.environ["OPENAI_API_KEY"]
model = os.environ["SELECT_AI_MODEL"]
if args.setup:
print("Connecting as administrator...", flush=True)
select_ai.connect(
user=os.environ["SELECT_AI_ADMIN_USER"],
password=os.environ["SELECT_AI_ADMIN_PASSWORD"],
dsn=dsn,
tcp_connect_timeout=15,
)
try:
with select_ai.cursor() as cursor:
cursor.connection.call_timeout = 120_000
print(f"Granting Select AI package privileges to {user}...", flush=True)
select_ai.grant_privileges(users=user)
print(f"Granting {user} HTTP access to api.openai.com...", flush=True)
select_ai.grant_network_access(
users=user, host="api.openai.com", privileges="http"
)
cursor.connection.commit()
finally:
select_ai.disconnect()
suffix = uuid.uuid4().hex[:12].upper()
table_name = f"SDK_DEMO_{suffix}"
credential_name = f"SDK_CRED_{suffix}"
profile_name = f"SDK_PROFILE_{suffix}"
print(f"Connecting as application user {user}...", flush=True)
select_ai.connect(user=user, password=password, dsn=dsn, tcp_connect_timeout=15)
table_created = credential_created = profile_created = False
try:
# This identifier is generated locally, rather than supplied by a caller.
with select_ai.cursor() as cursor:
cursor.connection.call_timeout = 120_000
cursor.execute("SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') FROM DUAL")
owner = cursor.fetchone()[0]
print("Creating the sample table...", flush=True)
cursor.execute(
f"CREATE TABLE {table_name} (id NUMBER PRIMARY KEY, name VARCHAR2(40))"
)
table_created = True
cursor.executemany(
f"INSERT INTO {table_name} (id, name) VALUES (:1, :2)",
[(1, "Notebook"), (2, "Keyboard"), (3, "Monitor")],
)
cursor.connection.commit()
print("Creating the OpenAI credential and profile...", flush=True)
select_ai.create_credential(
{
"credential_name": credential_name,
"username": "openai",
"password": api_key,
}
)
credential_created = True
profile = select_ai.Profile(
profile_name=profile_name,
attributes=select_ai.ProfileAttributes(
provider=select_ai.OpenAIProvider(model=model),
credential_name=credential_name,
object_list=[{"owner": owner, "name": table_name}],
),
)
profile_created = True
prompt = f"How many rows are in {table_name}? Return the count as ITEM_COUNT."
print("Generating SQL with OpenAI...", flush=True)
print("Generated SQL:", profile.show_sql(prompt), flush=True)
# run_sql generates and executes a fresh response to the prompt.
print("Generating and executing the query with OpenAI...", flush=True)
print(profile.run_sql(prompt))
finally:
print("Removing temporary objects and closing the connection...", flush=True)
# Attempt each cleanup even if an earlier cleanup fails.
try:
if profile_created:
select_ai.Profile.delete_profile(profile_name, force=True)
finally:
try:
if credential_created:
select_ai.delete_credential(credential_name, force=True)
finally:
try:
if table_created:
with select_ai.cursor() as cursor:
cursor.execute(f"DROP TABLE {table_name} PURGE")
finally:
select_ai.disconnect()
The script prints generated SQL and a DataFrame containing a count of three. Table names include a generated suffix. SQL formatting and the model’s chosen column label may vary; representative result output is:
ITEM_COUNT
0 3
The profile’s object_list includes the sample table using the schema name
resolved from the database connection. Other profile attributes use their
defaults, including object-list enforcement. The object list supplies metadata
for SQL generation; database privileges determine which objects the user can
access. See Profile attributes for explicit
enforcement settings.
show_sql() generates SQL without executing it. run_sql() makes a
separate generation request and executes that response; it does not execute
the SQL string returned by the earlier call. In applications that require
approval of an exact statement, review the generated statement and execute
that approved statement through your database API.
If connection fails, check the DSN, password, and wallet settings. For package or network authorization failures, return to the administrator setup. For model or provider errors, check the API key, model access, API billing, and project limits. See Error handling for Python exception handling. The sample’s cleanup removes only objects it successfully created; administrator grants remain in place for subsequent workflows.
4.4. Waiting and timeouts¶
The script prints progress before connecting, creating objects, and requesting
SQL from OpenAI. If it waits at Generating SQL with OpenAI..., the
database is processing the model request. Missing network ACL privileges usually raise
an authorization error; waiting alone does not prove that the ACL is missing.
Check database egress, provider availability, API billing, and project limits.
HTTP 429 can indicate quota or rate limits; retries may extend the wait.
The sample sets tcp_connect_timeout=15 for connection establishment and
connection.call_timeout=120_000 for database calls. The latter is in
milliseconds and limits each database round trip, rather than total script
runtime. Increase it deliberately for slower requests. See the
driver timeout documentation.