Oracle SQL function - wrong data type in function call

Viewed 96

I have a function inside my package that is meant to split up a comma-separated varchar2 input into rows, ie. 'one, two, three' into:

  1. one
  2. two
  3. three

I have declared the function as:

function unpack_list(
  string_in in varchar2
) return result_str 
is
  result_rows result_str;
begin
  
  with temp_table as (
  SELECT distinct trim(regexp_substr(string_in, '[^,]+', 1, level)) str
  FROM (SELECT string_in FROM dual) t
  CONNECT BY instr(string_in, ',', 1, level - 1) > 0)
  
  select str bulk collect into result_rows from temp_table;
  
  RETURN result_rows;
  
end;

and the return type as:

type result_str is table of varchar2(100);

However, calling the function like:

select * from unpack_list('one1, two2')

gives the following error:

ORA-00902: Invalid datatype

any ideas what causes this?

2 Answers

You are calling a PL/SQL function that returns a PL/SQL collection type (both defined in your package) from a SQL context. You can't do that directly. You can call the function from a PL/SQL context, assigning the result to a variable of the same type, but that isn't how you're trying to use it. db<>fiddle showing your set-up, your error, and it working in a PL/SQL block.

You could declare the type at schema level instead, as @Littlefoot showed:

create type result_str is table of varchar2(100);

and remove the package definition, which would clash; that works for both SQL and PL/SQL (db<>fiddle).

Or if you can't create a schema-level type, you could use a built-in one:

function unpack_list(
  string_in in varchar2
) return sys.odcivarchar2list 
is
  result_rows sys.odcivarchar2list;
begin
  
  with temp_table as (
  SELECT distinct trim(regexp_substr(string_in, '[^,]+', 1, level)) str
  FROM (SELECT string_in FROM dual) t
  CONNECT BY instr(string_in, ',', 1, level - 1) > 0)
  
  select str bulk collect into result_rows from temp_table;
  
  RETURN result_rows;
  
end;

which also works for both SQL and PL/SQL (db<>fiddle).

Or you could use a pipelined function, with your PL/SQL collection type:

function unpack_list(
  string_in in varchar2
) return result_str pipelined
is
begin
  
  for r in (
    SELECT distinct trim(regexp_substr(string_in, '[^,]+', 1, level)) str
    FROM (SELECT string_in FROM dual) t
    CONNECT BY instr(string_in, ',', 1, level - 1) > 0)
  loop
    pipe row (r.str);
  end loop;

  RETURN;

end;

which works in SQL, or in SQL running within PL/SQL, but not with direct assignment to a collection variable (db<>fiddle).

Which approach you take depends on how you need to call the function really. there may be some performance differences, but you might not notice unless they are called repeatedly and intensively.

The reason of the error was described earlier, so I will post another possible solution. For Oracle 19c (version 19.7) and above you may skip creation of table type and use SQL_MACRO addition. Returned query will be integrated into the main query.

create function unpack_list (
  string_in varchar2
)
return clob
sql_macro(table)
is
begin
  
  return q'[
    select distinct
      trim(regexp_substr(
        unpack_list.string_in,
        '[^,]+', 1, level
      )) as str
    from dual
    connect by
      instr(
        unpack_list.string_in,
        ',', 1, level - 1
      ) > 0
  ]';
  
end;
/
select *
from unpack_list(
  string_in => 'one,two'
)
| STR |
| :-- |
| one |
| two |

db<>fiddle here

Related