Search code examples
oracle-databaseoracle12c

"ON CONVERSION ERROR" fails with "ORA-43918: This argument must be a literal"


ON CONVERSION ERROR should allow conversion functions to handle errors such as "ORA-01858: a non-numeric character was found where a numeric was expected". But when I use the feature I only get a different error:

SQL> select to_date(the_date default null on conversion error, 'MM/DD/YYYY') the_date
  2  from
  3  (
  4      select '1/1/2021' the_date from dual union all
  5      select 'bad date' the_date from dual
  6  );
select to_date(the_date default null on conversion error, 'MM/DD/YYYY')
                                                           *
ERROR at line 1:
ORA-43918: This argument must be a literal

The error line and column numbers don't make sense because the code is already using a literal.


Solution

  • CURSOR_SHARING

    The ON CONVERSION ERROR feature does not work when the parameter CURSOR_SHARING is set to FORCE. To avoid this error, change the parameter at the system, session, or statement level.

    Ideally, CURSOR_SHARING should be set to EXACT for the entire system. But if we have an application that doesn't use bind variables, we probably can't run alter system set cursor_sharing=exact;.

    The parameter can be set at the session level with alter session set cursor_sharing=exact;, but it's not always convenient to constantly change session parameters.

    The parameter can be changed at the statement level with the hint CURSOR_SHARING_EXACT:

    SQL> select /*+ cursor_sharing_exact */ to_date(the_date default null on conversion error, 'MM/DD/YYYY') the_date
      2  from
      3  (
      4      select '1/1/2021' the_date from dual union all
      5      select 'bad date' the_date from dual
      6  );
    
    THE_DATE
    ---------
    01-JAN-21
    

    Parser/optimizer bug

    As @gouessej discovered, there is another potential reason for the ORA-43918 error that is not related to cursor sharing. There appear to be parsing or optimizer bugs related to transforming CASE and TO_ functions on some versions of Oracle.

    For example, the below SQL statement fails on Oracle 18c and 19c:

    SQL> select case when v_num is null then 0 else v_num end
      2  from
      3  (
      4      select to_number('120.3' default null on conversion error, '99999D99') as v_num
      5      from dual
      6  );
        select to_number('120.3' default null on conversion error, '99999D99') as v_num
                                                                   *
    ERROR at line 4:
    ORA-43918: This argument must be a literal
    

    I believe this is a parsing or optimizer bug because the error goes away if you stop transformations by adding a predicate like rownum >= 1. (When Oracle sees ROWNUM, it assumes the results must be displayed in a certain order and will not apply as many transformations to that query block.)

    SQL> select case when v_num is null then 0 else v_num end
      2  from
      3  (
      4      select to_number('120.3' default null on conversion error, '99999D99') as v_num
      5      from dual
      6  where rownum >= 1
      7  );
    
    CASEWHENV_NUMISNULLTHEN0ELSEV_NUMEND
    ------------------------------------
                                   120.3