mysql8 sql语法错误,错误信息是怎么通过网络发送给客户端的,C++源码展示

server/2025/2/9 2:35:28/

在 MySQL 8 中,错误信息通过网络发送给客户端的过程涉及多个步骤,主要包括错误信息的生成、格式化、以及通过网络协议(如 TCP/IP)将错误信息发送给客户端。以下是详细的流程和相关代码分析:

1. 错误信息的生成

当 MySQL 服务器遇到错误时,会调用 my_error 函数来生成错误信息。my_error 函数会根据错误编号(nr)和格式化参数(MyFlags)生成具体的错误信息。

my_error 函数

cpp

void my_error(int nr, myf MyFlags, ...) {const char *format;char ebuff[ERRMSGSIZE];DBUG_TRACE;DBUG_PRINT("my", ("nr: %d  MyFlags: %d  errno: %d", nr, MyFlags, errno));if (!(format = my_get_err_msg(nr)))(void)snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr);else {va_list args;va_start(args, MyFlags);(void)vsnprintf(ebuff, sizeof(ebuff), format, args);va_end(args);}/*Since this function is an error function, it will frequently be givenvalues that are too long (and thus truncated on byte boundaries,not code point or grapheme boundaries), values that are binary, etc.. Go through and replace every malformed UTF-8 byte with a question mark,so that the result is safe to send to the client and makes sense to readfor the user.*/for (char *ptr = ebuff, *end = ebuff + strlen(ebuff); ptr != end;) {my_wc_t ignored;int len = my_mb_wc_utf8mb4(&ignored, pointer_cast<const uchar *>(ptr),pointer_cast<const uchar *>(end));if (len > 0) {ptr += len;} else {*ptr++ = '?';}}(*error_handler_hook)(nr, ebuff, MyFlags);
}

2. 错误信息的处理

my_error 函数会调用 error_handler_hook,这是一个全局变量,指向处理错误信息的函数。在 MySQL 服务器中,error_handler_hook 通常被设置为 my_message_sql 函数。

my_message_sql 函数

cpp

void my_message_sql(uint error, const char *str, myf MyFlags) {THD *thd = current_thd;DBUG_TRACE;DBUG_PRINT("error", ("error: %u  message: '%s'", error, str));assert(str != nullptr);if (error == 0) {/* At least, prevent new abuse ... */assert(strncmp(str, "MyISAM table", 12) == 0);error = ER_UNKNOWN_ERROR;}if (thd) {(void)thd->raise_condition(error, nullptr, Sql_condition::SL_ERROR, str,MyFlags & ME_FATALERROR);}if (MyFlags & ME_ERRORLOG) {if (error < ER_SERVER_RANGE_START) {LogEvent().type(LOG_TYPE_ERROR).prio(ERROR_LEVEL).errcode(ER_ERROR_INFO_FROM_DA).lookup(ER_ERROR_INFO_FROM_DA, error, str);} else {LogEvent().type(LOG_TYPE_ERROR).prio(ERROR_LEVEL).errcode(error).verbatim(str);}} else if (!thd) {LogEvent().type(LOG_TYPE_ERROR).subsys(LOG_SUBSYSTEM_TAG).prio(ERROR_LEVEL).errcode((error < ER_SERVER_RANGE_START)? ER_SERVER_NO_SESSION_TO_SEND_TO: error).lookup(ER_SERVER_NO_SESSION_TO_SEND_TO, error, str);}
}

3. 错误信息发送给客户端

my_message_sql 函数中,如果存在一个连接的客户端(thd 不为 nullptr),会调用 thd->raise_condition 方法来处理错误信息。raise_condition 方法会将错误信息发送给客户端。

C++源码

##

int mysqld_main(int argc, char **argv) {
error_handler_hook = my_message_sql;
}

##

##

/**Fill in and print a previously registered error message.@noteGoes through the (sole) function registered in error_handler_hook@param nr        error number@param MyFlags   Flags@param ...       variable list matching that error format string
*/void my_error(int nr, myf MyFlags, ...) {const char *format;char ebuff[ERRMSGSIZE];DBUG_TRACE;DBUG_PRINT("my", ("nr: %d  MyFlags: %d  errno: %d", nr, MyFlags, errno));if (!(format = my_get_err_msg(nr)))(void)snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr);else {va_list args;va_start(args, MyFlags);(void)vsnprintf(ebuff, sizeof(ebuff), format, args);va_end(args);}/*Since this function is an error function, it will frequently be givenvalues that are too long (and thus truncated on byte boundaries,not code point or grapheme boundaries), values that are binary, etc..Go through and replace every malformed UTF-8 byte with a question mark,so that the result is safe to send to the client and makes sense to readfor the user.*/for (char *ptr = ebuff, *end = ebuff + strlen(ebuff); ptr != end;) {my_wc_t ignored;int len = my_mb_wc_utf8mb4(&ignored, pointer_cast<const uchar *>(ptr),pointer_cast<const uchar *>(end));if (len > 0) {ptr += len;} else {*ptr++ = '?';}}(*error_handler_hook)(nr, ebuff, MyFlags);
}

##

/**All global error messages are sent here where the first one is storedfor the client.
*/
/* ARGSUSED */
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);void my_message_sql(uint error, const char *str, myf MyFlags) {THD *thd = current_thd;DBUG_TRACE;DBUG_PRINT("error", ("error: %u  message: '%s'", error, str));assert(str != nullptr);/*An error should have a valid error number (!= 0), so it can be caughtin stored procedures by SQL exception handlers.Calling my_error() with error == 0 is a bug.Remaining known places to fix:- storage/myisam/mi_create.c, my_printf_error()TODO:assert(error != 0);*/if (error == 0) {/* At least, prevent new abuse ... */assert(strncmp(str, "MyISAM table", 12) == 0);error = ER_UNKNOWN_ERROR;}/* Caller wishes to inform client, and one is attached. */if (thd) {(void)thd->raise_condition(error, nullptr, Sql_condition::SL_ERROR, str,MyFlags & ME_FATALERROR);/*Now for an argument check.We're asserting after rather than before raising thecondition to make the culprit easier to track down.Messages intended for the error-log are in the rangestarting at ER_SERVER_RANGE_START (error_code 10,000);messages intended for sending to a client are in therange below ER_SERVER_RANGE_START. If a message is tobe sent to both a client and the error log, it mustbe added twice (once in each range), and two separatecalls (e.g. my_error() and LogErr()) must be added tothe code.Only error-codes from the client range should be seenin this if(). If your patch asserts here, one of twothings probably happened:- You added a new message to messages_to_error_log.txt:The message was added to the server range, but codewas added that tries to send the message to a client(my_error(), push_warning_printf(), etc.).=> Move the new message to messages_to_clients.txt.The copied message should be added at the end ofthe range for the lowest server version you're addingthe message to.Rebuild the server; rerun your test.- You used an existing message:The existing message is intended for use withthe error-log (it appears in messages_to_error_log.txt),but the new code tries to send it to a client (my_error(),push_warning_printf(), etc.).=> Copy the existing message to messages_to_clients.txt.- The copied message should be added at the end ofthe range for the lowest server version you're addingthe message to.- The copied message will need its own symbol;if in doubt, call the copy of ER_EXAMPLE_MESSAGEER_DA_EXAMPLE_MESSAGE (as this version is for usewith the diagnostics area).Then make sure that your new code referencesthis new symbol when it sends the messageto a client.Rebuild the server; rerun your test.We'll assert this here (rather than in raise_condition) asSQL's SIGNAL command also calls raise_condition, and SIGNALis currently allowed to set any error-code (regardless ofrange). SIGNALing an error-code from the error-log rangewill not result in writing to that log to prevent abuse.*/assert(error < ER_SERVER_RANGE_START);}/* When simulating OOM, skip writing to error log to avoid mtr errors */DBUG_EXECUTE_IF("simulate_out_of_memory", return;);/*Caller wishes to send to both the client and the error-log.This is legacy behaviour that is no longer legal as errors flaggedto a client and those sent to the error-log are in differentnumeric ranges now. If you own code that does this, see aboutupdating it by splitting it into two calls, one sending statusto the client, the other sending it to the error-log usingLogErr() and friends.*/if (MyFlags & ME_ERRORLOG) {/*We've removed most uses of ME_ERRORLOG in the server.This leaves three possible cases, in which we'll rewritethe error-code from one in the client-range to one inthe error-log range here:- EE_OUTOFMEMORY: Correct to ER_SERVER_OUT_OF_RESOURCES somysys can remain logger-agnostic.- HA_* range:     Correct to catch-all ER_SERVER_HANDLER_ERROR.- otherwise:      Flag as using info from the diagnostics area(ER_ERROR_INFO_FROM_DA). This is a failsafe;if your code triggers it, your code is probablywrong.*/if ((error == EE_OUTOFMEMORY) || (error == HA_ERR_OUT_OF_MEM))error = ER_SERVER_OUT_OF_RESOURCES;else if (error <= HA_ERR_LAST)error = ER_SERVER_HANDLER_ERROR;if (error < ER_SERVER_RANGE_START)LogEvent().type(LOG_TYPE_ERROR).prio(ERROR_LEVEL).errcode(ER_ERROR_INFO_FROM_DA).lookup(ER_ERROR_INFO_FROM_DA, error, str);elseLogEvent().type(LOG_TYPE_ERROR).prio(ERROR_LEVEL).errcode(error).verbatim(str);/*This is no longer supported behaviour except for the casesoutlined above, so flag anything else in debug builds!(We're bailing after rather than before printing to make theculprit easier to track down.)*/assert((error == ER_FEATURE_NOT_AVAILABLE) ||(error >= ER_SERVER_RANGE_START));}/*Caller wishes to send to client, but none is attached, so we sendto error-log instead.*/else if (!thd) {LogEvent().type(LOG_TYPE_ERROR).subsys(LOG_SUBSYSTEM_TAG).prio(ERROR_LEVEL).errcode((error < ER_SERVER_RANGE_START)? ER_SERVER_NO_SESSION_TO_SEND_TO: error).lookup(ER_SERVER_NO_SESSION_TO_SEND_TO, error, str);}
}

##gdb调用栈

#0  my_message_sql (error=1118,str=0x71f16e5fa830 "Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs", MyFlags=0) at /home/yym/mysql8/mysql-8.1.0/sql/mysqld.cc:3830
#1  0x00005f5a64be63bc in my_error (nr=1118, MyFlags=0) at /home/yym/mysql8/mysql-8.1.0/mysys/my_error.cc:249
#2  0x00005f5a63444e00 in mysql_prepare_create_table (thd=0x71f078000f10, error_schema_name=0x71f0783d2018 "grey", error_table_name=0x71f0783a1010 "o2o_hot_marketing_test",create_info=0x71f16e5fc2a0, alter_info=0x71f16e5fc3e0, file=0x71f0784426f0, is_partitioned=false, key_info_buffer=0x71f16e5fb470, key_count=0x71f16e5fb468,fk_key_info_buffer=0x71f16e5fb450, fk_key_count=0x71f16e5fb44c, existing_fks=0x0, existing_fks_count=0, existing_fks_table=0x0, fk_max_generated_name_number=0,select_field_count=0, find_parent_keys=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_table.cc:8349
#3  0x00005f5a63446fa2 in create_table_impl (thd=0x71f078000f10, schema=..., db=0x71f0783d2018 "grey", table_name=0x71f0783a1010 "o2o_hot_marketing_test",error_table_name=0x71f0783a1010 "o2o_hot_marketing_test", path=0x71f16e5fb550 "./grey/o2o_hot_marketing_test", create_info=0x71f16e5fc2a0, alter_info=0x71f16e5fc3e0,internal_tmp_table=false, select_field_count=0, find_parent_keys=true, no_ha_table=false, do_not_store_in_dd=false, is_trans=0x71f16e5fb7d7, key_info=0x71f16e5fb470,key_count=0x71f16e5fb468, keys_onoff=Alter_info::ENABLE, fk_key_info=0x71f16e5fb450, fk_key_count=0x71f16e5fb44c, existing_fk_info=0x0, existing_fk_count=0,existing_fk_table=0x0, fk_max_generated_name_number=0, table_def=0x71f16e5fb458, post_ddl_ht=0x71f16e5fb7e0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_table.cc:8916
#4  0x00005f5a6344835d in mysql_create_table_no_lock (thd=0x71f078000f10, db=0x71f0783d2018 "grey", table_name=0x71f0783a1010 "o2o_hot_marketing_test",create_info=0x71f16e5fc2a0, alter_info=0x71f16e5fc3e0, select_field_count=0, find_parent_keys=true, is_trans=0x71f16e5fb7d7, post_ddl_ht=0x71f16e5fb7e0)at /home/yym/mysql8/mysql-8.1.0/sql/sql_table.cc:9231
#5  0x00005f5a6344bf2d in mysql_create_table (thd=0x71f078000f10, create_table=0x71f0783d19c0, create_info=0x71f16e5fc2a0, alter_info=0x71f16e5fc3e0)at /home/yym/mysql8/mysql-8.1.0/sql/sql_table.cc:10148
#6  0x00005f5a63b44239 in Sql_cmd_create_table::execute (this=0x71f0783d3508, thd=0x71f078000f10) at /home/yym/mysql8/mysql-8.1.0/sql/sql_cmd_ddl_table.cc:435
#7  0x00005f5a63366fbb in mysql_execute_command (thd=0x71f078000f10, first_level=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:3736
#8  0x00005f5a6336ccb3 in dispatch_sql_command (thd=0x71f078000f10, parser_state=0x71f16e5fd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5447
#9  0x00005f5a633620d7 in dispatch_command (thd=0x71f078000f10, com_data=0x71f16e5fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#10 0x00005f5a6335ff77 in do_command (thd=0x71f078000f10) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#11 0x00005f5a635b7835 in handle_connection (arg=0x5f5a6c557d70) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#12 0x00005f5a654f6bdc in pfs_spawn_thread (arg=0x5f5a6c52c670) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#13 0x000071f17d494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#14 0x000071f17d526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

##gdb调用栈

(gdb) bt
#0  Protocol_classic::send_error (this=0x71f0784347c0, sql_errno=1118,err_msg=0x71f078003960 "Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs", sql_state=0x71f078003b60 "42000") at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:1344
#1  0x00005f5a63263b3d in THD::send_statement_status (this=0x71f078000f10) at /home/yym/mysql8/mysql-8.1.0/sql/sql_class.cc:2904
#2  0x00005f5a6336379e in dispatch_command (thd=0x71f078000f10, com_data=0x71f16e5fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2501
#3  0x00005f5a6335ff77 in do_command (thd=0x71f078000f10) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#4  0x00005f5a635b7835 in handle_connection (arg=0x5f5a6c557d70) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#5  0x00005f5a654f6bdc in pfs_spawn_thread (arg=0x5f5a6c52c670) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#6  0x000071f17d494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#7  0x000071f17d526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

##gdb调用栈网络发送sql错误信息

(gdb) bt
#0  net_write_buff (net=0x71f078002d48,packet=0x71f16e5fd500 "^\004#42000Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLO"..., len=202) at /home/yym/mysql8/mysql-8.1.0/sql-common/net_serv.cc:937
#1  0x00005f5a635883ac in net_write_command (net=0x71f078002d48, command=255 '\377', header=0x5f5a664abad0 "", head_len=0,packet=0x71f16e5fd500 "^\004#42000Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLO"..., len=202) at /home/yym/mysql8/mysql-8.1.0/sql-common/net_serv.cc:907
#2  0x00005f5a63abb524 in net_send_error_packet (net=0x71f078002d48, sql_errno=1118,err=0x71f078003960 "Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs", sqlstate=0x71f078003b60 "42000", bootstrap=false, client_capabilities=2147656357,character_set_results=0x5f5a6867d520 <my_charset_utf8mb3_general_ci>) at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:1236
#3  0x00005f5a63abb31d in net_send_error_packet (thd=0x71f078000f10, sql_errno=1118,err=0x71f078003960 "Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs", sqlstate=0x71f078003b60 "42000") at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:1177
#4  0x00005f5a63abb965 in Protocol_classic::send_error (this=0x71f0784347c0, sql_errno=1118,err_msg=0x71f078003960 "Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs", sql_state=0x71f078003b60 "42000") at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:1347
#5  0x00005f5a63263b3d in THD::send_statement_status (this=0x71f078000f10) at /home/yym/mysql8/mysql-8.1.0/sql/sql_class.cc:2904
#6  0x00005f5a6336379e in dispatch_command (thd=0x71f078000f10, com_data=0x71f16e5fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2501
#7  0x00005f5a6335ff77 in do_command (thd=0x71f078000f10) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#8  0x00005f5a635b7835 in handle_connection (arg=0x5f5a6c557d70) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#9  0x00005f5a654f6bdc in pfs_spawn_thread (arg=0x5f5a6c52c670) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#10 0x000071f17d494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#11 0x000071f17d526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81


http://www.ppmy.cn/server/166090.html

相关文章

【DeepSeek】DeepSeek小模型蒸馏与本地部署深度解析DeepSeek小模型蒸馏与本地部署深度解析

一、引言与背景 在人工智能领域&#xff0c;大型语言模型&#xff08;LLM&#xff09;如DeepSeek以其卓越的自然语言理解和生成能力&#xff0c;推动了众多应用场景的发展。然而&#xff0c;大型模型的高昂计算和存储成本&#xff0c;以及潜在的数据隐私风险&#xff0c;限制了…

Springboot中使用Elasticsearch(部署+使用+讲解 最完整)

目录 引言 一、docker中安装Elasticsearch 1、创建es专有的网络 2、开放端口 3、在es-net网络上安装es和kibana 4、可能出现的问题 5、测试 6、安装IK分词器 7、测试IK分词器 二、结合业务实战 1、准备依赖 2、配置yml 3、读取yml配置 4、准备es配置类 5、编写测…

软件模拟I2C案例(寄存器实现)

引言 在经过前面对I2C基础知识的理解&#xff0c;对支持I2C通讯的EEPROM芯片M24C02的简单介绍以及涉及到的时序操作做了整理。接下来&#xff0c;我们就正式进入该案例的实现环节了。本次案例是基于寄存器开发方式通过软件模拟I2C通讯协议&#xff0c;然后去实现相关的需求。 阅…

智慧城市(城市大脑)建设方案

城市建设面临的挑战 快速发展的城市面临着诸多挑战&#xff0c;如数据壁垒、缺乏数据整合、数据价值挖掘不足&#xff0c;以及应急指挥手段传统等问题。这些问题制约了城市管理的效率和效果&#xff0c;亟待解决。 智慧城市建设背景 在国家治理现代化战略的大背景下&#x…

【自动化测试】使用Python selenium类库模拟手人工操作网页

使用Python selenium类库模拟手人工操作网页 背景准备工作安装Python版本安装selenium类库下载selenium驱动配置本地环境变量 自动化脚本输出页面表单自动化填充相关代码 背景 待操作网页必须使用IE浏览器登录访问用户本地只有edge浏览器&#xff0c;通过edge浏览器IE模式访问…

实现一个 LRU 风格的缓存类

实现一个缓存类 需求描述豆包解决思路&#xff1a;实现代码&#xff1a;优化11. std::list::remove 的时间复杂度问题2. 代码复用优化后的代码优化说明 优化21. 边界条件检查2. 异常处理3. 代码封装性4. 线程安全优化后的代码示例优化说明 DeepSeek&#xff08;深度思考R1&…

力扣240 搜索二维矩阵 ll

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,…

C语言的物联网

C语言在物联网中的应用 物联网&#xff08;Internet of Things&#xff0c;IoT&#xff09;是一个通过网络将各种物理设备连接起来的系统&#xff0c;使其能够收集和交换数据。随着技术的进步&#xff0c;物联网已经走入了我们的日常生活&#xff0c;并在智能家居、智能城市、…