如何用MariaDB C API获取未知结构的预编译语句结果集?
使用MariaDB C API动态遍历预编译语句的结果集
针对你需要动态处理未知结构结果集的场景,核心是利用结果集元数据(metadata)获取列信息,再通过动态绑定缓冲区的方式获取每列的数据。以下是具体实现步骤和示例代码:
核心步骤
- 获取结果集元数据:执行预编译语句后,调用
mysql_stmt_result_metadata()拿到包含列结构的MYSQL_RES对象。 - 遍历列信息:用
mysql_num_fields()获取总列数,循环调用mysql_fetch_field_direct()获取每一列的类型、名称等元数据。 - 动态分配绑定结构:为每列创建
MYSQL_BIND结构体,根据列类型设置对应缓冲区类型,分配足够内存存储数据。 - 绑定结果集:调用
mysql_stmt_bind_result()将绑定结构与语句关联。 - 逐行读取数据:通过
mysql_stmt_fetch()循环读取每一行,直接访问MYSQL_BIND中的buffer指针获取底层数据。
示例代码
#include <mysql.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { MYSQL *conn; MYSQL_STMT *stmt; MYSQL_RES *meta_result; MYSQL_FIELD *field; MYSQL_BIND *bind = NULL; unsigned int num_fields; int i, ret; // 初始化并连接数据库(需自行替换连接参数) conn = mysql_init(NULL); if (!mysql_real_connect(conn, "localhost", "your_user", "your_pass", "your_db", 0, NULL, 0)) { fprintf(stderr, "%s\n", mysql_error(conn)); exit(1); } // 初始化预编译语句 stmt = mysql_stmt_init(conn); const char *query = "SELECT * FROM your_table WHERE id > ?"; if (mysql_stmt_prepare(stmt, query, strlen(query)) != 0) { fprintf(stderr, "Prepare failed: %s\n", mysql_stmt_error(stmt)); goto cleanup; } // 绑定查询参数(示例绑定int类型参数) int param_id = 10; MYSQL_BIND param_bind; memset(¶m_bind, 0, sizeof(MYSQL_BIND)); param_bind.buffer_type = MYSQL_TYPE_LONG; param_bind.buffer = ¶m_id; if (mysql_stmt_bind_param(stmt, ¶m_bind) != 0) { fprintf(stderr, "Bind param failed: %s\n", mysql_stmt_error(stmt)); goto cleanup; } // 执行预编译语句 if (mysql_stmt_execute(stmt) != 0) { fprintf(stderr, "Execute failed: %s\n", mysql_stmt_error(stmt)); goto cleanup; } // 获取结果集元数据 meta_result = mysql_stmt_result_metadata(stmt); if (!meta_result) { fprintf(stderr, "Metadata failed: %s\n", mysql_stmt_error(stmt)); goto cleanup; } // 获取列总数 num_fields = mysql_num_fields(meta_result); // 分配绑定结构数组 bind = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND) * num_fields); if (!bind) { fprintf(stderr, "Out of memory\n"); goto cleanup; } memset(bind, 0, sizeof(MYSQL_BIND) * num_fields); // 为每列配置绑定参数 for (i = 0; i < num_fields; i++) { field = mysql_fetch_field_direct(meta_result, i); // 根据列类型分配缓冲区并设置类型 switch (field->type) { case MYSQL_TYPE_TINY: bind[i].buffer_type = MYSQL_TYPE_TINY; bind[i].buffer = malloc(sizeof(char)); break; case MYSQL_TYPE_SHORT: bind[i].buffer_type = MYSQL_TYPE_SHORT; bind[i].buffer = malloc(sizeof(short)); break; case MYSQL_TYPE_LONG: bind[i].buffer_type = MYSQL_TYPE_LONG; bind[i].buffer = malloc(sizeof(int)); break; case MYSQL_TYPE_FLOAT: bind[i].buffer_type = MYSQL_TYPE_FLOAT; bind[i].buffer = malloc(sizeof(float)); break; case MYSQL_TYPE_DOUBLE: bind[i].buffer_type = MYSQL_TYPE_DOUBLE; bind[i].buffer = malloc(sizeof(double)); break; case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: bind[i].buffer_type = MYSQL_TYPE_STRING; bind[i].buffer = malloc(field->max_length + 1); bind[i].buffer_length = field->max_length + 1; break; default: fprintf(stderr, "Unsupported field type: %d\n", field->type); goto cleanup; } // 分配存储数据长度和NULL标识的内存 bind[i].length = malloc(sizeof(unsigned long)); bind[i].is_null = malloc(sizeof(my_bool)); } // 绑定结果集到语句 if (mysql_stmt_bind_result(stmt, bind) != 0) { fprintf(stderr, "Bind result failed: %s\n", mysql_stmt_error(stmt)); goto cleanup; } // 逐行读取并输出数据 while ((ret = mysql_stmt_fetch(stmt)) == 0) { printf("Row:\n"); for (i = 0; i < num_fields; i++) { field = mysql_fetch_field_direct(meta_result, i); printf(" %s (type: %d): ", field->name, field->type); if (*bind[i].is_null) { printf("NULL\n"); continue; } // 根据列类型输出对应数据 switch (field->type) { case MYSQL_TYPE_TINY: printf("%d\n", *(char *)bind[i].buffer); break; case MYSQL_TYPE_SHORT: printf("%d\n", *(short *)bind[i].buffer); break; case MYSQL_TYPE_LONG: printf("%d\n", *(int *)bind[i].buffer); break; case MYSQL_TYPE_FLOAT: printf("%f\n", *(float *)bind[i].buffer); break; case MYSQL_TYPE_DOUBLE: printf("%lf\n", *(double *)bind[i].buffer); break; case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: printf("%.*s\n", (int)*bind[i].length, (char *)bind[i].buffer); break; } } } if (ret != MYSQL_NO_DATA) { fprintf(stderr, "Fetch failed: %s\n", mysql_stmt_error(stmt)); } cleanup: // 释放所有分配的资源 if (bind) { for (i = 0; i < num_fields; i++) { if (bind[i].buffer) free(bind[i].buffer); if (bind[i].length) free(bind[i].length); if (bind[i].is_null) free(bind[i].is_null); } free(bind); } if (meta_result) mysql_free_result(meta_result); if (stmt) mysql_stmt_close(stmt); if (conn) mysql_close(conn); return 0; }
关键说明
- 元数据作用:
mysql_stmt_result_metadata()返回的MYSQL_RES对象,即使结果集为空也能提供完整的列结构信息,是动态处理的核心。 - 数据指针访问:
MYSQL_BIND的buffer字段就是底层数据的指针,只需根据列类型做对应类型转换即可读取数据。 - 内存管理:必须为每个
MYSQL_BIND的buffer、length、is_null分配内存,使用完毕后逐一释放,避免内存泄漏。 - 类型扩展:示例仅覆盖常见类型,你可以根据需求添加其他类型(如日期、二进制数据)的处理逻辑。
内容的提问来源于stack exchange,提问作者Gustavo
相关产品推荐
相关产品推荐

