How to Do Bit Operations with UTL_RAW

Handle RAW values byte by byte, and store and test yes-or-no flags in single bytes with AND, OR, XOR, and NOT.

RAW values are plain bytes, and UTL_RAW is the package for working with them: taking pieces, joining, comparing, and combining them bit by bit with AND, OR, XOR, and NOT. Bit operations make one byte hold up to eight yes-or-no flags, such as the services included in a cabin class.

Code for This Guide

The main examples are in the examples/pkg-raw-text folder of the Oracle Database 26ai code repository on GitHub, each with its output. They use NIMBUS, the sample schema of a fictional airline, which you can install with the scripts in the setup/nimbus folder.

They come from Oracle Database 26ai SQL and PL/SQL Book.

Syntax

utl_raw.bit_and(r1, r2)      utl_raw.bit_or(r1, r2)
utl_raw.bit_xor(r1, r2)      utl_raw.bit_complement(r)
utl_raw.substr(r, pos, len)  utl_raw.concat(r1, r2, ...)
utl_raw.length(r)            utl_raw.compare(r1, r2)

RAW Basics

CAST_TO_RAW turns text into its bytes, and CAST_TO_VARCHAR2 turns bytes back into text, so the results can be read.

Example:

declare
  v_raw raw(100) := utl_raw.cast_to_raw('NIMBUS');
  procedure show (p_label varchar2, p_value varchar2) is
  begin
    dbms_output.put_line(rpad(p_label, 10) || p_value);
  end;
begin
  show('bytes:', rawtohex(v_raw) || ' (' || utl_raw.length(v_raw) || ')');
  show('text:', utl_raw.cast_to_varchar2(v_raw));
  show('substr:', utl_raw.cast_to_varchar2(utl_raw.substr(v_raw, 2, 3)));
  show('reverse:', utl_raw.cast_to_varchar2(utl_raw.reverse(v_raw)));
  show('concat:', utl_raw.cast_to_varchar2(
                    utl_raw.concat(v_raw, utl_raw.cast_to_raw(' AIR'))));
  show('overlay:', utl_raw.cast_to_varchar2(
                    utl_raw.overlay(utl_raw.cast_to_raw('**'), v_raw, 3)));
  show('copies:', rawtohex(utl_raw.copies('00FF', 3)));
  show('xrange:', rawtohex(utl_raw.xrange('41', '46')));
  show('compare:', utl_raw.compare(v_raw, utl_raw.cast_to_raw('NIMBLE')));
end;
/

Output:

bytes:    4E494D425553 (6)
text:     NIMBUS
substr:   IMB
reverse:  SUBMIN
concat:   NIMBUS AIR
overlay:  NI**US
copies:   00FF00FF00FF
xrange:   414243444546
compare:  5

PL/SQL procedure successfully completed.

NIMBUS is the six bytes 4E494D425553. SUBSTR, REVERSE, CONCAT, and OVERLAY work on bytes, COPIES repeats a value, XRANGE lists every byte between two values, and COMPARE returns the position of the first byte that differs, 5, or 0 when the values are equal.

Flags in a Byte

Each service is one bit: 01 meal, 02 Wi-Fi, 04 lounge, 08 priority boarding. Business has all four, 0F; economy has meal and Wi-Fi, 03.

Example:

declare
  -- one bit per service: 01 = meal, 02 = Wi-Fi, 04 = lounge, 08 = priority boarding
  v_business raw(1) := hextoraw('0F');
  v_economy  raw(1) := hextoraw('03');
  v_wifi     raw(1) := hextoraw('02');
  procedure show (p_label varchar2, p_value varchar2) is
  begin
    dbms_output.put_line(rpad(p_label, 21) || p_value);
  end;
begin
  show('economy has Wi-Fi?', case when utl_raw.bit_and(v_economy, v_wifi) = v_wifi
                                   then 'yes' end);
  show('business only:', rawtohex(utl_raw.bit_xor(v_business, v_economy)));
  show('either cabin:', rawtohex(utl_raw.bit_or(v_business, v_economy)));
  show('not in economy:', rawtohex(utl_raw.bit_complement(v_economy)));
  show('1000, big-endian:', rawtohex(utl_raw.cast_from_binary_integer(1000)));
  show('1000, little-endian:',
       rawtohex(utl_raw.cast_from_binary_integer(1000, utl_raw.little_endian)));
  show('and back:', utl_raw.cast_to_binary_integer(hextoraw('000003E8')));
  show('NUMBER 2.5 inside:', rawtohex(utl_raw.cast_from_number(2.5)));
end;
/

Output:

economy has Wi-Fi?   yes
business only:       0C
either cabin:        0F
not in economy:      FC
1000, big-endian:    000003E8
1000, little-endian: E8030000
and back:            1000
NUMBER 2.5 inside:   C10333

PL/SQL procedure successfully completed.
  • BIT_AND with 02 keeps only the Wi-Fi bit, so economy has Wi-Fi.
  • BIT_XOR gives the bits that differ, 0C: lounge and priority boarding are business only.
  • BIT_OR combines both cabins, and BIT_COMPLEMENT flips every bit.

The last lines cast numbers to bytes, which is covered in its own guide.

Filter by a Flag in SQL

Example:

-- services as bits: 01 meal, 02 Wi-Fi, 04 lounge, 08 priority boarding
with cabins (cabin, services) as (
  select 'First',           hextoraw('0F') from dual union all
  select 'Business',        hextoraw('0F') from dual union all
  select 'Premium economy', hextoraw('0B') from dual union all
  select 'Economy',         hextoraw('03') from dual
)
select cabin, rawtohex(services) as bits,
       case when utl_raw.bit_and(services, hextoraw('04')) = hextoraw('04')
            then 'yes' else 'no' end as lounge
from   cabins;

Output:

CABIN              BITS    LOUNGE
__________________ _______ _________
First              0F      yes
Business           0F      yes
Premium economy    0B      no
Economy            03      no

BIT_AND with 04 tests the lounge bit for each cabin, so only First and Business have it.

Things to Know

  • When the two values differ in length, the extra bytes of the longer one are copied to the result unchanged, so keep flags the same length.
  • Write constants with HEXTORAW, as a RAW literal, to avoid implicit conversions.
  • For numbers, BITAND in SQL does the same for AND on integer values.

Related Guides

Conclusion

UTL_RAW handles RAW values byte by byte and bit by bit. Use SUBSTR, CONCAT, and COMPARE for bytes, and BIT_AND, BIT_OR, BIT_XOR, and BIT_COMPLEMENT to store and test flags packed into a few bytes.

Get more Vinish.dev tutorials on Google

See my articles more often when you search Google.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted