StarRocks to_binary 函数详解:将字符串按 hex / base64 / utf8 转换为二进制值

原创2026-09-17 10:26:101,941 阅读
文章标签:大数据OLAP数据仓库湖仓一体数据库数据分析

StarRocks to_binary 函数详解:将字符串按 hex / base64 / utf8 转换为二进制值

to_binary 是 StarRocks 提供的二进制类型转换函数,用于把 VARCHAR 字符串按照指定的二进制格式(binary_type)转换为二进制(BINARY/VARBINARY)值。它常与 BINARY/VARBINARY 数据类型配合使用,是"字符串 → 二进制"写入链路上的核心函数,也是 from_binary 的逆操作。读完本文,你将掌握 to_binary 的语法、三种格式(hex、encode64、utf8)的语义与边界行为,并通过源码与测试用例理解其底层实现原理。

函数概述

to_binary 将 VARCHAR 字符串转换为二进制值,支持以下三种二进制格式:hex、encode64、utf8。当未指定 binary_type 时,默认使用 hex。

官方定义见 to_binary 文档,其逆函数 from_binary 负责把二进制值反向编码为字符串。

语法与参数

to_binary(str[, binary_type])

参数说明

参数 类型 必填 说明
str VARCHAR 是 待转换的字符串
binary_type VARCHAR 常量 否 转换使用的二进制格式,可选 hex、encode64、utf8,默认为 hex

binary_type 三种格式语义

  • hex(默认):to_binary 假定输入字符串是一个十六进制字符串,其字符必须全部位于 '0123456789abcdef' 范围内。输入不区分大小写。函数会将字符串直接转换为二进制,例如 "abab" 会被转换为 x'abab'。若输入不是合法的十六进制字符串,则返回空二进制值(0x),不会抛出异常。
  • encode64:to_binary 假定输入字符串是 Base64 编码后的字符串,函数对其进行 Base64 解码,解码结果即为二进制值。例如 "YWJhYg==" 会被解码为 x'abab'。若输入不是合法的 Base64 字符串,则返回空二进制值,同样不会抛出异常。
  • utf8:to_binary 不做任何转换,直接将输入字符串的原始字节序列作为二进制值返回。

返回值

返回 VARCHAR 值。实际上,从使用角度理解,返回值是一个二进制值(BINARY/VARBINARY),在使用 MySQL 客户端访问 StarRocks 时,若启用了 --binary-as-hex 选项,二进制数据将以十六进制表示法显示。

使用示例

以下示例假定在通过 MySQL 客户端访问 StarRocks 时启用了 --binary-as-hex 选项,因此二进制数据以十六进制表示法显示。

hex 格式:十六进制字符串 → 二进制

mysql> select to_binary('ABAB', 'hex');
+----------------------------------------------------+
| to_binary('ABAB', 'hex')                           |
+----------------------------------------------------+
| 0xABAB                                             |
+----------------------------------------------------+
1 row in set (0.01 sec)

注意输入不区分大小写,'abab' 与 'ABAB' 等价,均得到 0xABAB。

encode64 格式:Base64 字符串 → 二进制

mysql> select to_binary('U1RBUlJPQ0tT', 'encode64');
+------------------------------------------------------------------------------+
| to_binary('U1RBUlJPQ0tT', 'encode64')                                        |
+------------------------------------------------------------------------------+
| 0x53544152524F434B53                                                         |
+------------------------------------------------------------------------------+
1 row in set (0.00 sec)

'U1RBUlJPQ0tT' 是 STARROCKS 的 Base64 编码,解码后得到字节序列 0x53544152524F434B53,与 utf8 格式下直接转换 'STARROCKS' 的结果一致。

utf8 格式:原始字节 → 二进制

mysql> select to_binary('STARROCKS', 'utf8');
+----------------------------------------------------------------+
| to_binary('STARROCKS', 'utf8')                                 |
+----------------------------------------------------------------+
| 0x53544152524F434B53                                           |
+----------------------------------------------------------------+
1 row in set (0.00 sec)

'STARROCKS' 每个字符的 ASCII 码依次为 53 54 41 52 52 4F 43 4B 53,因此 utf8 格式直接得到 0x53544152524F434B53。

格式不匹配时返回空二进制

-- The input string does not match the binary format and an empty binary is returned.

mysql> select to_binary('U1RBUlJPQ0tT', 'hex');
+--------------------------------------------------------------------+
| to_binary('U1RBUlJPQ0tT', 'hex')                                   |
+--------------------------------------------------------------------+
| 0x                                                                 |
+--------------------------------------------------------------------+
1 row in set (0.00 sec)

'U1RBUlJPQ0tT' 中包含 U、R、J、P、K、T 等不在 0123456789abcdef 范围内的字符,不符合 hex 格式要求,因此返回空二进制 0x,而不是抛出异常。

与 from_binary 互为逆操作

to_binary 与 from_binary 是一对方向相反的函数:to_binary 把字符串编码为二进制,from_binary 把二进制编码回字符串,二者配合可以完成"字符串 → 二进制 → 字符串"的完整闭环。以下示例来自 from_binary 文档:

mysql> select from_binary(to_binary('ABAB', 'hex'), 'hex');
+----------------------------------------------+
| from_binary(to_binary('ABAB', 'hex'), 'hex') |
+----------------------------------------------+
| ABAB                                         |
+----------------------------------------------+
1 row in set (0.02 sec)

mysql> select from_base64(from_binary(to_binary('U1RBUlJPQ0tT', 'encode64'), 'encode64'));
+-----------------------------------------------------------------------------+
| from_base64(from_binary(to_binary('U1RBUlJPQ0tT', 'encode64'), 'encode64')) |
+-----------------------------------------------------------------------------+
| STARROCKS                                                                   |
+-----------------------------------------------------------------------------+
1 row in set (0.01 sec)

mysql> select from_binary(to_binary('STARROCKS', 'utf8'), 'utf8');
+-----------------------------------------------------+
| from_binary(to_binary('STARROCKS', 'utf8'), 'utf8') |
+-----------------------------------------------------+
| STARROCKS                                           |
+-----------------------------------------------------+
1 row in set (0.01 sec)

源码实现解析

格式枚举与常量解析

在 BE 端,三种格式被定义为枚举类型 BinaryFormatType,见 be/src/exprs/binary_functions.h:

enum class BinaryFormatType {
    HEX = 0,
    ENCODE64 = 1,
    UTF8 = 2,
};

struct BinaryFormatState {
    BinaryFormatType to_binary_type;

    static BinaryFormatType to_binary_format(const std::string& s) {
        std::string str(s);
        std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
        if (str == "encode64") {
            return BinaryFormatType::ENCODE64;
        } else if (str == "utf8") {
            return BinaryFormatType::UTF8;
        } else {
            // by default
            return BinaryFormatType::HEX;
        }
    }
};

从 BinaryFormatState::to_binary_format 的实现可以看出两点:

  1. 格式字符串在解析前会被转换为小写,因此 'ENCODE64'、'UTF8' 等大小写变体同样有效;
  2. 格式字符串仅精确匹配 "encode64" 与 "utf8",其余任何字符串(包括 'base64')都会回落为 HEX。这解释了为什么 BINARY 数据类型文档 示例中 INSERT INTO test_binary select 6, to_binary('abab', 'base64'); 得到的结果仍是 0xABAB——'abab' 恰好是合法十六进制字符串,因此按 HEX 处理也能得到相同结果。实际使用时应严格使用 'encode64'。

prepare 阶段缓存格式常量

binary_type 参数在执行期是只读的常量,因此 to_binary 通过 to_binary_prepare 在 FRAGMENT_LOCAL 阶段解析一次格式,并在线程间共享,避免每个线程重复解析。实现见 be/src/exprs/binary_functions.cpp:

Status BinaryFunctions::to_binary_prepare(FunctionContext* context, FunctionContext::FunctionStateScope scope) {
    // BinaryFormatState is an immutable format choice derived from a constant arg; it is
    // read-only at eval time, so build it once in FRAGMENT_LOCAL and share it across threads
    // instead of keeping a per-thread copy.
    if (scope != FunctionContext::FRAGMENT_LOCAL) {
        return Status::OK();
    }
    auto* state = new BinaryFormatState();
    context->set_function_state(scope, state);

    if (!context->is_notnull_constant_column(1)) {
        return Status::OK();
    }

    auto column = context->get_constant_column(1);
    auto to_binary_type = ColumnHelper::get_const_value<TYPE_VARCHAR>(column);
    std::string to_binary_type_str = to_binary_type.to_string();
    state->to_binary_type = BinaryFormatState::to_binary_format(to_binary_type_str);

    return Status::OK();
}

to_binary_close 则在 FRAGMENT_LOCAL 作用域结束时释放 BinaryFormatState。

向量化执行核心

to_binary 的实际计算在 BinaryFunctions::to_binary 中完成,它根据解析出的格式委托给不同的底层函数,见 be/src/exprs/binary_functions.cpp:

StatusOr<ColumnPtr> BinaryFunctions::to_binary(FunctionContext* context, const Columns& columns) {
    auto state = reinterpret_cast<BinaryFormatState*>(context->get_function_state(FunctionContext::FRAGMENT_LOCAL));
    auto to_binary_type = state->to_binary_type;
    switch (to_binary_type) {
    case BinaryFormatType::UTF8: {
        auto& src_column = columns[0];
        return std::move(*src_column).mutate();
    }
    case BinaryFormatType::ENCODE64:
        return EncryptionFunctions::from_base64(context, columns);
    default:
        return StringFunctions::unhex(context, columns);
    }
    return Status::OK();
}

三种格式的实现路径非常清晰:

  • utf8:直接对输入列调用 mutate(),不经过任何转换,与文档"不做任何转换"的描述一致;
  • encode64:委托给 EncryptionFunctions::from_base64(Base64 解码),与 from_base64 字符串函数共用同一实现;
  • hex(default 分支):委托给 StringFunctions::unhex,即 be/src/exprs/string_functions.cpp 中基于 unhexImpl 的向量化严格一元函数实现,负责十六进制解码。

这也从源码层面印证了文档中"输入不合法时返回空二进制而非抛异常"的行为:unhex 与 from_base64 对非法输入按空结果处理。

测试用例佐证

BE 端提供了完整的单元测试 be/test/exprs/binary_functions_test.cpp,覆盖正常输入、非法输入与 NULL 输入三类场景:

  • 正常输入(TestToBinaryNormal):验证 hex 格式 "abab" → ABAB、"00" → 00;encode64 格式 "QUJBQg==" → ABAB、"MDA=" → 00;utf8 格式原样保留(包括 "0"、"0Xx" 等非十六进制字符);
  • 非法输入:验证 hex 格式下 "0"(奇数长度)与 "AX"(含非法字符)均返回空字符串,且不抛出异常(测试注释 // TODO: Return null if input is invalid. 表明当前实现返回空串而非 NULL);
  • NULL 输入(TestToBinaryNull):输入为 NULL 常量列时,结果保持 NULL;
  • 往返一致性(TestFromToBinaryNormal):验证 to_binary 与 from_binary 在同一种格式下互为逆操作,转换结果与原始字符串一致。

实际应用:向 VARBINARY 列写入数据

to_binary 最常见的实战场景是把 VARCHAR 数据转换为二进制后写入 VARBINARY 列。根据 BINARY/VARBINARY 数据类型文档,StarRocks 自 v3.0 起支持 BINARY/VARBINARY 类型(BINARY 是 VARBINARY 的别名),最大长度与 VARCHAR 一致,范围为 [1, 1048576] 字节,默认 1048576。

建表并写入二进制数据:

CREATE TABLE `test_binary` (
    `id` INT(11) NOT NULL COMMENT "",
    `j`  VARBINARY NULL COMMENT ""
) ENGINE=OLAP
DUPLICATE KEY(`id`)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`id`)
PROPERTIES (
    "replication_num" = "3",
    "storage_format" = "DEFAULT"
);

方法一:直接写入 x'' 前缀的二进制常量:

INSERT INTO test_binary (id, j) VALUES (1, x'abab');
INSERT INTO test_binary (id, j) VALUES (2, x'baba');
INSERT INTO test_binary (id, j) VALUES (3, x'010102');
INSERT INTO test_binary (id, j) VALUES (4, x'0000');

方法二:用 to_binary 将 VARCHAR 数据转换为二进制后写入:

INSERT INTO test_binary select 5, to_binary('abab', 'hex');
INSERT INTO test_binary select 6, to_binary('abab', 'encode64');
INSERT INTO test_binary select 7, to_binary('abab', 'utf8');

查询结果(启用 --binary-as-hex):

mysql> select * from test_binary;
+------+------------+
| id   | j          |
+------+------------+
|    1 | 0xABAB     |
|    2 | 0xBABA     |
|    3 | 0x010102   |
|    4 | 0x0000     |
|    5 | 0xABAB     |
|    6 | 0xABAB     |
|    7 | 0x61626162 |
+------+------------+
7 rows in set (0.08 sec)

注意第 7 行:utf8 格式不做任何解码,'abab' 的 ASCII 字节序列直接落库,因此得到 0x61626162(对应 a、b、a、b 的 ASCII 码),与 hex 格式的 0xABAB 完全不同。三种格式差异在此体现得十分直观。

除了 INSERT INTO,BINARY 数据还可通过 Broker Load(Parquet/ORC 文件的 BYTE_ARRAY/BINARY 类型直接映射)与 Stream Load(CSV 文件按 hex 格式解析)导入,具体见 BINARY/VARBINARY 文档。

生态与兼容性

to_binary 在 FE 端注册于 FunctionSet.java(TO_BINARY = "to_binary"、FROM_BINARY = "from_binary")。

在 Trino 兼容层中,StarRocks 将 Trino 的 to_utf8/from_utf8 函数自动映射为 to_binary/from_binary 并固定指定 utf8 格式,见 Trino2SRFunctionCallTransformer.java:

// to_utf8 -> to_binary
registerFunctionTransformer("to_utf8", 1, new FunctionCallExpr("to_binary",
        List.of(new PlaceholderExpr(1, Expr.class), new StringLiteral("utf8"))));

// from_utf8 -> from_binary
registerFunctionTransformer("from_utf8", 1, new FunctionCallExpr("from_binary",
        List.of(new PlaceholderExpr(1, Expr.class), new StringLiteral("utf8"))));

这意味着使用 Trino 语法的查询中,to_utf8(v) 会被等价改写为 to_binary(v, 'utf8'),保持了跨引擎 SQL 的兼容体验。

小结

  • to_binary(str[, binary_type]) 将 VARCHAR 字符串按 hex(默认)、encode64、utf8 三种格式转换为二进制值;
  • 输入格式不合法时返回空二进制(0x)而非抛出异常;NULL 输入保持 NULL;
  • 三种格式内部实现分别委托给 StringFunctions::unhex、EncryptionFunctions::from_base64 与列直传,binary_type 常量在 prepare 阶段解析一次并跨线程共享;
  • 与 from_binary 互为逆操作,与 BINARY/VARBINARY 数据类型 配合使用即可完成字符串到二进制的落库闭环。
登录后查看全文
starrocks