protocol.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:31k
- /* Copyright (C) 2000-2003 MySQL AB
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
- /*
- Low level functions for storing data to be send to the MySQL client
- The actual communction is handled by the net_xxx functions in net_serv.cc
- */
- #ifdef USE_PRAGMA_IMPLEMENTATION
- #pragma implementation // gcc: Class implementation
- #endif
- #include "mysql_priv.h"
- #include <stdarg.h>
- static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
- #ifndef EMBEDDED_LIBRARY
- bool Protocol::net_store_data(const char *from, uint length)
- #else
- bool Protocol_prep::net_store_data(const char *from, uint length)
- #endif
- {
- ulong packet_length=packet->length();
- /*
- The +9 comes from that strings of length longer than 16M require
- 9 bytes to be stored (see net_store_length).
- */
- if (packet_length+9+length > packet->alloced_length() &&
- packet->realloc(packet_length+9+length))
- return 1;
- char *to=(char*) net_store_length((char*) packet->ptr()+packet_length,
- (ulonglong) length);
- memcpy(to,from,length);
- packet->length((uint) (to+length-packet->ptr()));
- return 0;
- }
- /* Send a error string to client */
- void send_error(THD *thd, uint sql_errno, const char *err)
- {
- #ifndef EMBEDDED_LIBRARY
- uint length;
- char buff[MYSQL_ERRMSG_SIZE+2], *pos;
- #endif
- const char *orig_err= err;
- NET *net= &thd->net;
- DBUG_ENTER("send_error");
- DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno,
- err ? err : net->last_error[0] ?
- net->last_error : "NULL"));
- #ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
- query_cache_abort(net);
- #endif
- thd->query_error= 1; // needed to catch query errors during replication
- if (!err)
- {
- if (sql_errno)
- err=ER(sql_errno);
- else
- {
- if ((err=net->last_error)[0])
- sql_errno=net->last_errno;
- else
- {
- sql_errno=ER_UNKNOWN_ERROR;
- err=ER(sql_errno); /* purecov: inspected */
- }
- }
- orig_err= err;
- }
- #ifdef EMBEDDED_LIBRARY
- net->last_errno= sql_errno;
- strmake(net->last_error, err, sizeof(net->last_error)-1);
- strmov(net->sqlstate, mysql_errno_to_sqlstate(sql_errno));
- #else
- if (net->vio == 0)
- {
- if (thd->bootstrap)
- {
- /* In bootstrap it's ok to print on stderr */
- fprintf(stderr,"ERROR: %d %sn",sql_errno,err);
- }
- DBUG_VOID_RETURN;
- }
- if (net->return_errno)
- { // new client code; Add errno before message
- int2store(buff,sql_errno);
- pos= buff+2;
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- /* The first # is to make the protocol backward compatible */
- buff[2]= '#';
- pos= strmov(buff+3, mysql_errno_to_sqlstate(sql_errno));
- }
- length= (uint) (strmake(pos, err, MYSQL_ERRMSG_SIZE-1) - buff);
- err=buff;
- }
- else
- {
- length=(uint) strlen(err);
- set_if_smaller(length,MYSQL_ERRMSG_SIZE-1);
- }
- VOID(net_write_command(net,(uchar) 255, "", 0, (char*) err,length));
- #endif /* EMBEDDED_LIBRARY*/
- if (!thd->killed)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, sql_errno,
- orig_err ? orig_err : ER(sql_errno));
- thd->is_fatal_error=0; // Error message is given
- thd->net.report_error= 0;
- /* Abort multi-result sets */
- thd->lex->found_colon= 0;
- thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
- DBUG_VOID_RETURN;
- }
- /*
- Send a warning to the end user
- SYNOPSIS
- send_warning()
- thd Thread handler
- sql_errno Warning number (error message)
- err Error string. If not set, use ER(sql_errno)
- DESCRIPTION
- Register the warning so that the user can get it with mysql_warnings()
- Send an ok (+ warning count) to the end user.
- */
- void send_warning(THD *thd, uint sql_errno, const char *err)
- {
- DBUG_ENTER("send_warning");
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno,
- err ? err : ER(sql_errno));
- send_ok(thd);
- DBUG_VOID_RETURN;
- }
- /*
- Write error package and flush to client
- It's a little too low level, but I don't want to use another buffer for
- this
- */
- void
- net_printf(THD *thd, uint errcode, ...)
- {
- va_list args;
- uint length,offset;
- const char *format;
- #ifndef EMBEDDED_LIBRARY
- const char *text_pos;
- int head_length= NET_HEADER_SIZE;
- #else
- char text_pos[1024];
- #endif
- NET *net= &thd->net;
- DBUG_ENTER("net_printf");
- DBUG_PRINT("enter",("message: %u",errcode));
- thd->query_error= 1; // needed to catch query errors during replication
- #ifndef EMBEDDED_LIBRARY
- query_cache_abort(net); // Safety
- #endif
- va_start(args,errcode);
- /*
- The following is needed to make net_printf() work with 0 argument for
- errorcode and use the argument after that as the format string. This
- is useful for rare errors that are not worth the hassle to put in
- errmsg.sys, but at the same time, the message is not fixed text
- */
- if (errcode)
- format= ER(errcode);
- else
- {
- format=va_arg(args,char*);
- errcode= ER_UNKNOWN_ERROR;
- }
- offset= (net->return_errno ?
- ((thd->client_capabilities & CLIENT_PROTOCOL_41) ?
- 2+SQLSTATE_LENGTH+1 : 2) : 0);
- #ifndef EMBEDDED_LIBRARY
- text_pos=(char*) net->buff + head_length + offset + 1;
- length= (uint) ((char*)net->buff_end - text_pos);
- #else
- length=sizeof(text_pos)-1;
- #endif
- length=my_vsnprintf(my_const_cast(char*) (text_pos),
- min(length, sizeof(net->last_error)),
- format,args);
- va_end(args);
- /* Replication slave relies on net->last_* to see if there was error */
- net->last_errno= errcode;
- strmake(net->last_error, text_pos, sizeof(net->last_error)-1);
- #ifndef EMBEDDED_LIBRARY
- if (net->vio == 0)
- {
- if (thd->bootstrap)
- {
- /*
- In bootstrap it's ok to print on stderr
- This may also happen when we get an error from a slave thread
- */
- fprintf(stderr,"ERROR: %d %sn",errcode,text_pos);
- thd->fatal_error();
- }
- DBUG_VOID_RETURN;
- }
- int3store(net->buff,length+1+offset);
- net->buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
- net->buff[head_length]=(uchar) 255; // Error package
- if (offset)
- {
- uchar *pos= net->buff+head_length+1;
- int2store(pos, errcode);
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- pos[2]= '#'; /* To make the protocol backward compatible */
- memcpy(pos+3, mysql_errno_to_sqlstate(errcode), SQLSTATE_LENGTH);
- }
- }
- VOID(net_real_write(net,(char*) net->buff,length+head_length+1+offset));
- #else
- net->last_errno= errcode;
- strmake(net->last_error, text_pos, length);
- strmake(net->sqlstate, mysql_errno_to_sqlstate(errcode), SQLSTATE_LENGTH);
- #endif
- if (!thd->killed)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, errcode,
- text_pos ? text_pos : ER(errcode));
- thd->is_fatal_error=0; // Error message is given
- DBUG_VOID_RETURN;
- }
- /*
- Return ok to the client.
- SYNOPSIS
- send_ok()
- thd Thread handler
- affected_rows Number of rows changed by statement
- id Auto_increment id for first row (if used)
- message Message to send to the client (Used by mysql_status)
- DESCRIPTION
- The ok packet has the following structure
- 0 Marker (1 byte)
- affected_rows Stored in 1-9 bytes
- id Stored in 1-9 bytes
- server_status Copy of thd->server_status; Can be used by client
- to check if we are inside an transaction
- New in 4.0 protocol
- warning_count Stored in 2 bytes; New in 4.1 protocol
- message Stored as packed length (1-9 bytes) + message
- Is not stored if no message
- If net->no_send_ok return without sending packet
- */
- #ifndef EMBEDDED_LIBRARY
- void
- send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message)
- {
- NET *net= &thd->net;
- char buff[MYSQL_ERRMSG_SIZE+10],*pos;
- DBUG_ENTER("send_ok");
- if (net->no_send_ok || !net->vio) // hack for re-parsing queries
- DBUG_VOID_RETURN;
- buff[0]=0; // No fields
- pos=net_store_length(buff+1,(ulonglong) affected_rows);
- pos=net_store_length(pos, (ulonglong) id);
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- DBUG_PRINT("info",
- ("affected_rows: %lu id: %lu status: %u warning_count: %u",
- (ulong) affected_rows,
- (ulong) id,
- (uint) (thd->server_status & 0xffff),
- (uint) thd->total_warn_count));
- int2store(pos,thd->server_status);
- pos+=2;
- /* We can only return up to 65535 warnings in two bytes */
- uint tmp= min(thd->total_warn_count, 65535);
- int2store(pos, tmp);
- pos+= 2;
- }
- else if (net->return_status) // For 4.0 protocol
- {
- int2store(pos,thd->server_status);
- pos+=2;
- }
- if (message)
- pos=net_store_data((char*) pos, message, strlen(message));
- VOID(my_net_write(net,buff,(uint) (pos-buff)));
- VOID(net_flush(net));
- /* We can't anymore send an error to the client */
- thd->net.report_error= 0;
- DBUG_VOID_RETURN;
- }
- static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
- /*
- Send eof (= end of result set) to the client
- SYNOPSIS
- send_eof()
- thd Thread handler
- no_flush Set to 1 if there will be more data to the client,
- like in send_fields().
- DESCRIPTION
- The eof packet has the following structure
- 254 Marker (1 byte)
- warning_count Stored in 2 bytes; New in 4.1 protocol
- status_flag Stored in 2 bytes;
- For flags like SERVER_STATUS_MORE_RESULTS
- Note that the warning count will not be sent if 'no_flush' is set as
- we don't want to report the warning count until all data is sent to the
- client.
- */
- void
- send_eof(THD *thd, bool no_flush)
- {
- NET *net= &thd->net;
- DBUG_ENTER("send_eof");
- if (net->vio != 0)
- {
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- uchar buff[5];
- uint tmp= min(thd->total_warn_count, 65535);
- buff[0]=254;
- int2store(buff+1, tmp);
- /*
- The following test should never be true, but it's better to do it
- because if 'is_fatal_error' is set the server is not going to execute
- other queries (see the if test in dispatch_command / COM_QUERY)
- */
- if (thd->is_fatal_error)
- thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
- int2store(buff+3, thd->server_status);
- VOID(my_net_write(net,(char*) buff,5));
- VOID(net_flush(net));
- }
- else
- {
- VOID(my_net_write(net,eof_buff,1));
- if (!no_flush)
- VOID(net_flush(net));
- }
- }
- DBUG_VOID_RETURN;
- }
- /*
- Please client to send scrambled_password in old format.
- SYNOPSYS
- send_old_password_request()
- thd thread handle
-
- RETURN VALUE
- 0 ok
- !0 error
- */
- bool send_old_password_request(THD *thd)
- {
- NET *net= &thd->net;
- return my_net_write(net, eof_buff, 1) || net_flush(net);
- }
- #endif /* EMBEDDED_LIBRARY */
- /*
- Faster net_store_length when we know that length is less than 65536.
- We keep a separate version for that range because it's widely used in
- libmysql.
- uint is used as agrument type because of MySQL type conventions:
- uint for 0..65536
- ulong for 0..4294967296
- ulonglong for bigger numbers.
- */
- char *net_store_length(char *pkg, uint length)
- {
- uchar *packet=(uchar*) pkg;
- if (length < 251)
- {
- *packet=(uchar) length;
- return (char*) packet+1;
- }
- *packet++=252;
- int2store(packet,(uint) length);
- return (char*) packet+2;
- }
- /****************************************************************************
- Functions used by the protocol functions (like send_ok) to store strings
- and numbers in the header result packet.
- ****************************************************************************/
- /* The following will only be used for short strings < 65K */
- char *net_store_data(char *to,const char *from, uint length)
- {
- to=net_store_length(to,length);
- memcpy(to,from,length);
- return to+length;
- }
- char *net_store_data(char *to,int32 from)
- {
- char buff[20];
- uint length=(uint) (int10_to_str(from,buff,10)-buff);
- to=net_store_length(to,length);
- memcpy(to,buff,length);
- return to+length;
- }
- char *net_store_data(char *to,longlong from)
- {
- char buff[22];
- uint length=(uint) (longlong10_to_str(from,buff,10)-buff);
- to=net_store_length(to,length);
- memcpy(to,buff,length);
- return to+length;
- }
- /*****************************************************************************
- Default Protocol functions
- *****************************************************************************/
- void Protocol::init(THD *thd_arg)
- {
- thd=thd_arg;
- packet= &thd->packet;
- convert= &thd->convert_buffer;
- #ifndef DEBUG_OFF
- field_types= 0;
- #endif
- }
- bool Protocol::flush()
- {
- #ifndef EMBEDDED_LIBRARY
- return net_flush(&thd->net);
- #else
- return 0;
- #endif
- }
- /*
- Send name and type of result to client.
- SYNOPSIS
- send_fields()
- THD Thread data object
- list List of items to send to client
- flag Bit mask with the following functions:
- 1 send number of rows
- 2 send default values
- DESCRIPTION
- Sum fields has table name empty and field_name.
- RETURN VALUES
- 0 ok
- 1 Error (Note that in this case the error is not sent to the client)
- */
- #ifndef EMBEDDED_LIBRARY
- bool Protocol::send_fields(List<Item> *list, uint flag)
- {
- List_iterator_fast<Item> it(*list);
- Item *item;
- char buff[80];
- String tmp((char*) buff,sizeof(buff),&my_charset_bin);
- Protocol_simple prot(thd);
- String *local_packet= prot.storage_packet();
- CHARSET_INFO *thd_charset= thd->variables.character_set_results;
- DBUG_ENTER("send_fields");
- if (flag & 1)
- { // Packet with number of elements
- char *pos=net_store_length(buff, (uint) list->elements);
- (void) my_net_write(&thd->net, buff,(uint) (pos-buff));
- }
- #ifndef DEBUG_OFF
- field_types= (enum_field_types*) thd->alloc(sizeof(field_types) *
- list->elements);
- uint count= 0;
- #endif
- while ((item=it++))
- {
- char *pos;
- CHARSET_INFO *cs= system_charset_info;
- Send_field field;
- item->make_field(&field);
- prot.prepare_for_resend();
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- if (prot.store("def", 3, cs, thd_charset) ||
- prot.store(field.db_name, (uint) strlen(field.db_name),
- cs, thd_charset) ||
- prot.store(field.table_name, (uint) strlen(field.table_name),
- cs, thd_charset) ||
- prot.store(field.org_table_name, (uint) strlen(field.org_table_name),
- cs, thd_charset) ||
- prot.store(field.col_name, (uint) strlen(field.col_name),
- cs, thd_charset) ||
- prot.store(field.org_col_name, (uint) strlen(field.org_col_name),
- cs, thd_charset) ||
- local_packet->realloc(local_packet->length()+12))
- goto err;
- /* Store fixed length fields */
- pos= (char*) local_packet->ptr()+local_packet->length();
- *pos++= 12; // Length of packed fields
- if (item->collation.collation == &my_charset_bin || thd_charset == NULL)
- {
- /* No conversion */
- int2store(pos, field.charsetnr);
- int4store(pos+2, field.length);
- }
- else
- {
- /* With conversion */
- int2store(pos, thd_charset->number);
- uint char_len= field.length / item->collation.collation->mbmaxlen;
- int4store(pos+2, char_len * thd_charset->mbmaxlen);
- }
- pos[6]= field.type;
- int2store(pos+7,field.flags);
- pos[9]= (char) field.decimals;
- pos[10]= 0; // For the future
- pos[11]= 0; // For the future
- pos+= 12;
- }
- else
- {
- if (prot.store(field.table_name, (uint) strlen(field.table_name),
- cs, thd_charset) ||
- prot.store(field.col_name, (uint) strlen(field.col_name),
- cs, thd_charset) ||
- local_packet->realloc(local_packet->length()+10))
- goto err;
- pos= (char*) local_packet->ptr()+local_packet->length();
- #ifdef TO_BE_DELETED_IN_6
- if (!(thd->client_capabilities & CLIENT_LONG_FLAG))
- {
- pos[0]=3;
- int3store(pos+1,field.length);
- pos[4]=1;
- pos[5]=field.type;
- pos[6]=2;
- pos[7]= (char) field.flags;
- pos[8]= (char) field.decimals;
- pos+= 9;
- }
- else
- #endif
- {
- pos[0]=3;
- int3store(pos+1,field.length);
- pos[4]=1;
- pos[5]=field.type;
- pos[6]=3;
- int2store(pos+7,field.flags);
- pos[9]= (char) field.decimals;
- pos+= 10;
- }
- }
- local_packet->length((uint) (pos - local_packet->ptr()));
- if (flag & 2)
- item->send(&prot, &tmp); // Send default value
- if (prot.write())
- break; /* purecov: inspected */
- #ifndef DEBUG_OFF
- field_types[count++]= field.type;
- #endif
- }
- my_net_write(&thd->net, eof_buff, 1);
- DBUG_RETURN(prepare_for_send(list));
- err:
- send_error(thd,ER_OUT_OF_RESOURCES); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- bool Protocol::send_records_num(List<Item> *list, ulonglong records)
- {
- char *pos;
- char buff[20];
- pos=net_store_length(buff, (uint) list->elements);
- pos=net_store_length(pos, records);
- return my_net_write(&thd->net, buff,(uint) (pos-buff));
- }
- bool Protocol::write()
- {
- DBUG_ENTER("Protocol::write");
- DBUG_RETURN(my_net_write(&thd->net, packet->ptr(), packet->length()));
- }
- #endif /* EMBEDDED_LIBRARY */
- /*
- Send