sqlxmloracle-databaseoracle18cxmltable

Find problem XML values by catching XMLTABLE() errors in custom function


I'm using an Oracle 18c view called GDB_ITEMS_VW that has XML data in a clob column.

I can extract data from the XML column using the following query (source):

select      
    x.code,
    x.description,
    i.name as domain_name
from        
    sde.gdb_items_vw i
cross apply xmltable(
    '/GPCodedValueDomain2/CodedValues/CodedValue' 
    passing xmltype(i.definition)
    columns
        code        varchar2(255) path './Code',
        description varchar2(255) path './Name'
    ) x  

DOMAIN_NAME          CODE                 DESCRIPTION                             
-------------------- -------------------- ----------------------------------------
ATN_MATERIAL         A                    ASPHALT                                 
ATN_MATERIAL         O                    ASPHALT CAPPING                         
ATN_MATERIAL         B                    BRICK      

When I run that query in SQL Developer, it runs without errors, but that's just because it's only selecting the first 50 rows.

If I try to run the query on all rows (via CTRL+END), then it throws an error:

ORA-31011: XML parsing failed
ORA-19202: Error occurred in XML processing
LPX-00007: unexpected end-of-file encountered
ORA-06512: at "SYS.XMLTYPE", line 272
ORA-06512: at line 1
31011. 00000 -  "XML parsing failed"
*Cause:    XML parser returned an error while trying to parse the document.
*Action:   Check if the document to be parsed is valid.

So I want determine what specific rows are causing that error.

As mentioned by @MT0 in a related post, we can find the problem rows by:

Creating a function to wrap the call that is causing issues and catch the exception in the function.


I've attempted to adapt @MT0's function:

with function test_xmltable(v_xml clob) return number
is
  temp xmltype;
begin
  temp :=   xmltable(
            '/GPCodedValueDomain2/CodedValues/CodedValue' 
            passing xmltype(v_xml)
            columns
                code        varchar2(255) path './Code',
                description varchar2(255) path './Name'
            );
  return 1;
exception
  when others then
    return 0;
end;

select      
    i.name as domain_name,
    test_xmltable(i.definition)
from        
    sde.gdb_items_vw i
where  
    test_xmltable(i.definition) = 0;    

But I must be doing something wrong, because I'm getting an error:

ORA-00905: missing keyword
00905. 00000 -  "missing keyword"
*Cause:    
*Action:
Error at Line: 1 Column: 15

How can I find the problem XML values?


Solution

  • If your XML will return a single row, you can use:

    with function test_xmltable(v_xml clob) return number
    is
      v_code VARCHAR2(255);
      v_desc VARCHAR2(255);
    begin
      SELECT code, description
      INTO   v_code, v_desc
      FROM   xmltable(
               '/GPCodedValueDomain2/CodedValues/CodedValue' 
               passing xmltype(v_xml)
               columns
                 code        varchar2(255) path './Code',
                 description varchar2(255) path './Name'
             );
      return 1;
    exception
      when others then
        return 0;
    end;
    
    select i.name as domain_name,
           test_xmltable(i.definition)
    from   sde.gdb_items_vw i
    where  test_xmltable(i.definition) = 0; 
    

    If it will return multiple rows, you can use:

    with function test_xmltable(v_xml clob) return number
    is
      v_code SYS.ODCIVARCHAR2LIST;
      v_desc SYS.ODCIVARCHAR2LIST;
    begin
      SELECT code, description
      BULK COLLECT INTO v_code, v_desc
      FROM   xmltable(
               '/GPCodedValueDomain2/CodedValues/CodedValue' 
               passing xmltype(v_xml)
               columns
                 code        varchar2(255) path './Code',
                 description varchar2(255) path './Name'
             );
      return 1;
    exception
      when others then
        return 0;
    end;
    
    select i.name as domain_name,
           test_xmltable(i.definition)
    from   sde.gdb_items_vw i
    where  test_xmltable(i.definition) = 0; 
    

    db<>fiddle here