LATEST TOPICS

ORA-22992, ORA-64202: When you treat CLOB as string

One of my client contacted me to solve a database problem. The problem they were facing was that they were trying to insert a record in the source database (CDB1_PDB_1) by fetching (SELECT) records from a remote database (CDB2_PDB_1) over a database link (REMOTE_LINK) and the INSERT statement was failing with following errors.

---//
---// insert statement failed with following error //---
---//
ERROR at line 2:
ORA-64202: remote temporary or abstract LOB locator is encountered

---// 
---// select statement failed with following errors //---
---//
ERROR:
ORA-22992: cannot use LOB locators selected from remote tables

Here is what the error description states about these particular errors

---//
---// error descriptions //---
---//
64202, 00000, "remote temporary or abstract LOB locator is encountered"
//  *Cause:  Local database encountered a temporary or abstract LOB created on a
//           remote database. Remote access to temporary or abstract LOB is not
//           supported.
//  *Action: Avoid remote queries that return a temporary or abstract LOB.

22992, 00000, "cannot use LOB locators selected from remote tables"
// *Cause:  A remote LOB column cannot be referenced.
// *Action:  Remove references to LOBs in remote tables.

As per the error description, it is clear that the client is trying to select LOB data over the database link, which seems to be not supported. To provide you a better understanding, following was the statement (similar) that the client was executing.

---//
---// INSERT statement failing with LOB errors //---
---//
SQL> insert into event_message_copy (message_id, message)
  2  select message_id,message from event_message_v@remote_link;
select message_id,message from event_message_vw@remote_link
                  *
ERROR at line 2:
ORA-64202: remote temporary or abstract LOB locator is encountered

The message field in the select statement is of CLOB data type as found below.

---//
---// remote table description //---
---//
SQL> desc EVENT_MESSAGE_V
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 MESSAGE_ID                          NOT NULL NUMBER
 MESSAGE                                      CLOB

At the first look, it seems that the client should not try executing this statement as fetching of LOB data over database link is not supported. However, this is not completely true. Few years back, Thomas Kyte had demonstrated methods which can be used to fetch LOB data over database link. As per the demonstration, Thomas Kyte showed two methods that can be used to fetch LOB data over database link.

In the first method, he stated that we can create a view on the remote database that selects from the table having LOB data and then we can select from the created view over the database link.

In the second method, he showed how to use a temporary table to fetch LOB data over a database link.

You can refer to this link to find out more details about his methods.

Having a firm understanding about how the LOB should be fetched over the database link, I decided to check whether my client is using any of this stated method. I quickly logged into the remote database (CDB2_PDB_1) to check if the table referenced in the select statement is a VIEW or TABLE.

---//
---// checking if the remote object is view or table //---
---//
SQL> select owner,object_name,object_type,status from dba_objects where object_name='EVENT_MESSAGE_V';

OWNER      OBJECT_NAME          OBJECT_TYPE             STATUS
---------- -------------------- ----------------------- -------
MYAPP      EVENT_MESSAGE_V      VIEW                    VALID

As we can see, the client is certainly fetching LOB through a VIEW as stated by Thomas Kyte in one of the method. However, the statement was still failing with LOB fetch errors.

At this point I was sure that client is using a valid method to fetch the LOB over database link. Let’s take a quick look at the View definition to check if we find something.

---//
---// view definition from remote database //---
---//
SQL> select dbms_metadata.get_ddl('VIEW','EVENT_MESSAGE_V','MYAPP') ddl from dual;

DDL
------------------------------------------------------------------------------------------------
CREATE OR REPLACE FORCE EDITIONABLE VIEW "MYAPP"."EVENT_MESSAGE_V" ("MESSAGE_ID", "MESSAGE") AS
select message_id,
substr(message, 1,25) message
from event_message

---//
---// table referenced by the view //---
---//
SQL> desc EVENT_MESSAGE
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 MESSAGE_ID                          NOT NULL NUMBER
 MESSAGE                                      CLOB

As we can see from the View definition, the view is not fetching the entire CLOB data, rather it is fetching a portion of the data using the SUBSTR function. Since we have already validated that the method of fetching LOB over database link is correct, I was suspecting whether this function (SUBSTR) has anything to do with the errors that we have encountered during the LOB fetch.

I decided to quickly perform a simple test to check if the SUBSTR function is the culprit. Since we are dealing with LOB, we have a dedicated substring function DBMS_LOB.SUBSTR to operate on the LOB data. I created two different VIEWS on the remote database (CDB2_PDB_1), one using the SUBSTR function and the other using the DBMS_LOB.SUBSTR function as shown below.

---//
---// logged in to remote database (CDB2_PDB_1) //---
---//
SQL> show con_name

CON_NAME
------------------------------
CDB2_PDB_1

---//
---// view using SUBSTR function //---
---//
SQL> create view test_substr
  2  as
  3  select substr(message, 1,25) message
  4  from event_message;

View created.

---//
---// view using DBMS_LOB.SUBSTR function //---
---//
SQL> create view test_lobsubstr
  2  as
  3  select dbms_lob.substr(message, 25,1) message
  4  from event_message;

View created.

---//
---// validate queries against the views //---
---//
SQL> select message from test_substr;

MESSAGE
-------------------------
This is a sample message.

SQL> select message from test_lobsubstr;

MESSAGE
-------------------------
This is a sample message.

Now, let’s check what happens when we query from these views over the database link. Let’s login to the source database (CDB1_PDB_1) and query these view over database link.

---//
---// logged into source database (CDB1_PDB_1) //---
---//
SQL> show con_name

CON_NAME
------------------------------
CDB1_PDB_1

---//
---// check if views are accessible //---
---//
SQL> desc test_substr@remote_link
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 MESSAGE                                      CLOB

SQL> desc test_lobsubstr@remote_link
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 MESSAGE                                      VARCHAR2(4000)

---// 
---// fetch from view with SUBSTR function //---
---//
SQL> select message from test_substr@remote_link;
ERROR:
ORA-22992: cannot use LOB locators selected from remote tables



no rows selected

---//
---// fetch from view with DBMS_LOB.SUBSTR function //---
---//
SQL> select message from test_lobsubstr@remote_link;

MESSAGE
-------------------------
This is a sample message.

Here the culprit is. It is the SUBSTR function that was used to fetch portion of the CLOB data. Even though Oracle supports using SUBSTR function with CLOB data in local database context, it is not supported when called over the database link. We must use the DBMS_LOB.SUBSTR function which is specifically designed to operate on LOB data.

Let’s modify the original view (EVENT_MESSAGE_V) in remote database (CDB2_PDB_1) which was referenced in the INSERT statement.

---//
---// modify view to replace SUBSTR with DBMS_LOB.SUBSTR //---
---//
SQL> show con_name

CON_NAME
------------------------------
CDB2_PDB_1

SQL> create or replace view event_message_v
  2  as
  3  select message_id,
  4  dbms_lob.substr(message,25,1) message
  5  from event_message;

View created.

Now, let’s re-try the INSERT statement from source database.

---//
---// Re-try INSERT statement from source database //---
---//
SQL> show con_name

CON_NAME
------------------------------
CDB1_PDB_1

SQL> insert into event_message_copy (message_id, message)
  2  select message_id,message from event_message_v@remote_link;

1 row created.

SQL> commit;

Commit complete.


SQL> select * from  event_message_copy;

MESSAGE_ID MESSAGE
---------- ------------------------------
         1 This is a sample message.

As we can see, the INSERT statement is now executed successfully as it was able fetch LOB data over the database link (Thanks to the DBMS_LOB.SUBSTR function for coming into rescue).

Conclusion

We should not treat CLOB as normal string. Although we have the luxury to use some of the normal string functions with CLOB data, it is always the best practice to use dedicated DBMS_LOB functions to operate on LOB data. CLOB may appear as a sequence of strings. However, they are certainly not strings.

Reference

https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:950029833940

One Response
  1. venkat
%d bloggers like this:
Visit Us On LinkedinVisit Us On TwitterVisit Us On Google PlusCheck Our Feed