如何使用C语言的mysql_connector执行SQL文件?
解决C语言执行MySQL多语句SQL文件的问题
问题场景
尝试用C语言连接MySQL服务器执行SQL文件,最初用mysql_query(conn, "source path/to/file.sql")发现MySQL连接器不支持该命令,于是将SQL文件内容读取到缓冲区后传入mysql_query()执行,但出现语法错误:
Connected. Error : mysql_query() failed : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CREATE DATABASE vacance; USE vacance; CREATE TABLE Village ( CodeVillage i' at line 3 Exit with failure
对应的SQL文件内容:
DROP DATABASE IF EXISTS vacance; CREATE DATABASE vacance; USE vacance; CREATE TABLE Village ( CodeVillage int, NomVillage varchar(30), Situation varchar(30) );
错误原因
- mysql_query默认不支持多语句执行:默认情况下,
mysql_query()一次只能执行单条SQL语句,而SQL文件包含多条以分号分隔的语句,直接传入会触发语法错误。 - 字符串终止符缺失:
calloc(sql_file_sz, sizeof(char))分配的空间刚好等于文件大小,没有预留存储字符串结束符'\0'的位置,可能导致MySQL客户端解析时读取到额外垃圾数据。 - USE语句的上下文问题:即使开启多语句支持,
USE语句切换数据库后,后续语句的执行上下文可能出现异常。
解决方案
1. 开启多语句支持
在调用mysql_real_connect()时,添加CLIENT_MULTI_STATEMENTS标志,允许一次执行多条SQL语句:
if (!mysql_real_connect(conn, host, username, passwd, database, 0, NULL, CLIENT_MULTI_STATEMENTS)) { fprintf(stderr, "Error : mysql_real_connect() failed\n"); goto FatalError; }
2. 修正文件读取逻辑
分配缓冲区时多预留1字节存储'\0',确保字符串正确终止:
sql_file = (char *)calloc(sql_file_sz + 1, sizeof(char));
3. 处理多语句执行后的结果
执行多语句后,必须调用mysql_next_result()遍历所有语句的执行结果,否则连接会处于异常状态:
if (mysql_query(conn, sql_file) != 0) { fprintf(stderr, "Error : mysql_query() failed : %s\n", mysql_error(conn)); goto FatalError; } // 遍历所有多语句的执行结果 int status; do { status = mysql_next_result(conn); if (status > 0) { fprintf(stderr, "Error in multi-statement execution: %s\n", mysql_error(conn)); goto FatalError; } } while (status == 0);
4. 优化SQL语句(可选)
将SQL文件中的USE vacance;替换为在CREATE TABLE时指定数据库名,避免切换数据库的上下文问题:
DROP DATABASE IF EXISTS vacance; CREATE DATABASE vacance; CREATE TABLE vacance.Village ( CodeVillage int, NomVillage varchar(30), Situation varchar(30) );
修改后的完整代码
#include <stdio.h> #include <stdlib.h> #include <mysql/mysql.h> size_t get_file_size(char *path); int main() { /* Declaration */ MYSQL *conn; char username[] = "user"; char passwd[] = "user"; char host[] = "localhost"; char database[] = ""; char sql_file_path[] = "./db.sql"; char *sql_file; size_t sql_file_sz; FILE *sql_fs; /* Initialisation */ sql_file_sz = get_file_size(sql_file_path); // 多分配1字节存储'\0' sql_file = (char *)calloc(sql_file_sz + 1, sizeof(char)); sql_fs = fopen(sql_file_path, "r"); if (!sql_fs) { perror("Error : "); goto FatalError; } fread(sql_file, sql_file_sz, 1, sql_fs); if (ferror(sql_fs)) { perror("Error : "); goto FatalError; } conn = mysql_init(NULL); if (conn == NULL) { fprintf(stderr, "Error : mysql_init() failed\n"); goto FatalError; } // 连接时开启多语句支持 if (!mysql_real_connect(conn, host, username, passwd, database, 0, NULL, CLIENT_MULTI_STATEMENTS)) { fprintf(stderr, "Error : mysql_real_connect() failed\n"); goto FatalError; } printf("Connected.\n"); if (mysql_query(conn, sql_file) != 0) { fprintf(stderr, "Error : mysql_query() failed : %s\n", mysql_error(conn)); goto FatalError; } // 遍历所有多语句的执行结果 int status; do { status = mysql_next_result(conn); if (status > 0) { fprintf(stderr, "Error in multi-statement execution: %s\n", mysql_error(conn)); goto FatalError; } } while (status == 0); /* Leaving */ mysql_close(conn); fclose(sql_fs); free(sql_file); printf("Exit success.\n"); return EXIT_SUCCESS; FatalError: if (conn) mysql_close(conn); if (sql_fs) fclose(sql_fs); if (sql_file) free(sql_file); printf("Exit with failure.\n"); return EXIT_FAILURE; } /* Return file size. On error, return -1; */ size_t get_file_size(char *path) { size_t sz; FILE *fp; fp = fopen(path, "r"); if (!fp) { perror("Error : "); return -1; } fseek(fp, 0L, SEEK_END); sz = ftell(fp); rewind(fp); fclose(fp); return sz; }
验证结果
修改后重新编译执行代码,会输出:
Connected. Exit success.
登录MySQL客户端可确认数据库vacance和表Village已成功创建。
内容的提问来源于stack exchange,提问作者Zecksidd
相关产品推荐
相关产品推荐

