当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL C API 读取含零字节字段时怎么使用 mysql_fetch_lengths

MySQL C API 读取含零字节字段时怎么使用 mysql_fetch_lengths

来源:17golang原创 2026-11-11 04:16:19 0浏览 收藏

读取 MySQL 的 BLOB 或其他可能包含零字节的字段时,正确做法是:先让 mysql_fetch_row() 取得当前行,再调用 mysql_fetch_lengths() 取得每一列的字节数,最后用这个长度复制数据。不要用 strlen() 推断字段大小,因为它会把第一个 \0 当成字符串结尾。

官方地址:https://dev.mysql.com/doc/c-api/8.4/en/mysql-fetch-lengths.html

关键边界是:长度数组只对应当前行;长度不包含结尾的空字节;长度为 0 既可能是空字段,也可能是 SQL NULL,必须再看 row[index] 是否为 NULL。

为什么二进制字段不能交给 strlen

MySQL C API 返回的 MYSQL_ROW 是一组字段指针。文本字段通常以零字节结束,看起来像 C 字符串;但 BLOB、压缩内容、序列化数据或用户主动保存的二进制内容都可能在中间出现 \0。此时 strlen(row[index]) 只能找到第一个零字节,后半段仍在结果缓冲区中,却不会被复制。

mysql_fetch_lengths(result) 返回当前行每列的 unsigned long 长度,单位是字节,而且不包含额外的终止零字节。它还避免了为文本字段再次扫描内存;因此即使字段看起来是字符串,按长度处理也更稳定。

图1把结果集、当前行、长度数组和复制动作放在同一张静态关系图中,重点看 MYSQL_ROW 与 unsigned long 长度之间的对应关系。它是结构说明图,不是运行截图。

MYSQL_RES、mysql_fetch_row、MYSQL_ROW、mysql_fetch_lengths 和 memcpy 之间的静态关系说明图
图1:结构说明图,查看结果集、当前行指针和按字节长度复制之间的关系。

在当前行里用 mysql_fetch_lengths 复制完整字段

调用顺序不要颠倒:必须先成功取得一行,之后长度数组才有明确的当前行。下面的辅助函数复制一个字段到调用方拥有的缓冲区,适合 BLOB 或含嵌入零字节的数据。

#include 
#include 
#include 

/* 返回 1 表示读取成功;out == NULL 且 out_len == 0 表示 SQL NULL。 */
static int copy_binary_field(MYSQL_RES *result, MYSQL_ROW row,
                             unsigned int index, unsigned char **out,
                             unsigned long *out_len)
{
    unsigned long *lengths;
    unsigned int field_count;

    /* 长度数组只对刚刚取得的当前行有效。 */
    lengths = mysql_fetch_lengths(result);
    field_count = mysql_num_fields(result);
    if (lengths == NULL || index >= field_count) {
        return 0; /* 结果无效或列下标越界。 */
    }

    if (row[index] == NULL) {
        *out = NULL;
        *out_len = 0; /* NULL 列和空列都长为 0,要靠指针区分。 */
        return 1;
    }

    *out_len = lengths[index];
    *out = (unsigned char *)malloc(*out_len == 0 ? 1 : *out_len);
    if (*out == NULL) {
        return 0; /* 内存分配失败时不复制半截数据。 */
    }
    if (*out_len != 0) {
        memcpy(*out, row[index], *out_len); /* 按字节数复制,允许中间出现零字节。 */
    }
    return 1;
}

/* 当前行的生命周期结束后,调用方负责释放复制出来的缓冲区。 */
void consume_row(MYSQL_RES *result, unsigned int blob_index)
{
    MYSQL_ROW row = mysql_fetch_row(result);
    unsigned char *data = NULL;
    unsigned long data_len = 0;

    if (row == NULL) {
        return; /* 没有下一行,或使用流式结果时发生了读取错误。 */
    }
    if (!copy_binary_field(result, row, blob_index, &data, &data_len)) {
        return; /* 真实程序应记录错误并走统一清理路径。 */
    }
    if (data != NULL) {
        /* 这里可以把 data[0..data_len) 写入文件或交给二进制解析器。 */
        free(data);
    }
}

这里的 data_len 是字段的真实字节数,memcpy 不会因为中间有零字节而提前停止。复制完成后,数据不再依赖 MySQL 结果集内部的当前行缓冲区,可以在下一次 mysql_fetch_row() 或释放结果集后继续使用。

图2强调的是字段级对应:列下标选中一个指针和一个长度,源字节区按该长度进入应用侧缓冲区。图中只表达静态数据关系,不代表某次真实执行输出。

当前 MYSQL_ROW、列下标、长度数组和应用侧二进制缓冲区的静态关系图
图2:数据关系说明图,查看当前行字段如何按 lengths[index] 复制到应用缓冲区。

空字段、SQL NULL 和当前行生命周期要分开处理

官方 C API 对空字段和 SQL NULL 都返回长度 0,但两者的指针状态不同:空字段的 row[index] 不是 NULL,只是 lengths[index] == 0;SQL NULL 的字段指针才是 NULL。因此不要只依据长度判断是否缺失。

字段状态row[index]lengths[index]处理建议
SQL NULLNULL0按缺失值处理,不读取指针
空字段非 NULL0保留为空字节序列
普通文本非 NULL实际字节数可按长度复制,不依赖 strlen
含零字节的二进制非 NULL完整字节数必须用 memcpy 等长度感知接口

还有三个容易踩到的边界:

  • 只对当前行有效:取得下一行后,上一行的长度数组和字段指针都不应继续当作稳定存储。
  • 不包含终止零字节:如果业务最终要构造 C 字符串,应另行分配 length + 1 并手动补零,不能把补零空间算进数据库字段长度。
  • 调用时机要正确:在 mysql_fetch_row() 之前,或结果集的所有行都读完之后,调用 mysql_fetch_lengths() 都没有可用的当前行。

用这张检查表避免字段被截断

如果代码要把 MySQL 字段保存到文件、消息或自定义协议,按下面顺序检查即可:

  1. 是否在成功取得当前行之后调用了 mysql_fetch_lengths()?
  2. 是否用 lengths[index] 作为复制长度,而不是用 strlen()?
  3. 是否先判断 row[index] == NULL,再读取字段内容?
  4. 是否在下一次取行或释放结果集前,把需要长期保存的数据复制到自己的缓冲区?
  5. 复制后的缓冲区是否在所有分支都释放,且没有把二进制数据当作以零结尾的文本打印?

一句话总结:mysql_fetch_row() 给出当前行的字段指针,mysql_fetch_lengths() 给出这些指针对应的字节长度;只要数据可能包含零字节,就按“指针 + 长度”处理。

相关问题

  • mysql_fetch_lengths 返回的长度包含字符串结尾吗?不包含,长度只表示字段数据本身的字节数。
  • 长度为 0 能说明字段是 NULL 吗?不能,空字段和 SQL NULL 都可能是 0,要检查字段指针。
  • 可以直接保存 row[index] 指针吗?不建议。需要跨越下一次取行或结果集释放后使用时,应按返回长度复制。
版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL BLACKHOLE 存储引擎适合什么复制中转场景MySQL BLACKHOLE 存储引擎适合什么复制中转场景
上一篇
MySQL BLACKHOLE 存储引擎适合什么复制中转场景
MySQL C API 怎么用 mysql_set_server_option 动态开启多语句
下一篇
MySQL C API 怎么用 mysql_set_server_option 动态开启多语句
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    974次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    980次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1096次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1068次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    906次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码