/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ #include "mysql_priv.h" #include /* Send a error string to client */ void send_error(NET *net, uint sql_errno, const char *err) { uint length; char buff[MYSQL_ERRMSG_SIZE+2]; THD *thd=current_thd; DBUG_ENTER("send_error"); DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err ? err : net->last_error[0] ? net->last_error : "NULL")); if (thd) thd->query_error = 1; // needed to catch query errors during replication if (!err) { if (sql_errno) err=ER(sql_errno); else if (!err) { if ((err=net->last_error)[0]) sql_errno=net->last_errno; else { sql_errno=ER_UNKNOWN_ERROR; err=ER(sql_errno); /* purecov: inspected */ } } } if (net->vio == 0) { if (thd && thd->bootstrap) { fprintf(stderr,"ERROR: %d %s\n",sql_errno,err); } DBUG_VOID_RETURN; } if (net->return_errno) { // new client code; Add errno before message int2store(buff,sql_errno); length= (uint) (strmake(buff+2,err,MYSQL_ERRMSG_SIZE-1) - buff); err=buff; } else { length=(uint) strlen(err); set_if_smaller(length,MYSQL_ERRMSG_SIZE); } VOID(net_write_command(net,(uchar) 255,(char*) err,length)); if (thd) thd->fatal_error=0; // Error message is given DBUG_VOID_RETURN; } /* At some point we need to be able to distinguish between warnings and errors; The following function will help make this easier. */ void send_warning(NET *net, uint sql_errno, const char *err) { DBUG_ENTER("send_warning"); send_error(net,sql_errno,err); DBUG_VOID_RETURN; } /** ** write error package and flush to client ** It's a little too low level, but I don't want to allow another buffer */ /* VARARGS3 */ void net_printf(NET *net, uint errcode, ...) { va_list args; uint length,offset; const char *format,*text_pos; int head_length= NET_HEADER_SIZE; THD *thd=current_thd; DBUG_ENTER("net_printf"); DBUG_PRINT("enter",("message: %u",errcode)); if(thd) thd->query_error = 1; // if we are here, something is wrong :-) va_start(args,errcode); format=ER(errcode); offset= net->return_errno ? 2 : 0; text_pos=(char*) net->buff+head_length+offset+1; (void) vsprintf(my_const_cast(char*) (text_pos),format,args); length=(uint) strlen((char*) text_pos); if (length >= sizeof(net->last_error)) length=sizeof(net->last_error)-1; /* purecov: inspected */ va_end(args); if (net->vio == 0) { if (thd && thd->bootstrap) { fprintf(stderr,"ERROR: %d %s\n",errcode,text_pos); thd->fatal_error=1; } 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) int2store(text_pos-2, errcode); VOID(net_real_write(net,(char*) net->buff,length+head_length+1+offset)); if (thd) thd->fatal_error=0; // Error message is given DBUG_VOID_RETURN; } void send_ok(NET *net,ha_rows affected_rows,ulonglong id,const char *message) { if (net->no_send_ok) // hack for re-parsing queries return; char buff[MYSQL_ERRMSG_SIZE+10],*pos; DBUG_ENTER("send_ok"); buff[0]=0; // No fields pos=net_store_length(buff+1,(ulonglong) affected_rows); pos=net_store_length(pos, (ulonglong) id); if (net->return_status) { int2store(pos,*net->return_status); pos+=2; } if (message) pos=net_store_data((char*) pos,message); if (net->vio != 0) { VOID(my_net_write(net,buff,(uint) (pos-buff))); VOID(net_flush(net)); } DBUG_VOID_RETURN; } void send_eof(NET *net,bool no_flush) { static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ DBUG_ENTER("send_eof"); if (net->vio != 0) { VOID(my_net_write(net,eof_buff,1)); if (!no_flush) VOID(net_flush(net)); } DBUG_VOID_RETURN; } /**************************************************************************** ** Store a field length in logical packet ****************************************************************************/ char * net_store_length(char *pkg, ulonglong length) { uchar *packet=(uchar*) pkg; if (length < LL(251)) { *packet=(uchar) length; return (char*) packet+1; } /* 251 is reserved for NULL */ if (length < LL(65536)) { *packet++=252; int2store(packet,(uint) length); return (char*) packet+2; } if (length < LL(16777216)) { *packet++=253; int3store(packet,(ulong) length); return (char*) packet+3; } *packet++=254; int8store(packet,length); return (char*) packet+9; } 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; } /* The following will only be used for short strings < 65K */ char * net_store_data(char *to,const char *from) { uint length=(uint) strlen(from); 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; } bool net_store_null(String *packet) { return packet->append((char) 251); } bool net_store_data(String *packet,const char *from,uint length) { ulong packet_length=packet->length(); if (packet_length+5+length > packet->alloced_length() && packet->realloc(packet_length+5+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; } /* The following is only used at short, null terminated data */ bool net_store_data(String *packet,const char *from) { uint length=(uint) strlen(from); uint packet_length=packet->length(); if (packet_length+5+length > packet->alloced_length() && packet->realloc(packet_length+5+length)) return 1; char *to=(char*) net_store_length((char*) packet->ptr()+packet_length, length); memcpy(to,from,length); packet->length((uint) (to+length-packet->ptr())); return 0; } bool net_store_data(String *packet,uint32 from) { char buff[20]; return net_store_data(packet,(char*) buff, (uint) (int10_to_str(from,buff,10)-buff)); } bool net_store_data(String *packet, longlong from) { char buff[22]; return net_store_data(packet,(char*) buff, (uint) (longlong10_to_str(from,buff,10)-buff)); } bool net_store_data(String *packet,struct tm *tmp) { char buff[20]; sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d", ((int) (tmp->tm_year+1900)) % 10000, (int) tmp->tm_mon+1, (int) tmp->tm_mday, (int) tmp->tm_hour, (int) tmp->tm_min, (int) tmp->tm_sec); return net_store_data(packet,(char*) buff,19); } bool net_store_data(String* packet, I_List* str_list) { char buf[256]; String tmp(buf, sizeof(buf)); tmp.length(0); I_List_iterator it(*str_list); i_string* s; while((s=it++)) { if(tmp.length()) tmp.append(','); tmp.append(s->ptr); } return net_store_data(packet, (char*)tmp.ptr(), tmp.length()); } /* ** translate and store data; These are mainly used by the SHOW functions */ bool net_store_data(String *packet,CONVERT *convert, const char *from,uint length) { if (convert) return convert->store(packet, from, length); return net_store_data(packet,from,length); } bool net_store_data(String *packet, CONVERT *convert, const char *from) { uint length=(uint) strlen(from); if (convert) return convert->store(packet, from, length); return net_store_data(packet,from,length); }