Skip to content

[possible degradation] UTF-8 string is now printed in 0x hex form in some cases. #1511

Description

@your-diary

How to Reproduce

CREATE TABLE t (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    encrypted_email VARCHAR(2048) NOT NULL
);

SET
    @encryption_key = 'my_secret_key';

INSERT INTO
    t (encrypted_email)
VALUES
    (
        HEX(AES_ENCRYPT('foo@example.com', @encryption_key))
    );

SELECT
    AES_DECRYPT(UNHEX(encrypted_email), @encryption_key) AS email
FROM
    t;

Expected:

I'm 100% sure I used to get this result until recently:

+-----------------+
| email           |
+-----------------+
| foo@example.com |
+-----------------+

Actual:

+----------------------------------+
| email                            |
+----------------------------------+
| 0x666f6f406578616d706c652e636f6d |
+----------------------------------+

Possible Cause?

The changelog of 1.48.0 (2026/01/27) says

  • Use 0x-style hex literals for binaries in SQL output formats.

  • Render binary values more consistently as hex literals.

Are these related?

Activity

  1. rolandwalker commented on Feb 5, 2026

    @rolandwalker
    Contributor

    Hi!

    Yes, this is intentional. In #1483, we fixed a bug in which some binary values displayed as text, and other binary values displayed as hex literals, depending on whether the values could be interpreted as UTF-8.

    Your expression, simplified as

    SELECT AES_DECRYPT(UNHEX(HEX(AES_ENCRYPT('foo@example.com', 'my_secret_key'))),'my_secret_key') AS foo;
    ┌──────────────────────────────────┐
    │ foo                              │
    ├──────────────────────────────────┤
    │ 0x666f6f406578616d706c652e636f6d │
    └──────────────────────────────────┘

    is indeed binary! We can check this using CHARSET()

    SELECT CHARSET(AES_DECRYPT(UNHEX(HEX(AES_ENCRYPT('foo@example.com', 'my_secret_key'))),'my_secret_key')) AS foo;
    ┌────────┐
    │ foo    │
    ├────────┤
    │ binary │
    └────────┘

    The docs for AES_DECRYPT():

    explain that

    AES_DECRYPT() decrypts the encrypted string crypt_str using the key string key_str, and returns the original (binary) string in hexadecimal format. (To obtain the string as plaintext, cast the result to CHAR. Alternatively, start the mysql client with --skip-binary-as-hex to cause all binary values to be displayed as text.)

    So, using the documented recommendation works:

    SELECT CAST(AES_DECRYPT(UNHEX(HEX(AES_ENCRYPT('foo@example.com', 'my_secret_key'))),'my_secret_key') AS CHAR) AS foo;
    ┌─────────────────┐
    │ foo             │
    ├─────────────────┤
    │ foo@example.com │
    └─────────────────┘

    Though I personally would be more explicit with CONVERT … USING:

     SELECT CONVERT(AES_DECRYPT(UNHEX(HEX(AES_ENCRYPT('foo@example.com', 'my_secret_key'))),'my_secret_key') USING utf8mb4) AS foo;
    ┌─────────────────┐
    │ foo             │
    ├─────────────────┤
    │ foo@example.com │
    └─────────────────┘

    Finally, the display of the original value as a hex literal is consistent with the vendor client.

    However, mycli could consider an option like --skip-binary-as-hex which restored the previous behavior, so long as it was not the default.

  2. your-diary commented on Feb 7, 2026

    @your-diary
    Author

    @rolandwalker
    Thank you very much for the detailed explanation.
    I simply learned many things from your response.


    Finally, the display of the original value as a hex literal is consistent with the vendor client.

    I confirmed this after reading the documentation you quoted and that of --skip-binary-as-hex:

    # interactive session without `--skip-binary-as-hex`
    $ mysql
    
    mysql> SELECT AES_DECRYPT(UNHEX(HEX(AES_ENCRYPT('foo@example.com', 'my_secret_key'))),'my_secret_key') AS foo;
    +----------------------------------+
    | foo                              |
    +----------------------------------+
    | 0x666F6F406578616D706C652E636F6D |
    +----------------------------------+
    # interactive session with `--skip-binary-as-hex`
    $ mysql --skip-binary-as-hex
    
    mysql> SELECT AES_DECRYPT(UNHEX(HEX(AES_ENCRYPT('foo@example.com', 'my_secret_key'))),'my_secret_key') AS foo;
    +-----------------+
    | foo             |
    +-----------------+
    | foo@example.com |
    +-----------------+
    # non-interactive session
    $ echo "SELECT AES_DECRYPT(UNHEX(HEX(AES_ENCRYPT('foo@example.com', 'my_secret_key'))),'my_secret_key') AS foo;" | mysql
    
    foo
    foo@example.com

    However, mycli could consider an option like --skip-binary-as-hex which restored the previous behavior, so long as it was not the default.

    Thank you!

    I do want this because I have many existing SQL statements which use AES_DECRYPT() without CAST() or CONVERT(); rewriting all of them would be time-consuming.

  3. rolandwalker commented on Feb 7, 2026

    @rolandwalker
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions