summaryrefslogtreecommitdiff
path: root/sql/protocol.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/protocol.cc')
-rw-r--r--sql/protocol.cc1161
1 files changed, 1161 insertions, 0 deletions
diff --git a/sql/protocol.cc b/sql/protocol.cc
new file mode 100644
index 00000000000..a5944af829d
--- /dev/null
+++ b/sql/protocol.cc
@@ -0,0 +1,1161 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <stdarg.h>
+
+#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
+ 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 */
+ }
+ }
+ }
+
+#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 %s\n",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*/
+ 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;
+#endif
+ (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);
+
+#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 %s\n",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
+ 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;
+#ifndef DEBUG_OFF
+ field_types= 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)
+ int2store(pos, field.charsetnr);
+ else
+ int2store(pos, thd_charset->number);
+ int4store(pos+2, field.length);
+ 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 \0 end terminated string
+
+ SYNOPSIS
+ store()
+ from NullS or \0 terminated string
+
+ NOTES
+ In most cases one should use store(from, length) instead of this function
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+bool Protocol::store(const char *from, CHARSET_INFO *cs)
+{
+ if (!from)
+ return store_null();
+ uint length= strlen(from);
+ return store(from, length, cs);
+}
+
+
+/*
+ Send a set of strings as one long string with ',' in between
+*/
+
+bool Protocol::store(I_List<i_string>* str_list)
+{
+ char buf[256];
+ String tmp(buf, sizeof(buf), &my_charset_bin);
+ uint32 len;
+ I_List_iterator<i_string> it(*str_list);
+ i_string* s;
+
+ tmp.length(0);
+ while ((s=it++))
+ {
+ tmp.append(s->ptr);
+ tmp.append(',');
+ }
+ if ((len= tmp.length()))
+ len--; // Remove last ','
+ return store((char*) tmp.ptr(), len, tmp.charset());
+}
+
+
+/****************************************************************************
+ Functions to handle the simple (default) protocol where everything is
+ This protocol is the one that is used by default between the MySQL server
+ and client when you are not using prepared statements.
+
+ All data are sent as 'packed-string-length' followed by 'string-data'
+****************************************************************************/
+
+#ifndef EMBEDDED_LIBRARY
+void Protocol_simple::prepare_for_resend()
+{
+ packet->length(0);
+#ifndef DEBUG_OFF
+ field_pos= 0;
+#endif
+}
+
+bool Protocol_simple::store_null()
+{
+#ifndef DEBUG_OFF
+ field_pos++;
+#endif
+ char buff[1];
+ buff[0]= (char)251;
+ return packet->append(buff, sizeof(buff), PACKET_BUFFET_EXTRA_ALLOC);
+}
+#endif
+
+
+bool Protocol_simple::store(const char *from, uint length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
+ (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
+ field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
+ field_pos++;
+#endif
+ if (tocs && !my_charset_same(fromcs, tocs) &&
+ (fromcs != &my_charset_bin) &&
+ (tocs != &my_charset_bin))
+ {
+ convert.copy(from, length, fromcs, tocs);
+ return net_store_data(convert.ptr(), convert.length());
+ }
+ else
+ return net_store_data(from, length);
+}
+
+
+bool Protocol_simple::store(const char *from, uint length,
+ CHARSET_INFO *fromcs)
+{
+ CHARSET_INFO *tocs= this->thd->variables.character_set_results;
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
+ (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
+ field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
+ field_pos++;
+#endif
+ if (tocs && !my_charset_same(fromcs, tocs) &&
+ (fromcs != &my_charset_bin) &&
+ (tocs != &my_charset_bin))
+ {
+ convert.copy(from, length, fromcs, tocs);
+ return net_store_data(convert.ptr(), convert.length());
+ }
+ else
+ return net_store_data(from, length);
+}
+
+
+bool Protocol_simple::store_tiny(longlong from)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY);
+ field_pos++;
+#endif
+ char buff[20];
+ return net_store_data((char*) buff,
+ (uint) (int10_to_str((int) from,buff, -10)-buff));
+}
+
+
+bool Protocol_simple::store_short(longlong from)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_SHORT);
+ field_pos++;
+#endif
+ char buff[20];
+ return net_store_data((char*) buff,
+ (uint) (int10_to_str((int) from,buff, -10)-buff));
+}
+
+
+bool Protocol_simple::store_long(longlong from)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_INT24 ||
+ field_types[field_pos] == MYSQL_TYPE_LONG);
+ field_pos++;
+#endif
+ char buff[20];
+ return net_store_data((char*) buff,
+ (uint) (int10_to_str((int) from,buff, -10)-buff));
+}
+
+
+bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_LONGLONG);
+ field_pos++;
+#endif
+ char buff[22];
+ return net_store_data((char*) buff,
+ (uint) (longlong10_to_str(from,buff,
+ unsigned_flag ? 10 : -10)-
+ buff));
+}
+
+
+bool Protocol_simple::store(float from, uint32 decimals, String *buffer)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_FLOAT);
+ field_pos++;
+#endif
+ buffer->set((double) from, decimals, thd->charset());
+ return net_store_data((char*) buffer->ptr(), buffer->length());
+}
+
+
+bool Protocol_simple::store(double from, uint32 decimals, String *buffer)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DOUBLE);
+ field_pos++;
+#endif
+ buffer->set(from, decimals, thd->charset());
+ return net_store_data((char*) buffer->ptr(), buffer->length());
+}
+
+
+bool Protocol_simple::store(Field *field)
+{
+ if (field->is_null())
+ return store_null();
+#ifndef DEBUG_OFF
+ field_pos++;
+#endif
+ char buff[MAX_FIELD_WIDTH];
+ String str(buff,sizeof(buff), &my_charset_bin);
+ CHARSET_INFO *tocs= this->thd->variables.character_set_results;
+
+ field->val_str(&str);
+ if (tocs && !my_charset_same(field->charset(), tocs) &&
+ (field->charset() != &my_charset_bin) &&
+ (tocs != &my_charset_bin))
+ {
+ convert.copy(str.ptr(), str.length(), str.charset(), tocs);
+ return net_store_data(convert.ptr(), convert.length());
+ }
+ else
+ return net_store_data(str.ptr(), str.length());
+}
+
+
+/*
+ TODO:
+ Second_part format ("%06") needs to change when
+ we support 0-6 decimals for time.
+*/
+
+
+bool Protocol_simple::store(TIME *tm)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DATETIME ||
+ field_types[field_pos] == MYSQL_TYPE_TIMESTAMP);
+ field_pos++;
+#endif
+ char buff[40];
+ uint length;
+ length= my_sprintf(buff,(buff, "%04d-%02d-%02d %02d:%02d:%02d",
+ (int) tm->year,
+ (int) tm->month,
+ (int) tm->day,
+ (int) tm->hour,
+ (int) tm->minute,
+ (int) tm->second));
+ if (tm->second_part)
+ length+= my_sprintf(buff+length,(buff+length, ".%06d", (int)tm->second_part));
+ return net_store_data((char*) buff, length);
+}
+
+
+bool Protocol_simple::store_date(TIME *tm)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DATE);
+ field_pos++;
+#endif
+ char buff[40];
+ String tmp((char*) buff,sizeof(buff),&my_charset_bin);
+ make_date((DATE_TIME_FORMAT *) 0, tm, &tmp);
+ return net_store_data((char*) tmp.ptr(), tmp.length());
+}
+
+
+/*
+ TODO:
+ Second_part format ("%06") needs to change when
+ we support 0-6 decimals for time.
+*/
+
+bool Protocol_simple::store_time(TIME *tm)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_TIME);
+ field_pos++;
+#endif
+ char buff[40];
+ uint length;
+ uint day= (tm->year || tm->month) ? 0 : tm->day;
+ length= my_sprintf(buff,(buff, "%s%02ld:%02d:%02d",
+ tm->neg ? "-" : "",
+ (long) day*24L+(long) tm->hour,
+ (int) tm->minute,
+ (int) tm->second));
+ if (tm->second_part)
+ length+= my_sprintf(buff+length,(buff+length, ".%06d", (int)tm->second_part));
+ return net_store_data((char*) buff, length);
+}
+
+
+/****************************************************************************
+ Functions to handle the binary protocol used with prepared statements
+
+ Data format:
+
+ [ok:1] reserved ok packet
+ [null_field:(field_count+7+2)/8] reserved to send null data. The size is
+ calculated using:
+ bit_fields= (field_count+7+2)/8;
+ 2 bits are reserved for identifying type
+ of package.
+ [[length]data] data field (the length applies only for
+ string/binary/time/timestamp fields and
+ rest of them are not sent as they have
+ the default length that client understands
+ based on the field type
+ [..]..[[length]data] data
+****************************************************************************/
+
+bool Protocol_prep::prepare_for_send(List<Item> *item_list)
+{
+ Protocol::prepare_for_send(item_list);
+ bit_fields= (field_count+9)/8;
+ if (packet->alloc(bit_fields+1))
+ return 1;
+ /* prepare_for_resend will be called after this one */
+ return 0;
+}
+
+
+void Protocol_prep::prepare_for_resend()
+{
+ packet->length(bit_fields+1);
+ bzero((char*) packet->ptr(), 1+bit_fields);
+ field_pos=0;
+}
+
+
+bool Protocol_prep::store(const char *from,uint length, CHARSET_INFO *cs)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
+ (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
+ field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
+#endif
+ field_pos++;
+ return net_store_data(from, length);
+}
+
+bool Protocol_prep::store(const char *from,uint length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
+ (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
+ field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
+#endif
+ field_pos++;
+ return net_store_data(from, length);
+}
+
+bool Protocol_prep::store_null()
+{
+ uint offset= (field_pos+2)/8+1, bit= (1 << ((field_pos+2) & 7));
+ /* Room for this as it's allocated in prepare_for_send */
+ char *to= (char*) packet->ptr()+offset;
+ *to= (char) ((uchar) *to | (uchar) bit);
+ field_pos++;
+ return 0;
+}
+
+
+bool Protocol_prep::store_tiny(longlong from)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_TINY);
+#endif
+ char buff[1];
+ field_pos++;
+ buff[0]= (uchar) from;
+ return packet->append(buff, sizeof(buff), PACKET_BUFFET_EXTRA_ALLOC);
+}
+
+
+bool Protocol_prep::store_short(longlong from)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_SHORT ||
+ field_types[field_pos] == MYSQL_TYPE_YEAR);
+#endif
+ field_pos++;
+ char *to= packet->prep_append(2, PACKET_BUFFET_EXTRA_ALLOC);
+ if (!to)
+ return 1;
+ int2store(to, (int) from);
+ return 0;
+}
+
+
+bool Protocol_prep::store_long(longlong from)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_INT24 ||
+ field_types[field_pos] == MYSQL_TYPE_LONG);
+#endif
+ field_pos++;
+ char *to= packet->prep_append(4, PACKET_BUFFET_EXTRA_ALLOC);
+ if (!to)
+ return 1;
+ int4store(to, from);
+ return 0;
+}
+
+
+bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_LONGLONG);
+#endif
+ field_pos++;
+ char *to= packet->prep_append(8, PACKET_BUFFET_EXTRA_ALLOC);
+ if (!to)
+ return 1;
+ int8store(to, from);
+ return 0;
+}
+
+
+bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_FLOAT);
+#endif
+ field_pos++;
+ char *to= packet->prep_append(4, PACKET_BUFFET_EXTRA_ALLOC);
+ if (!to)
+ return 1;
+ float4store(to, from);
+ return 0;
+}
+
+
+bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DOUBLE);
+#endif
+ field_pos++;
+ char *to= packet->prep_append(8, PACKET_BUFFET_EXTRA_ALLOC);
+ if (!to)
+ return 1;
+ float8store(to, from);
+ return 0;
+}
+
+
+bool Protocol_prep::store(Field *field)
+{
+ /*
+ We should not increment field_pos here as send_binary() will call another
+ protocol function to do this for us
+ */
+ if (field->is_null())
+ return store_null();
+ return field->send_binary(this);
+}
+
+
+bool Protocol_prep::store(TIME *tm)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_DATETIME ||
+ field_types[field_pos] == MYSQL_TYPE_DATE ||
+ field_types[field_pos] == MYSQL_TYPE_TIMESTAMP);
+#endif
+ char buff[12],*pos;
+ uint length;
+ field_pos++;
+ pos= buff+1;
+
+ int2store(pos, tm->year);
+ pos[2]= (uchar) tm->month;
+ pos[3]= (uchar) tm->day;
+ pos[4]= (uchar) tm->hour;
+ pos[5]= (uchar) tm->minute;
+ pos[6]= (uchar) tm->second;
+ int4store(pos+7, tm->second_part);
+ if (tm->second_part)
+ length=11;
+ else if (tm->hour || tm->minute || tm->second)
+ length=7;
+ else if (tm->year || tm->month || tm->day)
+ length=4;
+ else
+ length=0;
+ buff[0]=(char) length; // Length is stored first
+ return packet->append(buff, length+1, PACKET_BUFFET_EXTRA_ALLOC);
+}
+
+bool Protocol_prep::store_date(TIME *tm)
+{
+ tm->hour= tm->minute= tm->second=0;
+ tm->second_part= 0;
+ return Protocol_prep::store(tm);
+}
+
+
+bool Protocol_prep::store_time(TIME *tm)
+{
+#ifndef DEBUG_OFF
+ DBUG_ASSERT(field_types == 0 ||
+ field_types[field_pos] == MYSQL_TYPE_TIME);
+#endif
+ char buff[15],*pos;
+ uint length;
+ field_pos++;
+ pos= buff+1;
+ pos[0]= tm->neg ? 1 : 0;
+ int4store(pos+1, tm->day);
+ pos[5]= (uchar) tm->hour;
+ pos[6]= (uchar) tm->minute;
+ pos[7]= (uchar) tm->second;
+ int4store(pos+8, tm->second_part);
+ if (tm->second_part)
+ length=11;
+ else if (tm->hour || tm->minute || tm->second || tm->day)
+ length=8;
+ else
+ length=0;
+ buff[0]=(char) length; // Length is stored first
+ return packet->append(buff, length+1, PACKET_BUFFET_EXTRA_ALLOC);
+}
+
+#ifdef EMBEDDED_LIBRARY
+/* Should be removed when we define the Protocol_cursor's future */
+bool Protocol_cursor::write()
+{
+ return Protocol_simple::write();
+}
+#endif
+