DBA Blogs

How to modify the database parameters in Data guard with RAC

Tom Kyte - Mon, 2026-08-24 10:15
I have a task to modify PDB parameters, for example, setting `RECYCLEBIN` to `ON`. I changed the parameter on the primary PDB, but the change is not reflected on the standby database. The Parmeter is dynamic even its not reflected on standby side.
Categories: DBA Blogs

Application Containers leaving behind "Fake" PDBs

Tom Kyte - Mon, 2026-08-24 10:15
Hello, I was messing around with Application Containers, Roots, and PDBs and ran into something very interesting that I do not fully understand. It appears that the Application Root creates clones of itself when performing application sync - more specifically an uninstall. Please see below example: <code> create pluggable database app_root2 as application container admin user pdbadmin identified by "c0nn0r_h3lp!" -- use only if you're not using oracle managed files -- ensure you set the directory to where your datafiles are stored file_name_convert=('/u03/datafiles/ORCL/pdbseed/','/u03/datafiles/ORCL/APP_ROOT2/'); alter pluggable database APP_ROOT2 open; alter session set container = APP_ROOT2; alter pluggable database application test1 begin install '1.0'; create table test1 sharing=data (column1 number, column2 varchar2(120)); alter pluggable database application test1 end install; create pluggable database cust3 admin user pdbadmin identified by "cust1_pwd!" file_name_convert=('/u03/datafiles/ORCL/pdbseed/','/u03/datafiles/ORCL/APP_ROOT2/CUST3/'); alter pluggable database cust3 open; -- look at the list of PDBs prior: alter session set container = CDB$ROOT; show pdbs; -- go back into the root and sync: alter session set container = APP_ROOT2; alter session set container = CUST3; alter pluggable database application test1 sync; alter session set container = APP_ROOT2; alter pluggable database application test1 begin uninstall; drop table test1; alter pluggable database application test1 end uninstall; alter session set container = cust3; alter pluggable database application test1 sync; alter session set container = CDB$ROOT; select con_id, name, application_root, application_pdb, application_root_con_id from v$pdbs where name like 'F%'; select con_id, name, application_root, application_pdb, application_root_con_id from v$pdbs -- be sure to put YOUR application root con id returned from the above query here in this where clause where con_id = 12; </code> Any attempt to close, alter, etc. this "fake" PDB is rejected with an "ORA-65266 - application root clone may not be dropped, unplugged or altered" Another interesting note it is classified as an application root and an application PDB - which if we look at the root PDB it is an Application Root but not an application PDB. My two questions: 1. What exactly is this PDB? 2. Why is it created during application synchronization/uninstallation? Thank you
Categories: DBA Blogs

Natural Language Support for Interactive Report Using OpenAI gpt-5.6 Models Fails

Tom Kyte - Mon, 2026-08-24 10:15
Hello Tom, I have just upgraded to APEX v26.1 and enabled Natural Language Support for Interactive Report on one of my IRs, as a new feature of this upgrade. I have also created a Generative AI provider using OpenAI using the gpt-5.6-terra model. Using this AI provider for other AI features of APEX works fine. When I then try to search or ask a question on the said IR, it throws an error: ?<i>ORA-20954: The HTTP request to Generative AI Service at https://api.openai.com/v1/chat/completions failed with HTTP-400: Function tools with reasoning_effort are not supported for gpt-5.6-terra in /v1/chat/completions. To use function tools, use /v1/responses or set reasoning_effort to 'none?.</i>" Upon reading the latest APEX documentation further, it clearly says: <i>OpenAI - Uses /chat/completions for chat and /embeddings for vector generation. OpenAI expects JSON with messages[] and model parameters, and authenticates using a Bearer token. APEX maps its internal format to the OpenAI schema, injects credentials, and normalizes responses so applications see a consistent structure across models and providers.</i> That explains the problem. OpenAI have also made it clear that they?re migration their tools to use /v1/responses, though /v1/chat/completions is till supported for some things. Is there a workaround for this, other than using a different AI provider? Personally, the limitation as far as I can tell is that Oracle APEX does not seem to be providing the flexibility, at least on the Natural Language Support for Interactive Report feature, to select a REST API to an AI provider tool. The Natural Language Support for Interactive Report feature relies on APEX?s preconfigured internal mapping to the supported AI providers. Am I wrong?
Categories: DBA Blogs

Compound Triggers: Are declarations in timing_point section allowed?

Tom Kyte - Mon, 2026-08-24 10:15
Hi, If I?m reading the documentation (picture of timing_point_section in https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/CREATE-TRIGGER-statement.html) correctly, variable declarations are not allowed within the timing-point-section. However, the compiler accepts the declarations and the code works as well. The question is: Is the documentation incomplete or is my code wrong? Here?s a meaningless but working example: <code> CREATE OR REPLACE TRIGGER cmptrg_test FOR UPDATE OR INSERT OR DELETE ON my_table COMPOUND TRIGGER l_var number; AFTER STATEMENT IS l_cnt NUMBER; --possible here? The documentation doesn?t mention this or am I wrong? BEGIN NULL; --do something with l_cnt... END AFTER STATEMENT; END cmptrg_test; /</code> Thank you for clarifying, Florian.
Categories: DBA Blogs

Best Practices for Optimizer Statistics on Large Volatile Tables in Oracle 19.31: Dynamic Sampling, Real-Time Statistics, or Manual Gathering?

Tom Kyte - Mon, 2026-08-24 10:15
I am looking for feedback from DBAs and performance experts who have experience managing highly volatile tables in Oracle Database 19.31. In our environment, we have work tables, processing tables, and staging tables that can grow from a few thousand rows to several million rows during the same processing cycle. Managing optimizer statistics on these objects has become a challenge. If statistics are unlocked, Oracle may automatically gather statistics while business processes are running. In some situations, this introduces additional overhead and can increase execution times. On the other hand, when statistics become stale or do not accurately reflect the current data volume, the optimizer may choose inefficient execution plans, resulting in significant performance regressions. I would like to understand the most effective strategy for this type of workload: ?Do you regularly gather statistics on highly volatile tables, or do you prefer to lock them? ?How do you handle tables whose row counts change dramatically within a single batch process? ?What role do Dynamic Statistics (Dynamic Sampling) and Real-Time Statistics play in this scenario? ?Can these features provide sufficiently accurate cardinality estimates to help the optimizer consistently choose good execution plans without frequent statistics gathering? ?Have you observed plan instability or regressions when relying mainly on Dynamic Statistics or Real-Time Statistics? ?Are there any Oracle 19c (specifically 19.31) best practices that you would recommend for this type of environment? My main objective is to maintain stable and efficient execution plans while minimizing the overhead of statistics collection on large volatile tables. I would be very interested in hearing about real-world experiences, lessons learned, and practical recommendations from those who have faced similar challenges. Thank you in advance for your insights.
Categories: DBA Blogs

How to Minimize Optimizer Regressions Without Hints or Forced Execution Plans?

Tom Kyte - Mon, 2026-08-24 10:15
I am interested in learning about the best practices for ensuring that the Oracle optimizer consistently chooses efficient execution plans without relying on hints, SQL patches, SQL profiles, or forced execution plans that may become outdated over time. In your experience, what database features, parameters, and maintenance practices should be implemented to help the optimizer make the right decisions and minimize plan regressions? For example: Which optimizer-related parameters are most important to review? What are the recommended statistics gathering strategies for Oracle 19c? How effective are Real-Time Statistics, Dynamic Statistics, SQL Plan Management (SPM), and Automatic Indexing in preventing regressions? What role do histograms, extended statistics, and accurate object statistics play? How do you manage volatile tables and changing data distributions? What monitoring and validation processes do you use to detect and prevent plan changes before they impact production? I am particularly interested in feedback from teams running large Oracle 19c environments and the lessons they have learned in maintaining stable performance while allowing the optimizer to adapt naturally to data changes. Thank you in advance for sharing your recommendations and real-world experiences.
Categories: DBA Blogs

Os Authnetication on CDB

Tom Kyte - Mon, 2026-08-24 10:15
We use Os authentication for management access control and automating backups and scripts on windows rdbms servers to avoid using any passwords. This works fine on PDBs, but on the cdb level, if we want to access the database as a user with no sys_dba for automating clonning of pdbs we cannot use that as the common_user_prefix and os_authentication_prefix does not allow for a user to be both... Is there a solution around this? I can set the prefix to be the same, but Then Os authentication will not be available on the pdbs!
Categories: DBA Blogs

How to automatically reconnect node-oracledb Thin connection pool after Amazon RDS Oracle restart?

Tom Kyte - Mon, 2026-08-24 10:15
Hello, I would like to ask for technical guidance on the following scenario. I have a Node.js 24 backend running on Linux, deployed either on AWS EC2 or in an Amazon ECS container. It uses node-oracledb 6.8, also tested with 6.9, in Thin mode with a connection pool to an Amazon RDS for Oracle 19c Single-AZ instance. During planned Oracle/RDS maintenance, the database becomes temporarily unavailable while the Node.js process remains running. After maintenance has completed and RDS is available again, the application does not correctly re-establish database connectivity. The service only starts working again after restarting the Node.js process or container. This has occurred several times with two independent Node.js applications in two different AWS accounts, both based on the same Node.js and RDS for Oracle architecture and configuration. I have already searched Ask TOM and reviewed these related questions: https://asktom.oracle.com/ords/f?p=100:11:0::::P11_QUESTION_ID:45249405283766 https://asktom.oracle.com/ords/f?p=100:11:0::::P11_QUESTION_ID:16008986393518 https://asktom.oracle.com/ords/asktom.search?tag=node-reliability-issues However, I could not find an answer covering this complete configuration and planned Amazon RDS maintenance scenario. Is this combination of AWS, Linux, Node.js, node-oracledb Thin mode, and RDS for Oracle 19c commonly used in production? According to current best practices, what is the correct configuration across AWS, RDS for Oracle, Linux, Node.js, and the node-oracledb parameters to handle this scenario reliably and safely? At the application level, how should the JavaScript code behave while the database is unavailable and after it becomes available again? How should connection-pool management, in-flight requests, and the return to normal operation be structured so that no restart is required? Thank you in advance for any technical guidance or direct experience you can share.
Categories: DBA Blogs

New JOIN TO ONE clause in 26.2 SELECT

Hemant K Chitale - Sat, 2026-08-08 04:20

 I've just published a short video on the new JOIN TO ONE clause in SELECT statements in Oracle 26ai 26.2

This clause allows you to let the database automatically determine JOIN columns based on Primary Key and Foreign Key relationships configured in the database.  JOIN TO ONE defaults to doing a LEFT OUTER JOIN so I also demonstrate how to use it for INNER JOINs



Categories: DBA Blogs

Tracing a Power BI DirectQuery Refresh in Oracle

Hemant K Chitale - Wed, 2026-08-05 08:42

 In today's video I have demonstrated how Power BI can use DirectQuery to query an Oracle database and refresh reports without actually storing the data in the Power BI file (as would be done if "Import" was used instead of DirectQuery).

I have used SQL Tracing in the Database Instance to identify the SQL statement that Power BI executes


For the first visual in Power BI which shows total salary by Department, the Power BI module and SQL statement are identified as :


MODULE NAME:(msmdsrv.exe) 
CLIENT DRIVER:(ODPM.NET : 23.6.0.0.0)

sqlid='c8a9qd2dzks8h'

SELECT * FROM (
SELECT
*
FROM 
(

SELECT
"t1"."DEPARTMENT_NAME" "c6", SUM ( "t4"."SALARY" )
 "a0"
FROM 
((
select "$Table"."EMPLOYEE_ID" as "EMPLOYEE_ID",
    "$Table"."FIRST_NAME" as "FIRST_NAME",
    "$Table"."LAST_NAME" as "LAST_NAME",
    "$Table"."EMAIL" as "EMAIL",
    "$Table"."PHONE_NUMBER" as "PHONE_NUMBER",
    "$Table"."HIRE_DATE" as "HIRE_DATE",
    "$Table"."JOB_ID" as "JOB_ID",
    "$Table"."SALARY" as "SALARY",
    "$Table"."COMMISSION_PCT" as "COMMISSION_PCT",
    "$Table"."MANAGER_ID" as "MANAGER_ID",
    "$Table"."DEPARTMENT_ID" as "DEPARTMENT_ID"
from "HR"."EMPLOYEES" "$Table"
) "t4"

 LEFT OUTER JOIN 

(
select "$Table"."DEPARTMENT_ID" as "DEPARTMENT_ID",
    "$Table"."DEPARTMENT_NAME" as "DEPARTMENT_NAME",
    "$Table"."MANAGER_ID" as "MANAGER_ID",
    "$Table"."LOCATION_ID" as "LOCATION_ID"
from "HR"."DEPARTMENTS" "$Table"
) "t1" on 
(
"t4"."DEPARTMENT_ID" = "t1"."DEPARTMENT_ID"
)
)

GROUP BY "t1"."DEPARTMENT_NAME"
)
 "MainTable"
WHERE 
(

NOT(
(
"a0" IS NULL 
)
)

)

ORDER BY "a0"
DESC
,"c6"
ASC
 ) WHERE ROWNUM (lessthan) 1001


For the second visual in Power BI which shows count of employees in each Department, the Power BI module and SQL statement are identified as :


MODULE NAME:(msmdsrv.exe)
CLIENT DRIVER:(ODPM.NET : 23.6.0.0.0)

sqlid='dh7nbfqsy942q'

SELECT
"t1"."DEPARTMENT_NAME" "c6",
COUNT("t4"."EMPLOYEE_ID")
 "a0"
FROM 
((
select "$Table"."EMPLOYEE_ID" as "EMPLOYEE_ID",
    "$Table"."FIRST_NAME" as "FIRST_NAME",
    "$Table"."LAST_NAME" as "LAST_NAME",
    "$Table"."EMAIL" as "EMAIL",
    "$Table"."PHONE_NUMBER" as "PHONE_NUMBER",
    "$Table"."HIRE_DATE" as "HIRE_DATE",
    "$Table"."JOB_ID" as "JOB_ID",
    "$Table"."SALARY" as "SALARY",
    "$Table"."COMMISSION_PCT" as "COMMISSION_PCT",
    "$Table"."MANAGER_ID" as "MANAGER_ID",
    "$Table"."DEPARTMENT_ID" as "DEPARTMENT_ID"
from "HR"."EMPLOYEES" "$Table"
) "t4"

 LEFT OUTER JOIN 

(
select "$Table"."DEPARTMENT_ID" as "DEPARTMENT_ID",
    "$Table"."DEPARTMENT_NAME" as "DEPARTMENT_NAME",
    "$Table"."MANAGER_ID" as "MANAGER_ID",
    "$Table"."LOCATION_ID" as "LOCATION_ID"
from "HR"."DEPARTMENTS" "$Table"
) "t1" on 
(
"t4"."DEPARTMENT_ID" = "t1"."DEPARTMENT_ID"
)
)

GROUP BY "t1"."DEPARTMENT_NAME" 
Thus, every refresh runs a number of queries -- some to synchronise the schema from Oracle to Power BI and others to refresh the numbers to present in the Visuals. This proves that the actual load of computing the GROUP BY and aggregations is in the *database instance* (because that is where the data actually resides) and not in the Power BI file (because no data is copied into the Power BI file)


Categories: DBA Blogs

Dealing with JSON serialization and how to convert JSON object strings back and forth

Flavio Casetta - Sat, 2026-07-25 07:12

A simple reminder about how JSON PL/SQL methods deal with JSON values, it easy to get confused when you mix up JSON objects and their serialized counterparts, especially if some if these parts are coming from JSON SQL functions and you need to combine them with other parts generated by PL/SQL.

The code below should clarify the difference between a "real" JSON value and its textual representation, especially when you are assembling a JSON object with values containing other JSON objects or their serialized representation.

When you PUT a serialized JSON string into a new JSON, the method escapes all the special characters that otherwise would break the syntax (lines 10-16).
In order to reconstruct a valid JSON string, something that you can PARSE as JSON, you need to retrieve the value with GET_STRING or GET_CLOB if it is large (lines 18-20).

If you need to include a serialized JSON object into a new JSON object thus avoiding the automatic escaping, then you need to convert the serialized JSON string into a proper JSON object and then PUT it inside the new object (lines 26-29). 

declare
    c  varchar2(255) := '{"key": 1, "value": "X"}';
    d  varchar2(255);
    j  json_object_t;
    j1 json_object_t;
    j2 json_object_t;
begin
    
    if c is json then
        dbms_output.put_line('c is a string containing a valid JSON');
        j := json_object_t.parse(c);

        j1 := new json_object_t;
        j1.put('document', c);
        dbms_output.put_line('c is now escaped and becomes a string literal value');
        dbms_output.put_line(j1.to_string());
        dbms_output.new_line;
        d := j1.get_string('document');
        dbms_output.put_line('c is converted back into the original JSON object string');
        dbms_output.put_line(d);


        dbms_output.new_line;
        dbms_output.new_line;

        j2 := new json_object_t;
        j2.put('document', j);
        dbms_output.put_line('c is still a json object value, now embedded in a new JSON object');
        dbms_output.put_line(j2.to_string());
    else
        dbms_output.put_line('NOT JSON');
    end if;
end;
/

Watch out for NULL values because GET_STRING and GET_CLOB show two different behaviors in older releases of Oracle 19c.

 

Categories: DBA Blogs

GoldenGate FUNCTIONSTACKSIZE: The Parameter You’ve Never Heard Of

DBASolved - Wed, 2026-07-08 21:24

GoldenGate's FUNCTIONSTACKSIZE error isn't about functions — wide tables over 200 columns trigger it. Here's the one-line fix.

The post GoldenGate FUNCTIONSTACKSIZE: The Parameter You’ve Never Heard Of appeared first on DBASolved.

Categories: DBA Blogs

Protect Your Oracle Database from SQL Injection and Unauthorized Access with SQL Firewall

Pakistan's First Oracle Blog - Wed, 2026-07-08 21:23

SQL injection and credential misuse remain serious threats to database security. Oracle SQL Firewall offers a practical way to defend against these risks by allowing only approved SQL statements and trusted connection paths for each database user.

Instead of relying solely on application-level controls, SQL Firewall operates directly inside the database kernel. This makes it difficult to bypass and gives you fine-grained control over what each user can do.

How SQL Firewall Works

SQL Firewall uses an allow-list approach. You first let it observe normal activity for a user, then create a policy that defines exactly which SQL statements and connection details are permitted. Anything outside that policy triggers a violation that can be logged or blocked in real time.

It evaluates both the SQL statement itself and the context in which it runs, including the client IP address, operating system user, and program name. This helps protect against stolen credentials and unexpected access paths.

Key Benefits in Practice
  • Inspects every SQL statement, including those generated inside PL/SQL
  • Works whether connections are local or remote, encrypted or not
  • Gives you the choice to log violations only or actively block them
  • Applies per database user, making it easy to protect application accounts or individual users
  • Integrates well with other Oracle security features like Database Vault and auditing
Setting Up SQL Firewall - A Practical Approach

Here is a straightforward way to implement SQL Firewall for an application service account.

Step 1: Enable SQL Firewall
EXEC DBMS_SQL_FIREWALL.ENABLE;
Step 2: Start Capturing Normal Activity

Begin recording what the target user typically does. This example captures activity for an application user named APP:

BEGIN
  DBMS_SQL_FIREWALL.CREATE_CAPTURE(
    username       => 'APP',
    top_level_only => TRUE,
    start_capture  => TRUE
  );
END;
/

Let the application run normally for a sufficient period so the firewall learns the expected SQL patterns and connection details.

Step 3: Review What Was Captured

Check the captured data to confirm it covers the expected workload:

SELECT SQL_TEXT 
FROM DBA_SQL_FIREWALL_CAPTURE_LOGS 
WHERE USERNAME = 'APP';
Step 4: Generate the Allow-List Policy

Once you are satisfied with the captured data, create the policy:

EXEC DBMS_SQL_FIREWALL.GENERATE_ALLOW_LIST('APP');

You can review the allowed SQL statements and connection contexts using the DBA_SQL_FIREWALL_ALLOWED_* views.

Step 5: Enable Enforcement

Activate protection for the user. This example enforces allowed SQL statements and blocks violations:

BEGIN
  DBMS_SQL_FIREWALL.ENABLE_ALLOW_LIST(
    username => 'APP',
    enforce  => DBMS_SQL_FIREWALL.ENFORCE_SQL,
    block    => TRUE
  );
END;
/

You can choose to enforce SQL statements, connection context, or both. You can also decide whether to block or only log violations.

Monitoring and Ongoing Management

Review violations regularly using this query:

SELECT SQL_TEXT, FIREWALL_ACTION, IP_ADDRESS, CAUSE, OCCURRED_AT
FROM DBA_SQL_FIREWALL_VIOLATIONS 
WHERE USERNAME = 'APP'
ORDER BY OCCURRED_AT DESC;

Periodically clean up old violation records and consider exporting policies using Data Pump for backup or movement between environments.

Important Considerations
  • SQL Firewall only captures statements that execute successfully
  • It normalizes SQL by replacing literal values before storing signatures
  • Connection context is checked at session creation time
  • You can add new allowed entries later from either the capture log or violation log
  • Existing sessions are not terminated when you first enable a policy
When to Use SQL Firewall

This feature works especially well for:

  • Protecting application service accounts that run a known set of SQL statements
  • Adding an extra layer of defense for sensitive users such as reporting accounts or DBAs
  • Quickly restricting direct database access to known IP addresses or programs
  • Detecting and responding to potential SQL injection attempts in real time
Final Thoughts

Oracle SQL Firewall provides a practical, database-native way to enforce least-privilege access at the SQL level. By combining allow-listing of both statements and connection contexts, it helps reduce the attack surface without requiring major changes to your applications.

Start with a focused rollout on your most critical application accounts, review the captured activity carefully, and gradually expand protection across your environment.

Categories: DBA Blogs

Using an LLM in a SELECT Loop

Bobby Durrett's DBA Blog - Wed, 2026-07-08 17:56
Introduction

This post describes a way to use a Large Language Model (LLM) to investigate Oracle database problems. In this approach, the LLM repeatedly generates SELECT statements based on previous queries and their results. Each query provides additional context, allowing the LLM to gather information step by step before producing a final report for a DBA to review.

My previous attempts at using an LLM only performed a single inference. I had to gather the input data myself and decide what output I wanted the LLM to produce. In the SELECT Loop approach the LLM can gather its own information through each query it generates. Every iteration expands the context available to the model, allowing it to investigate a problem in several steps instead of trying to answer immediately with incomplete information.

Here is the outline of how this approach works:

  1. Question -> LLM -> SELECT1 -> Oracle -> Output1
  2. Question + SELECT1 + Output1 -> LLM -> SELECT2 -> Oracle -> Output2
  3. Loop N times
  4. Question + SELECT1 + Output1 + … + SELECTN + OutputN -> LLM -> Report
  5. Report -> DBA
Question -> LLM -> SELECT1 -> Oracle -> Output1

The first step is to have the LLM generate a SQL query to try to answer some question about the database and then run it. The LLM’s prompt might look like this:

On an Oracle 11.2.0.4 database I want to investigate the performance of a sql statement with SQL_ID b4rj0h8hh0sxf.
Please generate a select statement that will give more information about b4rj0h8hh0sxf's performance.
Please only generate ASCII text.
Please keep to no more than 80 character lines.
Please output only the select statement that you want to run next.

The LLM generates some useful query against the database’s DBA or V$ views like:

SELECT
...
    s.iowait_delta           AS iowait_usecs,
    s.clwait_delta           AS clwait_usecs,
    s.apwait_delta           AS apwait_usecs,
    s.ccwait_delta           AS ccwait_usecs
FROM
    dba_hist_sqlstat   s
    JOIN dba_hist_snapshot sn
        ON  sn.snap_id     = s.snap_id
        AND sn.dbid        = s.dbid
        AND sn.instance_number = s.instance_number
WHERE
    s.sql_id = 'b4rj0h8hh0sxf'
    AND s.dbid = (SELECT dbid FROM v$database)
ORDER BY
    sn.begin_interval_time,
    s.plan_hash_value;

The next step is just to run the generated query against the database and get its output like:

   SNAP_ID BEGIN_TIME      END_TIME        PLAN_HASH_VALUE      EXECS  
---------- --------------- --------------- --------------- ----------
     88771 15-MAY-26 01:00 15-MAY-26 02:00      3752201481      11225
     88772 15-MAY-26 02:00 15-MAY-26 03:00      3752201481      22707
     88773 15-MAY-26 03:00 15-MAY-26 04:00      3752201481      11381
     88795 16-MAY-26 01:00 16-MAY-26 02:00      3752201481      11316
     88796 16-MAY-26 02:00 16-MAY-26 03:00      3752201481      23920
     88797 16-MAY-26 03:00 16-MAY-26 04:00      3752201481      10076
     88819 17-MAY-26 01:00 17-MAY-26 02:00      3752201481      11315
     88820 17-MAY-26 02:00 17-MAY-26 03:00      3752201481      24907
     88821 17-MAY-26 03:00 17-MAY-26 04:00      3752201481       9086
     88843 18-MAY-26 01:00 18-MAY-26 02:00      3752201481      11209
     88844 18-MAY-26 02:00 18-MAY-26 03:00      3752201481      24009
     88845 18-MAY-26 03:00 18-MAY-26 04:00      3752201481      10075
Question + SELECT1 + Output1 -> LLM -> SELECT2 -> Oracle -> Output2

Next take the original question and append the first SELECT statement and its output and request a second SELECT statement. The LLM suggested SELECT1. Now we are giving it both its SELECT1 query and its output. Then the LLM gets to take the next step in the problem investigation by generating a second SELECT statement. By showing it what it previously chose and the query output we are giving the LLM context that it didn’t have the first time we asked it the question. This prompt looks something like this:

On an Oracle 11.2.0.4 database I want to investigate the performance of a sql statement with SQL_ID b4rj0h8hh0sxf.
Following this prompt are the outputs of select statements that you recommended running. After evaluating them
please generate an additional select statement that will give more information about b4rj0h8hh0sxf's performance.
Please only generate ASCII text.
Please keep to no more than 80 character lines.
Please output only the select statement that you want to run next.
Output of your previous select statements:

This prompt would be followed by the first select statement and its output. I’m assuming that the output contains the SELECT statement and the query results. The query the LLM returns will probably be different from the first one because it already knows the result of the first query.

Once the LLM generates it, run the second query.

Loop N times

Now pass the original question plus all the queries and their outputs into the LLM requesting another SQL. Repeat this process N times. This pattern is the main point of this post. The LLM starts with the original question and generates a select statement. We run the query, append the query and its output to the prompt, and ask for another query. This process repeats until we have gathered enough information. The LLM is taking us down its own path investigating the question.

Keep generating new SELECT statements based on all the previous SELECT statements and their outputs.

Question + SELECT1 + Output1 + … + SELECTN + OutputN -> LLM -> Report

Finally stop generating new queries and take all the ones you have generated and all their outputs and ask the LLM to generate a final report. Here is part of a report:

## 4. The Plan Change Event on 26-JUN-26

On snap 89780 (26-JUN-26 02:00-03:00) plan 361274665 appeared for
35 executions alongside the normal plan. By snap 89781 (03:00-04:00)
the bad plan had taken over entirely.

Performance comparison for 26-JUN-26:

| Metric           | Normal plan  | Bad plan snap89781 | Bad plan snap89782 |
|------------------|-----------   |---------------------|---------------------|
| Executions       | 16,775 (03h) | 11                  | 13                  |
| Avg elapsed (s)  | 0.08         | 54.79               | 59.56               |
| Avg buffer gets  | 17,673       | 14,982,688          | 18,208,586          |
| Avg disk reads   | 37           | 6,832               | 6,463               |
| Avg I/O wait (s) | ~0           | 3.31                | 3.78                |
| Rows returned    | ~155         | ~73,765/exec        | ~72,639/exec        |

The bad plan is approximately **500x slower** per execution and
performs **800-1,000x more buffer gets**. Disk reads per execution
jump from ~37 to ~6,800.

What makes this interesting is that the LLM chose the sequence of queries that gathered the information used in this report.

Report -> DBA

The last step is for the DBA to review the report. No one should look at an AI generated report like this and act on it without an experienced DBA reviewing it. I don’t see this kind of report replacing DBAs. I see it as empowering them. Instead of me manually writing and running various queries to solve a problem I can give the problem to an LLM to take a first crack at it.

Conclusion

I struggled to find a practical use for LLMs in my DBA work until I started experimenting with this SELECT loop approach. By repeatedly generating queries, evaluating their results, and using that information to drive the next query, the LLM can investigate a problem before producing a final report. I don’t see this replacing DBAs, but I do see it helping experienced DBAs investigate problems more quickly.

P.S. I uploaded the sample code to a Github repository if you want to run it: https://github.com/bobbydurrett/SelectLoop

Categories: DBA Blogs

Run Fully Autonomous Oracle Databases Natively Inside AWS

Pakistan's First Oracle Blog - Fri, 2026-07-03 23:44

Organizations running Oracle workloads in AWS now have a powerful new option. Oracle Autonomous AI Database Serverless is generally available on Oracle AI Database@AWS. This allows teams to deploy a completely self-managing Oracle database directly through the AWS Console while using their existing AWS spending commitments.

The service handles patching, performance tuning, scaling, and security updates automatically, so teams spend less time on routine maintenance and more time on strategic initiatives.

The Shift Toward Hands-Off Database Management

Many companies want the benefits of automation without moving away from AWS. This release delivers exactly that. You get Oracle’s most advanced autonomous database technology running inside AWS data centers, with the same tools and purchasing experience your teams already use.

Independent research has shown strong returns for organizations adopting this approach, including major gains in team productivity and sharp reductions in unexpected outages. Those advantages are now available without leaving the AWS environment.

Reduced Day-to-Day Database Work

The service takes care of many tasks that traditionally require manual effort:

  • Patches and upgrades are applied automatically with no downtime
  • Indexing and query optimization happen continuously in the background
  • Compute and storage scale independently based on actual demand
  • Security updates are managed without scheduling windows

This automation helps database administrators and infrastructure teams become significantly more productive. They can shift focus toward architecture, data strategy, and innovation instead of ongoing maintenance.

Support for Different Workload Needs in One Service

Rather than running separate systems for different requirements, a single instance can handle multiple patterns:

  • High-volume transactional applications and mixed workloads
  • Analytics, data warehousing, and lakehouse scenarios with support for open table formats
  • Document-centric applications using familiar JSON interfaces
  • Rapid application development through low-code tools with built-in AI features
Reliable Protection for Important Systems

Mission-critical applications need strong uptime guarantees. The service includes:

  • A 99.995 percent availability commitment
  • Disaster recovery options across AWS regions
  • Local high availability with automatic failover between availability zones
  • Fully managed backups stored in Amazon S3
Deep Integration with AWS Services

Because the database runs on Oracle infrastructure inside AWS data centers, connectivity to other AWS services is fast and straightforward. Key integrations include:

  • Automated backups landing in Amazon S3 with options for immutability
  • Encryption using customer-managed keys through AWS KMS
  • Zero-ETL data movement into Amazon Redshift for analytics
  • Native metrics and events flowing into CloudWatch and EventBridge
  • Provisioning and management through the AWS Console, APIs, and CloudFormation templates
Best Fit Scenarios

This offering works especially well when teams want to:

  • Bring existing Oracle applications into AWS with limited changes
  • Lower the operational load on database and infrastructure teams
  • Build AI applications that need vector search and natural language capabilities
  • Consolidate different workload types onto a single managed platform
Availability and Next Steps

The service is available now in the US East (N. Virginia) and US West (Oregon) regions. Both Oracle AI Database 26ai and 19c are supported, along with flexible licensing models.

Teams already comfortable with AWS tools can start exploring the service through familiar interfaces while taking advantage of Oracle’s automation and resilience features.

Categories: DBA Blogs

Build a Production-Ready RAG Pipeline with LangChain and Oracle AI Database

Pakistan's First Oracle Blog - Tue, 2026-06-30 23:41

Creating a reliable retrieval-augmented generation (RAG) system usually involves a lot of boilerplate. With the langchain-oracledb integration, you can build a complete pipeline; document loading, chunking, vector storage, hybrid search, semantic caching, and conversation memory; in a clean and compact way.

This guide walks you through a practical, end-to-end implementation using Oracle AI Database as the single source of truth for vectors, text, cache, and history.

Why Oracle AI Database + LangChain?

Instead of juggling multiple systems (vector database + cache + message store), everything lives inside Oracle AI Database. This simplifies architecture, improves consistency, and gives you enterprise-grade features like transactions and security out of the box.

High-Level Architecture

Here’s how the pieces connect:

  • Ingestion: Load → Split → Embed → Store in OracleVS
  • Retrieval: Hybrid search combining semantic similarity and keyword search
  • Intelligence Layer: Semantic cache + persistent chat history
  • Answer Generation: Use retrieved context to generate responses
Step 1: Document Ingestion Pipeline

We’ll read runbook content from a database table, split it intelligently, and store the chunks with embeddings.

from langchain_oracledb.document_loaders import OracleDocLoader, OracleTextSplitter
from langchain_oracledb.vectorstores import OracleVS, DistanceStrategy
from langchain_community.embeddings import HuggingFaceEmbeddings

def ingest_documents(conn):
    # Load documents with metadata
    loader = OracleDocLoader(
        conn=conn,
        params={
            "owner": conn.username,
            "tablename": "RUNBOOK_SOURCE",
            "colname": "CONTENT",
            "mdata_cols": ["ID", "TITLE", "CATEGORY"]
        }
    )
    docs = loader.load()

    # Create vector store
    embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
    
    vs = OracleVS(
        conn,
        embeddings,
        table_name="DOCUMENT_VECTORS",
        distance_strategy=DistanceStrategy.COSINE
    )

    # Split and store
    splitter = OracleTextSplitter(
        conn=conn,
        params={"by": "words", "max": 250, "split": "sentence"}
    )

    vs.add_documents(docs, text_splitter=splitter)
    return vs
Step 2: Hybrid Retrieval

Combine semantic search (vector similarity) with keyword search for better results.

from langchain_oracledb.retrievers import OracleTextSearchRetriever

def hybrid_retrieve(vector_store, query, k=5):
    # Semantic search
    semantic_docs = vector_store.similarity_search(query, k=k)
    
    # Keyword search
    keyword_retriever = OracleTextSearchRetriever(
        vector_store=vector_store,
        k=k
    )
    keyword_docs = keyword_retriever.invoke(query)
    
    # Simple fusion logic (you can improve this with RRF)
    combined = semantic_docs + keyword_docs
    # Deduplicate and rank (basic version shown)
    seen = set()
    unique_docs = []
    for doc in combined:
        doc_id = doc.metadata.get("ID")
        if doc_id not in seen:
            seen.add(doc_id)
            unique_docs.append(doc)
    
    return unique_docs[:k]
Step 3: Add Semantic Caching

Reuse answers for similar questions to reduce cost and latency.

from langchain_oracledb.cache import OracleSemanticCache
from langchain.schema import Generation

def get_cached_or_generate(conn, embeddings, query, context):
    cache = OracleSemanticCache(
        conn,
        embeddings,
        table_name="SEMANTIC_CACHE",
        score_threshold=0.85
    )
    
    cached = cache.lookup(query, "answer_cache")
    
    if cached:
        return cached[0].text, True  # cache hit
    
    # Generate new answer (call your LLM here)
    answer = generate_response(query, context)
    
    cache.update(query, "answer_cache", [Generation(text=answer)])
    return answer, False
Step 4: Persist Conversation History

Keep full chat context across sessions.

from langchain_oracledb.chat_message_histories import OracleChatMessageHistory
from langchain.schema import HumanMessage, AIMessage

def save_to_history(conn, session_id, question, answer):
    history = OracleChatMessageHistory(
        session_id,
        client=conn,
        table_name="CONVERSATION_HISTORY"
    )
    
    history.add_messages([
        HumanMessage(content=question),
        AIMessage(content=answer)
    ])
    
    return len(history.messages)
Complete End-to-End Function

Here’s how everything works together in a single function:

def ask_question(conn, vector_store, question, session_id="default"):
    embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
    
    # 1. Hybrid retrieval
    relevant_docs = hybrid_retrieve(vector_store, question)
    context = "\n\n".join([doc.page_content for doc in relevant_docs])
    
    # 2. Check cache or generate
    answer, was_cached = get_cached_or_generate(conn, embeddings, question, context)
    
    # 3. Save to history
    history_length = save_to_history(conn, session_id, question, answer)
    
    return {
        "answer": answer,
        "cached": was_cached,
        "sources": len(relevant_docs),
        "history_messages": history_length
    }
How to Run This Locally
  1. Install dependencies with Poetry
  2. Run the sample script — it automatically spins up an Oracle AI Database Free container using Testcontainers
  3. Ask questions and observe hybrid retrieval, caching behavior, and growing chat history
Benefits of This Approach
  • Single database for vectors, documents, cache, and history
  • Hybrid search improves answer quality
  • Semantic caching reduces expensive LLM calls
  • Conversation state is durable and queryable
  • Very little custom code required thanks to langchain-oracledb
Final Thoughts

Building a solid RAG system doesn’t have to be complex. By leveraging Oracle AI Database through the official LangChain integration, you get a clean, maintainable pipeline that handles ingestion, retrieval, caching, and memory in one place.

This pattern scales well and keeps your architecture simple while delivering strong retrieval performance.

Start experimenting with the sample, you’ll be surprised how quickly you can get a capable system running.

Categories: DBA Blogs

Take Full Control of Your AI Agents with Archestra

Pakistan's First Oracle Blog - Tue, 2026-06-30 16:08

 If you have been wiring local AI agents to MCP servers, you already know the uneasy feeling. You connect a model, give it some tools, send it off on a task, and then you just hope it behaves. You have no real view into what it is calling, and no easy way to stop it when it does something it should not.

I spent some hands-on time with Archestra, an open source AI platform that fixes exactly this, and ran the whole thing locally on my own hardware.


What Archestra Is

Archestra is an all-in-one, open source platform for running AI agents safely. It pulls together the pieces you would normally wire up yourself: a chat interface, a no-code agent builder, an LLM gateway, an MCP gateway, a private MCP registry, deterministic guardrails, and full observability. One Docker command brings the whole stack up. The team behind it previously worked on Grafana OnCall, so the production thinking shows.

Running It Locally

I drove everything with a local model, Qwen3.6 27B served through Ollama, on my own GPU. No cloud dependency for inference. Archestra is provider agnostic, so pointing it at a local Ollama endpoint took a single configuration step.

The Part That Matters

I built a simple agent and gave it one tool: a website reader pulled from Archestra's MCP registry. What stood out is that every MCP server runs as its own isolated pod inside a Kubernetes cluster that Archestra spins up automatically. That is real isolation, not just a local process.

I ran a task and watched the agent make its tool call live. Every step it took was visible. Then came the payoff. I set a deterministic guardrail to block that tool, re-ran the task, and the agent could no longer touch it. The block is enforced at the platform level, so a prompt cannot talk its way around it.

That is the whole point. Seeing what your agents do is good. Being able to stop them, deterministically, is what makes agentic AI safe to run.

Try It Yourself

Archestra is open source and self-hostable. Grab it from GitHub, run the Docker quickstart, and try the same flow on your own machine.

Watch the full hands-on walkthrough in the video below.

https://youtu.be/9JiA6RYpEYo

Categories: DBA Blogs

Bug using SQL_MACRO (TABLE) with table parameter

Tom Kyte - Mon, 2026-06-29 17:06
Hello All, I encounter an error trying to use a SQL MACRO (TABLE) in freesql.com, in both versions 26ai and 23ai. The function top_n compiles successfully, but trying to call it fails: <code>create or replace function top_n (p_table DBMS_TF.TABLE_T, p_rows NUMBER) return varchar2 SQL_Macro is l_sql varchar2(200) := 'select * from top_n.p_table fetch first top_n.p_rows rows only'; begin dbms_output.put_line('sql='||l_sql); return l_sql; end; / Function TOP_N compiled -- test in SQL select * from top_n (scott.dept, 3) / ORA-00942: table or view "TOP_N"."P_TABLE" does not exist</code> The same example works ok in 19c <code>SQL> select * from top_n (scott.dept, 3) DEPTNO DNAME LOC ------ ------------ ---------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO sql=select * from top_n.p_table fetch first top_n.p_rows rows only 3 rows selected. </code> It looks like this is a database bug, as I also encounter the same problem in a different 26ai environment. Thanks a lot in advance & Best Regards, Iudith Mentzel
Categories: DBA Blogs

Oracle RAC Concepts (gestion des instances / hang / reconfiguration)

Tom Kyte - Sat, 2026-06-27 16:04
I am running Oracle Database 19c (RU 19.22) in a 4-node RAC extended cluster. During a recent incident, we experienced ORA-32701 ?Possible hangs detected? with Hang IDs (915, 916, 917). The Hang Manager detected a global hang situation where instance 1 was identified as the final blocker (CKPT process / DBRM session), and Oracle eventually evicted the instance due to a ?LOCAL, HIGH confidence hang?. We observed that _hang_resolution_scope is set to INSTANCE in our environment, along with _hang_detection_enabled = TRUE and _hang_resolution_policy = HIGH. My questions are: What is the recommended best practice for _hang_resolution_scope in Oracle 19c RAC 4-node clusters (especially extended RAC)? Should it remain PROCESS in production environments? Under what conditions does Oracle recommend switching to INSTANCE scope, and what are the risks (e.g. cascading instance evictions)? In global hang scenarios (ORA-32701), how does Hang Manager decide between process termination vs instance eviction in modern 19c releases? Are there known best practices to avoid false positives or unnecessary instance evictions in large RAC clusters under heavy batch workloads (PL/SQL, DBMS_SCHEDULER, materialized view refresh)? Any guidance or references to official Oracle best practices would be appreciated. more info for incident trace file for instance 1 *** 2026-06-01T11:36:12.118125+02:00 HM: Hang Statistics - only statistics with non-zero values are listed current number of local active sessions 78 current number of local hung sessions 38 instance health (in terms of hung sessions) 51.29% number of cluster-wide active sessions 224 number of cluster-wide hung sessions 104 cluster health (in terms of hung sessions) 53.58% -------------------------------------- 2297510041,1,0,2297510041,65128162,"06-01-2026 11:36:43.948768000",2502,36203,2,0,"",0,0,0,"",0,0,0,0,0,0,0,0,"",0,0,0,0,0,0,0,866018717,33792,3,0,0,30070,0,4294967291,0,1,4294967295,0,0,0,0,0,,0,0,165959219,"oracle@ffcrac41.francefrais.local (CKPT)","","","","ffcrac41",0,"" -------------------------------------- log file for instace 3 DIA0 Critical Database Process As Root: Hang ID 915 blocks 7 sessions Final blocker is session ID 2502 serial# 36203 OSPID 32174 on Instance 1 If resolvable, instance eviction will be attempted by Hang Manager
Categories: DBA Blogs

ORA-04031 when explain plan of a very big and complex query

Tom Kyte - Sat, 2026-06-27 16:04
Hi, this is a general question. I tried to explain a very long a 80Kb of aggregation and pivot query and I get an error "ORA-04031: unable to allocate 40 bytes of shared memory". Particularly when I try to add a parallel hint. Errors in file /tools/list/oracle/product/diag/rdbms/bnkppd/bnkppd/trace/bnkppd_ora_5178102.trc (incident=4708985): ORA-04031: unable to allocate 80 bytes of shared memory ("shared pool","explain plan set statement_i...","qmxqalgDiagDrv","qmxqalgDiag_newElem:qmxqalgDiagStackElem") The bad thing is that the full database hangs when this happened (on a test database). I think that I read somewhere that we can alter the size of the shared pool to accept bigger queries. Can you tell me which parameter I could change for this (even an hidden parameter is OK). Thank you.
Categories: DBA Blogs

Pages

Subscribe to Oracle FAQ aggregator - DBA Blogs