Sunday, June 21, 2026

End-to-End CSV Loading into Oracle Autonomous Database with OCI Object Storage

Problem statement: In many real-world ETL workflows, flat files are generated outside the database and need to be loaded into Autonomous Data Warehouse. This article demonstrates how to upload a CSV file to OCI Object Storage and load it into Oracle Autonomous Database using DBMS_CLOUD.COPY_DATA.Entire process was done using CLI so that we can automate the process.

Environment:

Source: Oracle Linux 8 VM running in OCI

File: Sample csv file ecommerce_orders_dataset.csv downloaded using this link from Kaggle

E-commerce Orders Dataset 2026 | SCRA

Target database: Oracle autonomous database

Pre-requisites:

OCI CLI installed and configured

Required IAM permissions for Object Storage and auth token creation

Autonomous Database available

SQL access to Autonomous Database

CSV file available on the source VM

DBMS_CLOUD package available in ADB

Solution: Following are the high level steps for the problem statement we have defined

·       Use OCI CLI tool to create a bucket under storage service

·       Upload csv into the  bucket we have created in previous step

·       Create an authorization token and save the password

·       Create a credential inside the autonomous database so that link can be established between object storage and database

·       Create table structure matching data type in CSV

·       Use DBMS_CLOUD.COPY_DATA procedure to load the data from csv to the table created in above steps

Now we will focus every steps in detail

1.       Use OCI CLI tool to create a bucket under storage service

Now here I will show you a very easy way to use oci cli tool which will help you a lot later if you want to explore various options without relying on google search everytime.Use below command to run oci cli in interactive mode.Here you can see I have typed oci os(Object storage) and it is giving us next set of command options.Lets select bucket using down arrow key

(Note:if the image is not clear click on the image)

oci -i


Select create


Select compartment-id


I don’t know my compartment-id so I have used same approach to find my compartment-id using below command.We know compartment falls under IAM 


So finally my command becomes

oci os bucket create --compartment-id ocid1.compartment.oc1..XXXXXXXXXXXXXXXXXXXXXXXXXX --name mybucket

{

  "data": {

    "approximate-count": null,

    "approximate-size": null,

    "auto-tiering": null,

    "compartment-id": "ocid1.compartment.oc1..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",

    "created-by": "ocid1.user.oc1..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",

    "defined-tags": {

      "Oracle-Tags": {

        "CreatedBy": "default/debdhritiman@yahoo.com",

        "CreatedOn": "2026-06-20T14:22:40.473Z"

      }

    },

    "etag": "33dbf38d-a582-453a-8162-253aee4f2a0f",

    "freeform-tags": {},

    "id": "ocid1.bucket.oc1.phx.aaaaaaaayetp5fv4l7smfyaq4w6zglglau52aqp52kdy4ig7lcvtsqxhjjwq",

    "is-read-only": false,

    "kms-key-id": null,

    "metadata": {},

    "name": "mybucket",

    "namespace": "axbybsdhxsix",

    "object-events-enabled": false,

    "object-lifecycle-policy-etag": null,

    "public-access-type": "NoPublicAccess",

    "replication-enabled": false,

    "storage-tier": "Standard",

    "time-created": "2026-06-20T14:22:40.483000+00:00",

    "versioning": "Disabled"

  },

  "etag": "33dbf38d-a582-453a-8162-253aee4f2a0f"

}

 

 

1.       Upload csv file into the bucket

Using same approach I have created the command

oci os object put -bn mybucket --file /home/debdhritim/ecommerce_orders_dataset.csv

Uploading object  [####################################]  100%

{

  "etag": "a99c103c-50d6-4ab3-ac2b-c224aa4b86fd",

  "last-modified": "Sat, 20 Jun 2026 14:31:58 GMT",

  "opc-content-md5": "x7DNfgxWSEqWWNd9c814AA=="

}

 

We can confirm using below command

oci os object list -bn mybucket -ns axbybsdhxsix                                                                                                                                                                                     

{

  "data": [

    {

      "archival-state": null,

      "etag": "a99c103c-50d6-4ab3-ac2b-c224aa4b86fd",

      "md5": "x7DNfgxWSEqWWNd9c814AA==",

      "name": "ecommerce_orders_dataset.csv",

      "size": 7730710,

      "storage-tier": "Standard",

      "time-created": "2026-06-20T14:31:58.105000+00:00",

      "time-modified": "2026-06-20T14:31:58.105000+00:00"

    }

  ],

  "prefixes": []

}

 

2.       Create an authorization token and save the password

We need ocid of the user using which we can create auth token. Hence use below query to get ocid of the user

oci iam user list | jq '.data[] | select(."name" =="debdhritiman@yahoo.com")'

{

  "capabilities": {

    "can-use-api-keys": true,

    "can-use-auth-tokens": true,

    "can-use-console-password": true,

    "can-use-customer-secret-keys": true,

    "can-use-db-credentials": true,

    "can-use-o-auth2-client-credentials": true,

    "can-use-smtp-credentials": true

  },

  "compartment-id": "ocid1.tenancy.XXXXXXXXXXXXXXXX ",

  "db-user-name": null,

  "defined-tags": {},

  "description": null,

  "email": "debdhritiman@yahoo.com",

  "email-verified": true,

  "external-identifier": "7f322816bfc24b2094ac507bef60e069",

  "freeform-tags": {},

  "id": "ocid1.user.XXXXXXXXXXXXXXXXXXX ",

  "identity-provider-id": null,

  "inactive-status": null,

  "is-mfa-activated": true,

  "last-successful-login-time": "2026-06-20T13:53:05.450000+00:00",

  "lifecycle-state": "ACTIVE",

  "name": "debdhritiman@yahoo.com",

  "previous-successful-login-time": null,

  "time-created": "2026-06-09T16:15:23.478000+00:00"

}

 

oci iam auth-token create --description "Auth token for database to connect object storage" --user-id ocid1.user.oc1..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                                                   

{

  "data": {

    "description": "Auth token for database to connect object storage",

    "id": "ocid1.credential.oc1..aaaaaaaaokz6lhkcqrrtwwg374k2tkq54s7ktiog7532zmhpqctoebfdt7da",

    "inactive-status": null,

    "lifecycle-state": "ACTIVE",

    "time-created": "2026-06-20T15:02:55.703000+00:00",

    "time-expires": null,

    "token": "Gzk}EGED-x]x3TKCHkvD", --This is the token we should save as this is not stored anywhere

    "user-id": "ocid1.user.oc1.. XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "

  },

  "etag": "68d2ed611c7c43bd86bf4d93ee360874"

}

 

3.       Create a credential inside the autonomous database so that link can be established between object storage and database

Connect to your ADB and use below PL/SQL to create the credential

--In case you want to drop and recreate

BEGIN

DBMS_CLOUD.DROP_CREDENTIAL( credential_name => 'OBJECT_STORE_CRED' );

END;

/

BEGIN

  DBMS_CLOUD.CREATE_CREDENTIAL(

    credential_name => 'OBJECT_STORE_CRED',

    username => 'debdhritiman@yahoo.com',

    password => 'Gzk}EGED-x]x3TKCHkvD'

  );

END;

/

Validate in data dictionary

SQL> SELECT    credential_name, username, enabled, comments FROM user_credentials WHERE credential_name = 'OBJECT_STORE_CRED';

 

CREDENTIAL_NAME      USERNAME                  ENABLED    COMMENTS                                                  

____________________ _________________________ __________ __________________________________________________________

OBJECT_STORE_CRED    debdhritiman@yahoo.com    TRUE       {"comments":"Created via DBMS_CLOUD.create_credential"}   

Validate connectivity using below query

SELECT object_name, bytes FROM DBMS_CLOUD.LIST_OBJECTS( credential_name => 'OBJECT_STORE_CRED', location_uri => 'https://objectstorage.us-phoenix-oraclecloud.com/n/axbybsdhxsix/b/mybucket/');

 

 

4.       Create table structure matching data type in CSV

CREATE TABLE ecommerce_orders (

    order_id     number PRIMARY KEY,

     customer_id        VARCHAR(20),

    order_date         DATE,

    year               number,

    month              number,

    day                number,

    weekday            VARCHAR(20),

    quarter                 number,

    age                number,

    gender             VARCHAR(10),

    country            VARCHAR(50),

    city               VARCHAR(50),

    customer_type      VARCHAR(20),

    product_id         VARCHAR(20),

    category           VARCHAR(50),

    subcategory        VARCHAR(50),

    brand              VARCHAR(50),

    price              number(10,2),

    quantity           number,

    discount           number(5,2),

    discount_amount    number(10,2),

    coupon_used        BOOLEAN,

    shipping_cost      number(10,2),

    tax                number(10,2),

    total_amount       number(12,2),

    payment_method     VARCHAR(30),

    device_type             VARCHAR(20),

    channel            VARCHAR(30),

    member_status      VARCHAR(10),

    delivery_type      VARCHAR(20),

    warehouse_region             VARCHAR(20),

    delivery_days      number,

    order_status             VARCHAR(20),

    is_returned        BOOLEAN,

    rating             number(3,1),

    Customer_Value     number(8,2),

    Profit_Margin      number(5,2),

    Profit_Amount      number(8,2),

    season             VARCHAR(20),

    holiday_season     BOOLEAN,

    hv_order            BOOLEAN

);

 

5.       Use DBMS_CLOUD.COPY_DATA procedure to load the data from csv to the table created in above steps

Executed below command

begin

 DBMS_CLOUD.COPY_DATA(

    table_name =>'ECOMMERCE_ORDERS',

    credential_name =>'OBJECT_STORE_CRED',

    file_uri_list =>'https://objectstorage.us-phoenix-1.oraclecloud.com/p/FC204TdtecHLSypF4cbrxOWa_dhQIqI86skHvIwVAHbLHnhv3alBMEMK4rr1cWU5/n/axbybsdhxsix/b/mybucket/o/ecommerce_orders_dataset.csv',

    format => json_object('type' value 'csv',  'skipheaders' value '1','dateformat' value 'yyyy-mm-dd')

 );

end;

/

It failed with some date format error

Use below query to find the log  details

SQL> select start_time,update_time,status,table_name,logfile_table,badfile_table  from user_Load_operations;

 

START_TIME                             UPDATE_TIME                            STATUS    TABLE_NAME          LOGFILE_TABLE    BADFILE_TABLE   

______________________________________ ______________________________________ _________ ___________________ ________________ ________________

20-JUN-26 04.17.45.610773000 PM GMT    20-JUN-26 04.17.45.965481000 PM GMT    FAILED    ECOMMERCE_ORDERS    COPY$15_LOG       COPY$15_BAD 

 

SQL> select * from COPY$15_LOG;

 RECORD

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  LOG file opened at 06/21/26 15:02:42

 Total Number of Files=1

 Data File: https://objectstorage.us-phoenix-1.oraclecloud.com/p/FC204TdtecHLSypF4cbrxOWa_dhQIqI86skHvIwVAHbLHnhv3alBMEMK4rr1cWU5/n/axbybsdhxsix/b/mybucket/o/ecommerce_orders_dataset.csv

 Log File: COPY$15_3030025.log

  LOG file opened at 06/21/26 15:02:42

 Bad File: COPY$15_1483975.bad

 Field Definitions for table COPY$183PPH0U8LQXAKX45WOG

  Record format DELIMITED BY NEWLINE

  Data in file has same endianness as the platform

  Rows with all null fields are accepted

 

  Fields in Data Source:

 

    ORDER_ID                        CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    CUSTOMER_ID                     CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    ORDER_DATE                      CHAR (255)

      Date datatype DATE, date mask yyyy-mm-dd

      Terminated by ","

      Enclosed by """ and """

    YEAR                            CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    MONTH                           CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    DAY                             CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    WEEKDAY                         CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    QUARTER                         CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    AGE                             CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    GENDER                          CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    COUNTRY                         CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    CITY                            CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    CUSTOMER_TYPE                   CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    PRODUCT_ID                      CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    CATEGORY                        CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    SUBCATEGORY                     CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    BRAND                           CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    PRICE                           CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    QUANTITY                        CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    DISCOUNT                        CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    DISCOUNT_AMOUNT                 CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    COUPON_USED                     CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    SHIPPING_COST                   CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    TAX                             CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    TOTAL_AMOUNT                    CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    PAYMENT_METHOD                  CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    DEVICE_TYPE                     CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    CHANNEL                         CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    MEMBER_STATUS                   CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    DELIVERY_TYPE                   CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    WAREHOUSE_REGION                CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    DELIVERY_DAYS                   CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    ORDER_STATUS                    CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    IS_RETURNED                     CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    RATING                          CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    CUSTOMER_VALUE                  CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    PROFIT_MARGIN                   CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    PROFIT_AMOUNT                   CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    SEASON                          CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    HOLIDAY_SEASON                  CHAR (255)

      Terminated by ","

      Enclosed by """ and """

    HV_ORDER                        CHAR (255)

      Terminated by ","

      Enclosed by """ and """

error processing column ORDER_DATE in row 2 for datafile https://objectstorage.us-phoenix-1.oraclecloud.com/p/FC204TdtecHLSypF4cbrxOWa_dhQIqI86skHvIwVAHbLHnhv3alBMEMK4rr1cWU5/n/axbybsdhxsix/b/mybucket

/o/ecommerce_orders_dataset.csv

 

ORA-01830: Date format picture ends before converting entire input string.

 

149 rows selected.

The reason for this failure was I used dateformat as 'yyyy-mm-dd' in DBMS_CLOUD.COPY_DATA procedure and my csv has mm/dd/yyyy format.This issue was resolved after I changed the data format as mm/dd/yyyy

format => json_object('type' value 'csv',  'skipheaders' value '1','dateformat' value 'yyyy-mm-dd')

 

Now I can see 30,000 rows have been inserted successfully.

 

Now the manual process is validated, the same steps can be automated using shell scripting, OCI CLI, and scheduled database jobs or external orchestration tools.

 

Hope you have learned something useful.

 

Reference:- So you have your CSV, TSV and JSON data lying in your Oracle Cloud Object Store. How do you get it over into your Autonomous Database? | autonomous-ai-database

 







·      

Thursday, June 4, 2026

How to build proxy pdb in multitenant architecture

 

Problem statement: You have multiple CDBs deployed across different data centers or environments (e.g., PROD, DR, Reporting, Cloud migration setup).
Application teams require unified access to PDBs across these environments, but:

  • You cannot consolidate all databases into a single CDB
  • Network latency and storage constraints prevent full database duplication
  • You want to avoid complex database links embedded in application code
  • You need transparent SQL access as if the database is local

Proxy PDB solves this by:

  • Creating a local PDB representation of a remote database
  • Allowing applications to connect without knowing about remote location
  • Eliminating the need for application-level DB links

Below is a simple architectural diagram

Now I will show some hands on for this concept.

Source container database:cdborcl

Source pluugable database:pdborcl

Target container database:cdbtest

Target pluggable database:pdbtest

 

1. Create a common user in the source database from where we want to create proxy pdb and provide necessary privileges

From the root container execute below SQL

create user c##dblinkuser identified by "welcome123##" default tablespace users temporary tablespace temp;

grant create session,create pluggable database,select_catalog_role to  c##dblinkuser container=ALL;

grant read on sys.enc$ to c##dblinkuser container=ALL;

Please make sure to use container=ALL otherwise it will not be a common role

Check privileges like below

SQL> select GRANTEE,PRIVILEGE,common from dba_sys_privs where GRANTEE='C##DBLINKUSER';

 

GRANTEE       PRIVILEGE                                COM

------------- ---------------------------------------- ---

C##DBLINKUSER CREATE PLUGGABLE DATABASE                YES

C##DBLINKUSER CREATE SESSION                           YES

 

 

2. Create database link in target database where porxy pdb will be created

SQL> create database link cdborcl_link connect to c##dblinkuser identified by "welcome123##" using 'cdborcl';

 

Database link created.

 

SQL> select OWNER,DB_LINK,HOST,VALID from dba_db_links;

 

OWNER                          DB_LINK                        HOST                           VAL

------------------------------ ------------------------------ ------------------------------ ---

SYS                            CDBORCL_LINK.WORLD                XXXX                         YES

 

validate

 

alter system set global_names=false;

 

System altered.

 

SQL> select sysdate from dual@cdborcl_link;

SYSDATE

---------

19-APR-26

 

3. Create proxy pdb

Here pdborcl is the pluggable database under cdborcl

 

CREATE PLUGGABLE DATABASE proxy_pdb AS PROXY FROM pdborcl@ccborcl_link;

alter pluggable database proxy_pdb open;

4. Check existence and status  of proxy pdb

SQL>  SELECT name,open_mode,proxy_pdb from v$pdbs where PROXY_PDB = 'YES';

NAME                           OPEN_MODE  PROXY_PDB

------------------------------ ---------- ------------------------------

PROXY_PDB                      MOUNTED    YES

 

4. Check new service with the same name as proxy pdb which is also registered in listener

 

SQL> select NAME,NETWORK_NAME from  v$services;

 

NAME                           NETWORK_NAME

------------------------------ ------------------------------

SYS$BACKGROUND

SYS$USERS

cdbtest.world                  cdbtest.world

cdbtestXDB                     cdbtestXDB

pdbtest                        pdbtest

proxy_pdb                      proxy_pdb

 

lsnrctl status listener | grep proxy_pdb

Service "proxy_pdb.world" has 1 instance(s).

 

Also in target host a system generated service will be created like below using which proxy pdb fetch data

 

SQL> SELECT CON_ID, TARGET_HOST, TARGET_PORT, TARGET_SERVICE FROM V$PROXY_PDB_TARGETS;

 

    CON_ID TARGET_HOST               TARGET_PORT  TARGET_SERVICE

---------- ------------------------------ ------------------   ------------------------------------------------------------

         4       XXXXXXX                                1521                      4fd1dba80f4d4496e063cb09800ac57c.world

 

5. Validation of objects created in source pdb and proxy_pdb

Execute below code in  source pdb to check whether we can see same table and its row count

SQL>  create table pdbadmin.temp_objects as select * from dba_objects;

 

Table created.

 

SQL> select count(*) from pdbadmin.temp_objects;

 

  COUNT(*)

----------

     73926

 

6. Add TNS entry of proxy pdb and connect using that tnsnames

 

SQL> conn sys/XXX@cdbtest as sysdba

Connected.

SQL> alter session set container=PROXY_PDB;

 

Session altered.

Use below query to check registered proxy pdb

SELECT CON_ID, TARGET_HOST, TARGET_PORT, TARGET_SERVICE

FROM V$PROXY_PDB_TARGETS;

Validate row count of the table for remote pdb like below

select count(*) from pdbadmin.temp_objects;

COUNT(*)

----------

     73926

 Hope you have learnt something useful....

 

 

 

How to install OCI CLI and instant client tool in Linux VM

  Problem statement: In an Oracle Autonomous Data Warehouse environment, we do not have access to the underlying compute nodes. However, ma...