0

I got the below sql base64 decoder from stackoverflow (forgot the link). I have slightly modified to my requirement. When I try to execute, the decoding is only partially working. I have max of 10MB worth of base64 encoded clob data per row in my DB.

Not sure what is that I am missing.

create or replace FUNCTION Decode64_CLOB3(IN_CLOB CLOB) RETURN CLOB IS
 clobOriginal     clob;
clobInBase64     clob;
substring        varchar2(2000);
n                pls_integer := 0;
substring_length pls_integer := 2000;

function from_base64(t in varchar2) return varchar2 is
 begin
return     
utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(t)));
 end from_base64;

 begin
    select myCLobData into clobInBase64 from myClobTable where ID = 3;
clobInBase64 :=IN_CLOB;
   n := 0;
  clobOriginal := null;

while true loop 

 substring := dbms_lob.substr(clobInBase64,
                          least(substring_length, length(clobInBase64) -   (substring_length * n + 1)));  

  if substring is null then
DBMS_OUTPUT.put_line('this is me 2');
  exit;
end if;  
clobOriginal := clobOriginal || from_base64(substring);  
n := n + 1;  
  end loop;
 return clobOriginal;
 end;

update2

I did some debugging and found out that the issue is with the substring. The substring works fine for the firstloop of (2000) char. but its not able to move the the next 2000 char. Not sure what is the problem. Can some one advise.

AKV
  • 183
  • 4
  • 24
  • I'm guessing you started [from this answer](http://stackoverflow.com/a/3806265/266304)? What does 'partially working' mean - what happens? And what is the decoded value - should it be decoding to a CLOB or a BLOB? – Alex Poole Jun 21 '16 at 18:20
  • @AlexPoole Yes, ur right. it prints only one line of decoded value and SQL developer goes into hung state. I tried spooling the result in a file, again it prints only 1 line. Not sure what is that I am missing. I am decoding it to a CLOB. – AKV Jun 21 '16 at 22:44

1 Answers1

0

You've changed the call to dbms_lob.substr() and in doing so you've removed the third argument, which is the offset. Since you aren't providing that it defaults to 1, so every time around the loop you're getting the same value - the first 2000 characters from your encoded string.

Well, you'll get 2000 for lots of calls, then once n gets big enough you'll get null, with the code you've posted. Since you said it goes into a 'hung' state that suggests what you're running is slightly different.

With some slight modifications this seemed to work:

create or replace function decode64_clob3(p_clob_encoded clob)
  return clob
is
  l_clob_decoded clob;
  substring varchar2(2048);
  substring_length pls_integer := 2048;
  n pls_integer := 0;

  function from_base64(t in varchar2) return varchar2
  is
  begin
    return utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(t)));
  end from_base64;

begin
  dbms_lob.createtemporary(l_clob_decoded, false);
  while true
  loop
    substring := dbms_lob.substr(p_clob_encoded, substring_length,
      (substring_length * n) + 1);
    if substring is null then
      exit;
    end if;
    dbms_lob.append(l_clob_decoded, from_base64(substring));
    n := n + 1;
    /* protective check for infinite loop while debugging
    if n > 10000 then
      dbms_output.put_line('n is ' || n);
      exit;
    end if;
    */
  end loop;

  return l_clob_decoded;
end;
/

I changed the CLOB handling slightly; concatenation is fine but this is a little more explicit. I'm not sure I'd bother with the nested function though, personally. And I'm not sure the least() calculation is really adding anything.

But this can error with either ORA-06502: PL/SQL: numeric or value error: invalid LOB locator specified: ORA-22275 or ORA-14553: cannot perform a lob write operation inside a query if the base64-encoded value is line-wrapped, which is usually the case. The first chunk is decoded OK, but the next chunk starts with an unconsumed newline which throws everything off and produces garbage; eventually that garbage is null or some other value that causes one of those exceptions.

So you need to track the position as you move through the CLOB, in multiples of the line-wrap length; and adjust the tracked position to take extra newlines into account:

create or replace function decode64_clob3(p_clob_encoded clob)
  return clob
is
  l_clob_decoded clob;
  l_substring varchar2(2048);
  l_substring_length pls_integer := 2048;
  l_pos pls_integer := 1;
begin
  dbms_lob.createtemporary(l_clob_decoded, false);
  while l_pos <= dbms_lob.getlength(p_clob_encoded)
  loop
    l_substring := dbms_lob.substr(p_clob_encoded, l_substring_length, l_pos);
    if l_substring is null then
      exit;
    end if;
    dbms_lob.append(l_clob_decoded,
      utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(l_substring))));

    /* Adjust l_pos to skip over CR/LF chars if base64 is line-wrapped */
    l_pos := l_pos + length(l_substring);
    while dbms_lob.substr(p_clob_encoded, 1, l_pos) in (chr(10), chr(13)) loop
      l_pos := l_pos + 1;
    end loop;
  end loop;

  return l_clob_decoded;
end;
/

Or strip out any potential newline/carriage return characters first:

create or replace function decode64_clob3(p_clob_encoded clob)
  return clob
is
  l_clob_encoded clob;
  l_clob_decoded clob;
  l_substring varchar2(2048);
  l_substring_length pls_integer := 2048;
  l_pos pls_integer := 1;
begin
  l_clob_encoded := replace(replace(p_clob_encoded, chr(10), null), chr(13), null);
  dbms_lob.createtemporary(l_clob_decoded, false);
  while l_pos <= dbms_lob.getlength(l_clob_encoded)
  loop
    l_substring := dbms_lob.substr(l_clob_encoded, l_substring_length, l_pos);
    if l_substring is null then
      exit;
    end if;
    dbms_lob.append(l_clob_decoded,
      utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(l_substring))));
    l_pos := l_pos + length(l_substring);
  end loop;

  return l_clob_decoded;
end;
/

This works with wrapped and unwrapped base64 values.

set serveroutput on
with t (c) as (
  select utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw('Hello world'))) from dual
)
select c, decode64_clob3(c)
from t;

C
--------------------------------------------------------------------------------
DECODE64_CLOB3(C)
--------------------------------------------------------------------------------
SGVsbG8gd29ybGQ=                                                                
Hello world

Also tested with encoded values that are larger than the substring_length value.

Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • Thanks for your update, however executing the function I am getting this errorr **ORA-06502: PL/SQL: numeric or value error: invalid LOB locator specified: ORA-22275 ORA-06512: at "SYS.DBMS_LOB", line 639** – AKV Jun 22 '16 at 10:38
  • @AKV - are you still initialising the CLOB (`l_clob_decoded` in my version) to null, or are you using `dbms_lob.createtemporary`? – Alex Poole Jun 22 '16 at 10:42
  • I am tried with 'dbms_lob.createtemporary' set to **true**. Also tried with setting the clob to null both ways i m getting the above mentioned error. – AKV Jun 22 '16 at 10:47
  • May I know how big your test clob data was? – AKV Jun 22 '16 at 10:55
  • now I am getting **RA-14553: cannot perform a lob write operation inside a query ORA-06512: at "SYS.DBMS_LOB", line 639** this error. My clob data can be max of 10 MB. – AKV Jun 22 '16 at 10:57
  • Complete error `ORA-14553: cannot perform a lob write operation inside a query ORA-06512: at "SYS.DBMS_LOB", line 639 ORA-06512: at "NEHR_C_APP.DECODE64_CLOB_FINAL", line 27 14553. 00000 - "cannot perform a lob write operation inside a query " *Cause: A lob write operation cannot be performed inside a query or a PDML slave. *Action: Ensure that the offending lob write operation is not performed or use an autonomous transaction to perform the operation within the query or PDML slave.` – AKV Jun 22 '16 at 11:00
  • I tested with small chuck of data around 2000 char, its working fine. The problem is when i expose entire clob data which is more than 140K char. The issue is with **dbms_lob.append(l_clob_decoded, from_base64(substring));** this line. Any other alternative? – AKV Jun 22 '16 at 11:20
  • @AKV - I was testing with large values; but I was constructing them without line wrapping. With the default 64-char line wrap produced by most tools I started seeing these errors. The problem hit when a substring ended right before a newline/carriage return; the next chunk started with that which it didn't like. Updated with a different approach. – Alex Poole Jun 22 '16 at 12:51
  • But still I am hitting errors. Now i am getting some generic errors. ORA-00600: internal error code, arguments: [kole_t2u], [34], [], [], [], [], [], [], [], [], [], [] ORA-06512: at "NEHR_C_APP.DECODE64_CLOB_FINALTEST", line 16 00600. 00000 - "internal error code, arguments: [%s], [%s], [%s], [%s], [%s], [%s], [%s], [%s]" *Cause: This is the generic internal error number for Oracle program exceptions. This indicates that a process has encountered an exceptional condition. *Action: Report as a bug - the first argument is the internal error number – AKV Jun 22 '16 at 14:21
  • now getting this error. Sorry for the cluttered codes. I am not sure how to format. – AKV Jun 22 '16 at 14:22
  • I was getting that for a while when I was building up a bigger CLOB source value. Where are your encoded values coming from? – Alex Poole Jun 22 '16 at 14:22
  • The users can upload multiple files from UI.the files will be binary encoded and saved in the DB. I need to rite a plslq to retrieve the encoded value from DB and decode the CLOB data – AKV Jun 22 '16 at 14:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/115320/discussion-between-alex-poole-and-akv). – Alex Poole Jun 22 '16 at 14:34