- Blogzote.com - https://www.blogzote.com -

Rawtochar en Oracle

En mi actual proyecto se necesitaba leer la información contenida en un campo de tipo BLOB, que no era más que a fin de cuentas una cadena XML pero guardada en un campo de la base de datos.

Conocía poco del tema pero San Google hace milagros y me tope fácilmente con el paquete DBMS_LOB, dentro de él existe la función SUBSTR que al igual que substr aplicado a una cadena normal regresa una porción de ella definida por los dos siguientes parámetros, aunque varia un poco su uso. (No hablare a detalle del uso del paquete en este post).

Pero resulta que al utilizar esta función el resultado no es el esperado:

SQL> SELECT dbms_lob.substr(campo_blob, 100, 1) campo_blob
  2    FROM tabla_con_campo_blob
  3   WHERE identificador = 1;

CAMPO_BLOB
--------------------------------------------------
3C3F786D6C2076657273696F6E3D22312E302220656E636F64

Lo que pasa es que la función dbms_lob.substr regresa una cadena de tipo RAW (tampoco hablare mucho al respecto) otra vez pidiendo ayuda a san google me tope con un paquete para convertir mi cadena raw a una cadena legible tipo varchar2, solo la adapte un poquito a lo que necesitaba, acá se las dejo:

CREATE OR REPLACE PACKAGE rawtochar IS

  FUNCTION hextonum(v_hex varchar2) 
    RETURN number;

  FUNCTION rawtochar(p_raw_data varchar2) 
    RETURN varchar2;

END;
CREATE OR REPLACE PACKAGE BODY rawtochar IS

  FUNCTION hextonum(v_hex VARCHAR2) 
   RETURN NUMBER IS 

    hex          VARCHAR2(4);
    num          NUMBER;
    num1         NUMBER;
    num2         NUMBER;

  BEGIN

    hex := SUBSTRB(v_hex,1,1);

    IF ( hex >= '0' and hex < = '9' ) THEN 
      num1  := TO_NUMBER(hex);
    END IF; 
    IF hex = 'A' THEN num1 := 10; END IF; 
    IF hex = 'B' THEN num1 := 11; END IF; 
    IF hex = 'C' THEN num1 := 12; END IF; 
    IF hex = 'D' THEN num1 := 13; END IF; 
    IF hex = 'E' THEN num1 := 14; END IF; 
    IF hex = 'F' THEN num1 := 15; END IF; 

    hex := SUBSTRB(v_hex,2,1);

    IF ( hex >= '0' and hex < = '9' ) THEN 
      num2  := TO_NUMBER(hex);
    END IF; 
    IF hex = 'A' THEN num2 := 10; END IF; 
    IF hex = 'B' THEN num2 := 11; END IF; 
    IF hex = 'C' THEN num2 := 12; END IF; 
    IF hex = 'D' THEN num2 := 13; END IF; 
    IF hex = 'E' THEN num2 := 14; END IF; 
    IF hex = 'F' THEN num2 := 15; END IF; 
 
    num := (num1*16)+num2;

    RETURN num;

  END;

  FUNCTION rawtochar(p_raw_data  varchar2) 
   RETURN VARCHAR2 IS

    raw_data          LONG RAW;
    rawlen            NUMBER;
    hex_data          VARCHAR2(32760);
    char_data         VARCHAR2(32760);
    loop_counter      NUMBER;

  BEGIN
  
    raw_data := p_raw_data;

    -- Convert the raw data to hex.    
    hex_data := rawtohex(raw_data);
    rawlen := length(hex_data);

    loop_counter := 1;

    -- Loop through and convert the hex to characters.
    WHILE loop_counter <= rawlen LOOP
        char_data := char_data || CHR(HEXTONUM(SUBSTRB(hex_data,loop_counter,2)));
        loop_counter := loop_counter + 2;
    END LOOP;

    RETURN char_data;

  END;

END;

Listo, ahora solo le aplico esta función y ya veo información más acorde:

SQL> SELECT rawtochar.rawtochar(dbms_lob.substr(campo_blob, 100, 1)) campo_xml
  2    FROM tabla_con_campo_blob
  3   WHERE identificador = 1;

CAMPO_XML
--------------------------------------------------------------------------------
< ?xml version="1.0" encoding="UTF-8"?>
< !DOCTYPE workflow PUBLIC "-//...

Para los que acabaron de leer el post y llegaron hasta acá, como moraleja déjenme decirles que no se vallan con la primera solución que encuentren, siempre habrá mejores maneras de hacer las cosas, algo que ya sabia pero que en esta ocasión por las prisas no considere, resulta que después de solucionado el problema me quede con la espinita, realmente no habrá pensado oracle en este problema ya?, la lógica me decía que seguramente si, y que creen? Así era, ya existe la manera de convertir un raw a varchar2 por lo que la función rawtochar no era necesaria:

SQL> SELECT utl_raw.cast_to_varchar2(dbms_lob.substr(campo_blob, 100, 1)) campo_xml
  2    FROM tabla_con_campo_blob
  3   WHERE identificador = 1;

CAMPO_XML
--------------------------------------------------------------------------------------
< ?xml version="1.0" encoding="UTF-8"?>
< !DOCTYPE workflow PUBLIC "-//...