Skip to Main Content
  • Questions
  • ORA-06502 with CHAR parameter. What am I missing?

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Mario.

Asked: September 27, 2017 - 5:21 pm UTC

Last updated: September 28, 2017 - 10:37 am UTC

Version: 11.2.0.4.0

Viewed 1000+ times

You Asked

Sorry to bother you with a ORA-06502 error.

But I'm not understanding this behavior.

As I saw, the length fits (see the dbms_output in result showing that the length is 16).
The only thing i can think is that in the procedure proc, pl/sql is changing (in the proc parameter) test_col.col%type with char(4000) or something like this.
If i use in pcol (at the select into) a function like trim, substr, rpad. It works.

Can anyone help me understanding this behavior?

How to reproduce:

create table test_ (  
    col char(16) default rpad('0', 16, '0')  
)  
/


declare  
    procedure proc(pcol in test_.col%type) is  
        r test_%rowtype;  
    begin  
        dbms_output.put_line('pcol length: ' || length(pcol));  
        select pcol  
          into r  
          from dual;  
    end;  
begin  
    proc('XXXXXXXXXXXXXXXX');  
end;  


pcol length: 16  
1 1 ORA-06502: PL/SQL: erro: character string buffer too small numérico ou de valor  
ORA-06512: em line 6  
ORA-06512: em line 11


Notes about the code:
- It´s a simplified version of my real code to reproduce the error. The "dual" actually is a join of tables in the real code;
I know that:
- r.col := pcol works, but using the select is simpler in my real code.
- works with varchar2, but the ERP (that is not mine) uses char.

My workaround solution was to assign pcol to a local variable of the same type then use the local variable.
But i would like to understand the problem.

My workaround (again, a simplified version):

declare  
    procedure proc(pcol in test_.col%type) is  
        r test_%rowtype;
        lcol test_.col%type;  
    begin  
        dbms_output.put_line('pcol length: ' || length(pcol));  
        lcol := pcol;
        select lcol -- plus many others columns in the real code 
          into r  
          from dual;  
    end;  
begin  
    proc('XXXXXXXXXXXXXXXX');  
end;  


with LiveSQL Test Case:

and Chris said...

When you pass parameters to PL/SQL programs, they don't inherit the size of the type. In this example, the parameter uses a varchar2(4). But you can pass and use strings much longer than this:

declare
  subtype tp is varchar2(4);
  procedure proc ( p tp ) is
  begin
    dbms_output.put_line(p);
  end;
begin
  proc('tooooo looooong');
end;
/

tooooo looooong


http://docs.oracle.com/database/122/LNPLS/plsql-subprograms.htm#GUID-BA13F32D-4E04-4004-8CE5-5B628678A253

So when you pass the string to your procedure, the database doesn't really know it's a char(16) anymore.

So why the error?

Parsing is expensive. So the database takes steps to reduce the amount you might do. The parameter could be any length. If it bound it to a variable matching the exact length this could lead to a lot of extra parsing. So it assigns it a default size. In this case char(32).

You can see this in the following:

create or replace procedure p ( pcol in test_.col%type ) as
  r test_%rowtype; 
begin
  select /*+ len */pcol
  into   r
  from   dual;  
  
  dbms_output.put_line(r.col);
end p;
/

exec p('1234567890123456'); 

ORA-06502: PL/SQL: numeric or value error: character string buffer too small

select sql_id, sql_text from v$sql
where  sql_text like '%/*+ len */%'
and    sql_text not like '%not this%'
and    sql_text not like 'declare%';

SQL_ID         SQL_TEXT                        
btm7nury991gm  SELECT /*+ len */:B1 FROM DUAL 

select name, datatype_string, max_length
from   v$sql_bind_capture
where  sql_id = 'btm7nury991gm';

NAME  DATATYPE_STRING  MAX_LENGTH  
:B1   CHAR(32)         32  


And of course, a char 32 is padded up to 32 characters. So doesn't fit in the local variable.

The easiest workaround is to assign the values in PL/SQL and cut out the SQL step. If you insist on sticking with SQL, you can get around this by casting the variables to the correct length:

create or replace procedure p ( pcol in test_.col%type ) as
  r test_%rowtype; 
begin
  select /*+ len */cast(pcol as char(16))
  into   r
  from   dual;  
  
  dbms_output.put_line(r.col);
end p;
/

exec p('1234567890123456');

1234567890123456


Though this will silently trim the input if it's longer:

exec p('12345678901234567');

1234567890123456


Is this answer out of date? If it is, please let us know via a Comment

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library