# Bytes, hex and base64
> Store raw bytes (hashes, keys, small images) and turn them into hex or base64 text and back.
- Category: Functions — JSON and bytes
- Keywords: BYTES, BYTEA, BLOB, binary, hex, base64, \\x, hash, file hash, image, avatar, bytes to text, X', convert\_to, convert\_from, encoding, UTF8, LATIN1, escape format
- See also: [JSON functions](functions-json.md), [Security functions](functions-security.md), [Column types](column-types.md), [Text functions](functions-text.md)
A `BYTES` value holds raw bytes: any byte from 0 to 255, also zero bytes. Good uses are a file hash (SHA-256 is 32 bytes), a key, a signature or a very small image.
`BYTEA` and `BLOB` are other names for the same column type (see [column types](column-types.md)). One value can be at most 16 MB; keep big files outside the database and save their path and hash.
- **Hex** writes every byte as two digits `0-9 a-f`: the bytes 0, 255, 16 are `00ff10`.
- **Base64** writes 3 bytes as 4 letters: the same bytes are `AP8Q`. It is shorter than hex, and is often used in JSON and HTTP.
- KDB **shows** `BYTES` as `\x` and hex (`\x00ff10`), like PostgreSQL. This is the same in the shell, CSV and HTTP answers.
- On `BYTES`, `length()`, `octet_length()`, `substr()` and `reverse()` work on bytes, not letters (see [text functions](functions-text.md)).
Setup used by the examples below:
```sql
CREATE TABLE reports (
id LONG PRIMARY KEY AUTO,
symbol STRING NOT NULL,
fiscal_year STRING NOT NULL,
pdf_url STRING,
pdf_sha256 BYTES
);
INSERT INTO reports (symbol, fiscal_year, pdf_url, pdf_sha256) VALUES
('CITY', '2082/83', 'https://files.example.com/city/2082-83-q4.pdf',
X'9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08'),
('VOLT', '2082/83', 'https://files.example.com/volt/2082-83-q4.pdf', NULL);
```
**Example: A report PDF stays outside; KDB keeps its SHA-256 hash**
```sql
SELECT symbol,
pdf_sha256,
length(pdf_sha256) AS bytes,
hex(pdf_sha256) AS hash_hex,
to_base64(pdf_sha256) AS hash_base64
FROM reports
ORDER BY symbol;
```
Result:
| symbol (STRING) | pdf_sha256 (BYTES) | bytes (LONG) | hash_hex (STRING) | hash_base64 (STRING) |
|---|---|---|---|---|
| CITY | \\x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 | 32 | 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 | n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= |
| VOLT | NULL | NULL | NULL | NULL |
(2 rows)
## X'...' bytes literal
Syntax:
```sql
X'0A0B' x'0a0b' X''
'\x0a0b' text that becomes BYTES in a BYTES column or CAST
CAST('\x0a0b' AS BYTES) '\x0a0b'::BYTES
```
`X'...'` writes bytes in hex inside SQL. Each byte is two hex digits; big or small letters both work. There must be no space between `X` and the quote.
When text goes into a `BYTES` column (or `CAST(... AS BYTES)`):
| Text | Bytes |
|---|---|
| starts with `\x`, like `'\x0a0b'` | the hex digits after `\x` |
| any other text, like `'hi'` | the UTF-8 bytes of the text (`\x6869`) |
- **Returns:** `BYTES`.
- **NULL:** `NULL` stays `NULL`.
- **Notes:** an odd number of digits or a letter that is not hex is an error (`X'ABC'`). `X'...'` is read as `unhex('...')`, so its error message says `unhex`.
- `'\x0a0b'` alone, outside a `BYTES` column or cast, is just text of 6 characters.
- A number cannot become `BYTES` (`CAST(5 AS BYTES)` is an error).
- Compare: `BYTES` compare byte by byte. `BYTES` compared with text read the text like the table above, so `X'6869' = 'hi'` is true.
- `X'01' || X'02'` joins bytes (see [||](functions-text.md#concat-operator)). Bytes joined with **text** give text: `X'0102' || 'ab'` is the text `'\x0102ab'`, not bytes.
**Example**
```sql
SELECT X'0A0B' AS a,
x'0a0b' AS small_letters,
X'' AS empty,
'\x0a0b'::BYTES AS from_hex_text,
CAST('hi' AS BYTES) AS from_text,
X'0102' || X'03' AS joined;
```
Result:
| a (BYTES) | small_letters (BYTES) | empty (BYTES) | from_hex_text (BYTES) | from_text (BYTES) | joined (BYTES) |
|---|---|---|---|---|---|
| \\x0a0b | \\x0a0b | \\x | \\x0a0b | \\x6869 | \\x010203 |
(1 row)
**Example: Store and compare file hashes**
Setup:
```sql
CREATE TABLE avatars (user_id LONG PRIMARY KEY, png_header BYTES, sha256 BYTES);
INSERT INTO avatars VALUES
(1, X'89504E470D0A1A0A', '\x2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'),
(2, 'not a png', NULL);
```
```sql
SELECT user_id, png_header, length(png_header) AS bytes,
png_header = X'89504E470D0A1A0A' AS is_png
FROM avatars
ORDER BY user_id;
```
Result:
| user_id (LONG) | png_header (BYTES) | bytes (LONG) | is_png (BOOL) |
|---|---|---|---|
| 1 | \\x89504e470d0a1a0a | 8 | true |
| 2 | \\x6e6f74206120706e67 | 9 | false |
(2 rows)
**Example: Bytes compared with text**
```sql
SELECT X'6869' = 'hi' AS same_as_text,
X'0A0B' = '\x0a0b' AS same_as_hex_text,
X'01' < X'02' AS smaller,
length('\x0a0b') AS text_letters,
length('\x0a0b'::BYTES) AS bytes;
```
Result:
| same_as_text (BOOL) | same_as_hex_text (BOOL) | smaller (BOOL) | text_letters (LONG) | bytes (LONG) |
|---|---|---|---|---|
| true | true | true | 6 | 2 |
(1 row)
**Example: An odd number of hex digits** (shows an error)
```sql
SELECT X'ABC';
```
Result:
Error: `line 1, column 8: unhex: 'ABC' is not hex (an even number of the digits 0-9 and a-f)`
**Example: Bad hex in a BYTES column** (shows an error)
```sql
INSERT INTO reports (symbol, fiscal_year, pdf_sha256) VALUES ('COAST', '2082/83', '\x9F86ZZ');
```
Result:
Error: `column 'pdf_sha256' (BYTES): '\x9F86ZZ' is not hex bytes; write them like '\x0a0b' or X'0A0B'`
## hex
Syntax:
```sql
hex(value)
```
Writes bytes as hex text: two small-letter digits for every byte. Text is written as its UTF-8 bytes. A whole number is written as a hex number.
| Argument | Type | Meaning |
|---|---|---|
| `value` | `BYTES`, `STRING` or a whole number | what to write as hex |
- **Returns:** `STRING`.
- **NULL:** `hex(NULL)` is `NULL`.
- **Notes:** `hex(255)` is `ff` (like MySQL). A negative number uses 16 digits: `hex(-1)` is `ffffffffffffffff`. Other values (a `DOUBLE`, a time) are first turned into text: `hex(2.5)` is the hex of the text `'2.5'`.
- A Nepali letter is 3 bytes in UTF-8, so it gives 6 hex digits.
- The opposite is `unhex`.
**Example**
```sql
SELECT hex(X'00FF10') AS from_bytes,
hex('CITY') AS from_text,
hex(255) AS from_number,
hex(-1) AS negative,
hex('न') AS nepali_letter;
```
Result:
| from_bytes (STRING) | from_text (STRING) | from_number (STRING) | negative (STRING) | nepali_letter (STRING) |
|---|---|---|---|---|
| 00ff10 | 43495459 | ff | ffffffffffffffff | e0a4a8 |
(1 row)
**Example: Show a short hash code for each report on a web page**
```sql
SELECT symbol, left(hex(pdf_sha256), 12) AS short_hash
FROM reports
WHERE pdf_sha256 IS NOT NULL;
```
Result:
| symbol (STRING) | short_hash (STRING) |
|---|---|
| CITY | 9f86d081884c |
(1 row)
## unhex
Syntax:
```sql
unhex(hex_text)
```
Reads hex text and gives the bytes. It is the opposite of `hex`.
| Argument | Type | Meaning |
|---|---|---|
| `hex_text` | `STRING` | an even number of hex digits (`0-9`, `a-f` or `A-F`), nothing else |
- **Returns:** `BYTES`.
- **NULL:** `unhex(NULL)` is `NULL`.
- **Notes:** no `\x` in front, no spaces. An odd number of digits or another sign is an error. `unhex('')` is empty bytes.
- `decode(hex_text, 'hex')` does the same.
**Example**
```sql
SELECT unhex('4E4142494C') AS a, unhex('00ff10') AS b, unhex('') AS empty;
```
Result:
| a (BYTES) | b (BYTES) | empty (BYTES) |
|---|---|---|
| \\x4e4142494c | \\x00ff10 | \\x |
(1 row)
**Example: Save a SHA-256 hash as 32 bytes instead of 64 letters**
Setup:
```sql
INSERT INTO reports (symbol, fiscal_year, pdf_url, pdf_sha256)
VALUES ('COAST', '2082/83', 'https://files.example.com/coast/2082-83-q4.pdf',
unhex('2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'));
```
```sql
SELECT symbol, length(pdf_sha256) AS bytes, hex(pdf_sha256) AS hash_hex
FROM reports
WHERE symbol = 'COAST';
```
Result:
| symbol (STRING) | bytes (LONG) | hash_hex (STRING) |
|---|---|---|
| COAST | 32 | 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae |
(1 row)
**Example: Spaces are not allowed** (shows an error)
```sql
SELECT unhex('4E 41 42');
```
Result:
Error: `line 1, column 8: unhex: '4E 41 42' is not hex (an even number of the digits 0-9 and a-f)`
## to_base64
Syntax:
```sql
to_base64(value)
```
Writes bytes as base64 text (letters `A-Z a-z 0-9 + /`, with `=` at the end to fill). Text is written as its UTF-8 bytes.
| Argument | Type | Meaning |
|---|---|---|
| `value` | `BYTES` or `STRING` | what to write as base64 |
- **Returns:** `STRING`.
- **NULL:** `to_base64(NULL)` is `NULL`.
- **Notes:** the result is one line (no line breaks). It uses the normal alphabet with `+` and `/`, not the URL alphabet (`-` and `_`).
- `encode(value, 'base64')` does the same. The opposite is `from_base64`.
**Example**
```sql
SELECT to_base64(X'00FF10') AS bytes_value,
to_base64('CITY') AS text_value,
to_base64('नेपाल') AS nepali,
to_base64('') AS empty;
```
Result:
| bytes_value (STRING) | text_value (STRING) | nepali (STRING) | empty (STRING) |
|---|---|---|---|
| AP8Q | Q0lUWQ== | 4KSo4KWH4KSq4KS+4KSy | |
(1 row)
**Example: Send a small image to an app inside JSON**
Setup:
```sql
CREATE TABLE avatars (user_id LONG PRIMARY KEY, image BYTES);
INSERT INTO avatars VALUES (7, X'89504E470D0A1A0A0000000D49484452');
```
```sql
SELECT json_object('user_id', user_id, 'image_base64', to_base64(image)) AS answer
FROM avatars;
```
Result:
| answer (JSON) |
|---|
| {"user_id":7,"image_base64":"iVBORw0KGgoAAAANSUhEUg=="} |
(1 row)
## from_base64
Syntax:
```sql
from_base64(text)
```
Reads base64 text and gives the bytes. It is the opposite of `to_base64`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | `STRING` | base64 text |
- **Returns:** `BYTES`.
- **NULL:** `from_base64(NULL)` is `NULL`.
- **Notes:** both alphabets work: `+ /` and the URL one `- _`. The `=` at the end may be left out. Spaces and line breaks are skipped. Any other sign is an error.
- `decode(text, 'base64')` does the same.
- There is no function yet to turn `BYTES` back into normal text: `CAST(bytes AS STRING)` gives the `\x...` hex text. Keep text in a `STRING` column.
**Example**
```sql
SELECT from_base64('AP8Q') AS a,
from_base64('TkFCSUw=') AS with_padding,
from_base64('TkFCSUw') AS without_padding,
from_base64('TkFC
SUw=') AS with_line_break;
```
Result:
| a (BYTES) | with_padding (BYTES) | without_padding (BYTES) | with_line_break (BYTES) |
|---|---|---|---|
| \\x00ff10 | \\x4e4142494c | \\x4e4142494c | \\x4e4142494c |
(1 row)
**Example: A mobile app uploads an avatar as base64**
Setup:
```sql
CREATE TABLE avatars (user_id LONG PRIMARY KEY, image BYTES);
```
```sql
INSERT INTO avatars VALUES (7, from_base64('iVBORw0KGgoAAAANSUhEUg=='));
SELECT user_id, image, length(image) AS bytes FROM avatars;
```
Result:
`INSERT INTO avatars VALUES (7, from_base64('iVBORw0KGgoAAAANSUhEUg=='))`
```text
1 row(s) inserted
```
`SELECT user_id, image, length(image) AS bytes FROM avatars`
| user_id (LONG) | image (BYTES) | bytes (LONG) |
|---|---|---|
| 7 | \\x89504e470d0a1a0a0000000d49484452 | 16 |
(1 row)
**Example: Not base64** (shows an error)
```sql
SELECT from_base64('abc$');
```
Result:
Error: `line 1, column 8: from_base64: the text is not valid base64`
## encode
Syntax:
```sql
encode(value, format)
```
Writes bytes as text in `format`: `'hex'`, `'base64'` or `'escape'`, as PostgreSQL does. `hex()` and `to_base64()` do the same as the first two.
| Argument | Type | Meaning |
|---|---|---|
| `value` | `BYTES` or `STRING` | what to write (text is written as its UTF-8 bytes) |
| `format` | `STRING` | `'hex'`, `'base64'` or `'escape'` (big or small letters) |
- **Returns:** `STRING`.
- **NULL:** `NULL` when an argument is `NULL`.
- **Notes:**
- `'base64'` breaks the text into lines of 76 characters, as PostgreSQL does; `to_base64()` keeps one line. `decode` reads both.
- `'escape'` writes a zero byte and bytes from 128 up as `\nnn` (three octal digits) and a backslash as `\\`; the rest stays as it is.
- `encode(sha256(x), 'hex')` encodes the hash's own bytes, so PostgreSQL code gives the same 64 characters ([sha256](functions-security.md#sha256)).
**Example**
```sql
SELECT encode(X'00FF10', 'hex') AS hex_text,
encode(X'00FF10', 'base64') AS base64_text,
encode(X'00FF10', 'escape') AS escape_text,
encode('CITY', 'HEX') AS text_as_hex;
```
Result:
| hex_text (STRING) | base64_text (STRING) | escape_text (STRING) | text_as_hex (STRING) |
|---|---|---|---|
| 00ff10 | AP8Q | \\000\\377 | 43495459 |
(1 row)
**Example: Other formats are an error** (shows an error)
```sql
SELECT encode(X'00FF10', 'utf8');
```
Result:
Error: `line 1, column 8: encode: format 'utf8' is not supported; use 'hex', 'base64' or 'escape'`
## decode
Syntax:
```sql
decode(text, format)
```
Reads text in `format` (`'hex'`, `'base64'` or `'escape'`) and gives the bytes. It is the opposite of `encode`, and does the same as `unhex()` and `from_base64()`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | `STRING` | hex, base64 or escape text |
| `format` | `STRING` | `'hex'`, `'base64'` or `'escape'` (big or small letters) |
- **Returns:** `BYTES`.
- **NULL:** `NULL` when an argument is `NULL`.
- **Notes:** bad hex, bad base64 or a backslash in escape text that is not `\\` or `\nnn` is an error. Line breaks in base64 are skipped. Another format is an error. To turn bytes into text of an encoding, use [convert_from](#convert_from).
**Example**
```sql
SELECT decode('00ff10', 'hex') AS from_hex,
decode('AP8Q', 'base64') AS from_base64_text,
decode('00ff10', 'hex') = decode('AP8Q', 'base64') AS same_bytes;
```
Result:
| from_hex (BYTES) | from_base64_text (BYTES) | same_bytes (BOOL) |
|---|---|---|
| \\x00ff10 | \\x00ff10 | true |
(1 row)
**Example: Check a download: compare the hash the server sent with the saved one**
```sql
SELECT symbol,
pdf_sha256 = decode('n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=', 'base64') AS hash_matches
FROM reports
WHERE symbol = 'CITY';
```
Result:
| symbol (STRING) | hash_matches (BOOL) |
|---|---|
| CITY | true |
(1 row)
**Example: Wrong format name** (shows an error)
```sql
SELECT decode('hello', 'utf8');
```
Result:
Error: `line 1, column 8: decode: format 'utf8' is not supported; use 'hex', 'base64' or 'escape'`
## convert_to
Syntax:
```sql
convert_to(text, encoding)
```
The bytes of a text in an encoding (PostgreSQL). `convert_to(token, 'UTF8')` is how PostgreSQL code turns a text into bytes before hashing it.
| Argument | Type | Meaning |
|---|---|---|
| `text` | `STRING` | the text |
| `encoding` | `STRING` | `'UTF8'` (also `'UTF-8'`), `'LATIN1'` or `'SQL_ASCII'` |
- **Returns:** `BYTES`.
- **NULL:** `NULL` when an argument is `NULL`.
- **Notes:** KDB keeps text as UTF-8, so `'UTF8'` gives those bytes as they are. `'LATIN1'` gives one byte per character; a character LATIN1 does not have (like Nepali letters) is an error, never a `?`. Other encodings are an error.
**Example**
```sql
SELECT convert_to('CITY', 'UTF8') AS utf8_bytes,
convert_to('नेपाल', 'UTF8') AS nepali_bytes,
convert_to('Æ', 'LATIN1') AS latin1_byte;
```
Result:
| utf8_bytes (BYTES) | nepali_bytes (BYTES) | latin1_byte (BYTES) |
|---|---|---|
| \\x43495459 | \\xe0a4a8e0a587e0a4aae0a4bee0a4b2 | \\xc6 |
(1 row)
**Example: Not in LATIN1** (shows an error)
```sql
SELECT convert_to('नेपाल', 'LATIN1');
```
Result:
Error: `line 1, column 8: convert_to: character 'न' has no equivalent in encoding "LATIN1"`
## convert_from
Syntax:
```sql
convert_from(bytes, encoding)
```
The text that bytes in an encoding stand for (PostgreSQL). The opposite of `convert_to`.
| Argument | Type | Meaning |
|---|---|---|
| `bytes` | `BYTES` | the bytes |
| `encoding` | `STRING` | `'UTF8'`, `'LATIN1'` or `'SQL_ASCII'` |
- **Returns:** `STRING`.
- **NULL:** `NULL` when an argument is `NULL`.
- **Notes:** with `'UTF8'` (and `'SQL_ASCII'`) the bytes must be valid UTF-8; bad bytes are an error that names the first one, as in PostgreSQL. `'LATIN1'` turns each byte into its character.
**Example**
```sql
SELECT convert_from('\x43495459'::bytea, 'UTF8') AS text,
convert_from('\xc6'::bytea, 'LATIN1') AS latin1;
```
Result:
| text (STRING) | latin1 (STRING) |
|---|---|
| CITY | Æ |
(1 row)
**Example: Not UTF-8** (shows an error)
```sql
SELECT convert_from('\xff'::bytea, 'UTF8');
```
Result:
Error: `line 1, column 8: convert_from: invalid byte sequence for encoding "UTF8": 0xff`