2

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?

3
  • your code works for me. Which version of Oracle you're using? Commented Feb 22, 2022 at 13:42
  • 2
    "function inside my package" - so it appears you've declared the result_str type inside the package, and you're now trying to make the call from plain SQL (though you haven't shown a package prefix on the call) - so like this? Do you need to call from SQL, or is this just a test - in which case you can use a PL/SQL block (also shown in the fiddle)? Commented Feb 22, 2022 at 14:34
  • Ah, yes - that's seems to be the case. What would fix this then? Both the Type and Function exist within the package Commented Feb 22, 2022 at 14:38

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! Decided to go with the sys.odcivarchar2list solution :) Built-in sounds better in terms of performance
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.