<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mark Nelson</title>
    <description>The latest articles on DEV Community by Mark Nelson (@mark_nelson_oracle).</description>
    <link>https://dev.to/mark_nelson_oracle</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3927505%2F13fcb607-d587-418c-bf02-eb061fcea3b9.jpg</url>
      <title>DEV Community: Mark Nelson</title>
      <link>https://dev.to/mark_nelson_oracle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mark_nelson_oracle"/>
    <language>en</language>
    <item>
      <title>Evaluate Oracle Database Operator for Kubernetes: A Practical First Run for Platform Teams</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Wed, 03 Jun 2026 14:43:50 +0000</pubDate>
      <link>https://dev.to/oracledevs/evaluate-oracle-database-operator-for-kubernetes-a-practical-first-run-for-platform-teams-1c95</link>
      <guid>https://dev.to/oracledevs/evaluate-oracle-database-operator-for-kubernetes-a-practical-first-run-for-platform-teams-1c95</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with the control loop, not production architecture.&lt;/strong&gt; A first run should prove that Kubernetes accepts an Oracle Database custom resource and that Oracle Database Operator for Kubernetes reconciles it into observable resources, status, events, and logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use one release tag and one installation path consistently&lt;/strong&gt;. This walkthrough uses the Oracle Database Operator for Kubernetes v2.1.0 release tag, the manifest-based installation path, and a supported SingleInstanceDatabase sample from the same release-tagged repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat storage, image access, secrets, and webhooks as first-class prerequisites&lt;/strong&gt;. Common first-run blockers include missing storage classes, Oracle Container Registry access problems, cert-manager issues, RBAC, and secrets in the wrong namespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A healthy demo is not a production approval&lt;/strong&gt;. A successful first run gives platform engineers and DBAs enough evidence to continue evaluation, not enough evidence to certify backup, recovery, HA, security, performance, or operating support.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Platform teams evaluating databases on Kubernetes usually start with a practical question: will an operator improve the operating model, or will it add another controller, webhook, CRD set, and upgrade path to maintain?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/oracle/oracle-database-operator" rel="noopener noreferrer"&gt;Oracle Database Operator for Kubernetes&lt;/a&gt; extends the Kubernetes API with custom resources and controllers for supported Oracle Database lifecycle management tasks. In platform terms, it lets you express database intent as Kubernetes API objects, then inspect how the operator reconciles that intent into related resources such as pods, services, persistent volume claims, events, and status.&lt;/p&gt;

&lt;p&gt;This article gives you a lean first run. You will install or verify the operator using the manifest-based path, apply one supported &lt;code&gt;SingleInstanceDatabase&lt;/code&gt; sample, validate reconciliation with &lt;code&gt;kubectl&lt;/code&gt;, troubleshoot common first-run failures, and clean up the evaluation namespace. The goal is to prove the control loop in a test environment, not to certify the database workload, storage design, backup process, or operating model for production.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This walkthrough uses the Oracle Database Operator for Kubernetes &lt;code&gt;v2.1.0&lt;/code&gt; release tag for install files, CRDs, samples, and API fields. The &lt;code&gt;v2.1.0&lt;/code&gt; manifest currently references the floating operator image &lt;code&gt;container-registry.oracle.com/database/operator:latest&lt;/code&gt;. For a disposable first run, that follows the release manifest. For reproducible or long-lived environments, verify the resolved image version or pin an approved image tag or digest according to your platform policy.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start by proving the operator control loop
&lt;/h2&gt;

&lt;p&gt;The operator model changes where database lifecycle intent lives. Instead of starting with hand-written pods, services, PVCs, and one-off scripts, a platform engineer applies a custom resource such as &lt;code&gt;SingleInstanceDatabase&lt;/code&gt;. The Kubernetes API stores that desired state. Oracle Database Operator for Kubernetes watches the resource and reconciles related Kubernetes objects.&lt;/p&gt;

&lt;p&gt;That gives platform engineers and DBAs a shared inspection surface. You can inspect the custom resource, status fields, conditions, events, operator logs, pods, services, PVCs, and referenced secrets. Those are familiar Kubernetes signals, but the object being reconciled represents database intent rather than a generic workload.&lt;/p&gt;

&lt;p&gt;That distinction matters. The operator can make supported lifecycle tasks easier to express and inspect, but it does not move every database decision into Kubernetes. DBAs and platform teams still own recovery goals, backup validation, patch policy, storage engineering, security posture, capacity planning, performance, and production runbooks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk5j1yp167geym861qfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk5j1yp167geym861qfb.png" alt="Oracle Database Operator for Kubernetes first-run control loop" width="799" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 1.&lt;/strong&gt; Oracle Database Operator for Kubernetes turns database intent into a Kubernetes reconciliation loop. A first evaluation should prove that loop before production architecture decisions.&lt;/p&gt;

&lt;p&gt;For this first run, success means your cluster accepts a supported Oracle Database custom resource, the operator reacts to it, related Kubernetes resources appear, and status, events, or logs explain progress or failure. That is enough for a first pass. It is not enough for production approval.&lt;/p&gt;

&lt;p&gt;Operators can make stateful workloads look deceptively simple. A pod appears, a PVC binds, and a status field moves forward. Those are useful first-run signals. By themselves, they do not prove backup readiness, security compliance, performance, high availability, or operational supportability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the prerequisites before installing anything
&lt;/h2&gt;

&lt;p&gt;Use a non-production Kubernetes cluster and a Linux/Bash shell with &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, and &lt;code&gt;curl&lt;/code&gt; installed. You need cluster administrator or equivalent permissions for this walkthrough because the default manifest-based installation creates CRDs, RBAC, webhooks, and operator resources.&lt;/p&gt;

&lt;p&gt;Start with basic cluster checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl version
kubectl get nodes
kubectl get storageclass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;kubectl version&lt;/code&gt; confirms that your client can reach the API server. &lt;code&gt;kubectl get nodes&lt;/code&gt; tells you whether the cluster has schedulable nodes. &lt;code&gt;kubectl get storageclass&lt;/code&gt; shows whether dynamic persistent storage is available. If the cluster has no default storage class, choose an explicit storage class for the sample manifest later in the walkthrough.&lt;/p&gt;

&lt;p&gt;A local cluster can prove the API and reconciliation path, but many local defaults are too small for Oracle Database images and persistent volumes. The &lt;code&gt;SingleInstanceDatabase&lt;/code&gt; sample in this article requests a &lt;code&gt;50Gi&lt;/code&gt; persistent volume claim. If your cluster has no suitable storage class, or if quotas prevent a &lt;code&gt;50Gi&lt;/code&gt; PVC, fix that before applying the database sample.&lt;/p&gt;

&lt;p&gt;Also confirm image access early. The sample used in this article pulls an Oracle Database Free Lite image from &lt;a href="https://container-registry.oracle.com/" rel="noopener noreferrer"&gt;Oracle Container Registry&lt;/a&gt;. Depending on your organization’s policy and network setup, you may need registry login, license acceptance, egress allow rules, image pull secrets, or an approved private registry mirror.&lt;/p&gt;

&lt;p&gt;If your organization requires image scanning or mirroring, complete that process before the demo. Treat an &lt;code&gt;ImagePullBackOff&lt;/code&gt; as an image access problem until you have ruled out registry, network, policy, and pull-secret causes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a dedicated namespace for the database resource
&lt;/h2&gt;

&lt;p&gt;Keep the database evaluation separate from the operator installation. The default operator namespace is &lt;code&gt;oracle-database-operator-system&lt;/code&gt;. The sample database resource in this article will live in &lt;code&gt;oracle-db-operator-demo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create the demo namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace oracle-db-operator-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation makes inspection and cleanup easier. It also makes the scope visible: the operator runs in its own system namespace, while the database custom resource lives in a namespace that represents the evaluation workload.&lt;/p&gt;

&lt;p&gt;This walkthrough uses the default cluster-scoped installation path, which means the operator can watch resources across namespaces. That scope is broader than the demo namespace and should be approved by the platform team. If your platform requires namespace-scoped installation, do not mix that model into this walkthrough. Follow the namespace-scoped installation instructions for your selected operator release from start to finish, including the required namespace role bindings and &lt;code&gt;WATCH_NAMESPACE&lt;/code&gt; configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install or verify cert-manager first
&lt;/h2&gt;

&lt;p&gt;Oracle Database Operator for Kubernetes uses admission and conversion webhooks with TLS certificates, so cert-manager must be healthy before you apply database resources.&lt;/p&gt;

&lt;p&gt;First, check whether your cluster already has cert-manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get namespace cert-manager
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager
kubectl get crds | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; cert-manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If cert-manager is already installed and managed by your platform team, use that installation. Confirm that it is approved for your cluster and compatible with the operator release you are evaluating. Do not install a second copy into a shared cluster.&lt;/p&gt;

&lt;p&gt;If this is a disposable lab cluster and cert-manager is not installed, use cert-manager &lt;code&gt;v1.16.2&lt;/code&gt; for this &lt;code&gt;v2.1.0&lt;/code&gt; walkthrough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the cert-manager deployments to become available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Available deployment/cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;180s

kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Available deployment/cert-manager-webhook &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;180s

kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Available deployment/cert-manager-cainjector &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;180s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a shared or production-like cluster, do not install cert-manager from this article. Use the cert-manager installation approved by your platform team.&lt;/p&gt;

&lt;p&gt;If the cert-manager pods do not move toward &lt;code&gt;Running&lt;/code&gt;, inspect the namespace events before continuing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager &lt;span class="nt"&gt;--sort-by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.lastTimestamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep cert-manager troubleshooting separate from database troubleshooting. Admission webhook and certificate problems are much easier to isolate before database resources are involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Oracle Database Operator for Kubernetes from a release tag
&lt;/h2&gt;

&lt;p&gt;Install the operator from a release tag rather than from the default branch so the installation manifests, samples, and API fields come from the same repository version.&lt;/p&gt;

&lt;p&gt;Clone the &lt;code&gt;v2.1.0&lt;/code&gt; repository tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v2.1.0 &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/oracle/oracle-database-operator.git
&lt;span class="nb"&gt;cd &lt;/span&gt;oracle-database-operator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the default cluster-scoped path, apply the cluster role binding and operator manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; rbac/cluster-role-binding.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; oracle-database-operator.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The release-tagged manifest is the right starting point for this first run, but remember that it references &lt;code&gt;container-registry.oracle.com/database/operator:latest&lt;/code&gt; for the controller image. If your platform requires repeatable builds, image attestations, or explicit rollback targets, resolve and pin the approved operator image before using the manifest in a long-lived environment.&lt;/p&gt;

&lt;p&gt;Wait for the operator deployment to roll out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout status deployment/oracle-database-operator-controller-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;300s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project README also links to an OperatorHub installation path for environments that standardize on Operator Lifecycle Manager. For this first run, stay with one path so troubleshooting remains clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm that the operator and CRDs are present
&lt;/h2&gt;

&lt;p&gt;Before applying a database resource, confirm both parts of the operator model: the Kubernetes API extension and the controller workload.&lt;/p&gt;

&lt;p&gt;Check the operator namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system
kubectl get deployments &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the operator controller manager deployment in &lt;code&gt;oracle-database-operator-system&lt;/code&gt;. Pods may take a short time to become ready while images pull and webhook certificates settle.&lt;/p&gt;

&lt;p&gt;Next, confirm that the Oracle Database custom resources are registered with the Kubernetes API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get crds | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'database.oracle.com'&lt;/span&gt;
kubectl api-resources &lt;span class="nt"&gt;--api-group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;database.oracle.com
kubectl api-resources | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'singleinstance'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CRDs prove the Kubernetes API server recognizes the custom resource type. A running operator workload proves the controller is available to watch and reconcile resources. You need both. CRDs alone do not prove reconciliation works.&lt;/p&gt;

&lt;p&gt;For this first run, verify that &lt;code&gt;singleinstancedatabases&lt;/code&gt; appears as an API resource. That tells you Kubernetes can accept the custom resource kind used by the sample.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozwsxq0r403jcwl8xu7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozwsxq0r403jcwl8xu7c.png" alt="First-run validation checkpoints for Oracle Database Operator for Kubernetes" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 2.&lt;/strong&gt; A first run is complete when you can validate each checkpoint and explain what the operator handled versus what the platform still owns. It is not a production-readiness checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the admin password secret
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SingleInstanceDatabase&lt;/code&gt; sample expects an admin password secret named &lt;code&gt;freedb-admin-secret&lt;/code&gt; with the key &lt;code&gt;oracle_pwd&lt;/code&gt;. Create it in the same namespace where the database custom resource will live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic freedb-admin-secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;oracle_pwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;choose-a-strong-password&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The angle-bracket placeholder is not part of the password. Replace the entire quoted value with a demo password that satisfies your organization’s password policy.&lt;/p&gt;

&lt;p&gt;Confirm the secret exists without printing its value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret freedb-admin-secret &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Command-line literal secrets are acceptable for a scoped demo only. For production, use your approved secret-management approach, such as a vault-backed workflow or another platform-approved secret delivery pattern. The first-run point is simple: the custom resource references a Kubernetes Secret, and the password should not be embedded in the database manifest or exposed in logs, screenshots, or terminal captures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply one supported &lt;code&gt;SingleInstanceDatabase&lt;/code&gt; sample
&lt;/h2&gt;

&lt;p&gt;Use the release-tagged sample manifest instead of building a custom resource from memory. Download the &lt;code&gt;v2.1.0&lt;/code&gt; Free Lite sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; singleinstancedatabase_free-lite.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/oracle/oracle-database-operator/v2.1.0/config/samples/sidb/singleinstancedatabase_free-lite.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before applying it, edit the file. The release-tagged sample uses &lt;code&gt;metadata.namespace: default&lt;/code&gt; and &lt;code&gt;spec.persistence.storageClass: "oci-bv"&lt;/code&gt;. Change the namespace to &lt;code&gt;oracle-db-operator-demo&lt;/code&gt;, and change the storage class to one that exists in your evaluation cluster.&lt;/p&gt;

&lt;p&gt;The relevant fields should look like this after editing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;database.oracle.com/v4&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SingleInstanceDatabase&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;freedb-lite-sample&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oracle-db-operator-demo&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;edition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;free&lt;/span&gt;
  &lt;span class="na"&gt;adminPassword&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;freedb-admin-secret&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pullFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;container-registry.oracle.com/database/free:latest-lite&lt;/span&gt;
  &lt;span class="na"&gt;persistence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50Gi&lt;/span&gt;
    &lt;span class="na"&gt;storageClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-storage-class&amp;gt;"&lt;/span&gt;
    &lt;span class="na"&gt;accessMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ReadWriteOnce"&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your cluster requires credentials for Oracle Container Registry or for an internal mirror, add the image pull secret field supported by the &lt;code&gt;SingleInstanceDatabase&lt;/code&gt; API for the operator release you are using, or follow your platform’s approved registry-mirroring process before applying the resource. The release sample shown here does not include a pull secret by default, so an otherwise correct manifest can still fail with &lt;code&gt;ImagePullBackOff&lt;/code&gt; if registry access is not ready.&lt;/p&gt;

&lt;p&gt;The sample requests &lt;code&gt;50Gi&lt;/code&gt; and uses &lt;code&gt;ReadWriteOnce&lt;/code&gt;. The &lt;code&gt;oci-bv&lt;/code&gt; storage class in the raw sample is appropriate for OCI Block Volume on Oracle Kubernetes Engine, but it will fail in clusters where that class does not exist.&lt;/p&gt;

&lt;p&gt;The sample also uses the floating database image tag &lt;code&gt;latest-lite&lt;/code&gt;. That is acceptable only for a tightly scoped evaluation where your team understands the reproducibility and security tradeoff. For retained, shared, or production-like environments, use a platform-approved, scanned, and pinned image tag or digest according to your release policy.&lt;/p&gt;

&lt;p&gt;Before applying the manifest, check the fields you changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'namespace:|storageClass:|pullFrom:'&lt;/span&gt; singleinstancedatabase_free-lite.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the edited manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; singleinstancedatabase_free-lite.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected API acceptance message looks similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;singleinstancedatabase.database.oracle.com/freedb-lite-sample created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you have expressed database intent through Kubernetes. API acceptance does not mean the database is ready. The next checks show whether the operator observes and reconciles that intent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate reconciliation through status, events, and resources
&lt;/h2&gt;

&lt;p&gt;Successful reconciliation is more than a running pod. Look for an accepted custom resource, operator activity, related Kubernetes resources, and useful status or events.&lt;/p&gt;

&lt;p&gt;Start with the custom resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get singleinstancedatabase &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
kubectl describe singleinstancedatabase freedb-lite-sample &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;kubectl get&lt;/code&gt; confirms that the resource exists in the demo namespace. &lt;code&gt;kubectl describe&lt;/code&gt; gives you the operator-facing story: status, conditions, and events associated with the resource.&lt;/p&gt;

&lt;p&gt;Now inspect the related Kubernetes resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pvc,pod,svc &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo &lt;span class="nt"&gt;--sort-by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.lastTimestamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A healthy path should show a PVC, a database pod, and a service appearing as reconciliation proceeds. The events stream is often more useful than a raw pod list because it tells you whether the cluster is scheduling the pod, provisioning storage, pulling the image, or waiting on another dependency.&lt;/p&gt;

&lt;p&gt;Check the concise status field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get singleinstancedatabase freedb-lite-sample &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"jsonpath={.status.status}{'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For SIDB, when &lt;code&gt;.status.status&lt;/code&gt; returns &lt;code&gt;Healthy&lt;/code&gt;, the database is open for connections. For this article, treat &lt;code&gt;Healthy&lt;/code&gt; as an operator-reported first-run readiness signal, not as production validation.&lt;/p&gt;

&lt;p&gt;You can also inspect the connect string fields when the operator reports them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get singleinstancedatabase freedb-lite-sample &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"jsonpath={.status.connectString}{'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get singleinstancedatabase freedb-lite-sample &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"jsonpath={.status.pdbConnectString}{'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the status is still progressing, keep watching events. Oracle Database images can be large, volume provisioning can take time, and database initialization is not instantaneous. A first-run evaluation is successful when the signals are explainable, not when every step finishes quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshoot first-run failures in the smallest scope possible
&lt;/h2&gt;

&lt;p&gt;Common first-run failures are platform prerequisites surfacing through normal Kubernetes signals. Start with the part of the system that failed, and avoid changing the manifest until you know what the cluster is reporting.&lt;/p&gt;

&lt;p&gt;If the operator pod is not running, start in the operator namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system
kubectl describe pod &amp;lt;operator-pod-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system
kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system &amp;lt;operator-pod-name&amp;gt; &lt;span class="nt"&gt;--all-containers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Likely causes include cert-manager readiness, RBAC problems, image pull issues, resource pressure, or webhook certificate problems. When admission fails or custom resources cannot be created, check cert-manager and webhook resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; cert-manager
kubectl get certificates &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system
kubectl get validatingwebhookconfiguration | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; oracle
kubectl get mutatingwebhookconfiguration | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; oracle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If CRDs are missing, stop before applying the sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get crds | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'database.oracle.com'&lt;/span&gt;
kubectl api-resources &lt;span class="nt"&gt;--api-group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;database.oracle.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Missing CRDs usually mean the manifest did not apply successfully, the installing user lacked cluster-level permissions, or the files do not match the release you intended to install.&lt;/p&gt;

&lt;p&gt;If reconciliation stalls after the custom resource is accepted, inspect the database namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pod &amp;lt;database-pod-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo &lt;span class="nt"&gt;--sort-by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.lastTimestamp
kubectl get pvc &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
kubectl describe pvc &amp;lt;claim-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A PVC stuck in &lt;code&gt;Pending&lt;/code&gt; points toward storage class, quota, access mode, capacity, or provisioner issues. An &lt;code&gt;ImagePullBackOff&lt;/code&gt; points toward registry access, image name, image tag, network egress, license acceptance, or pull-secret requirements. A missing secret or wrong key often appears through custom resource status, events, or pod startup errors.&lt;/p&gt;

&lt;p&gt;Confirm the secret exists in the same namespace and exposes the expected key without printing the secret value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret freedb-admin-secret &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
kubectl describe secret freedb-admin-secret &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
kubectl describe singleinstancedatabase freedb-lite-sample &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-db-operator-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;kubectl describe secret&lt;/code&gt; shows metadata and key sizes, not the decoded password. Do not run commands that decode or print the Secret value in screenshots, shared logs, or tickets.&lt;/p&gt;

&lt;p&gt;Operator logs are useful when status and events do not explain enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system &lt;span class="se"&gt;\&lt;/span&gt;
  deployment/oracle-database-operator-controller-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--all-containers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If deployment-level logs are not enough, inspect pods directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system
kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; oracle-database-operator-system &amp;lt;operator-pod-name&amp;gt; &lt;span class="nt"&gt;--all-containers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical default is to inspect status, events, PVCs, image pulls, secrets, RBAC, and operator logs before editing the custom resource. Random manifest changes can hide the real platform dependency that failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean up the evaluation resources
&lt;/h2&gt;

&lt;p&gt;Clean up the database resource before deleting the namespace when possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; singleinstancedatabase_free-lite.yaml
kubectl delete namespace oracle-db-operator-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then confirm the namespace is gone or terminating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get namespace oracle-db-operator-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check whether any persistent volumes or retained storage still need attention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pv
kubectl get pvc &lt;span class="nt"&gt;-A&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;oracle-db-operator-demo &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Storage cleanup depends on your storage class reclaim policy and whether dynamic or static provisioning was used. A namespace deletion may remove namespaced PVC objects, but the underlying persistent volume behavior depends on the storage backend and reclaim policy. Do not assume that all data-bearing resources are gone until you have checked the PVs and the backing storage system.&lt;/p&gt;

&lt;p&gt;Keep operator uninstall separate from this first-run cleanup. If custom resource instances still exist, operator-managed resources may have finalizers that need the operator to complete cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide what the first run tells you
&lt;/h2&gt;

&lt;p&gt;A first run is successful only if your team can explain what happened. You should be able to identify the CRD you applied, the namespace containing the database resource, the namespace containing the operator, the related Kubernetes resources that appeared, and where status, conditions, events, and logs live.&lt;/p&gt;

&lt;p&gt;You should also be able to answer practical ownership questions. Which storage class did the database use? Which secret did the resource reference? Which image did the cluster try to pull? Did the PVC bind? Did a service appear? Did the operator write status? If the run failed, did the failure point to storage, image access, webhook admission, RBAC, secret handling, scheduling, or database initialization?&lt;/p&gt;

&lt;p&gt;That is the operating-model value of the first run: it turns a vague success or failure into specific Kubernetes and database-operator signals that platform teams and DBAs can review together.&lt;/p&gt;

&lt;p&gt;The first run proves the control loop. It does not prove backup and restore, HA, Data Guard, RAC, patching, upgrades, observability, network policy, security compliance, cost efficiency, or production SLOs. Those are separate decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare the operator path with practical alternatives
&lt;/h2&gt;

&lt;p&gt;Oracle Database Operator for Kubernetes is most relevant when Oracle Database is already the required database engine and your team wants to test whether Kubernetes custom resources and reconciliation improve the operating model for supported Oracle Database scenarios.&lt;/p&gt;

&lt;p&gt;This evaluation is not a claim that every database belongs in Kubernetes. If the application can use a managed Oracle Database service, evaluate that path because managed services can shift many infrastructure and database administration tasks to the provider, depending on service tier and configuration. For many teams, that operating model is the stronger alternative when the organization values service-level ownership over platform-level control.&lt;/p&gt;

&lt;p&gt;Manual Kubernetes resources are also a valid baseline. A hand-written &lt;code&gt;StatefulSet&lt;/code&gt;, service, PVC, secret, and DBA script can teach you what Oracle Database needs from the cluster. The operator should earn its place by improving repeatability, inspection, and operational handoff, not by hiding the underlying dependencies.&lt;/p&gt;

&lt;p&gt;If your application can choose another database engine, evaluate that path on its own terms. The operator path should be judged against the workload, the required database engine, the platform team’s Kubernetes maturity, and the operating responsibilities your organization is willing to own.&lt;/p&gt;

&lt;h2&gt;
  
  
  First-run questions platform teams usually ask
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Oracle Database Operator for Kubernetes a replacement for DBA ownership?
&lt;/h3&gt;

&lt;p&gt;No. Oracle Database Operator for Kubernetes can automate supported lifecycle tasks through Kubernetes, but DBAs and platform teams still need to define storage, backup, recovery, security, observability, upgrade, and support runbooks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a successful first run prove production readiness?
&lt;/h3&gt;

&lt;p&gt;No. A successful first run proves the operator model and basic reconciliation path in one environment. Production readiness requires separate validation for storage, backup and recovery, security, monitoring, failure handling, upgrades, and support boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should I inspect first when reconciliation stalls?
&lt;/h3&gt;

&lt;p&gt;Start with the custom resource status, namespace events, operator logs, PVC state, image pull status, secrets, and RBAC errors. These signals usually show whether the issue is schema validation, permissions, registry access, storage, namespace mismatch, or cluster capacity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should the first evaluation cover every custom resource?
&lt;/h3&gt;

&lt;p&gt;No. The first evaluation should cover only the resource needed for a lean control-loop test. After that, study the custom resource model in depth and evaluate additional topologies or lifecycle operations only when the next operating question is clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continue only when the next question is clear
&lt;/h2&gt;

&lt;p&gt;If your first run succeeded, continue by studying the custom resource model in more depth. The next useful question is not “can I apply another YAML file?” It is “what belongs in &lt;code&gt;spec&lt;/code&gt;, what should I expect in &lt;code&gt;status&lt;/code&gt;, and how does reconciliation change the way platform teams and DBAs collaborate?”&lt;/p&gt;

&lt;p&gt;If the first run failed, keep the environment small and inspect prerequisites, image access, storage class behavior, secrets, events, webhooks, RBAC, and operator logs before expanding scope. Common first-run failures are easier to solve before you add more resources or topology choices.&lt;/p&gt;

&lt;p&gt;If your platform team cannot support storage, security, observability, backup, recovery, upgrades, and runbook ownership, treat the demo as a learning exercise rather than production evidence. A successful first run means the team has enough evidence to evaluate the next layer, not enough evidence to declare the platform production-ready.&lt;/p&gt;

</description>
      <category>data</category>
      <category>oracle</category>
      <category>operations</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>The trust layer for enterprise AI on Oracle: prove identity, scope, policy, and evidence for every assistant action</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Tue, 02 Jun 2026 13:27:23 +0000</pubDate>
      <link>https://dev.to/oracledevs/the-trust-layer-for-enterprise-ai-on-oracle-prove-identity-scope-policy-and-evidence-for-every-46j0</link>
      <guid>https://dev.to/oracledevs/the-trust-layer-for-enterprise-ai-on-oracle-prove-identity-scope-policy-and-evidence-for-every-46j0</guid>
      <description>&lt;p&gt;This is article 8 of 8 in my Oracle AI Database Skills series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Three receipts from independent sources — result rows, a session tag in &lt;code&gt;V$SESSION&lt;/code&gt;, and a row in the audit trail or MCP log — prove what happened without relying on the chat transcript. Trust is easier to defend when independent sources tell the same story.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Oracle's existing controls — VPD, Data Redaction, TDE, Unified Auditing, network ACLs — apply to AI-assisted actions without modification. There is no separate AI trust layer to build; the database you already operate is the control plane.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;EXEMPT ACCESS POLICY&lt;/code&gt; bypasses VPD for any session that holds it. Assign that privilege intentionally, audit who has it, and treat it as a deliberate design decision rather than a routine grant.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turning the mini-demo into a runbook — documenting the connection name, restrict level, &lt;code&gt;SHOWSQL&lt;/code&gt; step, and the three receipts — converts a one-time proof into repeatable operational practice that the whole team can follow.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;When you put an AI assistant in front of enterprise data, "we intended to be safe" is not enough. Security teams want receipts. They will ask three blunt questions: Who acted? Over what scope? Under which policies? If your answer is a shrug or a log row that says "user: ai-service," your rollout will stall.&lt;/p&gt;

&lt;p&gt;Oracle gives you a procedural way to answer those questions. Route assistants using a repository "skills" convention (for example, a &lt;code&gt;db/SKILL.md&lt;/code&gt; index you maintain) so they discover only the next relevant instruction. Let them act only through a bounded MCP (Model Context Protocol) surface—SQLcl MCP on a workstation or the Managed MCP Server on Autonomous Database. Keep Oracle governance in the path: database users and roles; outbound Network ACLs and Private Endpoints; Select AI profiles with enforced object scope; VPD and Data Redaction for runtime policy; TDE for at rest protection; and Unified Auditing (plus DBTOOLS$MCP_LOG) for traceability. Wire those pieces together deliberately and every assistant action becomes something you can prove, not just something you hope was safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version scope
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Identity, auditing, VPD, Data Redaction, and TDE apply to Oracle Database 19c and later.&lt;/li&gt;
&lt;li&gt;SQLcl MCP requires SQLcl 25.2+; confirm restrict-level defaults on your installed version (see SQLcl 25.4 docs for current defaults).&lt;/li&gt;
&lt;li&gt;Select AI profiles and &lt;code&gt;SHOWSQL&lt;/code&gt; are available on Oracle Database 26ai and on Autonomous Database where Select AI is enabled; confirm attribute names and required roles for your target release in current documentation.&lt;/li&gt;
&lt;li&gt;On Autonomous Database, direct access to &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt; can be restricted by service configuration; use &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; where appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;References: SQLcl MCP restrict levels (25.4), Using/Monitoring MCP, Select AI and DBMS_CLOUD_AI, Autonomous MCP, VPD, DBMS_REDACT, TDE, Unified Auditing (links in Further reading).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why governance is the trust layer
&lt;/h2&gt;

&lt;p&gt;Assistants are good at surprising you. That’s useful in exploration and dangerous in production. The practical way to keep surprises tolerable is to shrink the decision surface and add proof points at each hop.&lt;/p&gt;

&lt;p&gt;Routing is the first shrink. Use a progressive discovery pattern. Instead of handing the assistant your entire repo, maintain a small index file (for example, &lt;code&gt;db/SKILL.md&lt;/code&gt;) that links to the next needed skill. Load only that next file. This is a repository convention you implement, not a built-in Oracle feature.&lt;/p&gt;

&lt;p&gt;Action comes next—and only through a surface with defined verbs. SQLcl MCP exposes a small tool set over stdio (&lt;code&gt;list-connections&lt;/code&gt;, &lt;code&gt;connect&lt;/code&gt;, &lt;code&gt;disconnect&lt;/code&gt;, &lt;code&gt;run-sql&lt;/code&gt;, &lt;code&gt;run-sqlcl&lt;/code&gt;) and favors saved connections instead of inline credentials (see Using the SQLcl MCP Server). In SQLcl 25.4 documentation, the default restrict level is the most restrictive (level 4); verify this on your installed version and set it explicitly if needed (see SQLcl MCP restrict levels in Further reading).&lt;/p&gt;

&lt;p&gt;Finally, let the database do what it’s built to do: govern. Identities and roles decide who you are; Network ACLs and Private Endpoints set where you can call; Select AI profiles with &lt;code&gt;enforce_object_list&lt;/code&gt; pin the objects an NL2SQL call is allowed to touch; VPD filters rows at query time; Data Redaction masks sensitive values; TDE protects data at rest; and Unified Auditing records what actually happened. These aren’t add-ons; they’re the operating environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61ddoxui14qgq56r645w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61ddoxui14qgq56r645w.png" alt="Trust Layer Governance Path" width="540" height="817"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity and least privilege you can defend
&lt;/h2&gt;

&lt;p&gt;Give each assistant a real database identity—per assistant or per capability—and default to read-only while you explore. With SQLcl MCP, avoid credential sprawl by saving connections and having the assistant connect by name, not by password (see "Preparing Your Environment" in SQLcl docs). The MCP server’s surface area is intentionally small, and restrict levels let you disallow risky paths such as arbitrary shell access. In SQLcl 25.4 docs, the default restrict level is most restrictive (level 4); check your installed version and set it explicitly if needed (see SQLcl MCP restrict levels).&lt;/p&gt;

&lt;p&gt;Two habits make your audit trail readable. First, tag sessions. Use &lt;code&gt;DBMS_APPLICATION_INFO.SET_MODULE/SET_ACTION&lt;/code&gt; after you connect; SQLcl MCP also sets identifiable &lt;code&gt;MODULE&lt;/code&gt;/&lt;code&gt;ACTION&lt;/code&gt; values that show up in &lt;code&gt;V$SESSION&lt;/code&gt; and in audit rows (see SQLcl MCP "Using" and "Monitoring"). Second, name connections clearly so a human reviewer can reconstruct intent later. If a person must review production actions, include their initials in the connection name or in the module/action tag and correlate as you go. Note that querying &lt;code&gt;V$SESSION&lt;/code&gt; requires appropriate privileges (for example, &lt;code&gt;SELECT_CATALOG_ROLE&lt;/code&gt; or a direct grant on &lt;code&gt;V_$SESSION&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;On Autonomous Database (Serverless), the Managed MCP Server gives you a per-database endpoint governed by your database’s identities, roles, network policy, and auditing (see Autonomous Database MCP docs). Enablement requires appropriate OCI permissions and follows the procedure in the Autonomous Database "Use MCP Server" docs; in many tenancies this involves setting a free-form tag (key &lt;code&gt;adb$feature&lt;/code&gt;, value JSON &lt;code&gt;{"name":"mcp_server","enable":true}&lt;/code&gt;), but the exact key/value and UI can vary by region and service version. Choose a public posture or a Private Endpoint for isolation. Authentication is via OAuth or a short-lived bearer token; treat tokens like database passwords and confirm current token lifetimes in documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The network path is part of your security posture
&lt;/h2&gt;

&lt;p&gt;When Select AI reaches an external provider, outbound connectivity must be explicitly permitted. On self-managed databases, create database Network ACL entries for approved hosts. On Autonomous, use Private Endpoints and VCN rules to keep your blast radius small and your audit scope clean (see Autonomous Database ACL/VCN/Private Endpoint docs). The goal is simple: if data can’t leave the VCN except by policy, you can prove it didn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data minimization and enforceable scope
&lt;/h2&gt;

&lt;p&gt;The fastest way to make NL2SQL safe enough to try in a mixed-sensitivity schema is to reduce the surface area. Select AI Profiles bind reasoning to specific objects: pin the provider, credential, and model; list the allowed objects; and set &lt;code&gt;"enforce_object_list": true&lt;/code&gt; so generated SQL can’t wander to tables you didn’t name (see Select AI and DBMS_CLOUD_AI docs). Attribute names and boolean formats can vary by release; match the names and types shown in your target version’s docs. That simple move—paired with a least-privilege user—prevents most of the early "oops" moments.&lt;/p&gt;

&lt;p&gt;Inspection comes next. &lt;code&gt;SELECT AI SHOWSQL&lt;/code&gt; turns NL2SQL into a human-in-the-loop conversation: ask a question, inspect the candidate SQL, nudge it if needed, then run it intentionally (see Select AI examples). When you need narrative only—say you’re brainstorming a KPI definition—use &lt;code&gt;DBMS_CLOUD_AI.DISABLE_DATA_ACCESS&lt;/code&gt; to block sending table data to the LLM. Data-dependent features (including certain RAG/narration/synthetic flows) will return an error until you re-enable data access; check your release docs for exact behavior (DBMS_CLOUD_AI package).&lt;/p&gt;

&lt;p&gt;Row-level policy and masking push the same discipline deeper into runtime. VPD (via &lt;code&gt;DBMS_RLS&lt;/code&gt;) attaches predicates that filter rows per user, context, or session attribute; it works with ordinary SQL and with generated SQL because enforcement happens in the engine. Data Redaction (&lt;code&gt;DBMS_REDACT&lt;/code&gt;) masks sensitive columns at query time; even if a careless prompt tries to dump a column, the runtime policy wins. Remember that users with &lt;code&gt;EXEMPT ACCESS POLICY&lt;/code&gt; can bypass VPD; design policies and auditing with that in mind (see VPD and Data Redaction docs).&lt;/p&gt;

&lt;h2&gt;
  
  
  Encryption and key posture you can explain in a review
&lt;/h2&gt;

&lt;p&gt;At rest, Transparent Data Encryption (TDE) removes a class of risk from the conversation. It encrypts tablespaces or columns so that stolen media or mishandled backups don’t expose readable data. For most teams, the "receipt" here is straightforward: document that TDE is enabled for production databases, the keystore location and protection method, and the key-management process (including rotation cadence and audit hooks). This won’t solve every problem, but it makes risk discussions faster and clearer (see TDE overview docs).&lt;/p&gt;

&lt;h2&gt;
  
  
  Traceability you can hand to auditors
&lt;/h2&gt;

&lt;p&gt;When the review board asks "what happened, exactly," you should be able to tell one story that matches from three angles and lines up in time. Session identity and tags provide the first angle: &lt;code&gt;V$SESSION&lt;/code&gt; shows &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt;, so you can corroborate user context and timing (requires appropriate privileges). Unified Auditing adds the second angle: a durable record of who touched what and when, based on policies you configure for sensitive tables or operations. On managed services where direct access to unified audit views may be restricted, the SQLcl MCP activity log (&lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt;) provides a third path: model details, endpoint type, and messages from the MCP side. The overlap is intentional. Trust is easier to defend when independent sources tell the same story (see Unified Auditing and SQLcl MCP Monitoring docs).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezoozo0h06rko0wuyy6w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezoozo0h06rko0wuyy6w.png" alt="Evidence Triangulation" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini demo: prove scope → govern → audit
&lt;/h2&gt;

&lt;p&gt;In about fifteen minutes on a non-production database, you can gather three receipts: a concrete result set from a scoped query, an audit or MCP log entry that corroborates the action, and a &lt;code&gt;V$SESSION&lt;/code&gt; tag that ties the session back to the assistant. The point isn’t to show everything Oracle can do; it’s to practice the muscle memory of route → act → govern → evidence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rzzo386aldclbyri5x0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rzzo386aldclbyri5x0.png" alt="Demo Sequence" width="717" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle Database 19c+ (self-managed or Autonomous Database). For the Select AI step, use Oracle Database 26ai or an Autonomous Database with Select AI enabled.&lt;/li&gt;
&lt;li&gt;SQLcl 25.2+ installed locally.&lt;/li&gt;
&lt;li&gt;A read-only database user with &lt;code&gt;SELECT&lt;/code&gt; on a demo schema (for example, HR). Ensure this user can see the demo objects via grants; otherwise &lt;code&gt;ALL_TABLES&lt;/code&gt; won’t list HR objects.&lt;/li&gt;
&lt;li&gt;For Select AI steps, ensure the calling user has privileges to execute &lt;code&gt;DBMS_CLOUD_AI&lt;/code&gt; and to create/use AI Profiles (see DBMS_CLOUD_AI/Select AI docs for role/grant details).&lt;/li&gt;
&lt;li&gt;Ensure the provider credential referenced by your profile (for example, &lt;code&gt;GENAI_CRED&lt;/code&gt;) already exists and is configured for your chosen provider. Follow the credential-creation steps in the Select AI/DBMS_CLOUD_AI docs for your environment.&lt;/li&gt;
&lt;li&gt;Optional: Unified Auditing enabled and privilege to query &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt;. On managed services, this may be restricted; use &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;Optional (for &lt;code&gt;V$SESSION&lt;/code&gt;): privileges to read &lt;code&gt;V$SESSION&lt;/code&gt; (for example, &lt;code&gt;SELECT_CATALOG_ROLE&lt;/code&gt; or a direct grant on &lt;code&gt;V_$SESSION&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) Save a least-privilege connection in SQLcl&lt;br&gt;
Avoid putting raw credentials into prompts or MCP envelopes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sql /nolog
conn &lt;span class="nt"&gt;-save&lt;/span&gt; ro_demo &lt;span class="nt"&gt;-savepwd&lt;/span&gt; ro_user/ro_password@//host:1521/service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Security note: &lt;code&gt;-savepwd&lt;/code&gt; stores credentials for reuse. Use this only on a trusted workstation and follow your organization’s secrets policies.&lt;/p&gt;

&lt;p&gt;2) Start SQLcl MCP with a conservative posture&lt;br&gt;
Recent docs (SQLcl 25.4) describe the default restrict level as most restrictive (level 4). Verify on your version and set it explicitly if needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# default&lt;/span&gt;
sql &lt;span class="nt"&gt;-mcp&lt;/span&gt;

&lt;span class="c"&gt;# example of explicitly setting a restrict level (check your version/flags)&lt;/span&gt;
&lt;span class="c"&gt;# sql -mcp --restrict-level 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Option A — Prove scope via MCP tools&lt;br&gt;
Connect with the saved connection, tag the session for later correlation, then query. Tagging must occur in an active session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"list-connections"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:{}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"connect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"connection_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ro_demo"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"BEGIN DBMS_APPLICATION_INFO.SET_MODULE('assistant','schema-read'); END;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"SELECT owner, table_name FROM all_tables WHERE owner='HR' ORDER BY table_name FETCH FIRST 5 ROWS ONLY"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3 alt) Option B — Show generated SQL, but only for allowed objects (Select AI)&lt;br&gt;
Note: This step assumes you are on Oracle Database 26ai or an Autonomous Database with Select AI enabled, that your user has privileges to execute &lt;code&gt;DBMS_CLOUD_AI&lt;/code&gt; and create/use profiles, and that a provider credential named &lt;code&gt;GENAI_CRED&lt;/code&gt; already exists and is bound to your intended provider (see docs in Further reading). If not, create or reference an appropriate credential first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_PROFILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;profile_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR_RO'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributes&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{
      "provider": "oci",
      "credential_name": "GENAI_CRED",
      "object_list": [ { "owner": "HR", "name": "EMPLOYEES" } ],
      "enforce_object_list": true
    }'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_PROFILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HR_RO'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Tag the session for traceability (run after you are connected)&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_APPLICATION_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'assistant'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'hr-ro-showsql'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="c1"&gt;-- Inspect the candidate SQL without running it&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'list the 3 most recent employees hired'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Optional: disable data access to limit to reasoning-only features&lt;/span&gt;
&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISABLE_DATA_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the generated SQL references objects outside your profile’s &lt;code&gt;object_list&lt;/code&gt; with &lt;code&gt;"enforce_object_list": true&lt;/code&gt;, the call will be rejected. Attribute names and boolean formats can vary by release; match what your version’s docs show. If you disable data access, features that require data will return an error until you re-enable it.&lt;/p&gt;

&lt;p&gt;4) Show evidence&lt;br&gt;
If you have Unified Auditing access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dbusername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;unified_audit_trail&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If not, use the SQLcl MCP activity log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Object presence, schema, and columns vary by version and setup; consult docs.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;DBTOOLS&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;MCP_LOG&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Access to &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; may be restricted by schema/role in your environment. If the object is not visible, consult the SQLcl MCP Monitoring docs for the expected owner, grants, and pruning guidance.&lt;/p&gt;

&lt;p&gt;Optionally, confirm your tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'assistant'&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should leave with three receipts that line up in time: result rows from the HR query demonstrating scoped access; either audit rows or MCP log rows corroborating who did what and when; and a &lt;code&gt;V$SESSION&lt;/code&gt; row with &lt;code&gt;MODULE='assistant'&lt;/code&gt; and a matching &lt;code&gt;ACTION&lt;/code&gt;. If you used Select AI, keep the &lt;code&gt;SHOWSQL&lt;/code&gt; text that proves enforced object scope.&lt;/p&gt;

&lt;p&gt;Small policy add-ons (optional; non-prod only)&lt;br&gt;
To see runtime policy in action, attach a simple VPD policy (this requires creating a policy function first; test the predicate separately):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Example: attach policy after creating HR_SEC_PKG.F_REGION_PREDICATE(schema_name, object_name) RETURN VARCHAR2&lt;/span&gt;
&lt;span class="c1"&gt;-- Ensure the calling user is not granted EXEMPT ACCESS POLICY, which bypasses VPD.&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_RLS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADD_POLICY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;object_schema&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;object_name&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'EMPLOYEES'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_name&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR_REGION_FILTER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_function&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR_SEC_PKG.F_REGION_PREDICATE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;statement_types&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SELECT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_type&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DBMS_RLS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CONTEXT_SENSITIVE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or add Data Redaction to a sensitive column to see masking in real time (minimal working example using FULL redaction; choose function types and parameters to match application behavior):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Minimal, non-production demo using a common sample schema/column.&lt;/span&gt;
&lt;span class="c1"&gt;-- Requires privileges to execute DBMS_REDACT and to add a policy on the target table.&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDACT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADD_POLICY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;object_schema&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;object_name&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'EMPLOYEES'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;column_name&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'EMAIL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_name&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR_EMAIL_REDACT_ALWAYS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;function_type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DBMS_REDACT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;FULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expression&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'1=1'&lt;/span&gt;  &lt;span class="c1"&gt;-- apply to all sessions for demo&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caution: Substitute an appropriate table/column if HR is not available. Test application behavior—FULL redaction replaces values and can affect app logic. Users granted &lt;code&gt;EXEMPT REDACTION POLICY&lt;/code&gt; can bypass redaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can go wrong—and how to fix it
&lt;/h2&gt;

&lt;p&gt;"I can’t see &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt; on Autonomous." That’s expected in many managed postures. Use &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; for your demo and work with your DBA on a unified audit policy and an access pathway that fits your tenancy. When you move to production, confirm where audit data is stored and how your team retrieves it (see Unified Auditing docs).&lt;/p&gt;

&lt;p&gt;"Restrict levels don’t match the blog example." SQLcl evolves. In SQLcl 25.4 docs, the default is most restrictive (level 4). Check the defaults on your installed version and configure them explicitly so you’re not relying on memory (see SQLcl restrict levels).&lt;/p&gt;

&lt;p&gt;"My VPD policy didn’t fire." Users with &lt;code&gt;EXEMPT ACCESS POLICY&lt;/code&gt; can bypass VPD. Confirm that your policy function compiles, that any application context is set, and that you attached the policy to the base table(s) the query actually hits rather than to a view the optimizer can bypass (see VPD docs).&lt;/p&gt;

&lt;p&gt;"Select AI tried to reach a provider and failed." On self-managed estates, add outbound ACLs for the hostnames you intend to call; on Autonomous, verify Private Endpoint setup and security list rules (see Autonomous ACL/VCN docs; Select AI external-call examples).&lt;/p&gt;

&lt;p&gt;"We masked the column but the audit shows object names only." That’s normal—audits capture action metadata, not full result sets. If you need extra forensic detail for sensitive operations, add Fine-Grained Auditing (FGA) for targeted captures or instrument at the application layer (see Unified Auditing and FGA docs).&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it a runbook, not a one-off
&lt;/h2&gt;

&lt;p&gt;Treat "route → act → trust" as change control. Give assistants dedicated least-privilege users or roles and require SQLcl MCP to use saved connections with a verified restrict level at startup. Validate the network path for any assistant that can call outside the database by explicit policy—ACL entries, VCN rules, or a Private Endpoint. Narrow Select AI with profiles and &lt;code&gt;enforce_object_list&lt;/code&gt;, start with &lt;code&gt;SHOWSQL&lt;/code&gt;, and switch off data access when you only need narrative reasoning. Confirm your TDE posture in production and capture key-management audit hooks. And set &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt; early so your team can tie sessions to intent while a unified audit policy or &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; preserves the durable record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: bring receipts to every AI/database conversation
&lt;/h2&gt;

&lt;p&gt;Enterprise AI earns trust one verified action at a time. Oracle’s advantage is not that it hides your database behind a magic assistant; it’s that the database you already trust can stay in charge while the assistant works. Route using a repository "skills" convention (for example, a &lt;code&gt;db/SKILL.md&lt;/code&gt; index you control) so the model asks better, context-specific questions. Act only through SQLcl MCP or the Managed MCP Server so you know which verbs were allowed. Enforce scope and policy with Select AI profiles, VPD, and Redaction. Prove what happened with Unified Auditing and &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run the mini demo in a sandbox and stash three receipts—result rows, an audit or MCP log row, and a &lt;code&gt;V$SESSION&lt;/code&gt; tag—into your team wiki. From there, expand the same pattern to the workflows that matter. When someone asks, "How do you know the assistant didn’t touch X?" you’ll be able to show the answer on one page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SQLcl MCP Server: Using, Preparing Your Environment, Restrict Levels, Monitoring

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/using-oracle-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/using-oracle-sqlcl-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/preparing-your-environment.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/preparing-your-environment.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/configuring-restrict-levels-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/configuring-restrict-levels-sqlcl-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/monitoring-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/monitoring-sqlcl-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Autonomous Database MCP (enable via tag; endpoint; auth; Private Endpoint)

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/use-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/use-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/security-acl-vcn-private-endpoint.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/security-acl-vcn-private-endpoint.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Select AI and AI Profiles (SHOWSQL, object scoping, DISABLE_DATA_ACCESS; DBMS_CLOUD_AI)

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/iaas/autonomous-database-serverless/doc/dbms-cloud-ai-package.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/iaas/autonomous-database-serverless/doc/dbms-cloud-ai-package.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Security controls (VPD, Data Redaction, TDE)

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/using-oracle-vpd-to-control-data-access.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/using-oracle-vpd-to-control-data-access.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_REDACT.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_REDACT.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbtde/introduction-to-transparent-data-encryption.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dbtde/introduction-to-transparent-data-encryption.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Auditing (Unified Auditing; UNIFIED_AUDIT_TRAIL; Autonomous access caveats)

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/AUDIT-Unified-Auditing.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/AUDIT-Unified-Auditing.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/refrn/UNIFIED_AUDIT_TRAIL.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/23/refrn/UNIFIED_AUDIT_TRAIL.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dvgsg/creating-unified-audit-policies-and-accessing-audit-records.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dvgsg/creating-unified-audit-policies-and-accessing-audit-records.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>database</category>
      <category>security</category>
    </item>
    <item>
      <title>Agent‑safe change delivery on Oracle: discovery, online mechanics, idempotent migrations, and provable rollbacks</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Thu, 28 May 2026 15:04:13 +0000</pubDate>
      <link>https://dev.to/oracledevs/agent-safe-change-delivery-on-oracle-discovery-online-mechanics-idempotent-migrations-and-4hof</link>
      <guid>https://dev.to/oracledevs/agent-safe-change-delivery-on-oracle-discovery-online-mechanics-idempotent-migrations-and-4hof</guid>
      <description>&lt;p&gt;This is article 7 of 8 in my Oracle AI Database Skills series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The safety net is the process, not the model. Discovery before proposal, preview before apply, session-scoped verification before broad exposure, and a prepared rollback path — these steps protect against risk regardless of how confident the assistant sounds.&lt;/li&gt;
&lt;li&gt;Invisible indexes let you test the impact of a change in one session before committing it to all queries. A change that can be reversed with a single statement is the safest kind to ship.&lt;/li&gt;
&lt;li&gt;Liquibase updateSQL and Flyway dry-run output play the same role as SHOWSQL for migrations: they show exactly what SQL would execute before anything changes, giving a human an explicit approval moment.&lt;/li&gt;
&lt;li&gt;DBMS_REDEFINITION and Edition-Based Redefinition reduce blast radius for structural and code changes by keeping the previous version available as a rollback window through the entire cutover period.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
   
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Change is where ambiguity turns into outages. If you let an assistant propose schema or index changes on Oracle Database, slow the work down just enough to make it safe. The pattern across this series is route → act → trust: route proposals through Oracle Database Skills so the assistant performs discovery and establishes intent; act only through a bounded, inspectable MCP surface (SQLcl MCP, where MCP is Model Context Protocol—locally, or a managed MCP in Autonomous AI Database when available); and deliver changes with Oracle‑native online and editioning mechanics, inside a migration workflow that’s idempotent on rerun and leaves a trail you can prove later.&lt;/p&gt;

&lt;p&gt;This piece shows that posture in motion. We’ll take one additive change an assistant proposes, preview it, apply it safely, verify it helped, and capture the evidence—with a rollback ready if it didn’t.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydk8kl0tn0l66cinmmpj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydk8kl0tn0l66cinmmpj.png" alt="Agent-Safe Control Path" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Version scope and expectations
&lt;/h2&gt;

&lt;p&gt;Most examples target Oracle Database 19c and later. We rely on invisible indexes and session testing via the &lt;code&gt;OPTIMIZER_USE_INVISIBLE_INDEXES&lt;/code&gt; parameter (settable at the session or system level) as documented in the 19c Database Reference. For index maintenance, we use online index rebuilds where the 19c SQL Language Reference documents them for your index type and release (&lt;code&gt;ALTER INDEX ... REBUILD ONLINE&lt;/code&gt;). For structural table changes under DML load, we point to &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt; (19c PL/SQL Reference). For view/package evolution, we use Edition‑Based Redefinition (EBR). Migration previews use Liquibase or Flyway state tables and dry‑run modes. Evidence and rollback rely on session tagging, SQLcl MCP logging, and (optionally) Unified Auditing.&lt;/p&gt;

&lt;p&gt;If you’re on Oracle Database 26ai or Autonomous AI Database, you can also evolve vector indexes with a build‑new → validate → drop‑old pattern and measure recall/latency with &lt;code&gt;DBMS_VECTOR&lt;/code&gt;. Always confirm exact syntax and capabilities for your Release Update (RU) and service tier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ALTER INDEX (ONLINE for REBUILD): 19c SQL Language Reference&lt;/li&gt;
&lt;li&gt;OPTIMIZER_USE_INVISIBLE_INDEXES: 19c Database Reference&lt;/li&gt;
&lt;li&gt;Managing invisible indexes: 19c Admin Guide&lt;/li&gt;
&lt;li&gt;DBMS_REDEFINITION: 19c PL/SQL Reference&lt;/li&gt;
&lt;li&gt;CREATE VECTOR INDEX, VECTOR_DISTANCE, DBMS_VECTOR: 26ai docs&lt;/li&gt;
&lt;li&gt;SQLcl MCP restrict levels, tools, monitoring: 25.x SQLcl docs&lt;/li&gt;
&lt;li&gt;Liquibase and Flyway: vendor docs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why agent‑safe change matters most
&lt;/h2&gt;

&lt;p&gt;Assistants are good at pattern matching; they are bad at implicit context. Database change has a lot of implicit context: constraints you can’t see from one table, write paths you forgot exist, and optimizer behavior that swings when you add the “perfect” index. The way to make an assistant useful here is to force it to surface what it thinks it knows before it acts. That’s what Oracle Database Skills are for: schema discovery, intent disambiguation, destructive‑operation guards, and idempotency patterns live right next to delivery mechanics—migrations, online operations, EBR, and testing—so the work becomes a sequence you can reason about, not a free‑form prompt.&lt;/p&gt;

&lt;p&gt;The assistant can still draft the change. Your job becomes review and control: you see the SQL (Article 5’s &lt;code&gt;SHOWSQL&lt;/code&gt; discipline), you preview the migration (dry run), you apply through a constrained channel (MCP), and you verify impact before broad exposure (invisible index testing, or EBR for code). When that loop is the norm, teams stop arguing about whether to “let the model write DDL” and start shipping low‑risk improvements steadily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety before execution: discovery, intent, and rerunnable SQL
&lt;/h2&gt;

&lt;p&gt;Change work starts with a map. If the assistant suggests an index for &lt;code&gt;DOCUMENTS&lt;/code&gt;, its first step isn’t to create it; it is to list what already exists and establish the blast radius.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc65jdwm053g8t7p4jaor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc65jdwm053g8t7p4jaor.png" alt="Safety Gates Before Execution" width="800" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inventory what’s there (use &lt;code&gt;USER_*&lt;/code&gt; views in‑schema; &lt;code&gt;ALL_*&lt;/code&gt;/&lt;code&gt;DBA_*&lt;/code&gt; if querying across schemas):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_indexes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also list indexed columns so you don’t reinvent an existing index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;column_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_ind_columns&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the assistant claims “add an index to speed source and published date filters,” make it specify the target predicates and expected plan effect. If it can’t state the &lt;code&gt;WHERE&lt;/code&gt; clause and the rows accessed in plain terms, it doesn’t understand the workload yet. Article_06 showed why this matters: you don’t expose a new index broadly until you’ve seen it influence a plan the way you expect.&lt;/p&gt;

&lt;p&gt;For DML, destructive‑operation guards are non‑negotiable. Updates and deletes happen only with a &lt;code&gt;WHERE&lt;/code&gt; clause you can count. Preview the scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oracle doesn’t enforce &lt;code&gt;WHERE&lt;/code&gt; clauses on UPDATE/DELETE; enforce guardrails in tooling (preview gates) and code review.&lt;/p&gt;

&lt;p&gt;Reruns should be safe by default. On the data side, &lt;code&gt;MERGE&lt;/code&gt; turns brittle “insert then update” sequences into idempotent steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;MERGE&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'max_connections'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'100'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the DDL side, migration tools carry the idempotency for you with state tables and checksums. Liquibase tracks applied changeSets in &lt;code&gt;DATABASECHANGELOG&lt;/code&gt; and coordinates with &lt;code&gt;DATABASECHANGELOGLOCK&lt;/code&gt;, and Flyway uses &lt;code&gt;flyway_schema_history&lt;/code&gt;. Crucially, both can emit exactly what they would execute. Liquibase’s &lt;code&gt;updateSQL&lt;/code&gt; and Flyway’s &lt;code&gt;dryRunOutput&lt;/code&gt; are the human gate that pairs well with Article 5’s &lt;code&gt;SHOWSQL&lt;/code&gt;: you inspect the proposed SQL first, then approve the apply.&lt;/p&gt;

&lt;p&gt;Preview‑gate example (Liquibase):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Print the SQL that would run, without changing the database&lt;/span&gt;
liquibase &lt;span class="nt"&gt;--changelog-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changelog.xml updateSQL

&lt;span class="c"&gt;# Example output (truncated)&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; Changeset 20250108-idx-docs:1
CREATE INDEX docs_src_pub_idx ON documents&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;, published&lt;span class="o"&gt;)&lt;/span&gt; INVISIBLE&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; Changeset 20250108-config-merge:2
MERGE INTO config ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Liquibase prints the SQL it would execute against the current database state, based on &lt;code&gt;DATABASECHANGELOG&lt;/code&gt; and checksums.&lt;/p&gt;

&lt;p&gt;Alternatively, with Flyway (Teams/Enterprise for dry‑run variants):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate a dry-run script of pending migrations&lt;/span&gt;
flyway &lt;span class="nt"&gt;-locations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;filesystem:sql &lt;span class="nt"&gt;-dryRunOutput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;preview.sql migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dryRunOutput&lt;/code&gt; is not available in Community; use Teams/Enterprise for dry‑run script generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  From preview to apply: bounded MCP, not ad‑hoc credentials
&lt;/h2&gt;

&lt;p&gt;Once you’ve reviewed a change, you still need a controlled way to act. That’s the MCP layer: the SQLcl MCP Server locally, or a managed MCP experience in Autonomous AI Database if your tier and RU expose one.&lt;/p&gt;

&lt;p&gt;SQLcl MCP exposes a deliberately small tool surface—connect, disconnect, list saved connections, run SQL—over a &lt;code&gt;stdio&lt;/code&gt; server. In the 25.3 docs, Level 4 is the most restrictive. Check your installed version’s “Restrict levels” page for the default, and keep the highest level practical in shared environments. The recommended posture is to use pre‑saved least‑privilege connections; restrict levels can be configured to disallow ad‑hoc credentials and keep the tool surface small. The server tags sessions (&lt;code&gt;V$SESSION.MODULE&lt;/code&gt; and &lt;code&gt;.ACTION&lt;/code&gt;) and writes activity to &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; once used, so DBAs can see who did what and when. This is what “bounded action” looks like: the assistant can only do the few things the server allows, with least privilege and a record behind it. See “Using the SQLcl MCP Server,” “Restrict levels,” and “Monitoring” in the SQLcl docs for the precise tools, defaults, and logs for your version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fee09xwc5ygmd400zf6f5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fee09xwc5ygmd400zf6f5.png" alt="Bounded MCP action surfaces for governed Oracle Database access and auditing" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your Autonomous AI Database service and RU expose a managed MCP endpoint (see your service’s Help Center “Use MCP Server” page), you can integrate assistant actions with database identity, network controls, and auditing. Capabilities and APIs vary by service tier and RU; verify availability and procedures in your tenancy documentation. If a managed MCP endpoint isn’t available for your tier/RU, use the SQLcl MCP Server approach above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce blast radius with Oracle’s online mechanics
&lt;/h2&gt;

&lt;p&gt;Not every change needs a window. Oracle has first‑party features for evolving structures while the system stays available.&lt;/p&gt;

&lt;p&gt;Structural changes under write load use &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt;. The flow is predictable—eligibility check, start, copy dependent objects and constraints, sync the interim table, finish, and clean up—and the documentation spells out object and LOB rules per release (19c PL/SQL Reference). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddtpnkzk891z6dn92e3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddtpnkzk891z6dn92e3o.png" alt="DBMS_REDEFINTION Skeleton" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A compact, typical plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eligibility (choose key strategy; use &lt;code&gt;CONS_USE_PK&lt;/code&gt; when a primary key exists, or &lt;code&gt;CONS_USE_ROWID&lt;/code&gt; otherwise—confirm constraints and object types supported in your version):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAN_REDEF_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uname&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tname&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;options_flag&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CONS_USE_PK&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create interim table with desired structure (required unless supplying a column mapping to &lt;code&gt;START_REDEF_TABLE&lt;/code&gt;):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Example: like-for-like copy you will later adjust as needed&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;app_owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders_int&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;app_owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Start:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;START_REDEF_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uname&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tname&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;int_table&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS_INT'&lt;/span&gt;
    &lt;span class="c1"&gt;-- , col_mapping =&amp;gt; '...'  -- use when structures differ&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Copy dependents (constraints, triggers, indexes, grants) and track errors:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DECLARE&lt;/span&gt;
  &lt;span class="n"&gt;v_errs&lt;/span&gt; &lt;span class="n"&gt;PLS_INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COPY_TABLE_DEPENDENTS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uname&lt;/span&gt;            &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tname&lt;/span&gt;            &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;int_table&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS_INT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_errors&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v_errs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_triggers&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_constraints&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_indexes&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;copy_statistics&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Sync:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SYNC_INTERIM_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finish and swap:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_REDEFINITION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FINISH_REDEF_TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ORDERS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Cleanup: reconcile stats/grants, then drop interim artifacts as appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LOBs, materialized view logs, row movement, and editioning interactions have version‑specific rules—confirm them in your release documentation before use.&lt;/p&gt;

&lt;p&gt;When your change is in editioned code rather than the table itself, Edition‑Based Redefinition lets you run two versions at once and cut over on your terms. Tables are not editioned, so the safe pattern is to shield them behind editioning views and synonyms, evolve the views or packages in a child edition, test in that edition, then switch references.&lt;/p&gt;

&lt;p&gt;Prerequisites (typically applied by a DBA):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System privileges: &lt;code&gt;CREATE ANY EDITION&lt;/code&gt; (to create editions), &lt;code&gt;DROP ANY EDITION&lt;/code&gt; (to retire), and the ability to &lt;code&gt;ALTER USER ... ENABLE EDITIONS&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Object privileges in the target schema to create editioning views and editioned synonyms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Build‑plan: EBR cutover (compact)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wjsx025hx58vviwzpu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wjsx025hx58vviwzpu2.png" alt="Edition-Based Redefinition Rollback Path" width="800" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable editions for your user:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;app_owner&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="n"&gt;EDITIONS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In ORA$BASE (or your current default edition), create an editioning view to shield the underlying table. The view name is editioned; the table is not:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EDITIONING&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;ev_orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a child edition:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EDITION&lt;/span&gt; &lt;span class="n"&gt;app_v2&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;CHILD&lt;/span&gt; &lt;span class="k"&gt;OF&lt;/span&gt; &lt;span class="n"&gt;ORA&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Test in the child edition and evolve the API surface:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;EDITION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_v2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Evolve API in the child edition (example: add a computed projection)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;EDITIONING&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;ev_orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;order_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'CANCELLED'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;is_cancelled&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Cutover: change which edition clients use (via connection‑level edition, services, or app routing). Keep ORA$BASE as a rollback window.&lt;/li&gt;
&lt;li&gt;Rollback: switch sessions back to the previous edition while you investigate; retire the old edition once stable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Indexes get two useful mechanics. First, invisible indexes let you test plan influence without affecting other sessions. You can create a new index INVISIBLE or set an existing index invisible, then in your session enable it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;optimizer_use_invisible_indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If plans improve (verified via &lt;code&gt;EXPLAIN PLAN&lt;/code&gt; and &lt;code&gt;DBMS_XPLAN&lt;/code&gt;, and ideally timed execution), you can make the index visible for everyone. Second, &lt;code&gt;ALTER INDEX ... REBUILD ONLINE&lt;/code&gt; is documented in 19c for supported index types, letting DML continue during maintenance with some restrictions. Note the careful wording: in 19c the ONLINE clause is documented for REBUILD; do not assume &lt;code&gt;CREATE INDEX ONLINE&lt;/code&gt; for B‑tree indexes unless your exact release and index type say so.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnhdotxo5mbxrdfhaz4o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnhdotxo5mbxrdfhaz4o.png" alt="Index Evolution Patterns" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The same mindset for vector and relational indexes
&lt;/h2&gt;

&lt;p&gt;Oracle Database 26ai adds native vector indexes—HNSW (Hierarchical Navigable Small World) and IVF (Inverted File)—and vector distance operators. The safe evolution pattern is the same as for relational indexes: build a tuned index in parallel, validate it with representative queries, then drop the old one after a soak period.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Check your exact RU/service tier for required/optional clauses and defaults.&lt;/span&gt;
&lt;span class="c1"&gt;-- HNSW example (common defaults shown; tune per workload)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_hnsw_new&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;HNSW&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- IVF example (parameter defaults vary by RU)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_ivf_new&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;IVF&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your RU requires additional parameters (e.g., neighborhood or partition knobs), supply them as documented; prefer a build‑new → validate → drop‑old evolution unless your docs explicitly state online/rebuild semantics. Validate using workload queries or &lt;code&gt;DBMS_VECTOR&lt;/code&gt; advisers/accuracy checks, watching both recall and latency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving what happened—and preparing to undo it
&lt;/h2&gt;

&lt;p&gt;If an assistant participates in change, you need clean answers to who did what and how to reverse it. Start by tagging sessions at the application level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_APPLICATION_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mcp-agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'index-additive-change'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DBAs can then look in &lt;code&gt;V$SESSION&lt;/code&gt; for that module/action pair, and SQLcl MCP adds its own evidence in &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; with each tool‑mediated request. With Unified Auditing enabled, you can go further: define policies for specific DDL or DML and query &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt; to show who executed which statement and when. Access to dynamic performance views or audit trails depends on privileges; if your role is narrower, MCP logs still give you a reliable record. If you lack access to &lt;code&gt;V$SESSION&lt;/code&gt;, ask a DBA to expose a filtered view you can query.&lt;/p&gt;

&lt;p&gt;Rollback should be proportional to the change. For an index, the fastest backout is to mark it invisible; the hard backout is to drop it. For redefinition or EBR, the rollback is to keep the original structure or edition until final cutover and switch back if something misbehaves. For migrations, Liquibase can roll back by tag/changeSet/date when you’ve authored rollbacks (for supported change types); Flyway supports undo with authored scripts (Teams/Enterprise editions). The operative word is “authored”: write the backout path when you write the forward path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybp0y92bjwom802cpoy5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybp0y92bjwom802cpoy5.png" alt="Evidence and Rollback Map" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A quick Unified Auditing check (if enabled and you have privileges):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dbusername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql_text&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;unified_audit_trail&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;action_name&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CREATE INDEX'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'ALTER INDEX'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'DROP INDEX'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;object_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'APP_OWNER'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mini‑demo: propose, review, apply, verify, prove
&lt;/h2&gt;

&lt;p&gt;Let’s run one small, additive change that an assistant proposes. We’ll add a composite index to speed a common filter on &lt;code&gt;DOCUMENTS&lt;/code&gt;. The mechanics are deliberately conservative: INVISIBLE first, session‑level test, then visible or back out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F588afslapeys0r7h09yi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F588afslapeys0r7h09yi.png" alt="Mini-Demo Route" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prerequisites for this demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle Database 19c or later.&lt;/li&gt;
&lt;li&gt;Privileges: &lt;code&gt;CREATE INDEX&lt;/code&gt; on &lt;code&gt;DOCUMENTS&lt;/code&gt; owner; &lt;code&gt;ALTER SESSION&lt;/code&gt;; &lt;code&gt;EXPLAIN PLAN&lt;/code&gt;; &lt;code&gt;EXECUTE&lt;/code&gt; on &lt;code&gt;DBMS_XPLAN&lt;/code&gt; and &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Access to &lt;code&gt;V$SESSION&lt;/code&gt; requires &lt;code&gt;SELECT_CATALOG_ROLE&lt;/code&gt; or equivalent; if not available, rely on &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; for evidence.&lt;/li&gt;
&lt;li&gt;SQLcl MCP Server running with a pre‑saved least‑privilege connection to the target schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) Preflight discovery. If an assistant suggests an index, ask it to verify there isn’t already one that covers the same columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_indexes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;column_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_ind_columns&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Outside the owner schema, use &lt;code&gt;ALL_INDEXES&lt;/code&gt;/&lt;code&gt;ALL_IND_COLUMNS&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;2) Proposal. The assistant drafts an index for &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;published&lt;/code&gt;. Keep it INVISIBLE so only a session that opts in can use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;INVISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Apply through a bounded surface. Approve the statement and run it via SQLcl MCP’s SQL tool using a least‑privilege, pre‑saved connection. Configure restrict levels to keep the tool surface small and auditable; MCP logs the call and tags the session automatically in &lt;code&gt;V$SESSION&lt;/code&gt; (subject to your privileges).&lt;/p&gt;

&lt;p&gt;4) Test before broad exposure. Enable invisible indexes only in your session and check plan shape and timing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;optimizer_use_invisible_indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="n"&gt;PLAN&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;plan_table_output&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DBMS_XPLAN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISPLAY&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For final validation, prefer executed plans via &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR&lt;/code&gt; with &lt;code&gt;ALLSTATS LAST&lt;/code&gt; after running the query, as EXPLAIN PLAN can differ from runtime under binds or adaptive features. If &lt;code&gt;PLAN_TABLE&lt;/code&gt; isn’t present in your schema, either create it (DBA task) or use &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR&lt;/code&gt;, which doesn’t rely on &lt;code&gt;PLAN_TABLE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also capture actual timings on representative data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show the last executed statement's plan with runtime stats (if available)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DBMS_XPLAN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISPLAY_CURSOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ALLSTATS LAST'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the plan and timings improve, make the index visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt; &lt;span class="n"&gt;VISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5) Evidence. Tag the module/action explicitly and pull recent MCP entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_APPLICATION_INFO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mcp-agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'index-additive-change'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="c1"&gt;-- Evidence in V$SESSION (requires privileges)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'mcp-agent'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Evidence in MCP logs (column names can vary by version)&lt;/span&gt;
&lt;span class="c1"&gt;-- DESCRIBE DBTOOLS$MCP_LOG;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;DBTOOLS&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;MCP_LOG&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- If you need ordering, first identify a timestamp or sequence column via DESCRIBE/USER_TAB_COLUMNS,&lt;/span&gt;
&lt;span class="c1"&gt;-- then apply ORDER BY accordingly.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If logs appear empty, run a simple MCP &lt;code&gt;run-sql&lt;/code&gt; request and retry—the log table populates after the first tool interactions. Querying &lt;code&gt;V$SESSION&lt;/code&gt; requires &lt;code&gt;SELECT_CATALOG_ROLE&lt;/code&gt; or equivalent; if you don’t have it, rely on MCP logs and application‑level evidence or ask a DBA to expose a filtered view.&lt;/p&gt;

&lt;p&gt;6) Rollback if needed. If the index hurts a minority of queries or adds unacceptable write overhead, back it out quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt; &lt;span class="n"&gt;INVISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- tactical backout&lt;/span&gt;
&lt;span class="c1"&gt;-- or&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_src_pub_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;-- hard revert&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the loop: propose through discovery, preview the SQL, act through MCP, verify locally via INVISIBLE, and record the proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery mechanics that hold up in real pipelines
&lt;/h2&gt;

&lt;p&gt;The same loop translates cleanly to CI/CD and change boards. Migration tools handle state and retries; MCP provides execution discipline; Oracle features reduce blast radius.&lt;/p&gt;

&lt;p&gt;In practice, you keep DDL/DML changes in version control and insist on previews. Liquibase’s &lt;code&gt;updateSQL&lt;/code&gt; prints exactly what would run for a given changelog; Flyway’s &lt;code&gt;dryRunOutput&lt;/code&gt; does the same for pending migrations. Checksums and state tables prevent accidental reruns. Avoid hand‑run DDL outside the tool, which can desynchronize history tables from reality. For table changes that must happen while writes continue, &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt; is the predictable route; for code and view evolution, EBR gives you reversible cutovers. For online index maintenance, REBUILD ONLINE helps keep writes flowing, while INVISIBLE indexing keeps tests local until you’re ready.&lt;/p&gt;

&lt;p&gt;Testing belongs inside this posture, not as an afterthought. For PL/SQL and schema‑anchored behavior, &lt;code&gt;utPLSQL&lt;/code&gt; lets you write fast, repeatable tests and run them in CI. When you’re changing query paths, pair &lt;code&gt;DBMS_XPLAN&lt;/code&gt; plan displays with simple timing harnesses and capture them in artifacts the same way you capture dry‑run SQL. This is how agents and humans occupy the same workflow: the agent proposes, the pipeline proves.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short view for migration leads
&lt;/h2&gt;

&lt;p&gt;If you’re modernizing a system or switching database platforms, treat “agent‑safe change” as the daily rhythm and migrations as a route through it. The sequence has four stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 — Assess.&lt;/strong&gt; Inventory every object by type and complexity before writing a single migration script. Oracle’s skills repo includes a &lt;code&gt;migration-assessment.md&lt;/code&gt; skill that routes this step. In practice, complexity falls into three tiers: simple objects (tables with standard types, views, sequences) that translate with minimal manual effort; moderate objects (stored procedures, triggers, basic PL/SQL packages) that need construct-by-construct review; and complex objects (advanced types, Java stored procedures, database links, Oracle-specific partitioning strategies, or heavy use of &lt;code&gt;DBMS_*&lt;/code&gt; packages) that require hands-on expert review. An assistant can draft the inventory query and score the tiers—your job is to sanity-check the classification before committing to a timeline.&lt;/p&gt;

&lt;p&gt;A useful starting inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;all_objects&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SOURCE_SCHEMA&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2 — Translate with source-specific guidance.&lt;/strong&gt; Each source platform has predictable problem areas. For PostgreSQL migrations, the most common friction points documented in &lt;code&gt;migrate-postgres-to-oracle.md&lt;/code&gt; are: sequence syntax (&lt;code&gt;SERIAL&lt;/code&gt; → &lt;code&gt;GENERATED AS IDENTITY&lt;/code&gt;), &lt;code&gt;BOOLEAN&lt;/code&gt; columns (Oracle has no native BOOLEAN in SQL prior to 23ai; the standard convention is &lt;code&gt;NUMBER(1,0)&lt;/code&gt; with a &lt;code&gt;CHECK (col IN (0,1))&lt;/code&gt; constraint), &lt;code&gt;RETURNING&lt;/code&gt; clauses (Oracle restricts &lt;code&gt;RETURNING INTO&lt;/code&gt; to PL/SQL blocks, requiring application refactoring), &lt;code&gt;ILIKE&lt;/code&gt; (rewrite as &lt;code&gt;UPPER(column) LIKE UPPER(pattern)&lt;/code&gt; with a supporting function-based index), and &lt;code&gt;DATE&lt;/code&gt; semantics (Oracle's &lt;code&gt;DATE&lt;/code&gt; stores time-of-day, so equality filters like &lt;code&gt;WHERE created = DATE '2024-01-15'&lt;/code&gt; silently miss rows that have a non-midnight time component). An assistant loaded with &lt;code&gt;migrate-postgres-to-oracle.md&lt;/code&gt; from the skills repo can flag these automatically when reviewing source DDL—but always verify the translation with a human before running it in staging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 — Pilot additive changes in staging.&lt;/strong&gt; Before cutover, run a representative subset of translated objects through the full discovery → preview → bounded apply → verify loop described above. This is where invisible indexes, preview-gate SQL, and &lt;code&gt;DBMS_REDEFINITION&lt;/code&gt; earn their keep. Focus on the moderate and complex tiers first; the simple tier can usually be scripted and batch-applied. Confirm that application queries return the same results on Oracle as they did on the source—row counts, join shapes, and date arithmetic are the usual suspects for silent semantic differences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4 — Cutover and verify.&lt;/strong&gt; Keep ORA$BASE or a pre-cutover schema snapshot available as a rollback window. Use Liquibase or Flyway changelogs so the migration state is recorded and reruns are safe. After cutover, run your standard smoke tests plus any Oracle-specific validation queries you assembled during assessment. Tag the migration session with &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt; so post-cutover audit queries can isolate migration activity from normal application traffic.&lt;/p&gt;

&lt;p&gt;By the time you’re scheduling the production cutover, the mechanics should be boring. The assistant can draft scripts and surface incompatibilities; your tooling and Oracle’s online features make the process predictable and reversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to remember—and what’s next
&lt;/h2&gt;

&lt;p&gt;Agent‑safe change is not a bet on model intelligence; it’s a bet on process. Force discovery before action. Require a previewed SQL artifact. Act only through a bounded server with least privilege. Use Oracle’s online and editioning features to keep availability high. Tag sessions, log activity, and keep a rollback path that matches the change. When you operate this way, you can accept more assistant help without accepting more risk.&lt;/p&gt;

&lt;p&gt;In Article 8 we’ll close the loop with governance: identity and least privilege, policy, VPD, data redaction, and Unified Auditing as a procedural trust layer around the same route → act → trust loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SQLcl MCP Server: tools, restrict levels, monitoring and logs&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/using-oracle-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.2/sqcug/using-oracle-sqlcl-mcp-server.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/sqlcl-mcp-server-tools.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/sqlcl-mcp-server-tools.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/configuring-restrict-levels-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.3/sqcug/configuring-restrict-levels-sqlcl-mcp-server.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/monitoring-sqlcl-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/sql-developer-command-line/25.4/sqcug/monitoring-sqlcl-mcp-server.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Online operations and index behavior (19c)&lt;br&gt;
ALTER INDEX (ONLINE for REBUILD): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-INDEX.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-INDEX.html&lt;/a&gt;&lt;br&gt;
OPTIMIZER_USE_INVISIBLE_INDEXES: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/OPTIMIZER_USE_INVISIBLE_INDEXES.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/OPTIMIZER_USE_INVISIBLE_INDEXES.html&lt;/a&gt;&lt;br&gt;
Managing invisible indexes: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-indexes.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-indexes.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DBMS_REDEFINITION (19c)&lt;br&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_REDEFINITION.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_REDEFINITION.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;EBR (editions and editioning views)&lt;br&gt;
Start with the SQL Language Reference (CREATE EDITION; ALTER SESSION SET EDITION) and the Edition‑Based Redefinition Guide for your release (19c/23ai/26ai).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vector indexing and functions (26ai)&lt;br&gt;
CREATE VECTOR INDEX: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html&lt;/a&gt;&lt;br&gt;
VECTOR_DISTANCE: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html&lt;/a&gt;&lt;br&gt;
DBMS_VECTOR: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migrations tooling&lt;br&gt;
Liquibase: &lt;a href="https://www.liquibase.org/" rel="noopener noreferrer"&gt;https://www.liquibase.org/&lt;/a&gt;&lt;br&gt;
Flyway: &lt;a href="https://documentation.red-gate.com/fd" rel="noopener noreferrer"&gt;https://documentation.red-gate.com/fd&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Evidence and auditing&lt;br&gt;
DBMS_APPLICATION_INFO: PL/SQL Packages and Types Reference (match your version)&lt;br&gt;
Unified Auditing and UNIFIED_AUDIT_TRAIL: Oracle Database Security Guide (match your version)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Series continuity&lt;br&gt;
Article 5 (Select AI, AI Profiles, SHOWSQL) and Article 6 (index and vector evaluation) provide the inspection and evaluation habits used here.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Notes on differences across releases and editions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Index ONLINE semantics: In Oracle 19c, &lt;code&gt;ONLINE&lt;/code&gt; is documented for &lt;code&gt;ALTER INDEX ... REBUILD&lt;/code&gt; for supported index types with restrictions; check your exact RU and index type. Do not assume &lt;code&gt;CREATE INDEX ONLINE&lt;/code&gt; for B‑tree indexes in 19c.&lt;/li&gt;
&lt;li&gt;Invisible indexes: Controlled by &lt;code&gt;OPTIMIZER_USE_INVISIBLE_INDEXES&lt;/code&gt; at the session/system level. Validate on your target RU.&lt;/li&gt;
&lt;li&gt;Vector indexes: 26ai introduces HNSW and IVF; confirm syntax and capabilities for your RU/tier. Prefer build‑new → validate → drop‑old unless your docs state otherwise.&lt;/li&gt;
&lt;li&gt;SQLcl MCP: Tools and restrict‑level defaults are versioned. Verify the tool list and defaults for your installed SQLcl version.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>database</category>
      <category>devops</category>
      <category>sql</category>
    </item>
    <item>
      <title>How to Enforce Role-Based Data Access in AI Applications with Oracle Deep Data Security</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Thu, 28 May 2026 13:54:19 +0000</pubDate>
      <link>https://dev.to/oracledevs/how-to-enforce-role-based-data-access-in-ai-applications-with-oracle-deep-data-security-43om</link>
      <guid>https://dev.to/oracledevs/how-to-enforce-role-based-data-access-in-ai-applications-with-oracle-deep-data-security-43om</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI assistants should not rely on prompts or application code to enforce sensitive data access rules.&lt;/li&gt;
&lt;li&gt;Oracle Deep Data Security keeps row, column, and document authorization in the database before data reaches the LLM.&lt;/li&gt;
&lt;li&gt;DEAL uses the same SQL and vector retrieval for Linda and Wendy, while Oracle Database returns different authorized results.&lt;/li&gt;
&lt;li&gt;The companion GitHub repository contains the runnable Python demo, including ADB-S setup scripts and an optional OCI GenAI chatbot loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
   
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;AI-powered applications often use two kinds of retrieval at the same time: SQL for structured business records and vector search for contextual documents. A loan assistant might use SQL to list applications under review, then use vector search to retrieve policy guidance about income verification, debt-to-income exceptions, or credit risk. Both paths can reach sensitive data.&lt;/p&gt;

&lt;p&gt;That retrieval power creates a security problem. If an assistant can call broad SQL tools or broad RAG retrievers, it might fetch data the current user should not see. Prompt injection, missing metadata filters, a malformed tool call, or a forgotten Python redaction step can turn a useful chatbot into a data exposure path.&lt;/p&gt;

&lt;p&gt;The safest place to enforce access rules is the database, where the protected data already lives. Oracle Database security controls have been trusted for decades to enforce application access policies close to the rows and columns being protected. An LLM should not become the system of record for authorization decisions. It is probabilistic, prompt-influenced, and easy to steer into requesting more data than the user should receive.&lt;/p&gt;

&lt;p&gt;That distinction matters because data exfiltration is expensive. A single overbroad answer can expose regulated customer data, trigger incident response and disclosure work, create audit findings, violate compliance requirements, and erode trust in the AI application. Database security policy is also more provable and auditable than LLM behavior: you can inspect grants, test queries under known identities, review database audit records, and show that enforcement happens before data leaves the database. The goal is not to make the prompt better at saying no; the goal is to make sure the database only returns data the active end user is allowed to see.&lt;/p&gt;

&lt;p&gt;In this tutorial, we build &lt;strong&gt;DEAL — Deep Data Security Enabled Assistant for Lending&lt;/strong&gt;. DEAL is a terminal-based Python loan assistant that connects to &lt;strong&gt;Oracle AI Database 26ai on Autonomous Database Serverless&lt;/strong&gt; with a wallet. The demo uses direct logon as local Deep Sec end users: &lt;code&gt;linda&lt;/code&gt;, a loan officer, and &lt;code&gt;wendy&lt;/code&gt;, an underwriter.&lt;/p&gt;

&lt;p&gt;The proof point is simple: DEAL runs the same broad SQL and the same vector retrieval pattern for both users. Oracle Database enforces the configured data access policies for the demo's SQL and vector retrieval paths before data reaches Python, the tool layer, or the LLM. The Python code does not filter rows by user, redact restricted fields, or filter RAG results by audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Oracle Deep Data Security?
&lt;/h2&gt;

&lt;p&gt;Oracle Deep Data Security is a database-enforced data authorization framework in &lt;strong&gt;Oracle AI Database 26ai&lt;/strong&gt;. It lets applications pass end-user security context to the database so that the database can enforce fine-grained access to protected data.&lt;/p&gt;

&lt;p&gt;In DEAL, there are four important pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database owner user&lt;/strong&gt;: the database account Python uses for setup scripts, such as &lt;code&gt;DEEPSEC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application end user&lt;/strong&gt;: the human using the app. DEAL uses &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Sec end-user context&lt;/strong&gt;: the database identity that is active while a user-scoped operation runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data roles and data grants&lt;/strong&gt;: the Deep Data Security objects that define which rows, columns, and operations are available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The teaching pattern throughout the demo is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;same query shape
same SQL
different Deep Sec end user
different authorized result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The no-token path uses direct logon as local Deep Sec end users. Deep Sec also supports service-user context patterns where the application connects as a service user and sets end-user context before each database operation. Some service-user patterns use external identity tokens, and some use prepared local Deep Sec end-user identities.&lt;/p&gt;

&lt;p&gt;Use this table to choose the right identity path:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;How the database knows the end user&lt;/th&gt;
&lt;th&gt;External IAM or Entra token required?&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local Deep Sec direct logon&lt;/td&gt;
&lt;td&gt;Python connects directly as a local Deep Sec end user such as &lt;code&gt;linda&lt;/code&gt; or &lt;code&gt;wendy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Best for a compact tutorial, lab, or proof where each demo user can have a local Deep Sec password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service user with local Deep Sec context&lt;/td&gt;
&lt;td&gt;Python connects as the application/service schema, then sets context for a prepared local Deep Sec end user&lt;/td&gt;
&lt;td&gt;No external IAM token, but requires local end-user setup and context-key handling&lt;/td&gt;
&lt;td&gt;Useful when the app should keep a stable service connection while still avoiding an external identity provider for a demo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service user with OCI IAM or Microsoft Entra&lt;/td&gt;
&lt;td&gt;Python connects as the application/service schema, then sets context using a database access token issued by the identity provider&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Best for production-style apps that already authenticate users through OCI IAM, Entra, or a federated identity flow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rest of the tutorial uses the first path: local Deep Sec direct logon. That keeps the security proof focused on database-enforced row, column, and vector retrieval behavior instead of identity-provider setup.&lt;/p&gt;

&lt;p&gt;Deep Data Security requires Oracle Database 26ai and is supported only in &lt;code&gt;python-oracledb&lt;/code&gt; Thin mode. Do not call &lt;code&gt;oracledb.init_oracle_client()&lt;/code&gt; in this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxex6ktv9gk5v6cxpdiep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxex6ktv9gk5v6cxpdiep.png" alt="Architecture diagram showing Linda or Wendy using the Python DEAL assistant, which connects to Oracle AI Database 26ai with python-oracledb Thin mode as local Deep Sec end users and receives database-enforced loan rows, restricted columns, and policy document results." width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DEAL architecture: the Python assistant connects as local Deep Sec end users, and Oracle Database enforces the configured data access policies for the demo’s SQL and vector retrieval paths.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of Oracle Deep Data Security
&lt;/h2&gt;

&lt;p&gt;DEAL uses a small lending scenario to demonstrate the Deep Data Security capabilities that matter most to AI application developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data roles and end-user identity
&lt;/h3&gt;

&lt;p&gt;Deep Sec data roles group data access permissions. DEAL uses two data roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LOAN_OFFICER_ROLE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNDERWRITER_ROLE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The prepared Deep Sec end users are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;linda&lt;/code&gt;, assigned to &lt;code&gt;LOAN_OFFICER_ROLE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wendy&lt;/code&gt;, assigned to &lt;code&gt;UNDERWRITER_ROLE&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the no-token path, Linda and Wendy are local Deep Sec end users who can log on directly after an administrator grants a standard &lt;code&gt;CREATE SESSION&lt;/code&gt; role through the Deep Sec data roles. That keeps the tutorial free of external IAM or Entra token setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Row-level security
&lt;/h3&gt;

&lt;p&gt;Deep Sec data grants can include row predicates. DEAL uses row rules like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linda sees loan applications where &lt;code&gt;assigned_officer&lt;/code&gt; matches the active Deep Sec username.&lt;/li&gt;
&lt;li&gt;Wendy sees loan applications where &lt;code&gt;in_underwriting_queue = 'Y'&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application SQL does not add &lt;code&gt;WHERE assigned_officer = :end_user&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Column-level security
&lt;/h3&gt;

&lt;p&gt;Data grants can restrict column access. DEAL restricts sensitive financial and underwriting fields for Linda, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;customer_ssn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;customer_income&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;customer_credit_score&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;underwriting_decision&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;risk_score&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;underwriting_notes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the demo output, restricted values are displayed as &lt;code&gt;NULL&lt;/code&gt;. The Python code does not replace values with &lt;code&gt;None&lt;/code&gt; or hide fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deep Sec-scoped vector retrieval
&lt;/h3&gt;

&lt;p&gt;Role-scoped vector retrieval matters because RAG systems often search across policy, procedure, and knowledge-base documents that are not equally appropriate for every user. A loan officer might need intake and customer-document guidance, while an underwriter might need credit-risk escalation procedures. If the retriever ranks every nearby document first and asks the LLM to ignore unauthorized context later, sensitive guidance has already left the database. DEAL makes the authorization check happen before vector ranking returns results to the application.&lt;/p&gt;

&lt;p&gt;DEAL stores policy documents in Oracle Database with a &lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt; column. The core demo uses deterministic 3-dimensional demo vectors so the tutorial stays self-contained.&lt;/p&gt;

&lt;p&gt;The vector retrieval query runs under Linda’s or Wendy’s Deep Sec context. The application does not add a security filter such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, Deep Sec data grants govern which &lt;code&gt;loan_policies&lt;/code&gt; rows are visible to the query; the vector query ranks the rows visible under the active end-user context.&lt;/p&gt;

&lt;p&gt;We use &lt;code&gt;VECTOR_DISTANCE()&lt;/code&gt; in the SQL because it keeps the distance expression and metric explicit. Oracle AI Vector Search also provides shorthand vector distance operators, but this tutorial intentionally stays with &lt;code&gt;VECTOR_DISTANCE()&lt;/code&gt; so the Deep Sec proof is easier to read. The demo uses a small data set and does not create a vector index. A vector index is not useful for proving performance or plans on eight rows; add indexing later when you move to a larger corpus and can verify index metadata and execution behavior for your workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application trust boundary
&lt;/h3&gt;

&lt;p&gt;The assistant is not the security boundary. DEAL’s Python code is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting to Oracle AI Database 26ai.&lt;/li&gt;
&lt;li&gt;Binding values safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The database is responsible for enforcing the configured row, column, and policy-document access rules.&lt;/p&gt;

&lt;p&gt;The demo avoids these application-side security patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;assigned_officer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'general'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_ssn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those patterns can work in one carefully reviewed code path, but they are fragile as the application grows. Every new query, retriever, tool function, report, or chatbot action becomes another place where a developer has to remember the right row predicate, audience filter, and redaction rule. If one path forgets a filter, the database may return too much data before the mistake is noticed. Keeping the rules in Deep Sec data grants makes enforcement centralized, testable, and consistent across the application paths that reach the protected tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Started with Oracle Deep Data Security
&lt;/h2&gt;

&lt;p&gt;This tutorial assumes an administrator-prepared &lt;strong&gt;Oracle AI Database 26ai on Autonomous Database Serverless&lt;/strong&gt; environment where Oracle Deep Data Security is enabled and the required privileges have been granted. The demo targets ADB-S 26ai; it does not use Oracle Database Free or a local Oracle container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Database 26ai on Autonomous Database Serverless.&lt;/li&gt;
&lt;li&gt;A downloaded and unzipped Autonomous Database wallet.&lt;/li&gt;
&lt;li&gt;A tutorial owner schema user, such as &lt;code&gt;DEEPSEC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Database password for the tutorial owner schema.&lt;/li&gt;
&lt;li&gt;A TNS alias from the wallet, or a connect descriptor using host, port, and service name values from your Oracle AI Database 26ai environment.&lt;/li&gt;
&lt;li&gt;Wallet password.&lt;/li&gt;
&lt;li&gt;Prepared Deep Sec end users for &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Passwords for the local Deep Sec end users &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Privileges to create Deep Sec data roles and data grants for the tutorial objects, or an administrator who has prepared those objects.&lt;/li&gt;
&lt;li&gt;Optional: OCI Generative AI access for the chatbot extension.&lt;/li&gt;
&lt;li&gt;Python 3.10 or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prepared Deep Sec identity contract
&lt;/h3&gt;

&lt;p&gt;Before you run the demo, an administrator must create or provide the Deep Sec identity setup. This tutorial uses local Deep Sec users and direct logon, so the setup includes end users such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;linda&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;wendy&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those end users are not ordinary database schema users. Because the SQL does not quote &lt;code&gt;linda&lt;/code&gt; or &lt;code&gt;wendy&lt;/code&gt;, Oracle stores the end-user names using the normal uppercase identifier form: &lt;code&gt;LINDA&lt;/code&gt; and &lt;code&gt;WENDY&lt;/code&gt;. The setup scripts connect as the tutorial owner schema, such as &lt;code&gt;DEEPSEC&lt;/code&gt;, while the user-facing read and RAG scripts connect directly as &lt;code&gt;LINDA&lt;/code&gt; or &lt;code&gt;WENDY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tutorial owner needs the Deep Sec privileges required to create data roles, grant data roles, create data grants, create end users, and enable mandatory use of data grants for the demo tables. Ask an administrator to grant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;ADMINISTER&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt; &lt;span class="n"&gt;CONTEXT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deepsec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For direct logon without tokens, an administrator also creates a normal database role with &lt;code&gt;CREATE SESSION&lt;/code&gt;, then grants that role to the Deep Sec data roles used by the tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;loan_officer_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;underwriter_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;loan_officer_role&lt;/code&gt; and &lt;code&gt;underwriter_role&lt;/code&gt; do not exist yet, create &lt;code&gt;deal_direct_logon_role&lt;/code&gt; first, run Step 5 to create the Deep Sec data roles, and then run the two &lt;code&gt;GRANT deal_direct_logon_role ...&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;After the tutorial tables exist, the owner enables mandatory data-grant enforcement on the protected tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_applications&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_policies&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a service-user context setup that uses an external identity provider, the &lt;code&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/code&gt; value in &lt;code&gt;.env&lt;/code&gt; is not a database password and not a convenient static tutorial secret. It is a database access token issued by the configured identity provider, such as OCI IAM or Microsoft Entra ID, and it can expire. Your administrator or identity setup must tell you how to obtain and refresh it.&lt;/p&gt;

&lt;p&gt;For the no-token local-user setup, use the direct-logon pattern: Python connects directly as local Deep Sec end users such as &lt;code&gt;LINDA&lt;/code&gt; and &lt;code&gt;WENDY&lt;/code&gt;. In that mode, the database does not need &lt;code&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/code&gt;, but an administrator must grant a standard &lt;code&gt;CREATE SESSION&lt;/code&gt; role to the Deep Sec data roles.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;end_user_identity&lt;/code&gt; value passed to &lt;code&gt;oracledb.create_end_user_security_context()&lt;/code&gt; depends on the identity model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For IAM or Entra users, it is typically an identity token string.&lt;/li&gt;
&lt;li&gt;For local Deep Sec users in a service-user context pattern, it can be a tuple such as &lt;code&gt;("LINDA", key)&lt;/code&gt; where the key comes from the prepared local-user setup.&lt;/li&gt;
&lt;li&gt;For direct logon, the helper does not call &lt;code&gt;create_end_user_security_context()&lt;/code&gt;; it opens the connection as the local Deep Sec end user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The demo helper supports &lt;code&gt;DEEPSEC_CONTEXT_MODE=direct_logon&lt;/code&gt;, &lt;code&gt;external_token&lt;/code&gt;, or &lt;code&gt;local_tuple&lt;/code&gt;. Use &lt;code&gt;direct_logon&lt;/code&gt; for this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the project
&lt;/h3&gt;

&lt;p&gt;You can copy the files from this tutorial, or clone the companion GitHub repository that contains the same runnable demo project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/markxnelson/deep-data-security.git deal-deepsec
&lt;span class="nb"&gt;cd &lt;/span&gt;deal-deepsec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are copying the code from the article instead of cloning the repository, create a project directory first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;deal-deepsec
&lt;span class="nb"&gt;cd &lt;/span&gt;deal-deepsec

python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="nx"&gt;\Scripts\Activate.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you cloned the companion repository, create and activate the virtual environment inside that cloned directory before installing dependencies.&lt;/p&gt;

&lt;p&gt;The companion repository already includes &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;.env.example&lt;/code&gt;, &lt;code&gt;.gitignore&lt;/code&gt;, and the Python scripts. If you cloned it, use the file-creation sections below as reference and skip ahead to installing dependencies after your virtual environment is active.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oracledb
python-dotenv
oci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully installed oracledb ...
Successfully installed python-dotenv ...
Successfully installed oci ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add local environment variables
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.env.example&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Oracle AI Database 26ai on ADB-S connection.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DEEPSEC
&lt;span class="nv"&gt;ADB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-database-password

&lt;span class="c"&gt;# Use an ADB wallet TNS alias such as yourdb_high,&lt;/span&gt;
&lt;span class="c"&gt;# or a TCPS connect descriptor with host, port, and service_name values.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_DSN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-adb-tns-alias-or-connect-descriptor

&lt;span class="c"&gt;# Directory containing the unzipped wallet files, including tnsnames.ora and ewallet.pem.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/unzipped/adb-wallet

&lt;span class="c"&gt;# Wallet password created when the wallet was downloaded.&lt;/span&gt;
&lt;span class="c"&gt;# This is not the database user password.&lt;/span&gt;
&lt;span class="nv"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-wallet-password

&lt;span class="c"&gt;# Deep Sec context mode must match your administrator-prepared identity setup.&lt;/span&gt;
&lt;span class="c"&gt;# Use direct_logon for local Deep Sec users without IAM/Entra tokens.&lt;/span&gt;
&lt;span class="c"&gt;# Use external_token for IAM/Entra-style identity strings.&lt;/span&gt;
&lt;span class="c"&gt;# Use local_tuple when a service connection sets context for local end-user tuples.&lt;/span&gt;
&lt;span class="nv"&gt;DEEPSEC_CONTEXT_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;direct_logon

&lt;span class="c"&gt;# Required only for service-user context modes.&lt;/span&gt;
&lt;span class="c"&gt;# direct_logon does not use this value.&lt;/span&gt;
&lt;span class="nv"&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="c"&gt;# Direct-logon passwords for the local Deep Sec end users.&lt;/span&gt;
&lt;span class="nv"&gt;DEEPSEC_LINDA_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-linda-end-user-password
&lt;span class="nv"&gt;DEEPSEC_WENDY_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-wendy-end-user-password

&lt;span class="c"&gt;# Protected object owner used when direct-logon users query the demo tables.&lt;/span&gt;
&lt;span class="nv"&gt;DEAL_OBJECT_OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DEEPSEC

&lt;span class="c"&gt;# OCI Generative AI settings for the final chatbot step.&lt;/span&gt;
&lt;span class="nv"&gt;OCI_CONFIG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.oci/config
&lt;span class="nv"&gt;OCI_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DEFAULT
&lt;span class="nv"&gt;OCI_GENAI_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://inference.generativeai.us-chicago-1.oci.oraclecloud.com
&lt;span class="nv"&gt;OCI_GENAI_COMPARTMENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-compartment-ocid
&lt;span class="nv"&gt;OCI_GENAI_MODEL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-generative-ai-model-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy it to &lt;code&gt;.env&lt;/code&gt; and edit the values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never commit &lt;code&gt;.env&lt;/code&gt; to source control. Add &lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.venv/
__pycache__/
*.pyc
.pytest_cache/
.DS_Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  DEAL Demo Project
&lt;/h2&gt;

&lt;p&gt;The demo uses small scripts that build on each other. Run them in order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 01_verify_deepsec.py
python 02_create_schema.py
python 03_load_data.py
python 04_configure_deepsec.py
python 05_context_read_demo.py
python 07_vector_basics.py
python 08_secure_rag_retrieval.py
python 08b_keyword_vs_vector_check.py  &lt;span class="c"&gt;# optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After Step 8, the Deep Sec security proof is complete. The remaining scripts wrap the proven operations as DEAL tool functions, print a final terminal session, and optionally connect those tools to OCI Generative AI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 09_deal_tools_demo.py
python 10_run_deal_sessions.py
python 11_oci_genai_chatbot.py  &lt;span class="c"&gt;# optional, requires OCI GenAI configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Verify the ADB-S connection and Deep Sec metadata visibility
&lt;/h3&gt;

&lt;p&gt;Start with the smallest useful checkpoint: connect to Oracle AI Database 26ai using the wallet and confirm that &lt;code&gt;python-oracledb&lt;/code&gt; is running in Thin mode.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;01_verify_deepsec.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;


&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/path/to/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tnsnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tnsnames.ora&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set ADB_DSN or provide a wallet with tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No TNS aliases found in wallet tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;wallet_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_USERNAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_USER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL environment check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;select sys_context(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USERENV&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CURRENT_SCHEMA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;) from dual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connected as: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;select banner_full from v$version where rownum = 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;version_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select version_full
            from product_component_version
            where product like &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Oracle Database%&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
            fetch first 1 row only
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;version_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Database version: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;version_text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_thin_mode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python-oracledb mode: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Data Security requires python-oracledb Thin mode for this demo.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        select view_name
        from all_views
        where view_name in (
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBA_DATA_ROLES&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBA_DATA_ROLE_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DBA_DATA_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ALL_DATA_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USER_DATA_GRANTS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        )
        order by view_name
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;visible_views&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;visible_views&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Sec metadata visible to this schema: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visible_views&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Sec metadata views are not visible to this schema.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Continue only with an administrator-prepared Deep Sec environment.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 01_verify_deepsec.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEAL environment check
Connected as: DEEPSEC
Database version: Oracle AI Database 26ai Enterprise Edition Release 23.26.2.1.0
python-oracledb mode: Thin
Deep Sec metadata visible to this schema: ALL_DATA_GRANTS, USER_DATA_GRANTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your schema cannot see Deep Sec metadata views, you may see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deep Sec metadata views are not visible to this schema.
Continue only with an administrator-prepared Deep Sec environment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;ADB_DSN&lt;/code&gt;, or let the helper pick an alias from wallet &lt;code&gt;tnsnames.ora&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;ADB_WALLET_LOCATION&lt;/code&gt; contains the unzipped wallet files.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;ADB_WALLET_PASSPHRASE&lt;/code&gt; is the wallet password, not the database password.&lt;/li&gt;
&lt;li&gt;Confirm the database is Oracle AI Database 26ai.&lt;/li&gt;
&lt;li&gt;Do not switch to Thick mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Add a small connection helper
&lt;/h3&gt;

&lt;p&gt;Now that we have seen the full connection code, move the connection boilerplate into a small helper. Do not add authorization logic yet.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;deal_db.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;


&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/path/to/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tnsnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tnsnames.ora&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set ADB_DSN or provide a wallet with tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tnsnames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stripped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No TNS aliases found in wallet tnsnames.ora.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;wallet_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_USERNAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_USER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no command to run for this helper. Later scripts import &lt;code&gt;connect()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deal_db.py&lt;/code&gt; centralizes only connection setup.&lt;/li&gt;
&lt;li&gt;It does not filter data, redact columns, or set user context.&lt;/li&gt;
&lt;li&gt;It does not call &lt;code&gt;oracledb.init_oracle_client()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Create loan and policy tables
&lt;/h3&gt;

&lt;p&gt;Create two tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;loan_applications&lt;/code&gt; for structured loan data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loan_policies&lt;/code&gt; for policy documents and demo vectors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create &lt;code&gt;02_create_schema.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;


&lt;span class="n"&gt;DDL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    create table loan_applications (
        id number primary key,
        customer_name varchar2(100) not null,
        loan_amount number(12,2) not null,
        purpose varchar2(100) not null,
        status varchar2(40) not null,
        officer_notes varchar2(4000),
        customer_ssn varchar2(20),
        customer_income number(12,2),
        customer_credit_score number(4),
        underwriting_decision varchar2(40),
        risk_score number(5,2),
        underwriting_notes varchar2(4000),
        assigned_officer varchar2(40) not null,
        in_underwriting_queue char(1) check (in_underwriting_queue in (&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;))
    )
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    create table loan_policies (
        id number primary key,
        title varchar2(200) not null,
        body varchar2(4000) not null,
        audience varchar2(40) not null,
        embedding vector(3, float32)
    )
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drop_if_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;drop table &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; purge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;942&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;drop_if_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;drop_if_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dropped existing DEAL demo tables if they existed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;DDL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan_applications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan_policies with VECTOR(3, FLOAT32) embedding column.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Schema setup complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 02_create_schema.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dropped existing DEAL demo tables if they existed.
Created loan_applications.
Created loan_policies with VECTOR(3, FLOAT32) embedding column.
Schema setup complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The policy corpus is stored in Oracle Database, not in a separate vector store.&lt;/li&gt;
&lt;li&gt;The vector column is intentionally small: &lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Security will be added with Deep Sec data grants, not views or Python filters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Load synthetic data and deterministic demo vectors
&lt;/h3&gt;

&lt;p&gt;Load enough data to make security differences visible.&lt;/p&gt;

&lt;p&gt;The row design is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linda is assigned applications &lt;code&gt;101&lt;/code&gt;, &lt;code&gt;102&lt;/code&gt;, and &lt;code&gt;105&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wendy can see underwriting queue applications &lt;code&gt;102&lt;/code&gt;, &lt;code&gt;103&lt;/code&gt;, &lt;code&gt;105&lt;/code&gt;, and &lt;code&gt;106&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Policy documents are tagged as &lt;code&gt;general&lt;/code&gt;, &lt;code&gt;loan_officer&lt;/code&gt;, or &lt;code&gt;underwriter&lt;/code&gt;, but the application will not filter on that column.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The three-audience policy model is intentional. A simpler &lt;code&gt;all&lt;/code&gt;/&lt;code&gt;underwriter&lt;/code&gt; model can work, but &lt;code&gt;general&lt;/code&gt;, &lt;code&gt;loan_officer&lt;/code&gt;, and &lt;code&gt;underwriter&lt;/code&gt; makes the policy proof easier to inspect: both roles can receive general guidance, and each role can also receive role-specific documents without any Python-side audience filtering.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;03_load_data.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;


&lt;span class="n"&gt;loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Avery Stone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;320000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RECEIVED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing final pay stub.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;111-22-3333&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not reviewed yet.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LINDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;N&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Noah Rivers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;485000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNDER_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer uploaded tax documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;222-33-4444&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;151000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;695&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Watch revolving debt.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LINDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Maya Chen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;210000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Refinance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNDER_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Transferred from branch intake.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;333-44-5555&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;98000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;681&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check income variability.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Grace Hill&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home equity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NEEDS_DOCS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Awaiting insurance statement.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;444-55-6666&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;87000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;735&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NOT_STARTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not in queue.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amir&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;N&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Owen Park&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;640000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jumbo mortgage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNDER_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer requested expedited review.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;555-66-7777&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;220000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;705&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Large loan amount.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LINDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;106&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sofia Reyes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;390000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Investment property&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ESCALATED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Escalated by branch manager.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;666-77-8888&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;132000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;660&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exception review needed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;General lending eligibility&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Baseline eligibility requirements for consumer lending applications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Income verification basics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Required documents for income verification and employment review.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Document retention requirements&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Retention periods for lending documents and customer communications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loan officer workflow checklist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loan officer workflow for intake, status updates, and customer follow-up.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_officer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Officer notes standards&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Standards for writing clear customer-facing officer notes.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_officer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Credit risk escalation policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;When credit risk signals require underwriting escalation.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Debt-to-income exception review&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Underwriting guidance for debt-to-income ratio exceptions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Collateral review guidance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Collateral review requirements for underwriting decisions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        insert into loan_applications (
            id, customer_name, loan_amount, purpose, status, officer_notes,
            customer_ssn, customer_income, customer_credit_score,
            underwriting_decision, risk_score, underwriting_notes,
            assigned_officer, in_underwriting_queue
        ) values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14)
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        insert into loan_policies (id, title, body, audience, embedding)
        values (:1, :2, :3, :4, :5)
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Inserted &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; loan applications.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Inserted &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; loan policy documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Synthetic data load complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 03_load_data.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inserted 6 loan applications.
Inserted 8 loan policy documents.
Synthetic data load complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All data is synthetic.&lt;/li&gt;
&lt;li&gt;Sensitive columns are populated so column restrictions are visible.&lt;/li&gt;
&lt;li&gt;The vectors are deterministic demo vectors, not model-generated embeddings.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;audience&lt;/code&gt; column is a database policy anchor, not an application-side filter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Configure Oracle Deep Data Security data roles and data grants
&lt;/h3&gt;

&lt;p&gt;Now create the Deep Sec authorization model.&lt;/p&gt;

&lt;p&gt;This step assumes &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt; are prepared Deep Sec end users in your environment. The script creates data roles, grants those roles to the end users, and creates data grants on the two tutorial tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzdl6wu8xtijktz7vqfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzdl6wu8xtijktz7vqfc.png" alt="Role-based access rules diagram showing Linda with the LOAN_OFFICER_ROLE and Wendy with the UNDERWRITER_ROLE, including each role's loan application row rule, restricted column behavior, and policy document access." width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DEAL access rules: Deep Sec data roles and data grants define which loan rows, restricted columns, and policy documents Linda and Wendy can access.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;04_configure_deepsec.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Using prepared Deep Sec end users: LINDA, WENDY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create data role loan_officer_role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created data role: LOAN_OFFICER_ROLE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create data role underwriter_role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created data role: UNDERWRITER_ROLE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grant data role loan_officer_role to linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Granted LOAN_OFFICER_ROLE to LINDA.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grant data role underwriter_role to wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Granted UNDERWRITER_ROLE to WENDY.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;grants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_loan_officer_read as
        select (
            all columns except customer_ssn, customer_income,
            customer_credit_score, underwriting_decision,
            risk_score, underwriting_notes
        )
        on loan_applications
        where assigned_officer = ORA_END_USER_CONTEXT.username
        to loan_officer_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_underwriter_read as
        select on loan_applications
        where in_underwriting_queue = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to underwriter_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_general_to_officer as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to loan_officer_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_officer as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;loan_officer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to loan_officer_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_general_to_underwriter as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to underwriter_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        create or replace data grant deal_policy_underwriter as
        select on loan_policies
        where audience = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;underwriter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        to underwriter_role
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;grant&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;grants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan application read grants.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created loan policy read grants.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Configured Deep Data Security for DEAL.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it once against a fresh demo schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 04_configure_deepsec.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using prepared Deep Sec end users: LINDA, WENDY
Created data role: LOAN_OFFICER_ROLE
Created data role: UNDERWRITER_ROLE
Granted LOAN_OFFICER_ROLE to LINDA.
Granted UNDERWRITER_ROLE to WENDY.
Created loan application read grants.
Created loan policy read grants.
Configured Deep Data Security for DEAL.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the direct-logon role grants were waiting on the Deep Sec data roles, have the administrator run them now. Then enable mandatory data-grant enforcement on the two demo tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;loan_officer_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;deal_direct_logon_role&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;underwriter_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_applications&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;loan_policies&lt;/span&gt; &lt;span class="n"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm the tutorial schema has the required Deep Sec privileges.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;linda&lt;/code&gt; and &lt;code&gt;wendy&lt;/code&gt; exist as Deep Sec end users in your prepared environment.&lt;/li&gt;
&lt;li&gt;Confirm your schema owns the demo tables or your administrator has prepared the object-owner model.&lt;/li&gt;
&lt;li&gt;Do not replace Deep Data Security with views, triggers, VPD, or Python filters for this tutorial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data roles are part of the Deep Data Security authorization model.&lt;/li&gt;
&lt;li&gt;Data grants define row and column access in the database.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;loan_policies.audience&lt;/code&gt; column is used only inside database policy grants.&lt;/li&gt;
&lt;li&gt;Application SQL still remains broad.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6: Set end-user context and prove read and column enforcement
&lt;/h3&gt;

&lt;p&gt;Now add Deep Sec context support to the helper and prove that the same SQL returns different authorized results. In &lt;code&gt;direct_logon&lt;/code&gt; mode, the helper connects directly as &lt;code&gt;LINDA&lt;/code&gt; or &lt;code&gt;WENDY&lt;/code&gt; and qualifies protected objects through the owner schema.&lt;/p&gt;

&lt;p&gt;Add this function to the bottom of &lt;code&gt;deal_db.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;direct_logon_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;passwords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC_LINDA_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC_WENDY_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;passwords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown DEAL end user: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;wallet_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;first_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_LOCATION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_DSN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;wallet_dsn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;passwords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;wallet_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wallet_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSPHRASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_WALLET_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC_CONTEXT_MODE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;direct_logon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;direct_logon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;direct_logon_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This tutorial validates DEEPSEC_CONTEXT_MODE=direct_logon.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optional_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL_OBJECT_OWNER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helper uses the prepared local-user passwords from &lt;code&gt;.env&lt;/code&gt;. The public Python functions still accept friendly names such as &lt;code&gt;"linda"&lt;/code&gt; and &lt;code&gt;"wendy"&lt;/code&gt;, then the helper connects with the unquoted database identifiers &lt;code&gt;LINDA&lt;/code&gt; and &lt;code&gt;WENDY&lt;/code&gt;. No &lt;code&gt;DEEPSEC_DATABASE_ACCESS_TOKEN&lt;/code&gt; is required in &lt;code&gt;direct_logon&lt;/code&gt; mode. In a production app, your authentication layer must still establish the real user before choosing which local Deep Sec user or service-user context to use.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;05_context_read_demo.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;SQL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select *
from {loan_applications}
order by id
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_applications&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;FIELDS_TO_SHOW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_ssn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_credit_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;underwriting_decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NULL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rows_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rows_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;As &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Rows returned: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Visible application ids: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Selected sensitive and underwriting fields:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;FIELDS_TO_SHOW&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read and column enforcement demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 05_context_read_demo.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read and column enforcement demo

As linda:
  Rows returned: 3
  Visible application ids: 101, 102, 105
  Selected sensitive and underwriting fields:
    customer_ssn: NULL
    customer_credit_score: NULL
    underwriting_decision: NULL
    risk_score: NULL

As wendy:
  Rows returned: 4
  Visible application ids: 102, 103, 105, 106
  Selected sensitive and underwriting fields:
    customer_ssn: 222-33-4444
    customer_credit_score: 695
    underwriting_decision: PENDING_REVIEW
    risk_score: 72
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same SQL is used for Linda and Wendy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;loan_applications&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no &lt;code&gt;WHERE assigned_officer = :end_user&lt;/code&gt; predicate in the application.&lt;/p&gt;

&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;DEEPSEC_CONTEXT_MODE=direct_logon&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm Linda and Wendy can log on as local Deep Sec end users.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;DEAL_OBJECT_OWNER&lt;/code&gt; points to the owner of the demo tables.&lt;/li&gt;
&lt;li&gt;Confirm data role grants and data grants exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application does not add a user-specific &lt;code&gt;WHERE&lt;/code&gt; clause.&lt;/li&gt;
&lt;li&gt;The application does not redact restricted columns.&lt;/li&gt;
&lt;li&gt;The database returns the authorized row and column view for the active direct-logon Deep Sec end user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 7: Demonstrate vector distance with manual vectors
&lt;/h3&gt;

&lt;p&gt;Before using vector retrieval as a RAG building block, inspect a simple vector query.&lt;/p&gt;

&lt;p&gt;A vector is a list of numbers. In AI applications, embedding models produce vectors that place related concepts near one another. In this tutorial, we use small deterministic vectors so you can see the mechanics directly.&lt;/p&gt;

&lt;p&gt;The query vector &lt;code&gt;[0.9, 0.1, 0.0]&lt;/code&gt; is closest to loan-officer workflow style documents in our demo data.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;07_vector_basics.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select title,
       vector_distance(embedding, :query_vector, COSINE) as distance
from {loan_policies}
order by distance
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vector warm-up with manual 3-dimensional vectors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vector warm-up user context: linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Query vector: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Nearest policy vectors:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; distance: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 07_vector_basics.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vector warm-up with manual 3-dimensional vectors
Vector warm-up user context: linda
Query vector: [0.8999999761581421, 0.10000000149011612, 0.0]

Nearest policy vectors:
1. Loan officer workflow checklist     distance: 0.001723
2. General lending eligibility         distance: 0.013266
3. Officer notes standards             distance: 0.018206
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your exact distances may vary slightly by database patch level or floating-point formatting; the important result is the role-scoped result set.&lt;/p&gt;

&lt;p&gt;A quick metric mental model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cosine distance&lt;/strong&gt; compares vector direction and is common for embedding-style retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Euclidean distance&lt;/strong&gt; compares straight-line distance in vector space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dot product&lt;/strong&gt; is often used as a similarity score and is sensitive to vector magnitude.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Production embedding models may normalize vectors or document how they should be compared. Normalization can affect cosine, Euclidean, and dot-product rankings. DEAL uses one explicit &lt;code&gt;VECTOR_DISTANCE()&lt;/code&gt; expression so the query shape stays clear.&lt;/p&gt;

&lt;p&gt;Oracle AI Vector Search also supports vector indexes for larger corpora, including HNSW-style neighbor graph indexes and IVF/IVFFlat-style partitioned indexes. DEAL does not create an index because this security proof has eight policy rows; when you scale the corpus, validate a &lt;code&gt;CREATE VECTOR INDEX&lt;/code&gt; statement for your metric and inspect index metadata such as &lt;code&gt;USER_INDEXES&lt;/code&gt; before relying on performance claims.&lt;/p&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt; stores three &lt;code&gt;FLOAT32&lt;/code&gt; values.&lt;/li&gt;
&lt;li&gt;Python binds the query vector with &lt;code&gt;array.array("f", [...])&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The primitive vector example comes before any embedding model or chatbot tooling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 8: Run Deep Sec-scoped vector retrieval
&lt;/h3&gt;

&lt;p&gt;Now run a vector query that represents a RAG-style policy search. The search intent is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unstable cash flow or credit risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tutorial maps that intent to a fixed demo vector. In a real AI application, an embedding model such as OCI Generative AI would create the query vector and document vectors at the model’s actual dimension.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;08_secure_rag_retrieval.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;query_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unstable cash flow or credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select id, title, audience,
       vector_distance(embedding, :query_vector, COSINE) as distance
from {loan_policies}
order by distance
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deep Sec-scoped vector retrieval demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Policy search query: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query_text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Output below is scoped by the active Deep Sec end user.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;As &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;search_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; distance: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 08_secure_rag_retrieval.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deep Sec-scoped vector retrieval demo
Policy search query: unstable cash flow or credit risk
Output below is scoped by the active Deep Sec end user.

As linda:
  1. Income verification basics          distance: 0.529221
  2. Document retention requirements     distance: 0.604677
  3. General lending eligibility         distance: 0.686696

As wendy:
  1. Credit risk escalation policy      distance: 0.000000
  2. Debt-to-income exception review    distance: 0.001895
  3. Collateral review guidance         distance: 0.020925
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm Deep Sec grants exist on &lt;code&gt;loan_policies&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm the active end-user context is set.&lt;/li&gt;
&lt;li&gt;Confirm the vector query runs against the protected table.&lt;/li&gt;
&lt;li&gt;Confirm no application-side &lt;code&gt;audience&lt;/code&gt; filter has been added.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same query vector is used for Linda and Wendy.&lt;/li&gt;
&lt;li&gt;The same SQL shape is used for Linda and Wendy.&lt;/li&gt;
&lt;li&gt;The SQL does not filter by &lt;code&gt;audience&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The Python code does not filter by &lt;code&gt;audience&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Deep Sec data grants govern which policy rows are available to the vector query.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optional: Compare keyword and vector-style retrieval
&lt;/h3&gt;

&lt;p&gt;Vector retrieval and keyword retrieval answer different relevance questions. Keyword search finds literal text matches. Vector retrieval can find nearby concepts represented by numbers.&lt;/p&gt;

&lt;p&gt;Neither one should become the security boundary. Run both retrieval styles under the same Deep Sec contexts.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;08b_keyword_vs_vector_check.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;vector_sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select title
from {loan_policies}
order by vector_distance(embedding, :query_vector, COSINE)
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;keyword_sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
select title
from {loan_policies}
where lower(body) like :term
order by id
fetch first 3 rows only
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loan_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;vector_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyword_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%credit risk%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;keyword_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vector_titles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword_titles&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Optional vector-style vs keyword retrieval check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;vector_titles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;As &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Vector-style titles: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_titles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Keyword titles for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyword_titles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;keyword_titles&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it if you want the extra comparison:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 08b_keyword_vs_vector_check.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Representative output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Optional vector-style vs keyword retrieval check

As linda:
  Vector-style titles: Income verification basics, Document retention requirements, General lending eligibility
  Keyword titles for 'credit risk': none

As wendy:
  Vector-style titles: Credit risk escalation policy, Debt-to-income exception review, Collateral review guidance
  Keyword titles for 'credit risk': Credit risk escalation policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyword search and vector retrieval are relevance strategies.&lt;/li&gt;
&lt;li&gt;Deep Sec policy still scopes the rows available to the query.&lt;/li&gt;
&lt;li&gt;The app still does not use an &lt;code&gt;audience&lt;/code&gt; security filter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Try this next: Wrap the proven operations as DEAL tool functions
&lt;/h3&gt;

&lt;p&gt;Only after proving the database operations directly should we wrap them as assistant tools.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;deal_tools.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_for_user&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_query_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cash flow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_query_vector()&lt;/code&gt; function is relevance logic for this deterministic demo. It is not authorization logic.&lt;/p&gt;

&lt;p&gt;Append the read tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select *
            from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
            order by id
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select *
            from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
            where id = :id
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;WHERE id = :id&lt;/code&gt; predicate is a business lookup. It is not a user-specific authorization filter.&lt;/p&gt;

&lt;p&gt;Append the policy-search tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_query_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            select id, title, body, audience,
                   vector_distance(embedding, :query_vector, COSINE) as distance
            from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loan_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
            order by distance
            fetch first 3 rows only
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_rows_as_dicts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_run_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;09_deal_tools_demo.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool demo: linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;linda_loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;linda_policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  get_loan_applications returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  search_policies returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; policy documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Tool demo: wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wendy_loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wendy_policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  get_loan_applications returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  search_policies returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; policy documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 09_deal_tools_demo.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool demo: linda
  get_loan_applications returned 3 rows.
  search_policies returned 3 policy documents.

Tool demo: wendy
  get_loan_applications returned 4 rows.
  search_policies returned 3 policy documents.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before continuing, you should understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each tool runs database work under the requested Deep Sec end user.&lt;/li&gt;
&lt;li&gt;In direct-logon mode, each user-scoped operation opens its own connection as &lt;code&gt;LINDA&lt;/code&gt; or &lt;code&gt;WENDY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tools do not filter rows by user.&lt;/li&gt;
&lt;li&gt;Tools do not redact restricted columns.&lt;/li&gt;
&lt;li&gt;Tools do not filter policy documents by &lt;code&gt;audience&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Try this next: Run DEAL sessions and bypass checks
&lt;/h3&gt;

&lt;p&gt;Now run the final terminal assistant session. This script calls the tool functions and prints a short transcript for Linda, Wendy, and a bypass check.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;10_run_deal_sessions.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NULL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;linda_session&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL session: linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Visible applications: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Application ids: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Restricted risk_score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;risk_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Policy results: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wendy_session&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL session: wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;loans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Visible applications: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Application ids: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Underwriting risk_score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;display_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;risk_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Policy results: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append the bypass check and runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bypass_check&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bypass check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;linda_rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wendy_rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;linda_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wendy_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Broad loan query returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Linda-scoped rows for linda.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Broad loan query returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Wendy-scoped rows for wendy.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Linda policy titles: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linda_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Wendy policy titles: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wendy_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No application-side row filter, redaction, or audience filter was used.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;linda_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;wendy_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;bypass_check&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 10_run_deal_sessions.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;========================
DEAL session: linda
========================
Visible applications: 3
Application ids: 101, 102, 105
Restricted risk_score: NULL
Policy results: Income verification basics, Document retention requirements, General lending eligibility

========================
DEAL session: wendy
========================
Visible applications: 4
Application ids: 102, 103, 105, 106
Underwriting risk_score: 72
Policy results: Credit risk escalation policy, Debt-to-income exception review, Collateral review guidance

========================
Bypass check
========================
Broad loan query returned 3 Linda-scoped rows for linda.
Broad loan query returned 4 Wendy-scoped rows for wendy.
Linda policy titles: Income verification basics, Document retention requirements, General lending eligibility
Wendy policy titles: Credit risk escalation policy, Debt-to-income exception review, Collateral review guidance
No application-side row filter, redaction, or audience filter was used.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkin7af9us1khpmxjk79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkin7af9us1khpmxjk79.png" alt="Terminal-style output showing the final DEAL demo session, where Linda sees three authorized loan applications with restricted underwriting fields, Wendy sees four underwriting queue applications with underwriting fields visible, and bypass checks show broad loan and policy retrieval queries remain scoped by Deep Sec policies." width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Final DEAL session: the same tool functions run under Linda and Wendy’s Deep Sec context, and the database returns user-scoped loan and policy retrieval results.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before finishing, you should understand the full trust boundary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DEAL establishes the correct Deep Sec end user for each operation.&lt;/li&gt;
&lt;li&gt;The same broad read function returns different rows and restricted values.&lt;/li&gt;
&lt;li&gt;The same vector retrieval function returns different policy documents.&lt;/li&gt;
&lt;li&gt;The assistant is not trusted to filter or redact protected data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optional: Add an OCI GenAI chatbot loop
&lt;/h3&gt;

&lt;p&gt;The terminal session proves the secured tool functions directly. As an optional extension, add a small LLM loop that lets OCI Generative AI choose which DEAL tool to call. The OCI GenAI code path is an integration pattern; the Deep Sec read and vector security proof does not depend on it. Validate the OCI SDK request and response shape in your tenancy before relying on a captured chatbot transcript.&lt;/p&gt;

&lt;p&gt;The model is not trusted for authorization; it only selects an action. Every tool call still runs under the chosen Deep Sec end user and lets Oracle Database enforce the policy.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;11_oci_genai_chatbot.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oci&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oci.generative_ai_inference&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GenerativeAiInferenceClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oci.generative_ai_inference.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ChatDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;GenericChatRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;OnDemandServingMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UserMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;deal_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;TOOLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_loan_applications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get_loan_applications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_application_detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get_application_detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_policies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;search_policies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in .env before running this script.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;oci_client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;config_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_CONFIG_FILE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/.oci/config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_PROFILE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEFAULT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oci&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_file&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;GenerativeAiInferenceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;service_endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_GENAI_ENDPOINT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;message_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oci&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenericChatRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;api_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GenericChatRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API_FORMAT_GENERIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ROLE_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TEXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;compartment_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_GENAI_COMPARTMENT_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;serving_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;OnDemandServingMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;required_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI_GENAI_MODEL_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;chat_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;message_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;choose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are DEAL, a lending assistant running as end user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
Choose exactly one tool call for the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s request.
Return only JSON with keys tool and arguments.

Available tools:
- get_loan_applications: {{}}
- get_application_detail: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: number}}
- search_policies: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string}}

User request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;fenced&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;```

(?:json)?\s*(\{.*?\})\s*

```&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DOTALL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fenced&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fenced&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rfind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arguments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TOOLS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown tool requested by model: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TOOLS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;answer_with_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are DEAL, a lending assistant running as end user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
The database has already enforced Oracle Deep Data Security.
Answer the user briefly from the tool result only.
If the tool result is a list of loan applications, count the list and list the id values exactly.
If the tool result is a list of policies, list only the returned policy titles.
Do not say that no rows or policies were found unless the tool result is an empty list.

User request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Tool action: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Tool result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;chat_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCI GenAI DEAL session: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;========================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;choose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;answer_with_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;User: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arguments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEAL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;oci_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;run_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which loan applications can I see?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find policy guidance about credit risk.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;run_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wendy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which applications are in my underwriting queue?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find policy guidance about credit risk.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it after the direct tool demo succeeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python 11_oci_genai_chatbot.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;========================
OCI GenAI DEAL session: linda
========================

User: Which loan applications can I see?
Tool: get_loan_applications {}
DEAL: You can see 3 loan applications: 101, 102, 105.

User: Find policy guidance about credit risk.
Tool: search_policies {'query': 'credit risk'}
DEAL: Income verification basics, Document retention requirements, General lending eligibility

========================
OCI GenAI DEAL session: wendy
========================

User: Which applications are in my underwriting queue?
Tool: get_loan_applications {}
DEAL: There are 4 applications in your underwriting queue: 102, 103, 105, 106.

User: Find policy guidance about credit risk.
Tool: search_policies {'query': 'credit risk'}
DEAL: Credit risk escalation policy
Debt-to-income exception review
Collateral review guidance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is the tool transcript, not the wording of the generated answer. If the model asks for a broad loan query, broad application detail, or broad policy search, Oracle Database still scopes the result to the active Deep Sec end user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The connection fails
&lt;/h3&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ADB_DSN&lt;/code&gt; matches your ADB-S wallet alias or connect descriptor, or the wallet contains a usable &lt;code&gt;tnsnames.ora&lt;/code&gt; alias.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ADB_WALLET_LOCATION&lt;/code&gt; points to the unzipped wallet directory.&lt;/li&gt;
&lt;li&gt;The directory includes wallet files used by Thin mode, such as &lt;code&gt;tnsnames.ora&lt;/code&gt; and &lt;code&gt;ewallet.pem&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ADB_WALLET_PASSPHRASE&lt;/code&gt; is the wallet password, not the database password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not call &lt;code&gt;oracledb.init_oracle_client()&lt;/code&gt; for this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  The driver is in Thick mode
&lt;/h3&gt;

&lt;p&gt;Deep Data Security in this Python tutorial uses &lt;code&gt;python-oracledb&lt;/code&gt; Thin mode. Remove any call to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;oracledb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_oracle_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then rerun &lt;code&gt;01_verify_deepsec.py&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linda and Wendy see the same loan rows
&lt;/h3&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DEEPSEC_CONTEXT_MODE=direct_logon&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEEPSEC_LINDA_KEY&lt;/code&gt; and &lt;code&gt;DEEPSEC_WENDY_KEY&lt;/code&gt; contain the local Deep Sec end-user passwords.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEAL_OBJECT_OWNER&lt;/code&gt; points to the schema that owns &lt;code&gt;loan_applications&lt;/code&gt; and &lt;code&gt;loan_policies&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Data roles were granted to the correct Deep Sec end users.&lt;/li&gt;
&lt;li&gt;Data grants reference the expected Deep Sec username.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vector inserts or queries fail
&lt;/h3&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The table uses &lt;code&gt;VECTOR(3, FLOAT32)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Every vector bind uses exactly three values.&lt;/li&gt;
&lt;li&gt;Python binds vectors with &lt;code&gt;array.array("f", [...])&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The vector query uses the same metric expression consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  You plan to use connection pools
&lt;/h3&gt;

&lt;p&gt;The tutorial uses simple standalone connections so the request boundary is obvious. If you later switch to the service-user context path with a pool, set the Deep Sec context after acquiring a connection and clear it before releasing the connection. Keep this pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;context_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_end_user_security_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Run user-scoped database work.
&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear_end_user_security_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  You plan to replace demo vectors with real embeddings
&lt;/h3&gt;

&lt;p&gt;The core tutorial uses deterministic demo vectors so the security proof is self-contained. When you switch to a real embedding API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the model’s actual vector dimension in the table definition.&lt;/li&gt;
&lt;li&gt;Batch embedding requests where the provider supports batching.&lt;/li&gt;
&lt;li&gt;Track API cost and rate limits.&lt;/li&gt;
&lt;li&gt;Store enough metadata to regenerate embeddings when the model changes.&lt;/li&gt;
&lt;li&gt;Keep authorization in Deep Sec data grants, not in embedding metadata filters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/markxnelson/deep-data-security" rel="noopener noreferrer"&gt;Companion DEAL demo code repository&lt;/a&gt; — Clone the runnable Python project used in this tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/index.html" rel="noopener noreferrer"&gt;Oracle Deep Data Security Guide for Oracle AI Database 26ai&lt;/a&gt; — Learn the Deep Data Security concepts, objects, and administration model behind the tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/create-data-grants.html" rel="noopener noreferrer"&gt;Create Data Grants in Oracle Deep Data Security&lt;/a&gt; — Review how data grants define row, column, and operation access for protected tables.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/configure-data-roles-l.html" rel="noopener noreferrer"&gt;Configure Data Roles in Oracle Deep Data Security&lt;/a&gt; — See how Deep Sec data roles are created and assigned to end users.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#deep-data-security" rel="noopener noreferrer"&gt;python-oracledb Deep Data Security&lt;/a&gt; — Use the Python driver APIs for creating, setting, and clearing end-user security context.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://paul-parkinson.medium.com/develop-database-enforced-end-user-auth-with-oracle-ai-database-deep-data-security-and-java-5a845ba1ebfd" rel="noopener noreferrer"&gt;Paul Parkinson's Deep Data Security Java article&lt;/a&gt; — Compare the same Deep Sec application model in a Java application.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://livelabs.oracle.com/ords/r/dbpm/livelabs/run-workshop?p210_wid=4393" rel="noopener noreferrer"&gt;Oracle LiveLabs: Getting Started with Oracle Deep Data Security&lt;/a&gt; — Try a guided Deep Data Security workshop.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/index.html" rel="noopener noreferrer"&gt;Oracle AI Vector Search User's Guide&lt;/a&gt; — Review Oracle AI Vector Search concepts and SQL reference material.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://python-oracledb.readthedocs.io/en/latest/user_guide/vector_data_type.html" rel="noopener noreferrer"&gt;python-oracledb Vector Data Type&lt;/a&gt; — Learn how Python applications bind and fetch Oracle Database &lt;code&gt;VECTOR&lt;/code&gt; values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You built DEAL, a Python loan assistant that demonstrates database-enforced access control for structured SQL, vector retrieval, and LLM-driven tool calls. Linda and Wendy use the same broad tool functions, but Oracle Database applies the active Deep Sec end-user identity before returning loan rows, restricted columns, or policy documents.&lt;/p&gt;

&lt;p&gt;That matters because AI applications widen the blast radius of small authorization mistakes. A chatbot can call a broad query, a tool can forget a role predicate, and a prompt can ask for data the user should not receive. With Deep Data Security, the tutorial's security boundary sits in Oracle Database instead of in prompts, redaction branches, or application-side filters.&lt;/p&gt;

&lt;p&gt;For production, replace the deterministic demo vectors with embeddings from your chosen model, test the workflow in your own ADB-S environment, and add auditing around high-risk read and retrieval paths. Keep the same rule as you evolve the assistant: the model and application can request data, but the database decides what the end user is allowed to see.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>rag</category>
      <category>security</category>
    </item>
    <item>
      <title>Vector‑native RAG on Oracle: embeddings, HNSW/IVF, and hybrid search under database governance</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Wed, 27 May 2026 10:38:06 +0000</pubDate>
      <link>https://dev.to/oracledevs/vector-native-rag-on-oracle-embeddings-hnswivf-and-hybrid-search-under-database-governance-2ep1</link>
      <guid>https://dev.to/oracledevs/vector-native-rag-on-oracle-embeddings-hnswivf-and-hybrid-search-under-database-governance-2ep1</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Storing vectors in an Oracle &lt;code&gt;VECTOR&lt;/code&gt; column alongside content, metadata, and provenance means retrieval happens inside the database. Existing governance — row-level security, auditing, data masking — applies to vector queries the same way it applies to any other query.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hybrid retrieval is ordinary SQL: &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; handles semantic similarity and &lt;code&gt;WHERE&lt;/code&gt; clauses handle business predicates in the same statement. Any reviewer who can read a query can understand what rows qualified and why.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HNSW and IVF are index strategies with real trade-offs in recall, memory footprint, and query latency. Choosing between them requires measuring both on your own corpus — they are not interchangeable defaults.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The answer step can stay inspectable too. Connecting retrieval to a Select AI profile with &lt;code&gt;SHOWSQL&lt;/code&gt; means the SQL generation step is reviewable, not just the retrieval that fed it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Retrieval‑augmented generation works when you can fetch the right context quickly and explain—in plain SQL—why those rows qualified. Splitting storage between a stand‑alone vector store and the database of record speeds early prototypes but makes governance fragile: metadata is duplicated, filters are reimplemented, and audit trails often end at a gateway you do not control. Oracle’s vector‑native approach keeps the entire RAG path inside the database. Embeddings live in a VECTOR column next to the text, JSON, and provenance you already track; you index them with HNSW or IVF using CREATE VECTOR INDEX; you retrieve with SQL that combines semantic similarity and business predicates. Because retrieval runs on database objects, existing policies and audits—when configured on those objects—stay in the path. When you need an LLM to compose the answer, Select AI with AI Profiles lets you scope what the model can see and inspect generated SQL before execution. The result is a retrieval path that is fast enough for interactive use and reviewable under the same governance you already operate.&lt;/p&gt;

&lt;p&gt;Figure 1 — Route → act → trust for in‑database RAG. Retrieval is SQL; governance stays in the path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62gf3cmu38067bt9zmod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62gf3cmu38067bt9zmod.png" alt="Vector-Native RAG in One Database" width="653" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you read Article 5, you saw how AI Profiles keep NL2SQL inspectable. This article focuses on retrieval: how to store vectors, choose HNSW or IVF, and write hybrid queries you can defend in a review meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Database 26ai or Autonomous Database with AI Vector Search enabled for your RU/service tier.&lt;/li&gt;
&lt;li&gt;Privileges to CREATE TABLE and CREATE INDEX in a schema with sufficient quota.&lt;/li&gt;
&lt;li&gt;For external embeddings or Select AI, configure credentials and network ACLs per your organization’s policy.&lt;/li&gt;
&lt;li&gt;Verify CREATE VECTOR INDEX options and the DBMS_* package signatures in your RU before automating builds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What “vector‑native” means in Oracle
&lt;/h2&gt;

&lt;p&gt;Vector‑native means embeddings are first‑class database data, not a sidecar. You add a &lt;code&gt;VECTOR(dim, element_type)&lt;/code&gt; column to the table that holds your chunked content and provenance; you build an approximate nearest‑neighbor (ANN) index—HNSW for interactive recall or IVF for very large corpora—and you query with SQL. The practical effect is governance continuity: when Virtual Private Database (VPD) policies, Unified Auditing, Data Redaction, and Transparent Data Encryption (TDE) are configured on the relevant objects, they continue to filter, record, mask, and protect your retrieval path. When someone asks why a row entered the context window, you can point to the same table‑scoped policies that govern every other query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fots7aelunrssekyy8e7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fots7aelunrssekyy8e7j.png" alt="Oracle Vector Objects" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For architecture and security details, see the Oracle AI Vector Search overview and the Database Security Guide for VPD, Unified Auditing, Data Redaction, and TDE:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  From document to chunk to embedding
&lt;/h2&gt;

&lt;p&gt;RAG quality depends heavily on two choices you control: where you cut chunks and how you embed them. Oracle provides &lt;code&gt;DBMS_VECTOR_CHAIN&lt;/code&gt; utilities for deterministic chunking with tunable size and overlap, so the same document splits the same way as you iterate. For embeddings, you can run an in‑database model (see DBMS_VECTOR support for installed models and ONNX where documented for your RU) or call an external provider with credentials stored in the database and governed by network ACLs. Treat those decisions as versioned preferences you can update deliberately.&lt;/p&gt;

&lt;p&gt;Keep the storage shape simple: one row per chunk with the content, the provenance you already carry (source, timestamps, tenant), and a &lt;code&gt;VECTOR&lt;/code&gt; column for the embedding. That single‑row design makes hybrid retrieval straightforward because eligibility is a &lt;code&gt;WHERE&lt;/code&gt; clause and semantic similarity is an &lt;code&gt;ORDER BY&lt;/code&gt;, all in one statement.&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DBMS_VECTOR (embedding/model support)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DBMS_VECTOR_CHAIN (chunking and embedding helpers)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DBMS_HYBRID_VECTOR: keyword + semantic in one call
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DBMS_VECTOR_CHAIN&lt;/code&gt; and &lt;code&gt;DBMS_VECTOR&lt;/code&gt; handle pure vector workflows. When your retrieval also needs full‑text keyword matching—relevance ranking by term frequency, Oracle Text lexer rules, or multi‑language tokenization—&lt;code&gt;DBMS_HYBRID_VECTOR&lt;/code&gt; adds that layer. The package provides a JSON‑based &lt;code&gt;SEARCH&lt;/code&gt; API that searches by keywords and vectors against hybrid vector indexes. By integrating traditional keyword‑based text search with vector‑based similarity search in a single call, it can improve recall for queries where either approach alone misses relevant results.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;DBMS_HYBRID_VECTOR&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your corpus has mixed-quality writing (exact keywords matter for short queries, semantic similarity matters for paraphrased ones).&lt;/li&gt;
&lt;li&gt;You support multiple languages with different stemming rules that Oracle Text already handles.&lt;/li&gt;
&lt;li&gt;You want a single managed search API rather than hand-tuned SQL merging of separate text and vector result sets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For pure vector retrieval with business-predicate filters, the &lt;code&gt;WHERE&lt;/code&gt; + &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; pattern in the section below is simpler and sufficient. Reserve &lt;code&gt;DBMS_HYBRID_VECTOR&lt;/code&gt; for the cases where keyword recall meaningfully improves results on your corpus—measure both before committing to the more complex path.&lt;/p&gt;

&lt;p&gt;Doc:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DBMS_HYBRID_VECTOR
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_hybrid_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_hybrid_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing an index: HNSW or IVF
&lt;/h2&gt;

&lt;p&gt;HNSW and IVF both deliver sub‑second nearest‑neighbor search, but they trade memory, build time, and recall differently.&lt;/p&gt;

&lt;p&gt;HNSW builds an in‑memory navigable small‑world graph. In practice, it often yields high recall at low latency for interactive workloads, at the cost of a memory footprint that grows with corpus size and neighbor degree. Use the documented memory and accuracy advisors in &lt;code&gt;DBMS_VECTOR&lt;/code&gt; to size the index pool and validate recall on your corpus before you build. If you are powering a help center, runbooks, or an internal knowledge base where queries arrive piecemeal and you value accuracy, HNSW is a common starting point—measure and tune for your data.&lt;/p&gt;

&lt;p&gt;IVF clusters the vector space into partitions and probes only a subset per query. That design can lower memory footprint and sustain good throughput on very large corpora. You trade some recall and tune it by changing how many partitions to probe. If you manage millions of chunks and steady traffic, evaluate IVF operationally using Oracle’s accuracy and performance advisors.&lt;/p&gt;

&lt;p&gt;One optimizer detail matters to plans and expectations: the optimizer uses a vector index only when your query’s distance metric matches the index’s metric. If they differ, the index will not be used and an exact search is performed. If your embeddings use cosine similarity (common for text), create the index with &lt;code&gt;DISTANCE COSINE&lt;/code&gt; and use the same metric in &lt;code&gt;ORDER BY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flggcwksmxyi8hle0l3ii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flggcwksmxyi8hle0l3ii.png" alt="Choosing a Vector Index" width="742" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CREATE VECTOR INDEX (HNSW/IVF)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DBMS_VECTOR advisors and accuracy helpers
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Writing hybrid retrieval you can defend
&lt;/h2&gt;

&lt;p&gt;Hybrid search in Oracle is just SQL. Your business predicates—tenant, source system, publish date, access level—are ordinary filters in &lt;code&gt;WHERE&lt;/code&gt;, and &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; ranks eligible rows by semantic similarity. Because this happens on a single table with a single row shape, the execution plan can combine a vector index for similarity with conventional indexes for filters. When configured on the relevant objects, VPD policies still apply to the session.&lt;/p&gt;

&lt;p&gt;A typical query looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure the index’s &lt;code&gt;DISTANCE&lt;/code&gt; metric matches the query’s metric; otherwise the vector index may not be used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aes9wheh80hk6opoyxk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aes9wheh80hk6opoyxk.png" alt="Hybrid Retrieval You Can Review" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two small details avoid surprises in production. &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; returns smaller values for closer vectors, so you sort ascending and keep the metric explicit to match the index. Also, the query vector’s dimension and element type must match the column’s or the statement will error. In plans, you will often see the vector index satisfy similarity ordering while B‑trees or partitions satisfy filters; the optimizer’s choice depends on predicate selectivity, statistics, and metric/index alignment.&lt;/p&gt;

&lt;p&gt;If you prefer operator syntax, the &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; operator is the cosine distance operator in 26ai. Use it only if your target version documents it; otherwise prefer &lt;code&gt;VECTOR_DISTANCE(..., COSINE)&lt;/code&gt; explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VECTOR, TO_VECTOR, VECTOR_DISTANCE
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hybrid query patterns
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A tiny end‑to‑end demo
&lt;/h2&gt;

&lt;p&gt;This toy example shows the loop from rows to top‑K in a few statements. It runs on Oracle AI Database 26ai or Autonomous Database. Replace the three‑dimensional vectors with your model’s actual dimension (384/768/1536 are common), and use &lt;code&gt;FLOAT32&lt;/code&gt; for typical embeddings. For real data, prefer &lt;code&gt;CLOB&lt;/code&gt; when content exceeds 4000 bytes.&lt;/p&gt;

&lt;p&gt;1) Create a table that keeps content, provenance, and a vector together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;         &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="n"&gt;ALWAYS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;source&lt;/span&gt;     &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;published&lt;/span&gt;  &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt;    &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- use CLOB for larger text in production&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt;  &lt;span class="n"&gt;VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Insert a few rows with deterministic toy vectors so results are obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-15'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'How to reset your password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.10, 0.05, 0.90]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-02-10'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'How to export monthly invoices'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.80, 0.10, 0.10]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'runbook'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2025-03-05'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Rotate API keys every 90 days'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.15, 0.85, 0.10]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Create a cosine HNSW index and keep the metric explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_hnsw_idx&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;INMEMORY&lt;/span&gt; &lt;span class="n"&gt;NEIGHBOR&lt;/span&gt; &lt;span class="n"&gt;GRAPH&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ORGANIZATION&lt;/code&gt; clause names the HNSW structure as documented in the SQL Reference. Verify feature availability and any licensing considerations in your environment, and keep the query metric aligned (COSINE here) for the optimizer to consider this index.&lt;/p&gt;

&lt;p&gt;4) Run a top‑K query. &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt; returns smaller values for closer matches, so use ascending ORDER BY. Using a literal vector for illustration (in applications, pass vectors as binds rather than literals to avoid hard parsing and ensure type/dimension alignment):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.12, 0.04, 0.92]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="n"&gt;COSINE&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a bind (recommended in applications). Ensure &lt;code&gt;:qvec&lt;/code&gt; matches the column’s dimension and element type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Suppose :qvec is a VECTOR(3, FLOAT32) bind variable&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Operator shorthand (if documented in your RU; &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; is cosine):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qvec&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5) Add a business predicate to show hybrid eligibility in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'kb'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;VECTOR_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;TO_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.12, 0.04, 0.92]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="n"&gt;COSINE&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your corpus grows very large, evaluate IVF. Drop the HNSW index first. Keep a single active vector index per column to simplify planning and maintenance.&lt;/p&gt;

&lt;p&gt;Use the documented IVF clause (&lt;code&gt;NEIGHBOR PARTITIONS&lt;/code&gt;) in your RU’s SQL Reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_hnsw_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;docs_ivf_idx&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ORGANIZATION&lt;/span&gt; &lt;span class="n"&gt;NEIGHBOR&lt;/span&gt; &lt;span class="n"&gt;PARTITIONS&lt;/span&gt;
&lt;span class="n"&gt;DISTANCE&lt;/span&gt; &lt;span class="n"&gt;COSINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After creating IVF, verify search‑effort controls and probe counts for your RU in the SQL Reference and DBMS_VECTOR docs; use those documented knobs to balance recall and latency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjw55yapgdsdbdr4vw1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjw55yapgdsdbdr4vw1y.png" alt="Reviewable Answer Path" width="722" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Orchestrating the answer: in your app or with Select AI
&lt;/h2&gt;

&lt;p&gt;Retrieval yields top‑K rows with provenance; you still need to compose an answer. Many teams keep this step in the application: run the retrieval SQL, log the query and bind values, and pass the returned snippets—plus &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, and &lt;code&gt;published&lt;/code&gt;—to an LLM. Because the context is explicit, reviewers can reconstruct what the model saw and sign off on the path.&lt;/p&gt;

&lt;p&gt;If you prefer to orchestrate inside the database, use Select AI with a narrowly scoped AI Profile. During early review, enable SHOWSQL/EXPLAINSQL so a human can inspect generated SQL before execution. Syntax for USING/profile attributes varies by RU; follow the Select AI examples for your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- In a session with an AI Profile that scopes access&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'List the top 3 KB articles about password resets from 2025.'&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="s1"&gt;'profile = &amp;lt;your_ai_profile&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent table data from being sent to a provider during review, use the data‑access controls documented for your RU/service tier (see Select AI examples). Enable the appropriate session/profile setting rather than relying on defaults.&lt;/p&gt;

&lt;p&gt;Select AI can include sources in responses when configured; check the SOURCES option and relevant profile attributes for your RU, and keep SHOWSQL/EXPLAINSQL enabled until stakeholders are comfortable with the generated SQL.&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select AI examples (SHOWSQL, EXPLAINSQL, object scoping, data access)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Operating in production shape
&lt;/h2&gt;

&lt;p&gt;Demos are short; production is measurement and repeatability. For HNSW, memory depends on graph parameters and corpus size. Use &lt;code&gt;DBMS_VECTOR&lt;/code&gt; memory advisors before large builds so you can size the index pool without starving the buffer cache, and measure recall and latency as you adjust graph parameters. For IVF, recall depends on partitioning and probe counts. Use accuracy helpers to compare index settings against exact search on a representative sample, and decide whether a point of recall is worth the latency trade.&lt;/p&gt;

&lt;p&gt;Plan hygiene matters. Keep the query metric aligned with the index metric, and gather statistics after bulk loads so the optimizer can choose the intended plan. A minimal stats call looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_STATS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GATHER_TABLE_STATS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DOCUMENTS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_STATS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GATHER_INDEX_STATS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DOCS_HNSW_IDX'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gather index stats after index builds or rebuilds so the optimizer can cost similarity ordering accurately.&lt;/p&gt;

&lt;p&gt;If you evolve index settings or dimensions, make DDL idempotent and, where your release supports it, prefer online rebuilds to avoid unnecessary outages. Article_07 covers schema and index evolution strategies.&lt;/p&gt;

&lt;p&gt;Governance continuity does not require special workarounds. When policies are configured on the relevant objects, VPD continues to filter rows, Unified Auditing can capture activity, and Data Redaction can mask columns that should not appear in clear text. As with any sensitive path, set &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; at session start, run your retrieval, and make evidence easy to find in audit views. Consider tagging vector queries with a distinct &lt;code&gt;ACTION&lt;/code&gt; (for example, &lt;code&gt;RAG_RETRIEVAL&lt;/code&gt;) to simplify Unified Auditing filters.&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DBMS_VECTOR advisors and accuracy reporting
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security guide landing (VPD, Unified Auditing, Data Redaction, TDE)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and environment notes
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;VECTOR&lt;/code&gt; data type, &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt;, ANN vector indexes (HNSW/IVF via &lt;code&gt;CREATE VECTOR INDEX&lt;/code&gt;), &lt;code&gt;DBMS_VECTOR&lt;/code&gt;, &lt;code&gt;DBMS_VECTOR_CHAIN&lt;/code&gt;, and the Select AI features shown here require Oracle AI Database 26ai or Autonomous Database. If you are on 19c, you can persist arrays as JSON or BLOB and compute distances in the application, but you do not have native vector columns, vector indexes, or &lt;code&gt;VECTOR_DISTANCE&lt;/code&gt;. Always verify your exact RU or service tier for option names, index clauses, and package signatures before you automate builds. If you use external embedding providers, configure credentials and network ACLs before calling them from the database. Ensure the query vector dimension and element type match the column definition; otherwise the statement will error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Out of scope
&lt;/h2&gt;

&lt;p&gt;This article stays focused on storing vectors, building the two ANN index types, writing hybrid retrieval SQL, and keeping the answer path reviewable. It does not cover Oracle Text, summarization prompts, or application frameworks. For NL2SQL hardening and AI Profile design, see Article 5. For safe schema and index evolution, see Article 7.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Load 50–100 real chunks from your docs, store embeddings in a &lt;code&gt;VECTOR&lt;/code&gt; column, and build an HNSW index; compare recall and latency before and after switching to IVF on the same corpus.&lt;/li&gt;
&lt;li&gt;Turn on &lt;code&gt;SELECT AI SHOWSQL&lt;/code&gt; for the answer step so reviewers can sign off on generated SQL before execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle AI Vector Search overview
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/overview-ai-vector-search.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VECTOR type, TO_VECTOR, and VECTOR_DISTANCE
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/vector_distance.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CREATE VECTOR INDEX (HNSW/IVF)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/create-vector-index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Querying with similarity and hybrid predicates
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/query-data-similarity-and-hybrid-searches.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DBMS_VECTOR and DBMS_VECTOR_CHAIN
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector1.html&lt;/a&gt;
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_vector_chain1.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select AI examples (SHOWSQL, EXPLAINSQL, object scoping, data access)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security guide landing (VPD, Unified Auditing, Data Redaction, TDE)
&lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>rag</category>
      <category>sql</category>
    </item>
    <item>
      <title>Safer NL2SQL with Select AI and AI Profiles</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Mon, 25 May 2026 16:30:20 +0000</pubDate>
      <link>https://dev.to/oracledevs/safer-nl2sql-with-select-ai-and-ai-profiles-18jc</link>
      <guid>https://dev.to/oracledevs/safer-nl2sql-with-select-ai-and-ai-profiles-18jc</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AI Profiles bind the model to a defined list of database objects. With &lt;code&gt;enforce_object_list&lt;/code&gt; enabled, generated SQL cannot reach tables you did not explicitly name — scope is a constraint, not just a suggestion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;SHOWSQL&lt;/code&gt; returns the candidate SQL without executing it, using schema metadata rather than table data. You see what the assistant intends before any data moves or any query runs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;EXPLAINSQL&lt;/code&gt; surfaces the model's reasoning — the join choices, date windows, and grouping logic — so reviewers can catch wrong assumptions before they become surprises in a result set.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DISABLE_DATA_ACCESS&lt;/code&gt; keeps early experiments in a prompt-only lane: metadata-driven inspection continues, but actions that would send table data to the model are blocked until you re-enable them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Natural-language-to-SQL is fast until it guesses your schema, misreads a join, or touches the wrong table. On Oracle, you don’t have to accept that trade-off. AI Profiles bind provider and model to a defined set of database objects; Select AI adds database-native, inspectable actions so you can review intent and generated SQL before anything runs. In practice, you scope a profile, inspect with SHOWSQL and EXPLAINSQL, capture feedback, and only then execute. You keep the speed of NL2SQL without giving up control.&lt;/p&gt;

&lt;p&gt;This article builds on the managed MCP posture from Article 4, where we put a governed action surface in front of assistants. Here, we add an Oracle-native reasoning loop: Select AI’s profiles and actions make the model’s intent reviewable and controllable inside the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version scope and sandbox assumptions
&lt;/h2&gt;

&lt;p&gt;Select AI is available in Oracle Database 26ai and in Autonomous Database (Serverless, Dedicated, and Cloud@Customer). The examples assume Select AI is enabled, a provider credential exists (for example, created with DBMS_CLOUD for external providers), and any required network ACLs are in place. The HR sample schema is used for illustration with read-only access, and table/owner casing must match your environment. You’ll need EXECUTE on DBMS_CLOUD_AI and access to the referenced credential. Profiles are created via DBMS_CLOUD_AI.CREATE_PROFILE and take effect for the current session when set with DBMS_CLOUD_AI.SET_PROFILE. Supported providers and models vary by platform and region; consult the Providers and Models page for your environment.&lt;/p&gt;

&lt;p&gt;Prompts that include punctuation or special characters parse more reliably when enclosed in single quotes (for example, &lt;code&gt;SELECT AI SHOWSQL '…';&lt;/code&gt;). Some prompts work unquoted; if parsing fails, add quotes. All examples below use quoted prompts.&lt;/p&gt;

&lt;p&gt;Action tokens and profile JSON keys can be release-sensitive; confirm exact spellings and supported values in your target release. For current behavior, providers/models, action syntax, conversations, and end-to-end examples, see the Select AI docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overview: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Providers and models: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Examples (profiles, actions, scoping, data-access toggle, feedback): &lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Conversations and action syntax: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why reasoning quality matters for database AI
&lt;/h2&gt;

&lt;p&gt;LLMs know SQL patterns; they don’t know your database. Ask, “How many employees were hired last quarter by department?” and an open-ended model can guess a table name, pick the wrong join key, or hard-code a date window that doesn’t match your calendars. The remedy is twofold: make the model schema-aware, and force an inspection step before anything executes.&lt;/p&gt;

&lt;p&gt;AI Profiles handle schema awareness by binding the provider, model, and an object list the model may consider. Review then happens inside the database with &lt;code&gt;SELECT AI SHOWSQL&lt;/code&gt; (candidate SQL) and &lt;code&gt;SELECT AI EXPLAINSQL&lt;/code&gt; (rationale). That combination—scope plus inspection—turns NL2SQL from a clever demo into a workflow you can standardize. Per docs, for generation and inspection, SHOWSQL and EXPLAINSQL use database metadata (schemas/tables/columns/comments) rather than table contents by default. This keeps them safe defaults during review; consult docs for any action-specific exceptions in your release.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Profiles in practice: provider, credential, model, and scope
&lt;/h2&gt;

&lt;p&gt;An AI Profile is a database object created with &lt;code&gt;DBMS_CLOUD_AI.CREATE_PROFILE&lt;/code&gt;. It specifies which provider and model to use, which credential authorizes access, and which database objects are in bounds. The &lt;code&gt;object_list&lt;/code&gt; does double duty: it gives the model the vocabulary it needs and, when enforced, constrains generation to the allowed objects. Profiles are selected per session with &lt;code&gt;DBMS_CLOUD_AI.SET_PROFILE&lt;/code&gt;; after that, SELECT AI actions in the session use the bound provider/model and respect your scoping choices.&lt;/p&gt;

&lt;p&gt;The following example enables HR headcount questions without inviting schema drift. The model string is illustrative—choose a provider/model combination supported in your environment and release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_PROFILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;profile_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HR_READONLY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributes&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{
      "provider": "openai",
      "credential_name": "OPENAI_CRED",
      "model": "gpt-4o-mini",
      "object_list": [
        {"owner":"HR","name":"EMPLOYEES"},
        {"owner":"HR","name":"DEPARTMENTS"}
      ],
      "enforce_object_list": true,
      "case_sensitive_values": true
    }'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_PROFILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HR_READONLY'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace provider, model, and credential with values supported in your release. Supported providers vary by platform and region; check the Providers and Models page.&lt;/li&gt;
&lt;li&gt;Verify attribute keys and supported values (for example, &lt;code&gt;case_sensitive_values&lt;/code&gt;) against your release; use JSON booleans (&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;), not quoted strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few practical notes help avoid surprises. Profiles are session-scoped, so call &lt;code&gt;DBMS_CLOUD_AI.SET_PROFILE&lt;/code&gt; in each session that will use SELECT AI (new SQL Developer tabs, pooled connections, and so on). Creating or using profiles requires appropriate privileges (for example, EXECUTE on &lt;code&gt;DBMS_CLOUD_AI&lt;/code&gt;), access to the referenced credential, and any needed network ACLs. The object list can span multiple schemas; execution still honors your roles and any VPD policies. Keeping &lt;code&gt;"enforce_object_list": true&lt;/code&gt; curbs schema drift, though the constraint isn’t absolute—if your environment uses synonyms or views, validate with SHOWSQL that generated SQL stays within intended objects. The &lt;code&gt;case_sensitive_values&lt;/code&gt; attribute steers how string literal comparisons are produced; if your text data is mixed-case and recall matters more than exact match semantics, consider setting it to &lt;code&gt;false&lt;/code&gt; while confirming the exact predicate shape your chosen provider/model generates. For example, with &lt;code&gt;"case_sensitive_values": false&lt;/code&gt; a generated comparison might use a case-insensitive form (such as &lt;code&gt;UPPER(e.email) = UPPER(:email)&lt;/code&gt;), but exact SQL varies by model and release. Finally, attribute keys such as &lt;code&gt;enforce_object_list&lt;/code&gt;, &lt;code&gt;case_sensitive_values&lt;/code&gt;, and any automated scoping modes are release-sensitive; verify names and supported values against current docs before relying on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspectable actions: SHOWSQL first, then EXPLAINSQL if needed
&lt;/h2&gt;

&lt;p&gt;Once a profile is active, you can ask natural-language questions with database-native actions designed for review. SHOWSQL returns a candidate SQL statement without running it. Generation uses metadata—schemas, tables, and columns—rather than table contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'how many employees were hired last quarter by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EXPLAINSQL summarizes the model’s intent so you can catch wrong assumptions before execution. Like SHOWSQL, it relies on metadata, not table data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;EXPLAINSQL&lt;/span&gt; &lt;span class="s1"&gt;'how many employees were hired last quarter by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together, these two actions keep review and execution separate. SHOWSQL gives you something concrete to approve or adjust; EXPLAINSQL tells you why the model chose certain filters or join keys. Use them as your default path during early adoption. A third action, NARRATE, answers in natural language and can incorporate data when enabled; reserve it for contexts where your data-sharing posture allows model access to results and you have documented that decision (docs: Examples page).&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening the path: scoping, case handling, and data minimization
&lt;/h2&gt;

&lt;p&gt;Start with guardrails. Enforce the object list so the model stays inside approved tables or views. Decide on case handling up front so string comparisons behave predictably. And be explicit about what data, if any, can leave the database during NL2SQL interactions.&lt;/p&gt;

&lt;p&gt;Oracle provides an administrative toggle for data transfer. Calling &lt;code&gt;DBMS_CLOUD_AI.DISABLE_DATA_ACCESS&lt;/code&gt; blocks actions that would send table data or results to the model. Metadata-only flows—SHOWSQL and EXPLAINSQL—continue to work when disabled. Data-bearing actions (for example, NARRATE, and in some releases certain chat or result-enriched flows) are blocked until re-enabled. See the Select AI docs for the per-release action matrix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISABLE_DATA_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Still allowed: metadata-driven candidate SQL (no data sent)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'total headcount by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Expected to raise an error while disabled (data-bearing action)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;NARRATE&lt;/span&gt; &lt;span class="s1"&gt;'total headcount by department?'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;EXEC&lt;/span&gt; &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ENABLE_DATA_ACCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat this toggle as a policy switch you can document and audit. When disabled, reviewers can still shape and approve SQL; when enabled, teams can opt into natural-language answers in contexts where that’s appropriate. Roles, VPD, and auditing continue to apply to any approved SQL you run, regardless of whether the SQL was generated by Select AI (docs: Overview and Examples).&lt;/p&gt;

&lt;p&gt;Guardrails to standardize in your runbooks: keep &lt;code&gt;enforce_object_list&lt;/code&gt; enabled; choose &lt;code&gt;case_sensitive_values&lt;/code&gt; deliberately; default to SHOWSQL/EXPLAINSQL during review; and use the data-access toggle to separate prompt-only review from data-bearing actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick scope test you can try
&lt;/h3&gt;

&lt;p&gt;With the &lt;code&gt;HR_READONLY&lt;/code&gt; profile above (only EMPLOYEES and DEPARTMENTS in scope), ask something that tempts the model to reach for &lt;code&gt;HR.LOCATIONS&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- LOCATIONS is out of scope; we expect generation to stay within allowed objects&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'list each department with its city and headcount'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the candidate. With &lt;code&gt;enforce_object_list&lt;/code&gt; on, generation is constrained to the allowed objects. If the candidate includes a disallowed object (for example, &lt;code&gt;HR.LOCATIONS&lt;/code&gt;), do not approve it—either change the question or explicitly add the needed object to the profile and regenerate. Scope isn’t a security bypass; it’s a generation boundary that works best when the object list is precise. Synonyms and views can influence behavior, so verify the metadata your profile exposes. When you want to encapsulate joins and filters deliberately, consider scoping to views rather than base tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small, governed NL2SQL demo
&lt;/h2&gt;

&lt;p&gt;Here is a path your team can adopt without changing how you secure or audit the database. First, create and set a narrow profile; the &lt;code&gt;HR_READONLY&lt;/code&gt; example above is sufficient for headcount questions, and keeping &lt;code&gt;enforce_object_list&lt;/code&gt; enabled prevents drift to unapproved tables. Next, inspect with SHOWSQL and, when the intent needs clarification, EXPLAINSQL. For the HR schema, a typical review might begin with a candidate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Candidate SQL; not executed&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;SHOWSQL&lt;/span&gt; &lt;span class="s1"&gt;'list the number of employees hired in the last 90 days by department with department name'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the candidate looks structurally right but relies on &lt;code&gt;SYSDATE - 90&lt;/code&gt; and you need calendar quarters instead, either edit the date predicate yourself or nudge the prompt and generate another candidate. When you want to see the model’s assumptions—date windows, join keys, grouping logic—ask for a short rationale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Short rationale to help review&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;EXPLAINSQL&lt;/span&gt; &lt;span class="s1"&gt;'list the number of employees hired in the last 90 days by department with department name'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After review, execute the approved SQL manually under a read-only role. Generated or not, it is just SQL, so your roles, VPD, and unified auditing remain in force.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback and evaluation loops
&lt;/h2&gt;

&lt;p&gt;You’ll learn faster if you keep lightweight records of what you approved and why. A tiny table works well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ai_sql_feedback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;             &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;asked_at&lt;/span&gt;       &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;SYSTIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;profile_name&lt;/span&gt;   &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;nl_question&lt;/span&gt;    &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;generated_sql&lt;/span&gt;  &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;corrected_sql&lt;/span&gt;  &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;rating&lt;/span&gt;         &lt;span class="n"&gt;NUMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- 1–5&lt;/span&gt;
  &lt;span class="n"&gt;notes&lt;/span&gt;          &lt;span class="k"&gt;CLOB&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record your decision after each review round:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;ai_sql_feedback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nl_question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generated_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corrected_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'HR_READONLY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'list the number of employees hired in the last 90 days by department with department name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;paste_candidate_sql_here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;paste_corrected_or_approved_sql_here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Approved after switching SYSDATE-90 to quarter boundaries'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oracle also exposes a built-in feedback hook so you can record positive or negative signals tied to generated SQL. See &lt;code&gt;DBMS_CLOUD_AI.FEEDBACK&lt;/code&gt; in the Select AI docs for the current procedure signature; confirm parameter names such as &lt;code&gt;sql_text&lt;/code&gt; versus &lt;code&gt;sql_id&lt;/code&gt; and accepted feedback types/ratings for your release (docs: Examples).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Example only; verify parameter names/values for your release (e.g., sql_text vs sql_id)&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FEEDBACK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;feedback_type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SQL_QUALITY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;-- verify accepted values&lt;/span&gt;
    &lt;span class="n"&gt;rating&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;-- verify rating scale&lt;/span&gt;
    &lt;span class="n"&gt;sql_text&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;paste_candidate_sql_here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- or use sql_id =&amp;gt; '...' if supported&lt;/span&gt;
    &lt;span class="n"&gt;notes&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Good structure; adjusted date window to fiscal quarter'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the loop simple: store the question, what Select AI proposed, what you ran, and a short note. In reviews, patterns emerge quickly—repeated date-window mistakes, common join fixes, or &lt;code&gt;object_list&lt;/code&gt; gaps you can close in profiles. If you depend on case-insensitive matching, pilot with a small test set that validates generated predicates against your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when you do this under managed MCP
&lt;/h2&gt;

&lt;p&gt;In Article 4 we hosted an MCP server on Autonomous Database to make the database the control plane for actions. Select AI fits neatly into that posture. An agent can plan and call tools inside the database, but the way it generates and explains SQL remains inspectable and scoped by the active profile. Your identity, roles, private endpoints, VPD, and auditing don’t get bypassed; they’re the runway. This is series context, not a formal product integration; Select AI behavior and governance follow the documented database APIs. The operational benefit is simple: teams can standardize on "SHOWSQL first," even when an assistant is in the loop, and your audit trail still shows a normal query execution under the right account, module, and action once you approve the run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common failure modes, and how the Select AI pattern avoids them
&lt;/h2&gt;

&lt;p&gt;Open-ended NL2SQL tends to fail the same ways: hallucinated table names, ambiguous joins, and fragile filters. An enforced &lt;code&gt;object_list&lt;/code&gt; removes most schema drift by design. SHOWSQL gives reviewers something concrete to approve or fix, and EXPLAINSQL surfaces incorrect assumptions before they become surprises in production. When data sharing is sensitive or still under review, &lt;code&gt;DISABLE_DATA_ACCESS&lt;/code&gt; keeps experiments in the prompt-only lane. None of this is exotic. It is a repeatable workflow you can hand to a junior developer—or an assistant—and expect the same outcome: first we scope; then we inspect; then we run; and we prove it in logs and policy after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;This same discipline carries into retrieval-augmented generation. In Article 6, we will combine Select AI with Oracle AI Vector Search so answers are grounded in embeddings stored next to the business data itself. You’ll use the same pattern: bind provider and model with a profile, scope what’s visible, inspect what will run, and keep governance in the path.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tiny sequence to keep on a team wall
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhkvj7ao81i36lt0aku7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhkvj7ao81i36lt0aku7.png" alt="Review-first NL2SQL workflow" width="539" height="826"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About Select AI (overview, prompt/data handling): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-about.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Providers and models: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-your-ai-provider-and-llms.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Examples (profiles, SHOWSQL/EXPLAINSQL/NARRATE, scoping, data-access toggle, feedback): &lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/select-ai-examples.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Action syntax and conversations: &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/select-ai-enable-conversations.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;NL2SQL doesn’t have to be a black box. On Oracle, you bind the model and the data surface with AI Profiles, inspect what the model intends with SHOWSQL and EXPLAINSQL, and execute only when you’re satisfied—under the same roles, VPD, and auditing you already trust. Start narrow. Keep &lt;code&gt;enforce_object_list&lt;/code&gt; on. Use the data-access toggle when you’re in prompt-only mode. Log decisions. The result is faster iteration with guardrails you can explain—and prove.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>security</category>
      <category>sql</category>
    </item>
    <item>
      <title>Managed MCP in Autonomous AI Database: remote, governed tools per database</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Thu, 21 May 2026 10:32:46 +0000</pubDate>
      <link>https://dev.to/oracledevs/managed-mcp-in-autonomous-ai-database-remote-governed-tools-per-database-5edb</link>
      <guid>https://dev.to/oracledevs/managed-mcp-in-autonomous-ai-database-remote-governed-tools-per-database-5edb</guid>
      <description>&lt;p&gt;This is article 4 of 8 in my Oracle Database Skills series.&lt;/p&gt;

&lt;p&gt;Key Takeaways&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managed MCP moves the action surface into the database itself. Tools run under real database identities with existing network controls, VPD policies, and audit trails already in force — no separate trust stack to build.&lt;/li&gt;
&lt;li&gt;Custom tools are database objects. You define the logic in PL/SQL and register it with DBMS_CLOUD_AI_AGENT.CREATE_TOOL, so governance travels with the tool definition rather than depending on each caller to do the right thing.&lt;/li&gt;
&lt;li&gt;The five questions security teams ask — who acted, under which identity, from what network path, against which tool, and where is the audit record — are answerable by design when the endpoint is managed by the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most teams begin where Article 3 ended: a single developer running a local MCP (Model Context Protocol) server to prove out tools and workflows. That’s perfect for a solo experiment and brittle the moment work becomes shared. As soon as you put an IDE on a shared sandbox or add a second teammate, you need identity that isn’t “whoever is at the laptop,” network boundaries that aren’t home Wi‑Fi, and an audit trail your security team can actually review. Autonomous AI Database’s managed MCP server gives you those primitives without rebuilding your trust stack. Each database exposes its own HTTP MCP endpoint. Clients authenticate as a database user. Tools are defined and governed inside the database. Existing controls—roles, Access Control Lists (ACLs) or Private Endpoint reachability, Virtual Private Database (VPD)/redaction, and Unified Auditing—stay in the path for every tool call.&lt;/p&gt;

&lt;p&gt;If you liked the local model—small, named tools; least‑privilege credentials; proof after the fact—you keep it. What changes is location and blast radius. The server runs with the database service. Calls execute with a database user’s roles. Network policy and auditing you already rely on continue to apply over streamable HTTP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cnfq0myr5hotuestm84.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cnfq0myr5hotuestm84.png" alt=" " width="800" height="821"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sources: consult the Autonomous Database MCP documentation for concepts, enablement, security, and troubleshooting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/about-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/about-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/use-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/use-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/mcp-server-concepts.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/mcp-server-concepts.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/security.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/security.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/troubleshoot-mcp-server.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/troubleshoot-mcp-server.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and assumptions
&lt;/h2&gt;

&lt;p&gt;This article focuses on Autonomous Database Serverless (Autonomous AI Database) with its managed, per‑database HTTP MCP endpoint and on custom tool registration with &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt;. It does not cover on‑premises databases, self‑hosted proxies, or alternative deployment topologies. For authentication, follow the documented OAuth and short‑lived bearer token flows supported at publish time. The examples assume you can modify an Autonomous AI Database in Oracle Cloud Infrastructure (OCI) and create PL/SQL in your schema. Private Endpoint deployments require clients to run within, or be routed into, the VCN. Availability is documented as exclusive to Oracle Autonomous AI Database (versions 26ai and 19c) on the “About MCP Server” page; verify your environment against current docs before starting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’ll need (prerequisites)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An Autonomous AI Database (Serverless) you can modify in OCI.&lt;/li&gt;
&lt;li&gt;Permission in OCI to update free‑form tags on that database (enablement uses a tag).&lt;/li&gt;
&lt;li&gt;A database user to authenticate to MCP (least‑privilege strongly recommended).&lt;/li&gt;
&lt;li&gt;Database privileges to create/compile PL/SQL in your schema and to execute &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt; (DBA can grant &lt;code&gt;EXECUTE&lt;/code&gt; on the package).&lt;/li&gt;
&lt;li&gt;Network posture set appropriately:

&lt;ul&gt;
&lt;li&gt;Public endpoint constrained by ACLs to known IPs/CIDRs, or&lt;/li&gt;
&lt;li&gt;Private Endpoint with clients running inside or routed/peered into the VCN.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;An MCP‑aware client that supports streamable HTTP (e.g., Claude Desktop via &lt;code&gt;mcp-remote&lt;/code&gt;; VS Code with Cline). Ensure the client’s transport is set to streamable HTTP per its configuration schema.&lt;/li&gt;

&lt;li&gt;To view audit trails, privileges such as &lt;code&gt;AUDIT_VIEWER&lt;/code&gt; (or equivalent access to &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt;) and any required unified audit policies per your security team.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why move from local to managed
&lt;/h2&gt;

&lt;p&gt;Local MCP gets you speed but leaves basic enterprise questions unanswered: Who ran this? Under which roles? From where? What policy filtered the result? Where’s the audit record? A managed MCP endpoint answers them by design. Identity is a database user, so tools run with that user’s roles and any VPD or redaction policies. Network is first‑class: public endpoints honor ACLs; Private Endpoint confines reachability to your VCN. Evidence lives with the data in Unified Auditing, and—if your organization uses it—Oracle Data Safe can centralize review. You don’t assemble a separate trust stack for assistants; you reuse the one already protecting your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable the per‑database MCP endpoint
&lt;/h2&gt;

&lt;p&gt;Enabling MCP is an OCI operation. You turn it on per database with a free‑form tag. After a brief delay, the database details page shows the MCP URL. Use that exact URL; Private Endpoint deployments will reflect your VCN in the hostname. The documented public endpoint pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://dataaccess.adb.{region-identifier}.oraclecloudapps.com/adb/mcp/v1/databases/{database-ocid}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Private Endpoint databases, the documented pattern uses the database Private Endpoint hostname prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://{hostname_prefix}.adb.{region-identifier}.oraclecloudapps.com/adb/mcp/v1/databases/{database-ocid}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the OCI Console, open your Autonomous AI Database (Serverless) and add this free‑form tag:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;adb$feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Value: &lt;code&gt;{"name":"mcp_server","enable":true}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tighten network posture before you connect. For public endpoints, restrict inbound IPs with an ACL. For Private Endpoint, keep the MCP URL accessible only within your VCN; clients must run in or have routed/peered reachability to that network. The enablement and security pages in the docs provide step‑by‑step screens for both configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticate to the managed MCP endpoint
&lt;/h2&gt;

&lt;p&gt;The managed server accepts OAuth or short‑lived bearer tokens as documented. Tokens are issued per database and presented on every request as a bearer header over streamable HTTP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token endpoint path (documented): &lt;code&gt;/adb/auth/v1/databases/{database-ocid}/token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Present the token in client requests as: &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tokens are typically valid for approximately one hour; confirm the current duration and request body in the Use MCP Server page before automating refresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Client configuration varies slightly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Desktop via &lt;code&gt;mcp-remote&lt;/code&gt;: configure a remote MCP server using your database’s MCP URL and add the &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; header in the bridge’s HTTP headers field. Set the transport to streamable HTTP as required by your client (for example, a setting named &lt;code&gt;streamable-http&lt;/code&gt; or &lt;code&gt;streamableHttp&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;VS Code with Cline: add a remote HTTP MCP server pointing to the MCP URL and set &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; in the request headers section. Ensure the transport is explicitly set to streamable HTTP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal Cline-style configuration looks like this, with placeholders for your database URL and bearer token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"adb-managed-mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"streamableHttp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://dataaccess.adb.{region-identifier}.oraclecloudapps.com/adb/mcp/v1/databases/{database-ocid}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your database uses a Private Endpoint, make sure the client host has network reachability into the VCN. Valid credentials against an unreachable hostname still fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish a custom tool the database can govern
&lt;/h2&gt;

&lt;p&gt;Unlike local MCP servers that host code beside the client, the managed server looks up tools defined in the database. You create small, purposeful functions in your schema and register them with &lt;code&gt;DBMS_CLOUD_AI_AGENT.CREATE_TOOL&lt;/code&gt;. That keeps control centralized: the definitions live in the database, metadata is queryable from views, and execution inherits database roles and policy.&lt;/p&gt;

&lt;p&gt;To keep the example easy to reason about, we’ll publish a read‑only tool that returns a paginated JSON array of objects in a given schema that are visible to the executing database user and are not in Oracle‑maintained schemas. This variant excludes Oracle‑maintained schemas using &lt;code&gt;ALL_USERS.ORACLE_MAINTAINED&lt;/code&gt;. The Oracle docs also include a sample that filters on &lt;code&gt;ALL_OBJECTS.ORACLE_MAINTAINED = 'N'&lt;/code&gt;; either approach works on Autonomous Database.&lt;/p&gt;

&lt;p&gt;First, create and compile the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;list_objects_json&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;p_schema&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;p_offset&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;p_limit&lt;/span&gt;  &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;CLOB&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="n"&gt;v_json&lt;/span&gt;  &lt;span class="k"&gt;CLOB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;JSON_ARRAYAGG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
             &lt;span class="n"&gt;JSON_OBJECT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="s1"&gt;'SCHEMA_NAME'&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s1"&gt;'OBJECT_NAME'&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s1"&gt;'OBJECT_TYPE'&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt;
             &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="k"&gt;CLOB&lt;/span&gt;
           &lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="s1"&gt;'[]'&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;v_json&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_type&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;all_objects&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
      &lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
               &lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;all_users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
               &lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;owner&lt;/span&gt;
               &lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oracle_maintained&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'N'&lt;/span&gt;
             &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;
      &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="n"&gt;p_offset&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;NEXT&lt;/span&gt; &lt;span class="n"&gt;p_limit&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;v_json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check for compilation errors before registering a tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;ERRORS&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;list_objects_json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Or:&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_errors&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, register the function as an MCP tool. Use the attribute names exactly as shown in the documentation for &lt;code&gt;DBMS_CLOUD_AI_AGENT.CREATE_TOOL.attributes&lt;/code&gt;; mismatches can cause registration or invocation errors. Common keys include &lt;code&gt;instruction&lt;/code&gt;, &lt;code&gt;function&lt;/code&gt;, and &lt;code&gt;tool_inputs&lt;/code&gt;. For this example, rely on the PL/SQL defaults defined in the function and do not include unverified default values in the &lt;code&gt;tool_inputs&lt;/code&gt; JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI_AGENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_TOOL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'LIST_OBJECTS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="s1"&gt;'~{
      "instruction": "Return a JSON array of objects visible to the executing database user for the given schema (excluding Oracle-maintained schemas). Always respect p_limit and p_offset.",
      "function": "LIST_OBJECTS_JSON",
      "tool_inputs": [
        { "name": "p_schema", "type": "string", "required": true,  "description": "Schema name (case-insensitive)" },
        { "name": "p_offset", "type": "number", "required": false, "description": "Row offset for pagination" },
        { "name": "p_limit",  "type": "number", "required": false, "description": "Max rows to return" }
      ]
    }~'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that registration succeeded by querying the registry view. Column sets differ by version, so keep the check simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;user_cloud_ai_agent_tools&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see “insufficient privileges,” ask your DBA to grant &lt;code&gt;EXECUTE&lt;/code&gt; on &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt; to your user and retry.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt; supports both custom PL/SQL‑backed tools and built‑in tool types. For the current list of built‑in types and parameters, see the package reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/dbms-cloud-ai-agent-package.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/dbms-cloud-ai-agent-package.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also note: MLE JavaScript is not supported for MCP tools. Java can be used via PL/SQL calling Java stored procedures when &lt;code&gt;JAVAVM&lt;/code&gt; is enabled on the database (see the docs for details).&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect a client and try the tool
&lt;/h2&gt;

&lt;p&gt;Once authentication works and the tool is registered, connect an MCP‑aware client and ask it to use the tool. In Claude Desktop, add a remote MCP server that points to your database’s MCP URL, include the &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; header, and set transport to streamable HTTP per the client’s schema. In a fresh chat, prompt the assistant: “Use the LIST_OBJECTS tool to list 5 objects in the HR schema.” You should see the assistant invoke the tool and stream back JSON. In VS Code with Cline, add the same MCP URL in the extension’s settings, supply the same header, and ensure the streamable HTTP transport is selected; then request &lt;code&gt;LIST_OBJECTS&lt;/code&gt; with &lt;code&gt;p_schema=HR&lt;/code&gt; and &lt;code&gt;p_limit=5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A representative response from the tool looks like this (truncated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"SCHEMA_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COUNTRIES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TABLE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"SCHEMA_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EMPLOYEES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TABLE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"SCHEMA_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JOBS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"OBJECT_TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TABLE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Private Endpoint databases, run those clients from a host inside the VCN or with routed/peered access. Lack of reachability is the most common reason a seemingly correct configuration fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observe the audit trail (roles permitting)
&lt;/h2&gt;

&lt;p&gt;Unified Auditing on Autonomous Database records activity under your database user. Exact fields and event names depend on your version and your organization’s audit policy. With appropriate privileges (for example, &lt;code&gt;AUDIT_VIEWER&lt;/code&gt; or equivalent access to &lt;code&gt;UNIFIED_AUDIT_TRAIL&lt;/code&gt;), you can review a time‑bounded window of recent activity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dbusername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj_privilege&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unified_audit_policies&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;unified_audit_trail&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;SYSTIMESTAMP&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'15'&lt;/span&gt; &lt;span class="k"&gt;MINUTE&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt;    &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbusername&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Autonomous Database retains unified audit data for about 14 days by default; if you need longer retention or centralized reporting, integrate Oracle Data Safe. When you author VPD or audit policies for MCP, do not rely on &lt;code&gt;SESSION_USER&lt;/code&gt;. Use MCP context attributes instead—for example, &lt;code&gt;sys_context('MCP_SERVER_ACCESS_CONTEXT','USER_IDENTITY')&lt;/code&gt; as shown in the Security docs. Some concept pages may refer to the MCP context container using slightly different naming in prose; follow the exact string in the Security examples. Your security team may also enable additional unified audit policies to capture MCP‑specific attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operate safely and clean up
&lt;/h2&gt;

&lt;p&gt;Treat tools as product surface area. Keep them small and read‑heavy at first; add write paths only when you can articulate least privilege and validate inputs. Prefer schemas you own to avoid accidental privilege sprawl. Version tool registrations as code and require review before deployment.&lt;/p&gt;

&lt;p&gt;When a tool is no longer needed, remove both the registration and the underlying function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;DBMS_CLOUD_AI_AGENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DROP_TOOL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'LIST_OBJECTS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;list_objects_json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you expand usage, periodically re‑check ACLs on public endpoints and confirm Private Endpoint posture remains correct, especially after network changes. Rate limiting is enforced by the managed service; consult current release notes for behavior and limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Most issues fall into a few buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compilation and registration: fix PL/SQL errors (&lt;code&gt;SHOW ERRORS&lt;/code&gt;, &lt;code&gt;USER_ERRORS&lt;/code&gt;) before registering. Ensure your user has &lt;code&gt;EXECUTE&lt;/code&gt; on &lt;code&gt;DBMS_CLOUD_AI_AGENT&lt;/code&gt;. If registry queries fail with unknown columns, select only &lt;code&gt;tool_name&lt;/code&gt; and consult versioned docs for metadata.&lt;/li&gt;
&lt;li&gt;Connectivity: verify you’re using the MCP URL exactly as shown in the console. For public endpoints, confirm your IP/CIDR is in the database ACL. For Private Endpoint, confirm the client host has routed/peered access to the VCN.&lt;/li&gt;
&lt;li&gt;Authentication: obtain tokens from &lt;code&gt;/adb/auth/v1/databases/{database-ocid}/token&lt;/code&gt; and present them as &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;. Copy the exact request body and lifetime from the current docs. Place headers where your client expects them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a client claims to connect but tool calls fail, double-check that attribute keys in &lt;code&gt;CREATE_TOOL&lt;/code&gt; match the documentation exactly; case mismatches cause registration or invocation errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;With a per-database MCP endpoint, one governed tool, and a way to verify activity in Unified Auditing, you can expand safely. Start with a small set of read-heavy tools your team actually needs. Introduce narrow write paths with explicit inputs and clear privileges. Treat tool definitions like code: review them, version them, and track changes.&lt;/p&gt;

&lt;p&gt;The next article moves from action surfaces to reasoning scope: Select AI and AI Profiles. That is where you decide what natural-language-to-SQL should be allowed to see, how generated SQL should be inspected, and how to keep an assistant’s query generation inside a deliberate profile instead of a broad database connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Freshness note
&lt;/h2&gt;

&lt;p&gt;Content validated against the Autonomous Database MCP documentation on 2026-05-08. Re-check the Use MCP Server and Security pages for any changes to endpoint paths, JSON key names, token lifetimes, and audit details before publishing or automating.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>mcp</category>
      <category>security</category>
    </item>
    <item>
      <title>Controlled Oracle Actions with SQLcl MCP</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Tue, 19 May 2026 12:07:51 +0000</pubDate>
      <link>https://dev.to/oracledevs/controlled-oracle-actions-with-sqlcl-mcp-120e</link>
      <guid>https://dev.to/oracledevs/controlled-oracle-actions-with-sqlcl-mcp-120e</guid>
      <description>&lt;p&gt;This is article 3 of 8 in my Oracle Database Skills series.&lt;/p&gt;

&lt;p&gt;Kay Takeaways&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The assistant connects using a saved connection name, not a pasted password. Credentials stay in the SQLcl store and out of prompts, so the model sees the name but never the secret.&lt;/li&gt;
&lt;li&gt;Restrict levels define the tool surface at runtime. Starting at the most restrictive setting limits what the assistant can do; you widen the surface deliberately as the use case earns it.&lt;/li&gt;
&lt;li&gt;Session tagging and the MCP activity log give a DBA verifiable proof of what ran. Evidence comes from the database, not the chat transcript — that distinction matters when someone asks what the assistant actually did.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the last piece, you routed Oracle prompts through db/SKILL.md to keep intent explicit (see &lt;a href="https://blogs.oracle.com/developers/ai-agents-know-sql-oracle-database-skills-teach-them-oracle" rel="noopener noreferrer"&gt;Article 1&lt;/a&gt; and &lt;a href="https://blogs.oracle.com/developers/route-dont-flood-using-db-skill-md-to-steer-oracle%e2%80%91aware-assistants" rel="noopener noreferrer"&gt;Article 2&lt;/a&gt; in this series). Now it’s time to act—locally, with guardrails, and with an evidence trail your DBA can verify. SQLcl MCP gives you that posture. You launch it as a local process (&lt;code&gt;sql -mcp&lt;/code&gt;); it exposes a small catalog of named tools over stdio; it connects through saved connections you control; it starts in a highly restricted mode by default; and it leaves database‑native traces you can query later. That’s the right shape for early assistant experiments on a developer machine.&lt;/p&gt;

&lt;p&gt;Citations for versioned behavior are in Oracle’s SQLcl documentation (for example, “Using SQLcl MCP Server,” “SQLcl MCP Server tools,” “Starting and managing SQLcl MCP Server,” “Restrict Levels,” “Preparing your environment,” and “Saving connections” in the 25.4 and 26.1 editions). Treat any version‑sensitive claim here as “verify against your installed docs.”&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SQLcl 25.2.0 or newer with MCP support&lt;/li&gt;
&lt;li&gt;Java 17 or 21 available to SQLcl (per “Preparing your environment”)&lt;/li&gt;
&lt;li&gt;A non‑production Oracle Database (19c+ suffices for this demo)&lt;/li&gt;
&lt;li&gt;A least‑privilege, read‑only database user for testing&lt;/li&gt;
&lt;li&gt;A saved SQLcl connection that includes a stored password, created with &lt;code&gt;-save&lt;/code&gt; and &lt;code&gt;-savepwd&lt;/code&gt; (required for non‑interactive MCP connects)&lt;/li&gt;
&lt;li&gt;Permission to query &lt;code&gt;V$SESSION&lt;/code&gt; for evidence, or access to an equivalent DBA‑provided view that exposes &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; for your sessions&lt;/li&gt;
&lt;li&gt;If you use TNS aliases, configure Oracle Net as usual (for example, set &lt;code&gt;TNS_ADMIN&lt;/code&gt; to your &lt;code&gt;tnsnames.ora&lt;/code&gt; directory). If your MCP client launches &lt;code&gt;sql -mcp&lt;/code&gt; with a custom environment, pass &lt;code&gt;TNS_ADMIN&lt;/code&gt; there as needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security note: Storing passwords is a prerequisite for non‑interactive MCP connects. Keep this to a sandbox, use a read‑only account, and lock down your local SQLcl store (commonly under &lt;code&gt;~/.dbtools&lt;/code&gt;) per your organization’s secrets policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SQLcl MCP is the right local action layer
&lt;/h2&gt;

&lt;p&gt;A tempting shortcut is to paste credentials into a prompt and let a model “try something.” That’s exactly what you want to avoid. SQLcl MCP flips the pattern: you launch &lt;code&gt;sql -mcp&lt;/code&gt; locally and expose a small set of structured tools. Your client can list saved connections, connect by name, run SQL, and disconnect. The MCP connect path uses saved/named connections; the tool does not accept raw credentials in the call. For non‑interactive use, the password must be saved (&lt;code&gt;-savepwd&lt;/code&gt;). You decide which saved connections exist and what privileges they carry.&lt;/p&gt;

&lt;p&gt;Restrict levels are SQLcl’s runtime safety gates that limit which features and commands are allowed. In MCP mode the server defaults to restrict level 4 (most restrictive) unless you override it with &lt;code&gt;-R&lt;/code&gt;, so the surface stays small until you intentionally broaden it. That’s a posture you can explain to a DBA and adjust deliberately as you earn trust.&lt;/p&gt;

&lt;p&gt;References: SQLcl MCP overview and startup; Saved connections; Restrict levels (25.4/26.1 docs).&lt;/p&gt;

&lt;h2&gt;
  
  
  How the server behaves under the hood
&lt;/h2&gt;

&lt;p&gt;When your client launches &lt;code&gt;sql -mcp&lt;/code&gt;, SQLcl starts a small server over stdio and advertises a catalog of named tools. Message envelopes vary by client, but tool names and arguments follow the SQLcl MCP documentation. As of the 26.1 docs, commonly listed tools include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list-connections&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disconnect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run-sql&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run-sqlcl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some builds also include &lt;code&gt;schema-information&lt;/code&gt;. Tool availability and argument shapes can vary by version/build; verify against your installed docs.&lt;/p&gt;

&lt;p&gt;Connections are discovered from your local SQLcl store (for example, &lt;code&gt;~/.dbtools&lt;/code&gt;). To make a connection usable without a prompt, save it with a stored password. Oracle documents this as a prerequisite for hands‑off connection by name.&lt;/p&gt;

&lt;p&gt;Restrict levels shape what the process can do. In MCP mode, the server defaults to restrict level 4 (most restrictive) unless you override it with &lt;code&gt;-R&lt;/code&gt;. Outside MCP, generic SQLcl defaults to restrict level 0 (unrestricted) unless you pass &lt;code&gt;-R&lt;/code&gt;; always verify the “Restrict Levels” page for your exact build.&lt;/p&gt;

&lt;p&gt;References: SQLcl MCP tools; Saved connections; Restrict levels and &lt;code&gt;-R&lt;/code&gt; flag (25.4/26.1 docs).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ad104v03cpgapuc7ht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ad104v03cpgapuc7ht.png" alt="MCP Restrict Levels" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Traceability you can prove (monitoring and governance)
&lt;/h2&gt;

&lt;p&gt;Before you run anything, plan how you’ll show what happened. The most portable approach is to tag your session with &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt; and then confirm it in &lt;code&gt;V$SESSION&lt;/code&gt;. The tag appears in performance tooling and helps a DBA correlate what ran and why. By default, SQLcl MCP records the execution history of requests in &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; (per Oracle docs). Access, owner/schema, and privileges vary by environment, so confirm presence and permissions with your DBA.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tagging mechanism: &lt;code&gt;DBMS_APPLICATION_INFO.SET_MODULE&lt;/code&gt; and &lt;code&gt;SET_ACTION&lt;/code&gt; (documented in Oracle Database PL/SQL packages)&lt;/li&gt;
&lt;li&gt;Evidence view: &lt;code&gt;V$SESSION&lt;/code&gt; (privilege‑gated; ask a DBA for a scoped alternative if you lack access)&lt;/li&gt;
&lt;li&gt;Logs: &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt; records requests by default; access and retention are environment‑specific&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;References: &lt;code&gt;DBMS_APPLICATION_INFO&lt;/code&gt;; &lt;code&gt;V$SESSION&lt;/code&gt;; SQLcl MCP monitoring/logging pages (25.4/26.1 docs).&lt;/p&gt;

&lt;h2&gt;
  
  
  Version scope and environment notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep SQLcl at 25.2.0+ for MCP features and run it with Java 17 or 21 (per “Preparing your environment”).&lt;/li&gt;
&lt;li&gt;The tool catalog and defaults can evolve; the 25.4 and 26.1 guides capture current behavior, but verify against your installed version.&lt;/li&gt;
&lt;li&gt;The demo assumes Oracle Database 19c or later and avoids database features tied to 26ai.&lt;/li&gt;
&lt;li&gt;Some clients (for example, SQL Developer for VS Code) can launch and manage the MCP server for you; follow your client’s documentation for its configuration format and capabilities, which can change by version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A small, traceable demo you can run today
&lt;/h2&gt;

&lt;p&gt;This walkthrough saves a single read‑only connection, launches the MCP server, validates that the connection is discoverable, connects by name, tags its session, runs one read‑only discovery query, and disconnects. Each step includes copy‑paste commands and why the step matters.&lt;/p&gt;

&lt;p&gt;1) Save a named connection for a read‑only sandbox user&lt;/p&gt;

&lt;p&gt;Open SQLcl, connect once interactively, and persist the connection with a stored password so MCP can use it without a prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sql /nolog
&lt;span class="c"&gt;# If you connect by service name:&lt;/span&gt;
conn demo_ro@//host:1521/service &lt;span class="nt"&gt;-save&lt;/span&gt; demo_ro &lt;span class="nt"&gt;-savepwd&lt;/span&gt;
&lt;span class="c"&gt;# If you connect by TNS alias (ensure Oracle Net is configured and tnsnames.ora is discoverable):&lt;/span&gt;
&lt;span class="c"&gt;# conn demo_ro@ALIAS -save demo_ro -savepwd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted, enter the password interactively. The &lt;code&gt;-save&lt;/code&gt; and &lt;code&gt;-savepwd&lt;/code&gt; flags persist the named connection (commonly in &lt;code&gt;~/.dbtools&lt;/code&gt;) with a stored password so MCP can use it non‑interactively. Use a sandbox‑only, least‑privilege account and follow your org’s secrets policy for local credential storage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3pdi777ir9vojfm7mmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3pdi777ir9vojfm7mmd.png" alt="Saved Connection Handshake" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2) Start the MCP server (keep the default restrict level)&lt;/p&gt;

&lt;p&gt;Launch SQLcl in MCP mode and leave the terminal open. Unless your client does this for you, this process is the local server your MCP‑aware client will talk to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sql &lt;span class="nt"&gt;-mcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, restrict level 4 applies. If you ever see broader capability than expected, confirm that the process was not started with a lower restrict level (for example, &lt;code&gt;-R 0&lt;/code&gt;). For early experiments, stick with the default level 4.&lt;/p&gt;

&lt;p&gt;3) Point your MCP‑aware client at SQLcl&lt;/p&gt;

&lt;p&gt;Configure your client to launch &lt;code&gt;sql -mcp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Representative MCP server configuration for a desktop client. Field names and file locations vary by client; follow your client’s documentation for the exact schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sqlcl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"TNS_ADMIN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/network/admin"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Verify that the saved connection is discoverable&lt;/p&gt;

&lt;p&gt;Before you connect, ask the server to list saved connections. Seeing &lt;code&gt;demo_ro&lt;/code&gt; here confirms that the server can use it non‑interactively.&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s message envelope (for example, a surrounding &lt;code&gt;"type": "call_tool"&lt;/code&gt;), server name, and argument key names as defined by your client and the SQLcl MCP tools page for your installed version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list-connections"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;demo_ro&lt;/code&gt; does not appear, revisit the &lt;code&gt;conn ... -save -savepwd&lt;/code&gt; step and make sure you created the connection under the same OS user/profile that SQLcl is using.&lt;/p&gt;

&lt;p&gt;5) Connect by name and tag the session&lt;/p&gt;

&lt;p&gt;Connect using the saved name, then tag the session with &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; so you can find it later in performance views and, if accessible, MCP logs.&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names. Note: some clients use &lt;code&gt;name&lt;/code&gt;, others &lt;code&gt;connectionName&lt;/code&gt; or &lt;code&gt;connection_name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"connect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo_ro"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BEGIN DBMS_APPLICATION_INFO.SET_MODULE('sqlcl-mcp','schema-discovery'); END;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tagging is optional but strongly recommended. It creates a human‑readable breadcrumb for exactly this experiment.&lt;/p&gt;

&lt;p&gt;6) Run one read‑only discovery query&lt;/p&gt;

&lt;p&gt;To keep the example universal, start with a query against objects you own:&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SELECT table_name FROM user_tables ORDER BY table_name FETCH FIRST 5 ROWS ONLY"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your account has cross‑schema visibility, you can adjust the query to &lt;code&gt;ALL_TABLES&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SELECT owner, table_name FROM all_tables WHERE owner = UPPER('&amp;lt;YOUR_SCHEMA&amp;gt;') ORDER BY table_name FETCH FIRST 5 ROWS ONLY"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the action read‑only while you learn the surface and review the evidence with a DBA partner.&lt;/p&gt;

&lt;p&gt;Optional: Some clients also expose &lt;code&gt;run-sqlcl&lt;/code&gt; for SQLcl console commands (for example, &lt;code&gt;DESC&lt;/code&gt;). At restrict level 4, many SQLcl commands are disabled; simple describe operations may work depending on your build—verify in your environment. If enabled in your version and posture, a representative call looks like:&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-sqlcl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DESC USER_TABLES"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat this as optional; the minimal surface for early tests is &lt;code&gt;run-sql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;7) Disconnect when you are done&lt;/p&gt;

&lt;p&gt;Disconnecting is good hygiene and makes your test’s boundaries clear.&lt;/p&gt;

&lt;p&gt;Representative MCP tool call; adapt to your client’s envelope and argument names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"disconnect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prove what happened
&lt;/h2&gt;

&lt;p&gt;Evidence checks depend on privileges. If your account cannot query performance views, ask your DBA for a scoped alternative that exposes &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; for your sessions.&lt;/p&gt;

&lt;p&gt;See the tag in a live session view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'sqlcl-mcp'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, SQLcl MCP records requests in &lt;code&gt;DBTOOLS$MCP_LOG&lt;/code&gt;. You can review recent entries there as well. Presence is standard per the docs, but owner/schema and access vary by setup; in some environments the table resides under a DBA‑owned schema. Treat the query below as an example and ask your DBA for the owner or a scoped view if needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mcp_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_point_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_point_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;log_message&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;DBTOOLS&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;MCP_LOG&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt;  &lt;span class="k"&gt;FIRST&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;ONLY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If neither view is available to you directly, a DBA can run the checks or provide a narrow view limited to your test user.&lt;/p&gt;

&lt;h2&gt;
  
  
  When something goes wrong
&lt;/h2&gt;

&lt;p&gt;If the client prompts for a password, the saved connection likely lacks a stored secret. Recreate it with both &lt;code&gt;-save&lt;/code&gt; and &lt;code&gt;-savepwd&lt;/code&gt; so MCP can connect non‑interactively. When the tool catalog looks unfamiliar or is missing entries, you may be on a different SQLcl version; check the MCP tools page for your installed build (the 25.4 and 26.1 docs capture current behavior). If you don’t see your session tag in &lt;code&gt;V$SESSION&lt;/code&gt;, it’s probably a privilege boundary rather than a failure to tag; ask for a scoped view or a one‑time verification. If the server shows more capability than your risk posture allows, confirm the restrict level and restart without any &lt;code&gt;-R&lt;/code&gt; override so the default restrictive posture applies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this posture earns DBA trust
&lt;/h2&gt;

&lt;p&gt;DBAs reasonably resist assistants that can write to production. SQLcl MCP avoids that leap of faith. You connect through named entries you control. You default to a restrictive mode. You tag your sessions with &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; so activity shows up cleanly in performance tooling. And, where available, MCP logs give an after‑the‑fact record. The point is not speed; it’s reversibility and evidence. In a world where models sometimes propose creative fixes, that’s the currency that gets you permission to try again tomorrow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbuc74iqyrd98sluda8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbuc74iqyrd98sluda8d.png" alt="Local SQLcl MCP Evidence Trail" width="719" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s intentionally out of scope here
&lt;/h2&gt;

&lt;p&gt;This article focuses on local SQLcl MCP on a developer workstation, read‑only sandboxes, and minimal evidence checks. It does not cover managed or remote MCP hosting, enterprise audit/policy, advanced reasoning patterns (for example, Select AI or AI Profiles), vector‑native RAG, or change‑management workflows. Those topics are addressed next when we move beyond a single machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves you next
&lt;/h2&gt;

&lt;p&gt;You now have a local, developer‑friendly path for safe, traceable Oracle actions. Previously in this series: Article 1 (routing) and Article 2 (route‑to‑action bridge). Next up: Article 4, where we carry these guardrails into a managed path with audit, policy, and team controls so you can scale without losing the evidence and restraint you established here.&lt;/p&gt;

&lt;p&gt;Freshness Note: Version‑sensitive behavior (tool catalog, defaults, restrict levels) should be verified against your installed SQLcl documentation (notably the 25.4 and 26.1 editions).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>mcp</category>
      <category>security</category>
    </item>
    <item>
      <title>Route, Don’t Flood: Using db/SKILL.md to Steer Oracle‑Aware Assistants</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Fri, 15 May 2026 12:21:32 +0000</pubDate>
      <link>https://dev.to/oracledevs/route-dont-flood-using-dbskillmd-to-steer-oracle-aware-assistants-4el0</link>
      <guid>https://dev.to/oracledevs/route-dont-flood-using-dbskillmd-to-steer-oracle-aware-assistants-4el0</guid>
      <description>&lt;p&gt;If you’ve tried to make an assistant “Oracle‑aware,” you’ve likely hit the same wall: you paste a stack of links, the model blends vendor‑neutral habits with stale Oracle guidance, and the answers get vague when you need precision. The fix isn’t more context—it’s better routing. One Oracle skill at a time, in the right order, so every next move follows Oracle’s own sequence.&lt;/p&gt;

&lt;p&gt;In Article 1 we outlined an operating model for AI on Oracle Database: route → act → trust. This piece goes deep on the first verb. It shows how to use &lt;code&gt;db/SKILL.md&lt;/code&gt; in the public Oracle Database Skills repository as your front door; how to route by persona or by task; and how to enforce progressive discovery so your assistant loads exactly one file at a time. This is a no‑execution article; we stay in the routing lane and leave tools to Article 3 (SQLcl MCP). Prerequisite: you only need to browse &lt;a href="https://github.com/oracle/skills" rel="noopener noreferrer"&gt;https://github.com/oracle/skills&lt;/a&gt; in a web browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why routing beats context dumping
&lt;/h2&gt;

&lt;p&gt;Dumping a pile of links into a prompt feels thorough, but it blurs version lines and lets generic “SQL” patterns overrule Oracle‑specific guidance. It also bloats token budgets without improving the next decision. &lt;code&gt;db/SKILL.md&lt;/code&gt; fixes this by providing a maintained map of Oracle Database skills, pointing you to the right starting file for common jobs, and encoding short, opinionated sequences for multi‑step workflows. The goal is not to read everything; it’s to pick the next file, digest it, and decide whether one more file is warranted. When you’re actually ready to execute a command, you’ve left routing and entered “act” (Article 3 covers SQLcl MCP).&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat db/SKILL.md as the front door
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;db/SKILL.md&lt;/code&gt; is the router for the Database domain. It opens with a routing table and guidance to keep context tight—begin at the table, then read only the specific file or category you need.&lt;/p&gt;

&lt;p&gt;It also shows the shape of the Database skills, including these directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;db/agent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/features&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/frameworks&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/performance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/security&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/devops&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/migrations&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db/sqlcl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two parts matter most on your first pass. First, the file highlights concrete entry points for real jobs—where performance triage begins, where SQLcl basics and MCP server entries sit (setup is deferred to Article 3), how to approach schema migrations, and which agent behaviors (like schema discovery) must be understood before you write anything. Second, it outlines common sequences that act as guardrails. Examples include RAG (skills under &lt;code&gt;db/features&lt;/code&gt;: ai‑profiles → vector‑search → dbms‑vector), slow‑query diagnosis (skills under &lt;code&gt;db/performance&lt;/code&gt;: explain‑plan → wait‑events → optimizer‑stats → awr‑reports), and agent‑safe schema change (skills under &lt;code&gt;db/agent&lt;/code&gt; and &lt;code&gt;db/migrations&lt;/code&gt;: schema‑discovery → destructive‑op‑guards → idempotency‑patterns → schema‑migrations). There’s also an MCP setup path you’ll use later when you move from routing to action: sqlcl‑basics → a least‑privilege/privilege‑management skill in &lt;code&gt;db/security&lt;/code&gt; → sqlcl‑mcp‑server.&lt;/p&gt;

&lt;p&gt;Starting here changes how your assistant behaves. Instead of grabbing whatever looks related on the internet, it asks, “What’s the next Oracle‑authored file?” The result is fewer wrong turns and shorter, more reviewable prompts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route by persona when you’re scoping a project
&lt;/h2&gt;

&lt;p&gt;When you’re defining scope rather than responding to a ticket, match your role and load one file. Don’t skim the whole repository first—route, read, and decide.&lt;/p&gt;

&lt;h3&gt;
  
  
  App developer
&lt;/h3&gt;

&lt;p&gt;If you’re wiring a service to Oracle, start with frameworks and then layer on application‑specific skills. The &lt;code&gt;db/frameworks&lt;/code&gt; directory anchors guidance for stacks such as Spring, Django, and SQLAlchemy: connection configuration, driver/dialect selection, data type mapping, and recommended connection patterns. Once the framework is set, dip into app‑dev skills for JSON, Spatial, Text, or pooling nuances you actually need. Doing it in this order avoids needless trial and error—you adopt the Oracle‑tested path before you customize.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI engineer
&lt;/h3&gt;

&lt;p&gt;Begin in &lt;code&gt;db/agent&lt;/code&gt; and &lt;code&gt;db/features&lt;/code&gt;. The agent skills cover behaviors that matter when a model touches a database: discover schema before DML, guard against destructive operations, make steps idempotent so retries don’t double‑apply, and identify clients for traceability. The features skills introduce Oracle’s AI‑native building blocks—Select AI and AI Profiles for governed NL2SQL, and AI Vector Search/&lt;code&gt;DBMS_VECTOR&lt;/code&gt; for retrieval and RAG. Version gate: check each feature’s documentation. &lt;code&gt;DBMS_VECTOR&lt;/code&gt; and many Select AI/AI Profiles examples are documented for 26ai; several AI capabilities are also available in Autonomous Database. Do not assume 19c availability unless a skill or doc explicitly states it.&lt;/p&gt;

&lt;h3&gt;
  
  
  DBA
&lt;/h3&gt;

&lt;p&gt;The fastest way to get Oracle‑native outcomes from an assistant is to route through &lt;code&gt;db/performance&lt;/code&gt; and then &lt;code&gt;db/security&lt;/code&gt;. Performance skills teach the assistant to obtain and read the plan that actually executed (for example, via &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')&lt;/code&gt;), interpret row sources and cardinality, and pivot at the right moment from plan reading to wait events and then, if warranted, to AWR. Security skills ground privilege design, auditing, and encryption in Oracle’s vocabulary so “least privilege” becomes an actionable design rather than a slogan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migration lead
&lt;/h3&gt;

&lt;p&gt;Treat &lt;code&gt;db/migrations&lt;/code&gt; and &lt;code&gt;db/devops&lt;/code&gt; as two halves of one job. Migration skills help with assessment and translation; devops skills handle delivery mechanics such as schema change workflows, online operations, Edition‑Based Redefinition, and testing. Loading one file at a time keeps heterogeneous estates from collapsing into generic, lowest‑common‑denominator advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route by task when you’re on the clock
&lt;/h2&gt;

&lt;p&gt;When a ticket arrives with a single job, follow the ordered sequences in the repository. Oracle’s diagnostics and defaults are opinionated for a reason; ignoring their order usually costs time.&lt;/p&gt;

&lt;h3&gt;
  
  
  RAG on Oracle Database
&lt;/h3&gt;

&lt;p&gt;Start with AI Profiles, because that’s where you choose a provider and model and, crucially, define which database objects the model may see. Only after scope and governance are set should you learn retrieval patterns in vector‑search and then orchestrate pipelines with &lt;code&gt;DBMS_VECTOR&lt;/code&gt;. The payoff is a governed RAG flow that can be inspected (&lt;code&gt;SHOW SQL&lt;/code&gt;) and tightened (&lt;code&gt;OBJECT_LIST&lt;/code&gt; and data access controls), rather than a bag of embeddings taped onto a database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route (skills under &lt;code&gt;db/features&lt;/code&gt;): ai‑profiles → vector‑search → dbms‑vector&lt;/li&gt;
&lt;li&gt;Reference: Select AI examples (&lt;code&gt;SHOW SQL&lt;/code&gt;, &lt;code&gt;OBJECT_LIST&lt;/code&gt;, and data access controls): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Version note: Follow the feature docs. &lt;code&gt;DBMS_VECTOR&lt;/code&gt; and many Select AI/AI Profiles examples are documented for 26ai; several AI capabilities are also available in Autonomous Database. Do not assume 19c availability unless a skill or doc explicitly states it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Slow‑query diagnosis
&lt;/h3&gt;

&lt;p&gt;Begin by reading the plan that actually ran. In Oracle that means capturing the executed plan from the cursor (for example, &lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')&lt;/code&gt;), not relying solely on an EXPLAIN PLAN estimate. If the plan looks sane but runtime stalls, pivot to wait events. If rows and joins are misestimated, fix statistics and cardinality before widening the window with AWR.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route (skills under &lt;code&gt;db/performance&lt;/code&gt;): explain‑plan → wait‑events → optimizer‑stats → awr‑reports&lt;/li&gt;
&lt;li&gt;Availability note: AWR/ASH access depends on environment and licensing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agent‑safe schema change
&lt;/h3&gt;

&lt;p&gt;Discover what exists before you propose change, add loss‑prevention so a single bad predicate can’t harm a table, and make each step idempotent so a retry won’t double‑apply. Only then move into an audited migration workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route (skills under &lt;code&gt;db/agent&lt;/code&gt; and &lt;code&gt;db/migrations&lt;/code&gt;): schema‑discovery → destructive‑op‑guards → idempotency‑patterns → schema‑migrations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A compact prompt that enforces progressive discovery
&lt;/h2&gt;

&lt;p&gt;A small prompt can prevent freelancing by enforcing a loop: route, read one file, summarize, decide. That discipline keeps the context precise and affordable.&lt;/p&gt;

&lt;p&gt;Prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open db/SKILL.md. I’m [persona or task].
Which single skill file should I load next, and why?
Summarize that file in 5 bullets, then propose the next decision (read another file or stop).
Do not execute any actions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Progressive routing loop&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmfmmwuxt6yaw7o2aawr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmfmmwuxt6yaw7o2aawr.png" alt="Progressive disclosure loop" width="800" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini demos (no DB access, no execution)
&lt;/h2&gt;

&lt;p&gt;These prompts validate routing behavior before you connect any tools. They’re deliberately constrained to “read and decide.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo A: Persona‑first route for a Spring app developer
&lt;/h3&gt;

&lt;p&gt;Prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open db/SKILL.md. I’m wiring a Spring Boot service to Oracle.
Which single frameworks/appdev skill should I load next, and why?
Summarize that file in 5 bullets, then propose the next decision.
Do not execute any actions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example assistant response&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Next file: A Spring Data JPA with Oracle skill under db/frameworks/.
Why: It covers Oracle-specific connection configuration, driver/dialect selection, pooling, and type mapping.

Summary:
- Recommended Oracle JDBC URL formats and service naming for Spring Boot
- Dialect/driver selection and common DataSource pitfalls
- Transaction, batching, and fetch-size options tuned for Oracle
- LOB, DATE/TIMESTAMP, and NUMBER mapping considerations
- Starter checks and links to follow-up appdev skills

Next decision:
- If you need pooling/retry/circuit specifics, read a connection/pooling skill next.
- Otherwise, stop and apply these configuration steps before proceeding.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Demo B: Task‑first route for diagnosing a slow query
&lt;/h3&gt;

&lt;p&gt;Prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting from db/SKILL.md, list the ordered skill files for slow-query triage.
Load only the first file, summarize it in 5 bullets, then recommend whether to read the next file or stop.
Do not execute any actions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example assistant response&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ordered files:
1) The explain-plan skill in db/performance/
2) The wait-events skill in db/performance/
3) The optimizer-stats skill in db/performance/
4) The awr-reports skill in db/performance/

Loaded: explain-plan

Summary:
- Obtain the executed plan from the cursor (vs. EXPLAIN PLAN estimates) and where to find it
- Read row sources and cardinality; spot misestimates and join-order issues
- Recognize anti-patterns (forced full scans, skewed histograms, implicit conversions)
- Interpret plan notes for adaptive features and parallel decisions
- Decide when to pivot from plan analysis to wait-event inspection

Recommendation:
- Read wait-events next if the plan looks reasonable but runtime stalls.
- If the plan is clearly wrong, stop and fix plan issues before proceeding.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The “explain‑plan” skill in this route teaches capturing and reading the actual cursor plan, not just the EXPLAIN PLAN estimate.&lt;/li&gt;
&lt;li&gt;AWR/ASH may not be available in all environments and may require licensing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and context‑budget discipline
&lt;/h2&gt;

&lt;p&gt;Assume Oracle Database 19c as the baseline unless a skill states a higher requirement. For AI features, check per‑feature documentation: &lt;code&gt;DBMS_VECTOR&lt;/code&gt; and many Select AI/AI Profiles examples are documented for 26ai; several AI capabilities are also available in Autonomous Database. Do not assume 19c availability unless a skill or doc explicitly states it. Label those steps clearly when you route.&lt;/p&gt;

&lt;p&gt;Two habits keep assistants accurate and affordable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load one file or short index at a time, then summarize in five bullets.&lt;/li&gt;
&lt;li&gt;Decide whether to stop, read one more, or hand off to action (SQLcl MCP in Article 3). Don’t blur routing with execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why “route, don’t flood” holds up under pressure
&lt;/h2&gt;

&lt;p&gt;Teams tend to bulk‑load context when deadlines loom. Ironically, progressive discovery pays off most when time is tight. &lt;code&gt;db/SKILL.md&lt;/code&gt; compresses Oracle experience into short routes you can trust: read the executed plan before hunting waits, scope an AI Profile before experimenting with retrieval, and prove discovery and guards before you accept any schema change. Staying inside those lanes doesn’t slow you down; it eliminates the detours that burn days.&lt;/p&gt;

&lt;p&gt;Make the front door easy to reach. The simplest path is to browse the repository in a web browser and open &lt;code&gt;db/SKILL.md&lt;/code&gt;. If you prefer a local copy, install the Database domain directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add oracle/skills/db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This puts the domain files in your working directory so you can open &lt;code&gt;db/SKILL.md&lt;/code&gt; immediately. Then set your assistant’s “Oracle Database” system prompt to begin with “Open db/SKILL.md,” and keep the progressive‑discovery loop in place until the next decision is truly to act.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Oracle Database Skills is not a PDF dump for models. It’s an operating method that starts with routing. Use &lt;code&gt;db/SKILL.md&lt;/code&gt; as your first touch, load one file at a time, and stop before you wander into execution. In Article 3, we’ll introduce SQLcl MCP as the bounded action surface that pairs naturally with this approach. Until then: route → read one → summarize → decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oracle Database Skills repository root: &lt;a href="https://github.com/oracle/skills" rel="noopener noreferrer"&gt;https://github.com/oracle/skills&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Database router (&lt;code&gt;db/SKILL.md&lt;/code&gt;): &lt;a href="https://github.com/oracle/skills/blob/main/db/SKILL.md" rel="noopener noreferrer"&gt;https://github.com/oracle/skills/blob/main/db/SKILL.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Frameworks directory: &lt;a href="https://github.com/oracle/skills/tree/main/db/frameworks" rel="noopener noreferrer"&gt;https://github.com/oracle/skills/tree/main/db/frameworks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Performance directory: &lt;a href="https://github.com/oracle/skills/tree/main/db/performance" rel="noopener noreferrer"&gt;https://github.com/oracle/skills/tree/main/db/performance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select AI examples (&lt;code&gt;SHOW SQL&lt;/code&gt;, &lt;code&gt;OBJECT_LIST&lt;/code&gt;, and data access controls): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/selai/examples-using-select-ai.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DBMS_XPLAN.DISPLAY_CURSOR&lt;/code&gt; (19c): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_XPLAN.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_XPLAN.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Licensing Information User Manual (AWR/ASH availability/licensing): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/index.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DBMS_VECTOR&lt;/code&gt; (26ai): &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_VECTOR.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/DBMS_VECTOR.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>database</category>
      <category>llm</category>
    </item>
    <item>
      <title>AI agents know SQL. Oracle Database Skills teach them Oracle</title>
      <dc:creator>Mark Nelson</dc:creator>
      <pubDate>Wed, 13 May 2026 14:02:12 +0000</pubDate>
      <link>https://dev.to/oracledevs/ai-agents-know-sql-oracle-database-skills-teach-them-oracle-n5m</link>
      <guid>https://dev.to/oracledevs/ai-agents-know-sql-oracle-database-skills-teach-them-oracle-n5m</guid>
      <description>&lt;p&gt;This is article 1 of 8 in my Oracle Database Skills series.&lt;/p&gt;

&lt;p&gt;Most assistants can write SQL. That’s not the same as knowing Oracle Database. The difference shows up in production. A model that learned generic patterns can assume another vendor’s isolation semantics, borrow date arithmetic that silently miscounts in Oracle, rely on hints that “fix” one plan but fight the optimizer elsewhere, or forget Edition‑Based Redefinition (EBR) during a migration. Give that assistant a live connection and those differences turn into risk.&lt;/p&gt;

&lt;p&gt;Oracle Database Skills change the operating model. Instead of hoping a generalist remembers every Oracle rule, you route it—one decision at a time—to Oracle‑authored, source‑backed instructions. When action is appropriate, you expose only small, named tools via Oracle MCP servers (MCP = Model Context Protocol), never a blank connection string. And you keep Oracle’s governance in the path so identity, scope, and evidence ride with every request. In short: route first, act through bounded tools, and let the database prove what happened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz7qk0eibiasgs9d28cq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz7qk0eibiasgs9d28cq.png" alt=" " width="800" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why generic SQL knowledge fails on Oracle Database
&lt;/h2&gt;

&lt;p&gt;Large models generalize across vendors; your database does not. Oracle’s transaction semantics, time idioms, plan management, and governance are exactly where generic advice goes sideways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Isolation and consistency are different in Oracle. Many public answers treat isolation levels as dials that change write visibility. Oracle’s multi‑version read consistency gives readers a clean snapshot but doesn’t use the same language or behavior as those engines. Ask a generalist assistant to “make this serializable” and you can get a non‑Oracle explanation—or an irrelevant fix. See Oracle Database Concepts on consistent reads for details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time math bites hard. DATE/TIMESTAMP/INTERVAL semantics and NLS/time zone interactions vary by vendor; Oracle requires vendor‑specific idioms. Skills provide tested patterns for Oracle SQL and PL/SQL so an assistant doesn’t have to guess which expression respects your NLS settings, time zones, and interval choices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Plan stability demands a different order of operations. Oracle recommends maintaining accurate optimizer statistics and using SQL Plan Management (baselines) for stability; avoid over‑reliance on ad‑hoc hints in application code. Skills teach that progression so the assistant recommends fixes that survive the next gather‑stats job.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Governance cannot be an afterthought. “Connect and run” examples usually ignore Virtual Private Dat&lt;br&gt;
abase (VPD), Unified Auditing, Fine‑Grained Auditing, Data Redaction, Transparent Data Encryption (TDE), and network ACLs/private endpoints. In Oracle, those are how you make AI access presentable to a DBA, a security reviewer, or an auditor. If you want to ship, keep those controls in the path.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Route first with Oracle Database Skills (progressive discovery)
&lt;/h2&gt;

&lt;p&gt;The oracle/skills repository is not a doc dump. It’s installable operating instructions, loaded sparingly—one file at a time—so assistants (and humans) can make the next decision correctly without carrying 200 pages of context. The Database domain (oracle/skills/db) is organized the way you work: agent safety; features like Select AI, AI Profiles, and Vector Search; app frameworks; performance; DevOps and migrations; SQLcl; and security. The front door is a routing file: db/SKILL.md.&lt;/p&gt;

&lt;p&gt;Progressive discovery narrows the model’s attention to what moves the work forward, preserves version truth, and leaves a clean review trail. Because the assistant reads only the next skill, it doesn’t mix versions or unrelated features. Skills mark Oracle Database 19c as the baseline and flag when you need Autonomous Database (Serverless) or Oracle Database 23ai/26ai features. Teams can see which Oracle sources the assistant consulted, in what order, and why.&lt;/p&gt;

&lt;p&gt;A concrete path makes this real. Suppose you’re building retrieval‑augmented generation on Oracle. The route in db/SKILL.md steers you through Select AI profiles (to set provider and scope), then Vector Search basics (to choose the right data model and index), then DBMS_VECTOR (to make retrieval programmable). You don’t need AWR or EBR on day one. The route keeps you focused until you do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act through bounded tools: Oracle’s MCP surfaces
&lt;/h2&gt;

&lt;p&gt;Sometimes the assistant must do more than explain. It needs to run a read‑only query, inspect a schema, or check a plan. That’s where Oracle’s MCP servers help: they expose small, named capabilities rather than hand the assistant a general‑purpose connection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Local development: SQLcl MCP server. Built into SQLcl, it exposes a concise set of named tools—for example, to list saved connections, connect using a saved connection, and run SQL—and supports configurable “restrict levels,” with higher numbers enforcing tighter limits. As of SQLcl 25.4, the MCP server defaults to restrict level 4 (most restrictive). Confirm your version and configuration. Teams can set the most restrictive mode for local experiments. SQLcl MCP can also leave evidence when configured: it can log activity (for example, to a DBTOOLS$MCP_LOG table), set V$SESSION.MODULE and V$SESSION.ACTION to identifiable values (such as the MCP client and a client‑provided model/agent label), and tag LLM‑generated statements with a comment. See the SQLcl MCP docs linked below for version‑specific details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managed, governed execution: Autonomous Database (Serverless) MCP server. Enable it on a specific Autonomous Database (via an OCI free‑form tag) and you get a per‑database HTTP endpoint and authentication that serve built‑in tools or custom tools you define with DBMS_CLOUD_AI_AGENT.CREATE_TOOL. Identity and roles apply as usual. So do network ACLs and private endpoints, VPD policies, and Unified Auditing and Fine‑Grained Auditing when configured. If your team wants a controlled tool surface without hosting anything, this is the right shape: the database remains the control plane for what tools exist and who can use them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The shared property is restraint. In both cases, the assistant gets the minimum set of named actions appropriate for the task, and Oracle‑native controls keep doing their job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Oracle governance in the path (trust and evidence)
&lt;/h2&gt;

&lt;p&gt;Oracle’s governance features aren’t an appendix; they are the safety net that lets you ship. Roles and least privilege define what an assistant can reach. Network ACLs and private endpoints restrict egress. VPD enforces row‑level policies you already rely on. Unified Auditing and Fine‑Grained Auditing can be configured to record the activity you care about. Data Redaction and TDE protect sensitive data at query time and at rest. These controls are available and can be configured; they are not enabled by default in every environment.&lt;/p&gt;

&lt;p&gt;The benefit for AI work is simple once you see it: your proof is in the database. When actions go through SQLcl MCP or the Autonomous Database (Serverless) MCP server, the same identity, scoping, and auditing options that protect your applications also protect your assistant. The question becomes less “Can we trust the agent?” and more “Show me the database evidence.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini demo: install skills and open the routing door
&lt;/h2&gt;

&lt;p&gt;Begin without touching a database or setting up MCP. Fetch the Database Skills domain and open the routing file. From there, pick the path that matches your role and objective.&lt;/p&gt;

&lt;p&gt;You only need internet access, Node.js installed, and a text editor. This step does not connect to a database, handle credentials or secrets, or change any MCP restrict levels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx skills add oracle/skills/db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs the Database domain into your working directory. Open db/SKILL.md in your editor. If you’re an app developer wiring a Spring app, follow the frameworks route. If you’re an AI engineer, start with the agent and features routes (Select AI, AI Profiles, Vector) before you let any assistant write SQL. If you’re a DBA, begin with performance and security so the assistant speaks AWR/ASH/optimizer and respects governance. Keep the habit: load only the next file you need, then stop. That discipline keeps model context clean and your review surface small.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to find this in Oracle docs and repos
&lt;/h2&gt;

&lt;p&gt;The instruction layer is the skills repo itself, with db/SKILL.md acting as the router. The controlled action layer is documented for both entry points: SQLcl MCP (restrict levels, tool catalog examples, saved connections, monitoring, activity logging and tagging) and the Autonomous Database (Serverless) MCP server (enablement via OCI free‑form tag, per‑database endpoint and authentication, custom tool publication, and governance integration). The trust layer is the familiar security stack: roles and privileges; network ACLs and private endpoints; VPD; Unified Auditing and Fine‑Grained Auditing when configured; Data Redaction; encryption. Each is a first‑class Oracle feature you can point to in docs and verify in your environment.&lt;/p&gt;

&lt;p&gt;Source links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skills repo and routing (start at db/SKILL.md)&lt;/li&gt;
&lt;li&gt;SQLcl MCP (using the server; logging/tagging; restrict levels)&lt;/li&gt;
&lt;li&gt;Autonomous Database (Serverless) MCP server (enable and use)&lt;/li&gt;
&lt;li&gt;Oracle Database Concepts — Consistent Reads/MVRC&lt;/li&gt;
&lt;li&gt;Oracle SQL Language Reference — Data Types (DATE/TIMESTAMP/INTERVAL and NLS)&lt;/li&gt;
&lt;li&gt;Optimizer statistics and SQL Plan Management&lt;/li&gt;
&lt;li&gt;Oracle Database Security Guide (19c baseline)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version scope and environment notes
&lt;/h2&gt;

&lt;p&gt;Most skills target Oracle Database 19c as the baseline. Files that require features from Autonomous Database (Serverless) or newer Oracle Database 23ai/26ai releases—such as Select AI, AI Profiles, Vector Search, or the Autonomous Database (Serverless) MCP server—are labeled in the repo. If you work in a mixed‑version estate, treat those labels as gates and route accordingly. For hands‑on tries in this opener, you need only a local machine with git and a text editor. MCP client steps and database connectivity come later, when action is appropriate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s out of scope (in this opener)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Enabling or configuring the Autonomous Database (Serverless) MCP server&lt;/li&gt;
&lt;li&gt;Connecting to any database or handling credentials&lt;/li&gt;
&lt;li&gt;Changing SQLcl MCP restrict levels&lt;/li&gt;
&lt;li&gt;Configuring Unified Auditing or Fine‑Grained Auditing&lt;/li&gt;
&lt;li&gt;Demonstrating Select AI or Vector Search live&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A short story from the field
&lt;/h2&gt;

&lt;p&gt;A team asked an assistant to “optimize a daily sales query.” The first answer added an index and a couple of hints that looked fine in isolation. The skills route would have sent it somewhere else: read the explain-plan and optimizer-stats files first; check cardinalities and stale stats; consider a plan baseline if volatility is the real problem. That path took longer but avoided chasing a hint that didn’t survive the next gather‑stats job. The difference wasn’t model IQ—it was having Oracle‑specific operating instructions and the habit of loading only what came next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this series goes from here
&lt;/h2&gt;

&lt;p&gt;Everything you need to begin is in your editor: the skills repo, the routing file, and the mental model—route, act, trust. We’ll build outward from that front door, showing how local and managed MCP surfaces work when action is warranted, and how Select AI and Vector Search improve reasoning close to your data. Expect short, source‑backed posts that follow the same grammar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route: pick a path in db/SKILL.md for your role and task.&lt;/li&gt;
&lt;li&gt;Act: use SQLcl MCP locally or the Autonomous Database (Serverless) MCP server when you need controlled actions.&lt;/li&gt;
&lt;li&gt;Guardrail: keep roles, ACLs/private endpoints, VPD, and auditing in‑path for evidence and scope.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The eight posts are organized into four groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Foundation
1 — this post; 2 — Route, Don’t Flood
Mental model and &lt;code&gt;db/SKILL.md&lt;/code&gt; as routing layer&lt;/li&gt;
&lt;li&gt;Action
3 — SQLcl MCP; 4 — Managed MCP in Autonomous AI Database
Local and remote bounded action surfaces&lt;/li&gt;
&lt;li&gt;Intelligence
5 — Select AI and AI Profiles; 6 — Vector‑Native RAG
NL2SQL reasoning quality and in‑database retrieval&lt;/li&gt;
&lt;li&gt;Rollout
7 — Agent‑Safe Change Delivery; 8 — The Trust Layer
Safe change workflows and enterprise governance&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: a safer default for Oracle‑backed AI
&lt;/h2&gt;

&lt;p&gt;If your assistant can touch Oracle Database, “just let it connect” isn’t an acceptable plan. Put the skills repo between the model and the problem so it learns to work the Oracle way. When execution is appropriate, send it through SQLcl MCP or the Autonomous Database (Serverless) MCP server so actions are small, named, and governed. Keep Oracle’s controls—roles, ACLs, VPD, auditing, redaction, encryption—in the path so every action is scoped and traceable.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>database</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
