9. November 2015

Blog Posting 0x100 - aus gegebenem Anlass geht es um Hexcodes

Dies ist das Posting Nr. 256 - im Hex wäre das die Nummer 0x100; bei so einer schönen runden Zahl bietet es sich an, ein wenig über das Thema Hexcodes in Oracle zu schreiben, wann und wo man die verwenden kann ... ich denke nicht, dass dies eine vollständige Aufzählung werden wird, aber einige Dinge sind vielleicht für den einen oder anderen interessant ...
Fangen wir mal ganz leicht an. Selektiert man in SQL*Plus einen BLOB oder einen RAW-Datentypen, so werden Hexcodes zurückgegeben.
SQL> desc jsontable
 Name                                      Null?    Typ
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL VARCHAR2(255)
 CREATED_ON                                NOT NULL TIMESTAMP(6)
 LAST_MODIFIED                             NOT NULL TIMESTAMP(6)
 VERSION                                   NOT NULL VARCHAR2(255)
 JSON_DOCUMENT                                      BLOB

SQL> select json_document from jsontable;

JSON_DOCUMENT
--------------------------------------------------------------------------------
7B227265747765657465645F737461747573223A7B22636F6E7472696275746F7273223A6E756C6C
2C2274657874223A2223636F756E74646F776E206E616172202341706578776F726C6420404F4768
Möchte man im SQL*Plus bereits sehen, was sich hinter diesen Hexcodes verbirgt, so kann man diese on-the-fly umwandeln. Hierzu schneidet man mit der Funktion DBMS_LOB.SUBSTR einen Teil des BLOBs aus - man erhält eine Instanz vom Typ RAW. So wie VARCHAR2 zum CLOB steht, so steht RAW zum BLOB: Es ist ein Datentyp für Folgen bis zu 2000 Bytes in SQL - in PL/SQL kann ein RAW bis zu 32767 Bytes aufnehmen. Mit der Funktion UTL_RAW.CAST_TO_VARCHAR2 kann man die Datenbank nun dazu bringen, die RAW-Bytes wie ein VARCHAR2 aufzufassen - SQL*Plus wird versuchen, den Text darzustellen, was in diesem Beispiel auch gut gelingt ...
SQL> select utl_raw.cast_to_varchar2(dbms_lob.substr(json_document,100,1)) from jsontable;

UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(JSON_DOCUMENT,100,1))
--------------------------------------------------------------------------------
{"retweeted_status":{"contributors":null,"text":"#countdown naar #Apexworld @OGh
_nl #iadvise_live @Y
Umgekehrt kann man Oracle mit UTL_RAW.CAST_TO_RAW dazu bringen, einen VARCHAR2-Wert als RAW aufzufassen - SQL*Plus wird uns dann die Hexcodes für die Bytes anzeigen, aus denen der String besteht. Bei ASCII-Zeichen ist das recht eindeutig; bei Umlauten hängt es vom Datenbank-Zeichensatz ab, was wir sehen werden.
SQL> select utl_raw.cast_to_raw('ABCDEF') from dual;

UTL_RAW.CAST_TO_RAW('ABCDEF')
--------------------------------------------------------------------------------
414243444546

SQL> select utl_raw.cast_to_raw('Müller') from dual;

UTL_RAW.CAST_TO_RAW('MÜLLER')
--------------------------------------------------------------------------------
4DC3BC6C6C6572
Wichtig ist, dass diese CAST-Funktionen den Inhalt des Wertes nicht verändern; die Bytes werden lediglich anders aufgefasst. RAW-Values werden im SQL*Plus, wie man sieht, direkt als Hexcode angezeigt. Analog dazu lassen sich mit Hexcodes auch RAW-Values erzeugen - hier braucht es aber die SQL-Funktion HEXTORAW - sie nimmt einen VARCHAR-String mit Hexcodes entgegen und liefert den korrespondierenden RAW-Wert zurück.
SQL> select hextoraw('000000') as RAWVAL, dump(hextoraw('000000')) as DUMP from dual;

RAWVAL DUMP
------ -------------------
000000 Typ=23 Len=3: 0,0,0

1 Zeile wurde ausgewählt.
Hier muss man jetzt aufpassen - das übergebene VARCHAR2 besteht aus 6 Zeichen (sechs Bytes), aus denen mit der HEXTORAW-Funktion aber ein RAW generiert wird - zwei Zeichen stehen dann für ein Byte. Der tatsächliche Wert sind dann drei Nullbytes, die auf der Kommandozeile eigentlich gar nicht dargestellt werden könnten - daher nimmt SQL*Plus zur Darstellung wieder die Hexcodes her (tatsächlich besteht der Wert aber aus drei Nullbytes).
Mit der umgekehrten Funktion RAWTOHEX generiert die Datenbank aus einem RAW-Wert wieder einen VARCHAR2, wobei hierfür wieder Hexcodes für die einzelnen Bytes gebildet werden - aus den drei Nullbytes wird dann wieder dreimal die Zeichenfolge 00. Das ist ein wesentlicher Unterschied zur Funktion UTL_RAW.CAST_TO_VARCHAR2, welche die Bytes so lässt, wie sie sind, und einfach als VARCHAR2 auffasst. Die folgende Abfrage macht den Unterschied deutlich. Beide Ergebnisspalten sind vom Typ VARCHAR2.
select 
  rawtohex(raw_column) hex, 
  '"' || utl_raw.cast_to_varchar2(raw_column) || '"' as rawval 
from table_with_raw_contents;

HEX                  RAWVAL
-------------------- --------------------
000000               "   "
404142               "@AB"
Bevor man RAW-Values als Zeichenkette (VARCHAR2) weiterverarbeitet (bspw. um sie anzuzeigen), muss man also stets überlegen, wie das konkret verfolgen soll. Die Umwandlung in Hexcodes sorgt dafür, dass jede Konsole etwas anzeigen kann; allerdings sind selbst reine Texte dann nicht mehr leicht lesbar. Ein einfacher Cast nach VARCHAR2 fast alle Bytes als Zeichen auf, was bei manchen Zeichen zu unleserlicher Ausgabe führen kann. RAW sind halt eben Bytes; und jede Darstellung als TEXT ist irgendwo nur ein Hilfsmittel ...
Öfter möchte man einfach eine Dezimalzahl in ein Hexzahl umwandeln und umgekehrt. Dazu braucht es keinerlei RAW-Funktionen; hier gibt es eine Formatmaske in den Funktionen TO_CHAR und TO_NUMBER - diese liefert aber keinen RAW zurück; man erhält einen VARCHAR2 - eben mit Hexcodes als Inhalt.
SQL> select to_char(256, 'FM0XXX') HEX from dual;

HEX
----
0100

SQL> select to_number('100', 'XXXX') DEC from dual;

DEC
-------------
          256
Man kann sich aus diesen Informationen nun eine "Anregung" holen - und zwar für die Fälle, in denen man RAW oder BLOB-Objekte per SQL-Skript erzeugen möchte. Das kann bei Installation einer Anwendung interessant sein, wenn in eine der Tabellen BLOBs abgelegt werden sollen - Export/Import aber nicht gewünscht ist. Oracle Application Express löst dieses Problem schon sehr lange - denn jedes APEX Export-File ist ein SQL-Skript, welches mit SQL*Plus eingespielt werden kann. Enthält eine APEX-Anwendung Bilder oder andere statische Dateien, so werden diese wie folgt im SQL-Skript kodiert.
:

begin
  wwv_flow_api.g_varchar2_table := wwv_flow_api.empty_varchar2_table;
  wwv_flow_api.g_varchar2_table(1) := '73657420646566696E65206F66660A73657420766572696679206F66660A736574...';
  wwv_flow_api.g_varchar2_table(2) := '5F666C6F772E675F696D706F72745F696E5F70726F6772657373203A3D20747275...';
  wwv_flow_api.g_varchar2_table(3) := '414120204141202020202020505020205050202045452020202020202058582020...';
  :
  :
  wwv_flow_api.g_varchar2_table(25) := '20202050505050502020204545454520202020202020585858580A2D2D20202041...';
  wwv_flow_api.g_varchar2_table(26) := '796C6573686565743E';
  wwv_flow_api.create_app_static_file(
   p_id              => 1768395483840570815+wwv_flow_api.g_id_offset
  ,p_file_name       => 'some_static_APEX_image.png'
  ,p_mime_type       => 'image/png'
  ,p_file_content    => wwv_flow_api.varchar2_to_blob(wwv_flow_api.g_varchar2_table)
  );
end;

:
Auf diese Weise können binäre Dateien problemlos in ein SQL-Skript gepackt und - ganz ohne Export/Import - problemlos auf andere Datenbanken übertragen werden. Wenn Ihr das mal ohne APEX und für eigene Tabellen versuchen wollt, schaut euch dieses Blog-Posting aus dem Jahr 2008 an; dort findet Ihr eine PL/SQL-Prozedur, die euch einen BLOB in ein SQL-Skript umwandelt. Das ist natürlich nur für eine überschaubare Anzahl BLOBs interessant - eben dann, wenn ein SQL-Skript Tabellen, Views, PL/SQL-Prozeduren anlegen und die Tabellen gleichzeitig mit Seed-Daten inkl. BLOBs füllen soll.
Zum Abschluß des Blog-Postings habe ich noch ein kleines Geschenk was für euch: zum Blog-Posting 0x100 gibt es einen Hex Viewer für BLOBs in SQL*Plus - spielt einfach diese PL/SQL-Funktion hier ein ...
create or replace function display_blob(
  p_blob    in blob
 ,p_start   in number default 1
 ,p_lines   in number default 16
) return varchar2 authid current_user is
  l_raw   raw(2000);
  l_vc    varchar2(4000);

  l_linesize pls_integer := 16;
  l_finish   boolean     := false;
begin
  if p_lines > 50 then
    raise_application_error(-20000, 'MAXIMUM OF 50 LINES');
  end if;

  l_raw := dbms_lob.substr(p_blob, p_lines * l_linesize, p_start);

  for i in 0 .. p_lines - 1 loop
    l_vc := l_vc || to_char((p_start-1) + i * l_linesize, 'FM0XXXXXXX')||': ';
    for j in 1 .. l_linesize loop
      if i * l_linesize + j <= utl_raw.length(l_raw) then
        l_vc := l_vc || rawtohex(utl_raw.substr(l_raw, (i * l_linesize) + j, 1)) ||' ';
      else 
        l_vc := l_vc || '   ';
        l_finish := true;
      end if;
    end loop;
    l_vc := l_vc || '| ';
    for j in 1 .. l_linesize loop
      if i * l_linesize + j <= utl_raw.length(l_raw) then
        if ascii(utl_raw.cast_to_varchar2(utl_raw.substr(l_raw, (i * l_linesize) + j, 1))) between 32 and 126 then 
          l_vc := l_vc || utl_raw.cast_to_varchar2(utl_raw.substr(l_raw, (i * l_linesize) + j, 1)); 
        else 
          l_vc := l_vc || '.';
        end if;
      end if;
    end loop;
    l_vc := l_vc || chr(10);
    if l_finish then exit;  end if;
  end loop;
  return l_vc;
end display_blob;
/
sho err
Die könnt Ihr nun mit einem BLOB aufrufen - der erste Parameter ist der BLOB, den Ihr euch ansehen wollt, der zweite die Startposition und der dritte die Anzahl "Zeilen"; die Ihr sehen wollt - eine Zeile umfasst dabei 16 Bytes; da intern mit einem VARCHAR2 gearbeitet wird, könnt Ihr nicht mehr als 50 "Zeilen" auf einmal zeigen.
FUNCTION display_blob RETURNS VARCHAR2
 Argument Name                  Typ                     In/Out Defaultwert?
 ------------------------------ ----------------------- ------ --------
 P_BLOB                         BLOB                    IN
 P_START                        NUMBER                  IN     DEFAULT
 P_LINES                        NUMBER                  IN     DEFAULT

SQL> select display_blob(content) from  myblobs;

DISPLAY_BLOB(CONTENT)
--------------------------------------------------------------------------------
00000000: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 | .PNG........IHDR
00000010: 00 00 00 C8 00 00 00 FA 08 02 00 00 00 6B 88 9F | .............k..
00000020: 6F 00 00 00 09 70 48 59 73 00 00 0E C4 00 00 0E | o....pHYs.......
00000030: C4 01 95 2B 0E 1B 00 00 1E F1 49 44 41 54 78 9C | ...+......IDATx.
00000040: ED 9D FB 93 64 47 95 DF BF 27 33 EF A3 1E FD 9A | ....dG...'3.....
00000050: 9E 97 34 92 46 A3 11 A3 D1 20 06 09 21 B1 C0 2F | ..4.F.... ..!../
00000060: A0 C5 B1 AC B1 0D 18 3B D6 26 36 8C 20 C2 86 25 | .......;.&6. ..%
00000070: FC C3 FE 01 76 40 2C B1 E1 DD D8 B5 83 88 F5 0F | ....v@,.........
00000080: E6 A7 B5 31 D8 44 38 58 36 C2 86 DD 8D 58 60 0C | ...1.D8X6....X`.
00000090: 78 59 AD 78 49 02 C4 82 A4 41 48 48 83 34 A3 E9 | xY.xI....AHH.4..
000000A0: E9 47 D5 7D 64 E6 F1 0F 59 75 BB BA BA A7 A7 67 | .G.}d...Yu.....g
000000B0: E6 66 55 DF EE FC 68 66 54 DD 55 95 99 37 F3 7B | .fU...hfT.U..7.{
000000C0: 4F E6 3D 27 1F C4 CC D8 35 B8 C2 10 91 7B 4D 44 | O.='....5....{MD
000000D0: 55 F1 88 88 87 1F B2 CC D6 5A CB CC CC D6 32 83 | U........Z....2.
000000E0: 99 D9 32 1B C3 44 30 D6 82 11 25 31 11 19 63 86 | ..2..D0...%1..c.
000000F0: 6F 59 00 EE 47 00 96 D9 5A 03 06 6F C8 19 00 08 | oY..G...Z..o....
:
Viel Spaß beim Ausprobieren - ich weiss, dass man zu RAW, Binary und Hexcodes noch viel mehr sagen könnte, belasse es für heute aber dabei. Mehr gibt es dann beim Blogposting Nr. 0x1000.
This is my blog posting #256 - and in hex this is 0x100. We are in the IT industry, so I declare this an anniversary. And with such a nice round (hex) number, I take the freedom to post something about working with the Oracle database and hex codes (at the end of the posting you'll find a present). Let's start simple: When selecting a BLOB or RAW value in SQL*Plus, you'll see the contents in hex encoding.
SQL> desc jsontable
 Name                                      Null?    Typ
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL VARCHAR2(255)
 CREATED_ON                                NOT NULL TIMESTAMP(6)
 LAST_MODIFIED                             NOT NULL TIMESTAMP(6)
 VERSION                                   NOT NULL VARCHAR2(255)
 JSON_DOCUMENT                                      BLOB

SQL> select json_document from jsontable;

JSON_DOCUMENT
--------------------------------------------------------------------------------
7B227265747765657465645F737461747573223A7B22636F6E7472696275746F7273223A6E756C6C
2C2274657874223A2223636F756E74646F776E206E616172202341706578776F726C6420404F4768
That is, because SQL*Plus has no other choice. A BLOB or RAW value can contain anything; not necessarily readable text. To avoid confusion or terminal errors due to control characters, SQL*Plus takes the safe way and encodes all in hex. You can force SQL*Plus to display the binary content as text (at your own risk ;-)): to do so, you simply need to let the database treat the BLOB like a VARCHAR2. First, use DBMS_LOB.SUBSTR to clip out a part of the BLOB, which will get you an instance of the RAW datatype. A RAW is related to a BLOB like a VARCHAR2 to a CLOB - it can take up to 2000 bytes in the SQL and up to 32767 bytes in the PL/SQL area. The function UTL_RAW.CAST_TO_VARCHAR2 then converts the RAW to a VARCHAR2 type, but without changing its contents; the RAW bytes are being "treated" as VARCHAR2. Thus SQL*Plus will display as text and in that very instance (where is BLOB contains a JSON document), this works out well ...
SQL> select utl_raw.cast_to_varchar2(dbms_lob.substr(json_document,100,1)) from jsontable;

UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(JSON_DOCUMENT,100,1))
--------------------------------------------------------------------------------
{"retweeted_status":{"contributors":null,"text":"#countdown naar #Apexworld @OGh
_nl #iadvise_live @Y
We can also do the other way around: UTL_RAW.CAST_TO_RAW treats a VARCHAR2 value as a RAW instance; SQL*Plus will then display the bytes as hex codes. For ASCII characters, results are the same in all databases, for umlauts or other non-ASCII characters, results are dependent of the database character set.
SQL> select utl_raw.cast_to_raw('ABCDEF') from dual;

UTL_RAW.CAST_TO_RAW('ABCDEF')
--------------------------------------------------------------------------------
414243444546

SQL> select utl_raw.cast_to_raw('Müller') from dual;

UTL_RAW.CAST_TO_RAW('MÜLLER')
--------------------------------------------------------------------------------
4DC3BC6C6C6572
Note, that these UTL_RAW cast functions do not change the actual bytes; the client only treats them differently. When we have a RAW value, SQL*Plus shows hexcodes, when we have VARCHAR2, SQL*Plus shows text.
When using hexcodes to actually create RAW instances, we don't want to use these CAST functions. Why? Because we want to have one byte in the RAW instance when we type 00. The simple CAST function would give us two. The HEXTORAW SQL function takes hex-encoded bytes as VARCHAR2 - and returns a RAW instance. Now the hexcodes will be interpreted - as the following example shows.
SQL> select hextoraw('000000') as RAWVAL, dump(hextoraw('000000')) as DUMP from dual;

RAWVAL DUMP
------ -------------------
000000 Typ=23 Len=3: 0,0,0
Take care: the VARCHAR2 value we passed to the HEXTORAW function consists of 6 zeroes - which takes 6 bytes. But HEXTORAW returns a RAW value which consists of three (zero) bytes. So the input has been interpreted: two digits make up one byte. When SQL*Plus has to display the three zero bytes, it falls back to hex codes again - therefore I added the DUMP function for more clarity.
RAWTOHEX works the other way around. A RAW value is being converted to a VARCHAR2 where each byte will be "rendered" as a 2-digit hex code. So our three zero bytes become 000000. The important difference between HEXTORAW/RAWTOHEX and the UTL_RAW CAST functions is that the former functions will change the actual bytes, whereas the latter ones don't. The following SQL example illustrates this.
select 
  rawtohex(raw_column) hex, 
  '"' || utl_raw.cast_to_varchar2(raw_column) || '"' as rawval 
from table_with_raw_contents;

HEX                  RAWVAL
-------------------- --------------------
000000               "   "
404142               "@AB"
So when you are about to convert a RAW to a VARCHAR2 (in order to display or further process it), you have the choice: You might use the UTL_RAW cast functions to just treat the RAW values as Text - all bytes with a value less than 32 will lead to funny output on your console (control characters), but all bytes representing ASCII characters will be readably. On the other hand, RAWTOHEX will convert everything to hexcodes - an "A" within the RAW value will become "41" in the output. The "best" approach depends on your requirements.
Sometimes you don't have RAW bytes, but decimal numbers - and these must be converted to hex or vice-versa. We don't need any RAW functions for this - we have hex support in the TO_CHAR and TO_NUMBER functions.
SQL> select to_char(256, 'FM0XXX') HEX from dual;

HEX
----
0100

SQL> select to_number('100', 'XXXX') DEC from dual;

DEC
-------------
          256
All this might lead to an idea: sometimes we want to create RAW or BLOB instances with SQL scripts. We might have a SQL script creating tables, views, PL/SQL objects and finally our script inserts some seed data. In today's times, images might be part of this seed data. One approach is to have two steps: first create all the objects with a script, then run a Data Pump import to loads the BLOBs. But having the above in our mind, we could also do both steps only with SQL scripting.
Oracle Application Express is doing this for years: As the APEX users know, an APEX export is a SQL script. We can install APEX applications by running the export files in SQL*Plus. When such an export file contains static files (images, CSS files or other content), these files will be stored in the target APEX instance as BLOBs - and their binary content is "somehow" encoded in the SQL export file. Let's have a look ...
:

begin
  wwv_flow_api.g_varchar2_table := wwv_flow_api.empty_varchar2_table;
  wwv_flow_api.g_varchar2_table(1) := '73657420646566696E65206F66660A73657420766572696679206F66660A736574...';
  wwv_flow_api.g_varchar2_table(2) := '5F666C6F772E675F696D706F72745F696E5F70726F6772657373203A3D20747275...';
  wwv_flow_api.g_varchar2_table(3) := '414120204141202020202020505020205050202045452020202020202058582020...';
  :
  :
  wwv_flow_api.g_varchar2_table(25) := '20202050505050502020204545454520202020202020585858580A2D2D20202041...';
  wwv_flow_api.g_varchar2_table(26) := '796C6573686565743E';
  wwv_flow_api.create_app_static_file(
   p_id              => 1768395483840570815+wwv_flow_api.g_id_offset
  ,p_file_name       => 'some_static_APEX_image.png'
  ,p_mime_type       => 'image/png'
  ,p_file_content    => wwv_flow_api.varchar2_to_blob(wwv_flow_api.g_varchar2_table)
  );
end;

:
The script contains all binary content hex-encoded in some PL/SQL arrays of type VARCHAR2. First, the script creates the array, then it's being passed to a WWV_FLOW_API procedure, which generates the BLOB from it. We can imagine that this procedure uses HEXTORAW somewhere in the background. If you want to use this approach, without APEX, for your own tables, have a look into this blog posting from 2008; it contains a custom function which takes a BLOB as input, and generates similar PL/SQL code - you can have binary content encoded in your SQL script the same way as APEX does it. The code can then be added to your own SQL script. For a reasonable number of BLOBs, for instance, seed data of an installation script, this is a very nice and working approach to get binary data into your database.
At the end of this "anniversary" blog posting, I have a special "present" for you. We can use the HEXTORAW, RAWTOHEX and UTL_RAW cast functions to present binary data better than SQL*Plus does it out-of-the-box. Here is the SQL Hex Viewer function. Simply create the following PL/SQL function ...
create or replace function display_blob(
  p_blob    in blob
 ,p_start   in number default 1
 ,p_lines   in number default 16
) return varchar2 authid current_user is
  l_raw   raw(2000);
  l_vc    varchar2(4000);

  l_linesize pls_integer := 16;
  l_finish   boolean     := false;
begin
  if p_lines > 50 then
    raise_application_error(-20000, 'MAXIMUM OF 50 LINES');
  end if;

  l_raw := dbms_lob.substr(p_blob, p_lines * l_linesize, p_start);

  for i in 0 .. p_lines - 1 loop
    l_vc := l_vc || to_char((p_start-1) + i * l_linesize, 'FM0XXXXXXX')||': ';
    for j in 1 .. l_linesize loop
      if i * l_linesize + j <= utl_raw.length(l_raw) then
        l_vc := l_vc || rawtohex(utl_raw.substr(l_raw, (i * l_linesize) + j, 1)) ||' ';
      else 
        l_vc := l_vc || '   ';
        l_finish := true;
      end if;
    end loop;
    l_vc := l_vc || '| ';
    for j in 1 .. l_linesize loop
      if i * l_linesize + j <= utl_raw.length(l_raw) then
        if ascii(utl_raw.cast_to_varchar2(utl_raw.substr(l_raw, (i * l_linesize) + j, 1))) between 32 and 126 then 
          l_vc := l_vc || utl_raw.cast_to_varchar2(utl_raw.substr(l_raw, (i * l_linesize) + j, 1)); 
        else 
          l_vc := l_vc || '.';
        end if;
      end if;
    end loop;
    l_vc := l_vc || chr(10);
    if l_finish then exit;  end if;
  end loop;
  return l_vc;
end display_blob;
/
sho err
And try it out with a BLOB. The functions' first parameter is the BLOB itself, the second is the first byte which is to be displayed (defaults to 1). The third parameter determines, how many "lines" are to be returned - each line represents 16 bytes. Internally, the functions works with a VARCHAR2, so we can only retrieve up to 50 lines in one call. You'll get a binary display like the hex viewers in the old days ...
SQL> desc display_blob
FUNCTION display_blob RETURNS VARCHAR2
 Argument Name                  Typ                     In/Out Defaultwert?
 ------------------------------ ----------------------- ------ --------
 P_BLOB                         BLOB                    IN
 P_START                        NUMBER                  IN     DEFAULT
 P_LINES                        NUMBER                  IN     DEFAULT

SQL> select display_blob(content) from  myblobs;

DISPLAY_BLOB(CONTENT)
--------------------------------------------------------------------------------
00000000: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 | .PNG........IHDR
00000010: 00 00 00 C8 00 00 00 FA 08 02 00 00 00 6B 88 9F | .............k..
00000020: 6F 00 00 00 09 70 48 59 73 00 00 0E C4 00 00 0E | o....pHYs.......
00000030: C4 01 95 2B 0E 1B 00 00 1E F1 49 44 41 54 78 9C | ...+......IDATx.
00000040: ED 9D FB 93 64 47 95 DF BF 27 33 EF A3 1E FD 9A | ....dG...'3.....
00000050: 9E 97 34 92 46 A3 11 A3 D1 20 06 09 21 B1 C0 2F | ..4.F.... ..!../
00000060: A0 C5 B1 AC B1 0D 18 3B D6 26 36 8C 20 C2 86 25 | .......;.&6. ..%
00000070: FC C3 FE 01 76 40 2C B1 E1 DD D8 B5 83 88 F5 0F | ....v@,.........
00000080: E6 A7 B5 31 D8 44 38 58 36 C2 86 DD 8D 58 60 0C | ...1.D8X6....X`.
00000090: 78 59 AD 78 49 02 C4 82 A4 41 48 48 83 34 A3 E9 | xY.xI....AHH.4..
000000A0: E9 47 D5 7D 64 E6 F1 0F 59 75 BB BA BA A7 A7 67 | .G.}d...Yu.....g
000000B0: E6 66 55 DF EE FC 68 66 54 DD 55 95 99 37 F3 7B | .fU...hfT.U..7.{
000000C0: 4F E6 3D 27 1F C4 CC D8 35 B8 C2 10 91 7B 4D 44 | O.='....5....{MD
000000D0: 55 F1 88 88 87 1F B2 CC D6 5A CB CC CC D6 32 83 | U........Z....2.
000000E0: 99 D9 32 1B C3 44 30 D6 82 11 25 31 11 19 63 86 | ..2..D0...%1..c.
000000F0: 6F 59 00 EE 47 00 96 D9 5A 03 06 6F C8 19 00 08 | oY..G...Z..o....
:
Have fun when trying this out. I know, there are so many things which I could talk related to BLOB and RAW values or hex codes. But for today, this concludes my posting - you might see more on this in post #0x1000.

Beliebte Postings