2003-02-04 20:52:14 +01:00
|
|
|
/* Copyright (C) 2000-2003 MySQL AB
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
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
|
2006-12-23 20:17:15 +01:00
|
|
|
the Free Software Foundation; version 2 of the License.
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2005-05-26 12:09:14 +02:00
|
|
|
#ifdef USE_PRAGMA_IMPLEMENTATION
|
2002-12-11 08:17:51 +01:00
|
|
|
#pragma implementation // gcc: Class implementation
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "mysql_priv.h"
|
2003-09-16 14:26:08 +02:00
|
|
|
#include "sp_rcontext.h"
|
2002-12-11 08:17:51 +01:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2004-06-09 01:21:50 +02:00
|
|
|
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
|
2005-06-30 14:17:10 +02:00
|
|
|
static void write_eof_packet(THD *thd, NET *net);
|
2006-02-24 17:34:15 +01:00
|
|
|
void net_send_error_packet(THD *thd, uint sql_errno, const char *err);
|
2004-06-09 01:21:50 +02:00
|
|
|
|
2003-01-15 09:11:44 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
bool Protocol::net_store_data(const char *from, uint length)
|
2003-09-18 09:25:00 +02:00
|
|
|
#else
|
|
|
|
bool Protocol_prep::net_store_data(const char *from, uint length)
|
|
|
|
#endif
|
2003-01-15 09:11:44 +01:00
|
|
|
{
|
|
|
|
ulong packet_length=packet->length();
|
2003-02-04 20:52:14 +01:00
|
|
|
/*
|
|
|
|
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))
|
2003-01-15 09:11:44 +01:00
|
|
|
return 1;
|
|
|
|
char *to=(char*) net_store_length((char*) packet->ptr()+packet_length,
|
2006-11-13 11:28:55 +01:00
|
|
|
length);
|
2003-01-15 09:11:44 +01:00
|
|
|
memcpy(to,from,length);
|
|
|
|
packet->length((uint) (to+length-packet->ptr()));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-23 03:58:14 +02:00
|
|
|
/*
|
|
|
|
Send a error string to client
|
|
|
|
|
|
|
|
Design note:
|
2002-12-11 08:17:51 +01:00
|
|
|
|
2006-08-23 03:58:14 +02:00
|
|
|
net_printf_error and net_send_error are low-level functions
|
|
|
|
that shall be used only when a new connection is being
|
|
|
|
established or at server startup.
|
|
|
|
For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
|
|
|
|
critical that every error that can be intercepted is issued in one
|
|
|
|
place only, my_message_sql.
|
|
|
|
*/
|
2004-10-20 15:06:54 +02:00
|
|
|
void net_send_error(THD *thd, uint sql_errno, const char *err)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
|
|
|
NET *net= &thd->net;
|
2005-03-09 19:22:30 +01:00
|
|
|
bool generate_warning= thd->killed != THD::KILL_CONNECTION;
|
2004-10-20 15:06:54 +02:00
|
|
|
DBUG_ENTER("net_send_error");
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno,
|
|
|
|
err ? err : net->last_error[0] ?
|
|
|
|
net->last_error : "NULL"));
|
|
|
|
|
2006-08-23 03:58:14 +02:00
|
|
|
DBUG_ASSERT(!thd->spcont);
|
|
|
|
|
2005-01-20 09:41:37 +01:00
|
|
|
if (net && net->no_send_error)
|
|
|
|
{
|
|
|
|
thd->clear_error();
|
|
|
|
DBUG_PRINT("info", ("sending error messages prohibited"));
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
Bug#8153 (Stored procedure with subquery and continue handler, wrong result)
Before this fix,
- a runtime error in a statement in a stored procedure with no error handlers
was properly detected (as expected)
- a runtime error in a statement with an error handler inherited from a non
local runtime context (i.e., proc a with a handler, calling proc b) was
properly detected (as expected)
- a runtime error in a statement with a *local* error handler was executed
as follows :
a) the statement would succeed, regardless of the error condition, (bug)
b) the error handler would be called (as expected).
The root cause is that functions like my_messqge_sql would "forget" to set
the thread flag thd->net.report_error to 1, because of the check involving
sp_rcontext::found_handler_here().
Failure to set this flag would cause, later in the call stack,
in Item_func::fix_fields() at line 190, the code to return FALSE and consider
that executing the statement was successful.
With this fix :
- error handling code, that was duplicated in different places in the code,
is now implemented in sp_rcontext::handle_error(),
- handle_error() correctly sets thd->net.report_error when a handler is
present, regardless of the handler location (local, or in the call stack).
A test case, bug8153_subselect, has been written to demonstrate the change
of behavior before and after the fix.
Another test case, bug8153_function_a, as also been writen.
This test has the same behavior before and after the fix.
This test has been written to demonstrate that the previous expected
result of procedure bug18787, was incorrect, since select no_such_function()
should fail and therefore not produce a result.
The incorrect result for bug18787 has the same root cause as Bug#8153,
and the expected result has been adjusted.
sql/mysqld.cc:
Bug#8153, use sp_rcontext::handle_error() to handle errors.
sql/sql_error.cc:
Bug#8153, use sp_rcontext::handle_error() to handle errors.
sql/protocol.cc:
Bug#8153, use sp_rcontext::handle_error() to handle errors.
sql/sp_rcontext.h:
Bug#8153, created helper sp_rcontext::handle_error() to handle errors.
sql/sp_rcontext.cc:
Bug#8153, created helper sp_rcontext::handle_error() to handle errors.
mysql-test/t/sp.test:
Bug#8153, added test cases.
mysql-test/r/sp.result:
Bug#8153, added test cases, fixed expected result of bug18787.
2006-08-03 07:18:49 +02:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
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])
|
2005-02-24 22:33:42 +01:00
|
|
|
{
|
2002-12-11 08:17:51 +01:00
|
|
|
sql_errno=net->last_errno;
|
2005-02-24 22:33:42 +01:00
|
|
|
generate_warning= 0; // This warning has already been given
|
|
|
|
}
|
2002-12-11 08:17:51 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
sql_errno=ER_UNKNOWN_ERROR;
|
|
|
|
err=ER(sql_errno); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
}
|
2005-02-24 22:33:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (generate_warning)
|
|
|
|
{
|
|
|
|
/* Error that we have not got with my_error() */
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, sql_errno, err);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
2002-12-16 15:58:55 +01:00
|
|
|
|
2006-02-24 17:34:15 +01:00
|
|
|
net_send_error_packet(thd, sql_errno, err);
|
2002-12-11 08:17:51 +01:00
|
|
|
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->is_fatal_error=0; // Error message is given
|
2002-12-11 08:17:51 +01:00
|
|
|
thd->net.report_error= 0;
|
2003-11-18 12:47:27 +01:00
|
|
|
|
|
|
|
/* Abort multi-result sets */
|
2004-02-09 14:26:38 +01:00
|
|
|
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
|
2002-12-11 08:17:51 +01:00
|
|
|
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
|
2006-08-23 03:58:14 +02:00
|
|
|
|
|
|
|
Design note:
|
|
|
|
|
|
|
|
net_printf_error and net_send_error are low-level functions
|
|
|
|
that shall be used only when a new connection is being
|
|
|
|
established or at server startup.
|
|
|
|
For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
|
|
|
|
critical that every error that can be intercepted is issued in one
|
|
|
|
place only, my_message_sql.
|
2002-12-11 08:17:51 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(THD *thd, uint errcode, ...)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
uint length,offset;
|
2002-12-16 15:58:55 +01:00
|
|
|
const char *format;
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
const char *text_pos;
|
2004-04-05 12:56:05 +02:00
|
|
|
int head_length= NET_HEADER_SIZE;
|
2002-12-16 15:58:55 +01:00
|
|
|
#else
|
2003-05-26 18:01:20 +02:00
|
|
|
char text_pos[1024];
|
2002-12-16 15:58:55 +01:00
|
|
|
#endif
|
2002-12-11 08:17:51 +01:00
|
|
|
NET *net= &thd->net;
|
2002-12-16 15:58:55 +01:00
|
|
|
|
2004-10-20 15:06:54 +02:00
|
|
|
DBUG_ENTER("net_printf_error");
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_PRINT("enter",("message: %u",errcode));
|
|
|
|
|
2006-08-23 03:58:14 +02:00
|
|
|
DBUG_ASSERT(!thd->spcont);
|
|
|
|
|
2005-01-20 09:41:37 +01:00
|
|
|
if (net && net->no_send_error)
|
|
|
|
{
|
|
|
|
thd->clear_error();
|
|
|
|
DBUG_PRINT("info", ("sending error messages prohibited"));
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
thd->query_error= 1; // needed to catch query errors during replication
|
2002-12-16 15:58:55 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2002-12-11 08:17:51 +01:00
|
|
|
query_cache_abort(net); // Safety
|
2002-12-16 15:58:55 +01:00
|
|
|
#endif
|
2002-12-11 08:17:51 +01:00
|
|
|
va_start(args,errcode);
|
|
|
|
/*
|
2004-10-20 15:06:54 +02:00
|
|
|
The following is needed to make net_printf_error() work with 0 argument
|
|
|
|
for errorcode and use the argument after that as the format string. This
|
2002-12-11 08:17:51 +01:00
|
|
|
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;
|
|
|
|
}
|
2003-05-26 18:01:20 +02:00
|
|
|
offset= (net->return_errno ?
|
|
|
|
((thd->client_capabilities & CLIENT_PROTOCOL_41) ?
|
|
|
|
2+SQLSTATE_LENGTH+1 : 2) : 0);
|
2002-12-16 15:58:55 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-05-26 18:01:20 +02:00
|
|
|
text_pos=(char*) net->buff + head_length + offset + 1;
|
2004-08-26 17:26:38 +02:00
|
|
|
length= (uint) ((char*)net->buff_end - text_pos);
|
2004-08-19 03:02:09 +02:00
|
|
|
#else
|
|
|
|
length=sizeof(text_pos)-1;
|
2002-12-16 15:58:55 +01:00
|
|
|
#endif
|
2004-08-19 03:02:09 +02:00
|
|
|
length=my_vsnprintf(my_const_cast(char*) (text_pos),
|
|
|
|
min(length, sizeof(net->last_error)),
|
2004-08-18 19:57:55 +02:00
|
|
|
format,args);
|
2002-12-11 08:17:51 +01:00
|
|
|
va_end(args);
|
|
|
|
|
2004-10-06 18:14:33 +02:00
|
|
|
/* 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);
|
|
|
|
|
2002-12-16 15:58:55 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2002-12-11 08:17:51 +01:00
|
|
|
if (net->vio == 0)
|
|
|
|
{
|
|
|
|
if (thd->bootstrap)
|
|
|
|
{
|
2003-01-09 02:55:26 +01:00
|
|
|
/*
|
|
|
|
In bootstrap it's ok to print on stderr
|
|
|
|
This may also happen when we get an error from a slave thread
|
|
|
|
*/
|
2002-12-11 08:17:51 +01:00
|
|
|
fprintf(stderr,"ERROR: %d %s\n",errcode,text_pos);
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->fatal_error();
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
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)
|
2003-05-26 18:01:20 +02:00
|
|
|
{
|
|
|
|
uchar *pos= net->buff+head_length+1;
|
|
|
|
int2store(pos, errcode);
|
|
|
|
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
|
|
|
{
|
2003-06-04 17:28:51 +02:00
|
|
|
pos[2]= '#'; /* To make the protocol backward compatible */
|
|
|
|
memcpy(pos+3, mysql_errno_to_sqlstate(errcode), SQLSTATE_LENGTH);
|
2003-05-26 18:01:20 +02:00
|
|
|
}
|
|
|
|
}
|
2002-12-11 08:17:51 +01:00
|
|
|
VOID(net_real_write(net,(char*) net->buff,length+head_length+1+offset));
|
2002-12-16 15:58:55 +01:00
|
|
|
#else
|
|
|
|
net->last_errno= errcode;
|
|
|
|
strmake(net->last_error, text_pos, length);
|
2003-07-23 12:23:20 +02:00
|
|
|
strmake(net->sqlstate, mysql_errno_to_sqlstate(errcode), SQLSTATE_LENGTH);
|
2002-12-16 15:58:55 +01:00
|
|
|
#endif
|
2005-03-09 19:22:30 +01:00
|
|
|
if (thd->killed != THD::KILL_CONNECTION)
|
2005-03-07 16:45:19 +01:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, errcode,
|
|
|
|
text_pos ? text_pos : ER(errcode));
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->is_fatal_error=0; // Error message is given
|
2002-12-11 08:17:51 +01:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2002-12-11 08:17:51 +01:00
|
|
|
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");
|
2003-02-14 10:47:41 +01:00
|
|
|
|
|
|
|
if (net->no_send_ok || !net->vio) // hack for re-parsing queries
|
2005-06-23 17:29:10 +02:00
|
|
|
{
|
|
|
|
DBUG_PRINT("info", ("no send ok: %s, vio present: %s",
|
|
|
|
(net->no_send_ok ? "YES" : "NO"),
|
|
|
|
(net->vio ? "YES" : "NO")));
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2005-06-23 17:29:10 +02:00
|
|
|
}
|
2003-02-14 10:47:41 +01:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
buff[0]=0; // No fields
|
2006-11-13 11:28:55 +01:00
|
|
|
pos=net_store_length(buff+1,affected_rows);
|
|
|
|
pos=net_store_length(pos, id);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
|
|
|
{
|
2003-11-28 11:18:13 +01:00
|
|
|
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));
|
2002-12-11 08:17:51 +01:00
|
|
|
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));
|
2003-11-20 21:06:25 +01:00
|
|
|
/* We can't anymore send an error to the client */
|
|
|
|
thd->net.report_error= 0;
|
2005-01-20 09:41:37 +01:00
|
|
|
thd->net.no_send_error= 1;
|
2005-02-26 11:19:02 +01:00
|
|
|
DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
|
2005-01-20 09:41:37 +01:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2004-02-10 16:33:06 +01:00
|
|
|
static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
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;
|
A fix and a test case for Bug#15752 "Lost connection to MySQL server
when calling a SP from C API"
The bug was caused by lack of checks for misuse in mysql_real_query.
A stored procedure always returns at least one result, which is the
status of execution of the procedure itself.
This result, or so-called OK packet, is similar to a result
returned by INSERT/UPDATE/CREATE operations: it contains the overall
status of execution, the number of affected rows and the number of
warnings. The client test program attached to the bug did not read this
result and ivnoked the next query. In turn, libmysql had no check for
such scenario and mysql_real_query was simply trying to send that query
without reading the pending response, thus messing up the communication
protocol.
The fix is to return an error from mysql_real_query when it's called
prior to retrieval of all pending results.
client/mysqlbinlog.cc:
net_safe_read -> cli_safe_read
include/mysql.h:
Remove a private function from the public header.
include/mysql_com.h:
Remove a define that is never used.
include/sql_common.h:
Add a declaration for cli_safe_read - a function that reads one packet
from the server.
libmysql/libmysql.c:
net_safe_read -> cli_safe_read
Return CR_COMMANDS_OUT_OF_SYNC on attempt to execute a statement
using a connection which has pending result sets.
sql-common/client.c:
Actual fix for Bug#15752: if the server has pending result sets for
the client, return CR_COMMANDS_OUT_OF_SYNC on attempt to execute
another query. Similarly to the behaviour of mysql_use_result(),
multiple result sets block the connection and must be fetched
before it can be used for another query.
This uncovered an error in the protocol: the server doesn't drop
SERVER_MORE_RESULTS_EXISTS status flag upon an error, so in case of
a multi-query like SELECT 1; SELECT syntax_error; SELECT 2;
the client has no way to know that the server won't ever come to
execution of the third query and won't return any result sets for it.
For now, fix it in cli_safe_read, as a proper fix requires extension
of the client-server protocol.
sql/protocol.cc:
Remove a name that is never used.
sql/slave.cc:
net_safe_read -> cli_safe_read
tests/mysql_client_test.c:
Make 'query' a local variable to avoid name clash.
Add a test case for Bug#15752 "Lost connection to MySQL server when
calling an SP from C API"
2006-07-24 12:56:53 +02:00
|
|
|
For flags like SERVER_MORE_RESULTS_EXISTS
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
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
|
2005-06-30 14:17:10 +02:00
|
|
|
send_eof(THD *thd)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
|
|
|
NET *net= &thd->net;
|
|
|
|
DBUG_ENTER("send_eof");
|
2003-11-27 16:48:21 +01:00
|
|
|
if (net->vio != 0 && !net->no_send_eof)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
2005-06-30 14:17:10 +02:00
|
|
|
write_eof_packet(thd, net);
|
|
|
|
VOID(net_flush(net));
|
2005-01-20 09:41:37 +01:00
|
|
|
thd->net.no_send_error= 1;
|
2005-02-26 11:19:02 +01:00
|
|
|
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
2003-07-18 16:25:54 +02:00
|
|
|
|
2005-06-30 14:17:10 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Format EOF packet according to the current protocol and
|
|
|
|
write it to the network output buffer.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void write_eof_packet(THD *thd, NET *net)
|
|
|
|
{
|
|
|
|
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
|
|
|
{
|
|
|
|
uchar buff[5];
|
|
|
|
/*
|
|
|
|
Don't send warn count during SP execution, as the warn_list
|
|
|
|
is cleared between substatements, and mysqltest gets confused
|
|
|
|
*/
|
|
|
|
uint tmp= (thd->spcont ? 0 : 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));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
VOID(my_net_write(net, eof_buff, 1));
|
|
|
|
}
|
|
|
|
|
2003-07-18 16:25:54 +02:00
|
|
|
/*
|
|
|
|
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;
|
2004-02-10 16:33:06 +01:00
|
|
|
return my_net_write(net, eof_buff, 1) || net_flush(net);
|
2003-07-18 16:25:54 +02:00
|
|
|
}
|
|
|
|
|
2006-02-24 17:34:15 +01:00
|
|
|
|
|
|
|
void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
|
|
|
|
{
|
|
|
|
NET *net= &thd->net;
|
|
|
|
uint length;
|
|
|
|
char buff[MYSQL_ERRMSG_SIZE+2], *pos;
|
|
|
|
|
|
|
|
DBUG_ENTER("send_error_packet");
|
|
|
|
|
|
|
|
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));
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-12-16 15:58:55 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
/*
|
2003-09-03 16:07:00 +02:00
|
|
|
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.
|
2002-12-11 08:17:51 +01:00
|
|
|
*/
|
|
|
|
|
2006-11-13 11:28:55 +01:00
|
|
|
static char *net_store_length_fast(char *pkg, uint length)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2006-11-13 11:28:55 +01:00
|
|
|
to=net_store_length_fast(to,length);
|
2002-12-11 08:17:51 +01:00
|
|
|
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);
|
2006-11-13 11:28:55 +01:00
|
|
|
to=net_store_length_fast(to,length);
|
2002-12-11 08:17:51 +01:00
|
|
|
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);
|
2006-11-13 11:28:55 +01:00
|
|
|
to=net_store_length_fast(to,length);
|
2002-12-11 08:17:51 +01:00
|
|
|
memcpy(to,buff,length);
|
|
|
|
return to+length;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
Default Protocol functions
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
void Protocol::init(THD *thd_arg)
|
|
|
|
{
|
|
|
|
thd=thd_arg;
|
|
|
|
packet= &thd->packet;
|
2004-05-25 00:03:49 +02:00
|
|
|
convert= &thd->convert_buffer;
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
field_types= 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
|
2004-11-02 19:13:27 +01:00
|
|
|
bool Protocol::flush()
|
|
|
|
{
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
return net_flush(&thd->net);
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
/*
|
|
|
|
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
|
Port of cursors to be pushed into 5.0 tree:
- client side part is simple and may be considered stable
- server side part now just joggles with THD state to save execution
state and has no additional locking wisdom.
Lot's of it are to be rewritten.
include/mysql.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new statement attribute STMT_ATTR_CURSOR_TYPE
- MYSQL_STMT::flags to store statement cursor type
- MYSQL_STMT::server_status to store server status (i. e. if the server
was able to open a cursor for this query).
include/mysql_com.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new COMmand, COM_FETCH, to fetch K rows from read-only cursor.
By design should support scrollable cursors as well.
- a few new server statuses:
SERVER_STATUS_CURSOR_EXISTS is sent by server in reply to COM_EXECUTE,
when cursor was successfully opened for this query
SERVER_STATUS_LAST_ROW_SENT is sent along with the last row to prevent one
more round trip just for finding out that all rows were fetched from
this cursor (this is server mem savier also).
- and finally, all possible values of STMT_ATTR_CURSOR_TYPE,
while now we support only CURSORT_TYPE_NO_CURSOR and
CURSOR_TYPE_READ_ONLY
libmysql/libmysql.c:
Cursor patch to push into the main tree, client library part (considered
stable):
- simple additions to mysql_stmt_fetch implementation to read data
from an opened cursor: we can read up to iteration count rows per
one request; read rows are buffered in the same way as rows of
mysql_stmt_store_result.
- now send stmt->flags to server to let him now if we wish to have
a cursor for this statement.
- support for setting/getting statement cursor type.
libmysqld/examples/Makefile.am:
Testing cursors was originally implemented in C++. Now when these tests
go into client_test, it's time to convert it to C++ as well.
libmysqld/lib_sql.cc:
- cleanup: send_fields flags are now named.
sql/ha_innodb.cc:
- cleanup: send_fields flags are now named.
sql/mysql_priv.h:
- cursors support: declaration for server-side handler of COM_FETCH
sql/protocol.cc:
- cleanup: send_fields flags are now named.
- we can't anymore assert that field_types[field_pos] is sensible:
if we have COM_EXCUTE(stmt1), COM_EXECUTE(stmt2), COM_FETCH(stmt1)
field_types[field_pos] will point to fields of stmt2.
sql/protocol.h:
- cleanup: send_fields flag_s_ are now named.
sql/protocol_cursor.cc:
- cleanup: send_fields flags are now named.
sql/repl_failsafe.cc:
- cleanup: send_fields flags are now named.
sql/slave.cc:
- cleanup: send_fields flags are now named.
sql/sp.cc:
- cleanup: send_fields flags are now named.
sql/sp_head.cc:
- cleanup: send_fields flags are now named.
sql/sql_acl.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.h:
- cleanup: send_fields flags are now named.
sql/sql_error.cc:
- cleanup: send_fields flags are now named.
sql/sql_handler.cc:
- cleanup: send_fields flags are now named.
sql/sql_help.cc:
- cleanup: send_fields flags are now named.
sql/sql_parse.cc:
Server side support for cursors:
- handle COM_FETCH
- enforce assumption that whenever we free thd->free_list,
we reset it to zero. This way it's much easier to handle free_list
in prepared statements implementation.
sql/sql_prepare.cc:
Server side support for cursors:
- implementation of mysql_stmt_fetch (fetch some rows from open cursor).
- management of cursors memory is quite tricky now.
- execute_stmt can't be reused anymore in mysql_stmt_execute and
mysql_sql_stmt_execute
sql/sql_repl.cc:
- cleanup: send_fields flags are now named.
sql/sql_select.cc:
Server side support for cursors:
- implementation of Cursor::open, Cursor::fetch (buggy when it comes to
non-equi joins), cursor cleanups.
- -4 -3 -0 constants indicating return value of sub_select and end_send are
to be renamed to something more readable:
it turned out to be not so simple, so it should come with the other patch.
sql/sql_select.h:
Server side support for cursors:
- declaration of Cursor class.
- JOIN::fetch_limit contains runtime value of rows fetched via cursor.
sql/sql_show.cc:
- cleanup: send_fields flags are now named.
sql/sql_table.cc:
- cleanup: send_fields flags are now named.
sql/sql_union.cc:
- if there was a cursor, don't cleanup unit: we'll need it to fetch
the rest of the rows.
tests/Makefile.am:
Now client_test is in C++.
tests/client_test.cc:
A few elementary tests for cursors.
BitKeeper/etc/ignore:
Added libmysqld/examples/client_test.cc to the ignore list
2004-08-03 12:32:21 +02:00
|
|
|
4 don't write eof packet
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
*/
|
|
|
|
|
2002-12-17 16:33:25 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2004-11-03 11:39:38 +01:00
|
|
|
bool Protocol::send_fields(List<Item> *list, uint flags)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
|
|
|
List_iterator_fast<Item> it(*list);
|
|
|
|
Item *item;
|
|
|
|
char buff[80];
|
2003-02-26 11:08:31 +01:00
|
|
|
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
|
2002-12-11 08:17:51 +01:00
|
|
|
Protocol_simple prot(thd);
|
2003-11-28 11:18:13 +01:00
|
|
|
String *local_packet= prot.storage_packet();
|
2003-05-30 20:09:35 +02:00
|
|
|
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ENTER("send_fields");
|
|
|
|
|
Port of cursors to be pushed into 5.0 tree:
- client side part is simple and may be considered stable
- server side part now just joggles with THD state to save execution
state and has no additional locking wisdom.
Lot's of it are to be rewritten.
include/mysql.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new statement attribute STMT_ATTR_CURSOR_TYPE
- MYSQL_STMT::flags to store statement cursor type
- MYSQL_STMT::server_status to store server status (i. e. if the server
was able to open a cursor for this query).
include/mysql_com.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new COMmand, COM_FETCH, to fetch K rows from read-only cursor.
By design should support scrollable cursors as well.
- a few new server statuses:
SERVER_STATUS_CURSOR_EXISTS is sent by server in reply to COM_EXECUTE,
when cursor was successfully opened for this query
SERVER_STATUS_LAST_ROW_SENT is sent along with the last row to prevent one
more round trip just for finding out that all rows were fetched from
this cursor (this is server mem savier also).
- and finally, all possible values of STMT_ATTR_CURSOR_TYPE,
while now we support only CURSORT_TYPE_NO_CURSOR and
CURSOR_TYPE_READ_ONLY
libmysql/libmysql.c:
Cursor patch to push into the main tree, client library part (considered
stable):
- simple additions to mysql_stmt_fetch implementation to read data
from an opened cursor: we can read up to iteration count rows per
one request; read rows are buffered in the same way as rows of
mysql_stmt_store_result.
- now send stmt->flags to server to let him now if we wish to have
a cursor for this statement.
- support for setting/getting statement cursor type.
libmysqld/examples/Makefile.am:
Testing cursors was originally implemented in C++. Now when these tests
go into client_test, it's time to convert it to C++ as well.
libmysqld/lib_sql.cc:
- cleanup: send_fields flags are now named.
sql/ha_innodb.cc:
- cleanup: send_fields flags are now named.
sql/mysql_priv.h:
- cursors support: declaration for server-side handler of COM_FETCH
sql/protocol.cc:
- cleanup: send_fields flags are now named.
- we can't anymore assert that field_types[field_pos] is sensible:
if we have COM_EXCUTE(stmt1), COM_EXECUTE(stmt2), COM_FETCH(stmt1)
field_types[field_pos] will point to fields of stmt2.
sql/protocol.h:
- cleanup: send_fields flag_s_ are now named.
sql/protocol_cursor.cc:
- cleanup: send_fields flags are now named.
sql/repl_failsafe.cc:
- cleanup: send_fields flags are now named.
sql/slave.cc:
- cleanup: send_fields flags are now named.
sql/sp.cc:
- cleanup: send_fields flags are now named.
sql/sp_head.cc:
- cleanup: send_fields flags are now named.
sql/sql_acl.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.h:
- cleanup: send_fields flags are now named.
sql/sql_error.cc:
- cleanup: send_fields flags are now named.
sql/sql_handler.cc:
- cleanup: send_fields flags are now named.
sql/sql_help.cc:
- cleanup: send_fields flags are now named.
sql/sql_parse.cc:
Server side support for cursors:
- handle COM_FETCH
- enforce assumption that whenever we free thd->free_list,
we reset it to zero. This way it's much easier to handle free_list
in prepared statements implementation.
sql/sql_prepare.cc:
Server side support for cursors:
- implementation of mysql_stmt_fetch (fetch some rows from open cursor).
- management of cursors memory is quite tricky now.
- execute_stmt can't be reused anymore in mysql_stmt_execute and
mysql_sql_stmt_execute
sql/sql_repl.cc:
- cleanup: send_fields flags are now named.
sql/sql_select.cc:
Server side support for cursors:
- implementation of Cursor::open, Cursor::fetch (buggy when it comes to
non-equi joins), cursor cleanups.
- -4 -3 -0 constants indicating return value of sub_select and end_send are
to be renamed to something more readable:
it turned out to be not so simple, so it should come with the other patch.
sql/sql_select.h:
Server side support for cursors:
- declaration of Cursor class.
- JOIN::fetch_limit contains runtime value of rows fetched via cursor.
sql/sql_show.cc:
- cleanup: send_fields flags are now named.
sql/sql_table.cc:
- cleanup: send_fields flags are now named.
sql/sql_union.cc:
- if there was a cursor, don't cleanup unit: we'll need it to fetch
the rest of the rows.
tests/Makefile.am:
Now client_test is in C++.
tests/client_test.cc:
A few elementary tests for cursors.
BitKeeper/etc/ignore:
Added libmysqld/examples/client_test.cc to the ignore list
2004-08-03 12:32:21 +02:00
|
|
|
if (flags & SEND_NUM_ROWS)
|
2002-12-11 08:17:51 +01:00
|
|
|
{ // Packet with number of elements
|
2006-11-13 11:28:55 +01:00
|
|
|
char *pos=net_store_length(buff, list->elements);
|
2002-12-11 08:17:51 +01:00
|
|
|
(void) my_net_write(&thd->net, buff,(uint) (pos-buff));
|
|
|
|
}
|
|
|
|
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
field_types= (enum_field_types*) thd->alloc(sizeof(field_types) *
|
|
|
|
list->elements);
|
|
|
|
uint count= 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while ((item=it++))
|
|
|
|
{
|
|
|
|
char *pos;
|
2003-03-17 10:14:04 +01:00
|
|
|
CHARSET_INFO *cs= system_charset_info;
|
2002-12-11 08:17:51 +01:00
|
|
|
Send_field field;
|
|
|
|
item->make_field(&field);
|
2004-12-06 01:00:37 +01:00
|
|
|
|
|
|
|
/* Keep things compatible for old clients */
|
|
|
|
if (field.type == MYSQL_TYPE_VARCHAR)
|
|
|
|
field.type= MYSQL_TYPE_VAR_STRING;
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
prot.prepare_for_resend();
|
|
|
|
|
|
|
|
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
|
|
|
{
|
2005-11-20 19:47:07 +01:00
|
|
|
if (prot.store(STRING_WITH_LEN("def"), cs, thd_charset) ||
|
2003-05-26 18:01:20 +02:00
|
|
|
prot.store(field.db_name, (uint) strlen(field.db_name),
|
|
|
|
cs, thd_charset) ||
|
2003-04-07 10:52:48 +02:00
|
|
|
prot.store(field.table_name, (uint) strlen(field.table_name),
|
2003-05-26 18:01:20 +02:00
|
|
|
cs, thd_charset) ||
|
2003-04-07 10:52:48 +02:00
|
|
|
prot.store(field.org_table_name, (uint) strlen(field.org_table_name),
|
2003-05-26 18:01:20 +02:00
|
|
|
cs, thd_charset) ||
|
2003-04-07 10:52:48 +02:00
|
|
|
prot.store(field.col_name, (uint) strlen(field.col_name),
|
2003-05-26 18:01:20 +02:00
|
|
|
cs, thd_charset) ||
|
2003-04-07 10:52:48 +02:00
|
|
|
prot.store(field.org_col_name, (uint) strlen(field.org_col_name),
|
2003-05-26 18:01:20 +02:00
|
|
|
cs, thd_charset) ||
|
2003-11-28 11:18:13 +01:00
|
|
|
local_packet->realloc(local_packet->length()+12))
|
2002-12-11 08:17:51 +01:00
|
|
|
goto err;
|
2003-02-04 02:19:19 +01:00
|
|
|
/* Store fixed length fields */
|
2003-11-28 11:18:13 +01:00
|
|
|
pos= (char*) local_packet->ptr()+local_packet->length();
|
2003-05-26 18:01:20 +02:00
|
|
|
*pos++= 12; // Length of packed fields
|
2004-04-06 16:57:33 +02:00
|
|
|
if (item->collation.collation == &my_charset_bin || thd_charset == NULL)
|
2004-12-14 12:58:30 +01:00
|
|
|
{
|
|
|
|
/* No conversion */
|
2004-04-06 16:57:33 +02:00
|
|
|
int2store(pos, field.charsetnr);
|
2004-12-14 12:58:30 +01:00
|
|
|
int4store(pos+2, field.length);
|
|
|
|
}
|
2004-04-06 16:57:33 +02:00
|
|
|
else
|
2004-12-14 12:58:30 +01:00
|
|
|
{
|
|
|
|
/* With conversion */
|
2006-01-18 20:50:31 +01:00
|
|
|
uint max_char_len;
|
2004-12-14 12:58:30 +01:00
|
|
|
int2store(pos, thd_charset->number);
|
2006-01-18 20:50:31 +01:00
|
|
|
/*
|
|
|
|
For TEXT/BLOB columns, field_length describes the maximum data
|
|
|
|
length in bytes. There is no limit to the number of characters
|
|
|
|
that a TEXT column can store, as long as the data fits into
|
|
|
|
the designated space.
|
|
|
|
For the rest of textual columns, field_length is evaluated as
|
|
|
|
char_count * mbmaxlen, where character count is taken from the
|
|
|
|
definition of the column. In other words, the maximum number
|
|
|
|
of characters here is limited by the column definition.
|
|
|
|
*/
|
|
|
|
max_char_len= (field.type >= (int) MYSQL_TYPE_TINY_BLOB &&
|
|
|
|
field.type <= (int) MYSQL_TYPE_BLOB) ?
|
|
|
|
field.length / item->collation.collation->mbminlen :
|
|
|
|
field.length / item->collation.collation->mbmaxlen;
|
|
|
|
int4store(pos+2, max_char_len * thd_charset->mbmaxlen);
|
2004-12-14 12:58:30 +01:00
|
|
|
}
|
2003-05-26 18:01:20 +02:00
|
|
|
pos[6]= field.type;
|
|
|
|
int2store(pos+7,field.flags);
|
|
|
|
pos[9]= (char) field.decimals;
|
2003-02-04 02:19:19 +01:00
|
|
|
pos[10]= 0; // For the future
|
2003-05-26 18:01:20 +02:00
|
|
|
pos[11]= 0; // For the future
|
|
|
|
pos+= 12;
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-04-07 10:52:48 +02:00
|
|
|
if (prot.store(field.table_name, (uint) strlen(field.table_name),
|
2003-05-26 18:01:20 +02:00
|
|
|
cs, thd_charset) ||
|
2003-04-07 10:52:48 +02:00
|
|
|
prot.store(field.col_name, (uint) strlen(field.col_name),
|
2003-05-26 18:01:20 +02:00
|
|
|
cs, thd_charset) ||
|
2003-11-28 11:18:13 +01:00
|
|
|
local_packet->realloc(local_packet->length()+10))
|
2002-12-11 08:17:51 +01:00
|
|
|
goto err;
|
2003-11-28 11:18:13 +01:00
|
|
|
pos= (char*) local_packet->ptr()+local_packet->length();
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
#ifdef TO_BE_DELETED_IN_6
|
2003-02-04 02:19:19 +01:00
|
|
|
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
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
2003-02-04 02:19:19 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
2003-11-28 11:18:13 +01:00
|
|
|
local_packet->length((uint) (pos - local_packet->ptr()));
|
Port of cursors to be pushed into 5.0 tree:
- client side part is simple and may be considered stable
- server side part now just joggles with THD state to save execution
state and has no additional locking wisdom.
Lot's of it are to be rewritten.
include/mysql.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new statement attribute STMT_ATTR_CURSOR_TYPE
- MYSQL_STMT::flags to store statement cursor type
- MYSQL_STMT::server_status to store server status (i. e. if the server
was able to open a cursor for this query).
include/mysql_com.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new COMmand, COM_FETCH, to fetch K rows from read-only cursor.
By design should support scrollable cursors as well.
- a few new server statuses:
SERVER_STATUS_CURSOR_EXISTS is sent by server in reply to COM_EXECUTE,
when cursor was successfully opened for this query
SERVER_STATUS_LAST_ROW_SENT is sent along with the last row to prevent one
more round trip just for finding out that all rows were fetched from
this cursor (this is server mem savier also).
- and finally, all possible values of STMT_ATTR_CURSOR_TYPE,
while now we support only CURSORT_TYPE_NO_CURSOR and
CURSOR_TYPE_READ_ONLY
libmysql/libmysql.c:
Cursor patch to push into the main tree, client library part (considered
stable):
- simple additions to mysql_stmt_fetch implementation to read data
from an opened cursor: we can read up to iteration count rows per
one request; read rows are buffered in the same way as rows of
mysql_stmt_store_result.
- now send stmt->flags to server to let him now if we wish to have
a cursor for this statement.
- support for setting/getting statement cursor type.
libmysqld/examples/Makefile.am:
Testing cursors was originally implemented in C++. Now when these tests
go into client_test, it's time to convert it to C++ as well.
libmysqld/lib_sql.cc:
- cleanup: send_fields flags are now named.
sql/ha_innodb.cc:
- cleanup: send_fields flags are now named.
sql/mysql_priv.h:
- cursors support: declaration for server-side handler of COM_FETCH
sql/protocol.cc:
- cleanup: send_fields flags are now named.
- we can't anymore assert that field_types[field_pos] is sensible:
if we have COM_EXCUTE(stmt1), COM_EXECUTE(stmt2), COM_FETCH(stmt1)
field_types[field_pos] will point to fields of stmt2.
sql/protocol.h:
- cleanup: send_fields flag_s_ are now named.
sql/protocol_cursor.cc:
- cleanup: send_fields flags are now named.
sql/repl_failsafe.cc:
- cleanup: send_fields flags are now named.
sql/slave.cc:
- cleanup: send_fields flags are now named.
sql/sp.cc:
- cleanup: send_fields flags are now named.
sql/sp_head.cc:
- cleanup: send_fields flags are now named.
sql/sql_acl.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.h:
- cleanup: send_fields flags are now named.
sql/sql_error.cc:
- cleanup: send_fields flags are now named.
sql/sql_handler.cc:
- cleanup: send_fields flags are now named.
sql/sql_help.cc:
- cleanup: send_fields flags are now named.
sql/sql_parse.cc:
Server side support for cursors:
- handle COM_FETCH
- enforce assumption that whenever we free thd->free_list,
we reset it to zero. This way it's much easier to handle free_list
in prepared statements implementation.
sql/sql_prepare.cc:
Server side support for cursors:
- implementation of mysql_stmt_fetch (fetch some rows from open cursor).
- management of cursors memory is quite tricky now.
- execute_stmt can't be reused anymore in mysql_stmt_execute and
mysql_sql_stmt_execute
sql/sql_repl.cc:
- cleanup: send_fields flags are now named.
sql/sql_select.cc:
Server side support for cursors:
- implementation of Cursor::open, Cursor::fetch (buggy when it comes to
non-equi joins), cursor cleanups.
- -4 -3 -0 constants indicating return value of sub_select and end_send are
to be renamed to something more readable:
it turned out to be not so simple, so it should come with the other patch.
sql/sql_select.h:
Server side support for cursors:
- declaration of Cursor class.
- JOIN::fetch_limit contains runtime value of rows fetched via cursor.
sql/sql_show.cc:
- cleanup: send_fields flags are now named.
sql/sql_table.cc:
- cleanup: send_fields flags are now named.
sql/sql_union.cc:
- if there was a cursor, don't cleanup unit: we'll need it to fetch
the rest of the rows.
tests/Makefile.am:
Now client_test is in C++.
tests/client_test.cc:
A few elementary tests for cursors.
BitKeeper/etc/ignore:
Added libmysqld/examples/client_test.cc to the ignore list
2004-08-03 12:32:21 +02:00
|
|
|
if (flags & SEND_DEFAULTS)
|
2002-12-11 08:17:51 +01:00
|
|
|
item->send(&prot, &tmp); // Send default value
|
|
|
|
if (prot.write())
|
|
|
|
break; /* purecov: inspected */
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
field_types[count++]= field.type;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
Port of cursors to be pushed into 5.0 tree:
- client side part is simple and may be considered stable
- server side part now just joggles with THD state to save execution
state and has no additional locking wisdom.
Lot's of it are to be rewritten.
include/mysql.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new statement attribute STMT_ATTR_CURSOR_TYPE
- MYSQL_STMT::flags to store statement cursor type
- MYSQL_STMT::server_status to store server status (i. e. if the server
was able to open a cursor for this query).
include/mysql_com.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new COMmand, COM_FETCH, to fetch K rows from read-only cursor.
By design should support scrollable cursors as well.
- a few new server statuses:
SERVER_STATUS_CURSOR_EXISTS is sent by server in reply to COM_EXECUTE,
when cursor was successfully opened for this query
SERVER_STATUS_LAST_ROW_SENT is sent along with the last row to prevent one
more round trip just for finding out that all rows were fetched from
this cursor (this is server mem savier also).
- and finally, all possible values of STMT_ATTR_CURSOR_TYPE,
while now we support only CURSORT_TYPE_NO_CURSOR and
CURSOR_TYPE_READ_ONLY
libmysql/libmysql.c:
Cursor patch to push into the main tree, client library part (considered
stable):
- simple additions to mysql_stmt_fetch implementation to read data
from an opened cursor: we can read up to iteration count rows per
one request; read rows are buffered in the same way as rows of
mysql_stmt_store_result.
- now send stmt->flags to server to let him now if we wish to have
a cursor for this statement.
- support for setting/getting statement cursor type.
libmysqld/examples/Makefile.am:
Testing cursors was originally implemented in C++. Now when these tests
go into client_test, it's time to convert it to C++ as well.
libmysqld/lib_sql.cc:
- cleanup: send_fields flags are now named.
sql/ha_innodb.cc:
- cleanup: send_fields flags are now named.
sql/mysql_priv.h:
- cursors support: declaration for server-side handler of COM_FETCH
sql/protocol.cc:
- cleanup: send_fields flags are now named.
- we can't anymore assert that field_types[field_pos] is sensible:
if we have COM_EXCUTE(stmt1), COM_EXECUTE(stmt2), COM_FETCH(stmt1)
field_types[field_pos] will point to fields of stmt2.
sql/protocol.h:
- cleanup: send_fields flag_s_ are now named.
sql/protocol_cursor.cc:
- cleanup: send_fields flags are now named.
sql/repl_failsafe.cc:
- cleanup: send_fields flags are now named.
sql/slave.cc:
- cleanup: send_fields flags are now named.
sql/sp.cc:
- cleanup: send_fields flags are now named.
sql/sp_head.cc:
- cleanup: send_fields flags are now named.
sql/sql_acl.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.h:
- cleanup: send_fields flags are now named.
sql/sql_error.cc:
- cleanup: send_fields flags are now named.
sql/sql_handler.cc:
- cleanup: send_fields flags are now named.
sql/sql_help.cc:
- cleanup: send_fields flags are now named.
sql/sql_parse.cc:
Server side support for cursors:
- handle COM_FETCH
- enforce assumption that whenever we free thd->free_list,
we reset it to zero. This way it's much easier to handle free_list
in prepared statements implementation.
sql/sql_prepare.cc:
Server side support for cursors:
- implementation of mysql_stmt_fetch (fetch some rows from open cursor).
- management of cursors memory is quite tricky now.
- execute_stmt can't be reused anymore in mysql_stmt_execute and
mysql_sql_stmt_execute
sql/sql_repl.cc:
- cleanup: send_fields flags are now named.
sql/sql_select.cc:
Server side support for cursors:
- implementation of Cursor::open, Cursor::fetch (buggy when it comes to
non-equi joins), cursor cleanups.
- -4 -3 -0 constants indicating return value of sub_select and end_send are
to be renamed to something more readable:
it turned out to be not so simple, so it should come with the other patch.
sql/sql_select.h:
Server side support for cursors:
- declaration of Cursor class.
- JOIN::fetch_limit contains runtime value of rows fetched via cursor.
sql/sql_show.cc:
- cleanup: send_fields flags are now named.
sql/sql_table.cc:
- cleanup: send_fields flags are now named.
sql/sql_union.cc:
- if there was a cursor, don't cleanup unit: we'll need it to fetch
the rest of the rows.
tests/Makefile.am:
Now client_test is in C++.
tests/client_test.cc:
A few elementary tests for cursors.
BitKeeper/etc/ignore:
Added libmysqld/examples/client_test.cc to the ignore list
2004-08-03 12:32:21 +02:00
|
|
|
if (flags & SEND_EOF)
|
2005-06-30 14:17:10 +02:00
|
|
|
write_eof_packet(thd, &thd->net);
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_RETURN(prepare_for_send(list));
|
|
|
|
|
|
|
|
err:
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol::write()
|
|
|
|
{
|
|
|
|
DBUG_ENTER("Protocol::write");
|
2003-01-15 09:11:44 +01:00
|
|
|
DBUG_RETURN(my_net_write(&thd->net, packet->ptr(), packet->length()));
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
2003-01-15 09:11:44 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
|
|
|
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2003-03-17 10:14:04 +01:00
|
|
|
bool Protocol::store(const char *from, CHARSET_INFO *cs)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
|
|
|
if (!from)
|
|
|
|
return store_null();
|
|
|
|
uint length= strlen(from);
|
2003-03-17 10:14:04 +01:00
|
|
|
return store(from, length, cs);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Send a set of strings as one long string with ',' in between
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool Protocol::store(I_List<i_string>* str_list)
|
|
|
|
{
|
|
|
|
char buf[256];
|
2003-02-26 11:08:31 +01:00
|
|
|
String tmp(buf, sizeof(buf), &my_charset_bin);
|
2002-12-14 16:43:01 +01:00
|
|
|
uint32 len;
|
2002-12-11 08:17:51 +01:00
|
|
|
I_List_iterator<i_string> it(*str_list);
|
|
|
|
i_string* s;
|
|
|
|
|
2002-12-14 16:43:01 +01:00
|
|
|
tmp.length(0);
|
2002-12-11 08:17:51 +01:00
|
|
|
while ((s=it++))
|
|
|
|
{
|
|
|
|
tmp.append(s->ptr);
|
2002-12-14 16:43:01 +01:00
|
|
|
tmp.append(',');
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
2002-12-14 16:43:01 +01:00
|
|
|
if ((len= tmp.length()))
|
|
|
|
len--; // Remove last ','
|
2003-03-17 10:14:04 +01:00
|
|
|
return store((char*) tmp.ptr(), len, tmp.charset());
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
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'
|
|
|
|
****************************************************************************/
|
|
|
|
|
2003-01-15 09:11:44 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2002-12-11 08:17:51 +01:00
|
|
|
void Protocol_simple::prepare_for_resend()
|
|
|
|
{
|
|
|
|
packet->length(0);
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
field_pos= 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Protocol_simple::store_null()
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
field_pos++;
|
|
|
|
#endif
|
|
|
|
char buff[1];
|
2003-01-03 12:52:53 +01:00
|
|
|
buff[0]= (char)251;
|
2004-06-09 01:21:50 +02:00
|
|
|
return packet->append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
2003-01-20 15:47:25 +01:00
|
|
|
#endif
|
2002-12-11 08:17:51 +01:00
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
|
2004-05-25 00:03:49 +02:00
|
|
|
/*
|
|
|
|
Auxilary function to convert string to the given character set
|
|
|
|
and store in network buffer.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool Protocol::store_string_aux(const char *from, uint length,
|
|
|
|
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
|
|
|
|
{
|
|
|
|
/* 'tocs' is set 0 when client issues SET character_set_results=NULL */
|
|
|
|
if (tocs && !my_charset_same(fromcs, tocs) &&
|
|
|
|
fromcs != &my_charset_bin &&
|
|
|
|
tocs != &my_charset_bin)
|
|
|
|
{
|
2004-10-29 14:00:39 +02:00
|
|
|
uint dummy_errors;
|
|
|
|
return convert->copy(from, length, fromcs, tocs, &dummy_errors) ||
|
2004-05-25 00:03:49 +02:00
|
|
|
net_store_data(convert->ptr(), convert->length());
|
|
|
|
}
|
|
|
|
return net_store_data(from, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-04-07 10:52:48 +02:00
|
|
|
bool Protocol_simple::store(const char *from, uint length,
|
|
|
|
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2002-12-14 16:43:01 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
|
2004-12-17 15:06:05 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_BIT ||
|
2005-02-08 23:50:45 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL ||
|
2002-12-14 16:43:01 +01:00
|
|
|
(field_types[field_pos] >= MYSQL_TYPE_ENUM &&
|
|
|
|
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
2002-12-11 08:17:51 +01:00
|
|
|
field_pos++;
|
|
|
|
#endif
|
2004-05-25 00:03:49 +02:00
|
|
|
return store_string_aux(from, length, fromcs, tocs);
|
2003-04-07 10:52:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_simple::store(const char *from, uint length,
|
|
|
|
CHARSET_INFO *fromcs)
|
|
|
|
{
|
2003-05-21 14:44:12 +02:00
|
|
|
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2003-04-07 10:52:48 +02:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
|
|
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
|
2004-12-17 15:06:05 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_BIT ||
|
2005-02-08 23:50:45 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL ||
|
2003-04-07 10:52:48 +02:00
|
|
|
(field_types[field_pos] >= MYSQL_TYPE_ENUM &&
|
|
|
|
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
|
|
|
field_pos++;
|
|
|
|
#endif
|
2004-05-25 00:03:49 +02:00
|
|
|
return store_string_aux(from, length, fromcs, tocs);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_simple::store_tiny(longlong from)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2003-03-15 11:56:03 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
|
|
|
char buff[20];
|
2003-01-15 09:11:44 +01:00
|
|
|
return net_store_data((char*) buff,
|
2002-12-11 08:17:51 +01:00
|
|
|
(uint) (int10_to_str((int) from,buff, -10)-buff));
|
|
|
|
}
|
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol_simple::store_short(longlong from)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2005-04-21 16:36:10 +02:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_YEAR ||
|
2003-03-15 11:56:03 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_SHORT);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
|
|
|
char buff[20];
|
2003-01-15 09:11:44 +01:00
|
|
|
return net_store_data((char*) buff,
|
2002-12-11 08:17:51 +01:00
|
|
|
(uint) (int10_to_str((int) from,buff, -10)-buff));
|
|
|
|
}
|
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol_simple::store_long(longlong from)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2003-03-15 11:56:03 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
|
|
field_types[field_pos] == MYSQL_TYPE_INT24 ||
|
|
|
|
field_types[field_pos] == MYSQL_TYPE_LONG);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
|
|
|
char buff[20];
|
2003-01-15 09:11:44 +01:00
|
|
|
return net_store_data((char*) buff,
|
2005-04-29 23:05:15 +02:00
|
|
|
(uint) (int10_to_str((long int)from,buff, (from <0)?-10:10)-buff));
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2003-03-15 11:56:03 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_LONGLONG);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
|
|
|
char buff[22];
|
2003-01-15 09:11:44 +01:00
|
|
|
return net_store_data((char*) buff,
|
2002-12-11 08:17:51 +01:00
|
|
|
(uint) (longlong10_to_str(from,buff,
|
|
|
|
unsigned_flag ? 10 : -10)-
|
|
|
|
buff));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-08 23:50:45 +01:00
|
|
|
bool Protocol_simple::store_decimal(const my_decimal *d)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2005-02-08 23:50:45 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL);
|
|
|
|
field_pos++;
|
|
|
|
#endif
|
2005-02-19 17:58:27 +01:00
|
|
|
char buff[DECIMAL_MAX_STR_LENGTH];
|
|
|
|
String str(buff, sizeof(buff), &my_charset_bin);
|
|
|
|
(void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
|
2005-02-08 23:50:45 +01:00
|
|
|
return net_store_data(str.ptr(), str.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol_simple::store(float from, uint32 decimals, String *buffer)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2003-03-15 11:56:03 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_FLOAT);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
2003-04-07 13:10:27 +02:00
|
|
|
buffer->set((double) from, decimals, thd->charset());
|
2003-01-15 09:11:44 +01:00
|
|
|
return net_store_data((char*) buffer->ptr(), buffer->length());
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol_simple::store(double from, uint32 decimals, String *buffer)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2003-03-15 11:56:03 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_DOUBLE);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
2003-04-07 13:10:27 +02:00
|
|
|
buffer->set(from, decimals, thd->charset());
|
2003-01-15 09:11:44 +01:00
|
|
|
return net_store_data((char*) buffer->ptr(), buffer->length());
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_simple::store(Field *field)
|
|
|
|
{
|
2002-12-14 16:43:01 +01:00
|
|
|
if (field->is_null())
|
|
|
|
return store_null();
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
field_pos++;
|
|
|
|
#endif
|
|
|
|
char buff[MAX_FIELD_WIDTH];
|
2003-03-21 12:18:52 +01:00
|
|
|
String str(buff,sizeof(buff), &my_charset_bin);
|
2003-05-21 14:44:12 +02:00
|
|
|
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
|
|
|
|
|
::reset(), HA_FAST_KEY_READ, disable_indexes(), enable_indexes(), start_bulk_insert(), end_bulk_insert()
Field::val_str simplification, comment
include/my_base.h:
typos fixed
mysql-test/r/myisam.result:
alter table enable/disable keys
mysql-test/t/help.test:
cleanup
mysql-test/t/myisam.test:
alter table enable/disable keys
sql/field.cc:
Field::val_str() simplification
sql/field.h:
Field::val_str() simplification and comment
sql/field_conv.cc:
Field::val_str() simplification
sql/ha_berkeley.cc:
::reset(), HA_FAST_KEY_READ
sql/ha_berkeley.h:
::reset(), HA_FAST_KEY_READ
sql/ha_heap.cc:
::reset(), HA_FAST_KEY_READ
sql/ha_heap.h:
::reset(), HA_FAST_KEY_READ
sql/ha_innodb.cc:
::reset(), HA_FAST_KEY_READ
sql/ha_innodb.h:
::reset(), HA_FAST_KEY_READ
sql/ha_isam.cc:
::reset(), HA_FAST_KEY_READ
sql/ha_isam.h:
::reset(), HA_FAST_KEY_READ
sql/ha_isammrg.cc:
::reset(), HA_FAST_KEY_READ
sql/ha_isammrg.h:
::reset(), HA_FAST_KEY_READ
sql/ha_myisam.cc:
::reset(), HA_FAST_KEY_READ, disable_indexes(), enable_indexes(), start_bulk_insert(), end_bulk_insert()
sql/ha_myisam.h:
::reset(), HA_FAST_KEY_READ, disable_indexes(), enable_indexes(), start_bulk_insert(), end_bulk_insert()
sql/ha_myisammrg.cc:
::reset(), HA_FAST_KEY_READ
sql/ha_myisammrg.h:
::reset(), HA_FAST_KEY_READ
sql/handler.h:
::reset(), HA_FAST_KEY_READ, disable_indexes(), enable_indexes(), start_bulk_insert(), end_bulk_insert()
sql/item.cc:
Field::val_str() simplification
sql/item_sum.cc:
Field::val_str() simplification
sql/key.cc:
Field::val_str() simplification
sql/opt_range.cc:
Field::val_str() simplification
sql/protocol.cc:
Field::val_str() simplification
sql/records.cc:
HA_FAST_KEY_READ
sql/sql_acl.cc:
Field::val_str() simplification
sql/sql_base.cc:
::reset
sql/sql_insert.cc:
::reset(), start_bulk_insert(), end_bulk_insert()
sql/sql_load.cc:
start_bulk_insert(), end_bulk_insert()
sql/sql_show.cc:
Field::val_str() simplification
sql/sql_table.cc:
disable_indexes(), enable_indexes(), start_bulk_insert(), end_bulk_insert()
sql/table.cc:
Field::val_str() simplification
2004-04-06 21:35:26 +02:00
|
|
|
field->val_str(&str);
|
2004-05-25 00:03:49 +02:00
|
|
|
return store_string_aux(str.ptr(), str.length(), str.charset(), tocs);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-08 12:06:05 +02:00
|
|
|
/*
|
|
|
|
TODO:
|
|
|
|
Second_part format ("%06") needs to change when
|
|
|
|
we support 0-6 decimals for time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol_simple::store(TIME *tm)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2002-12-14 16:43:01 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_DATETIME ||
|
|
|
|
field_types[field_pos] == MYSQL_TYPE_TIMESTAMP);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
|
|
|
char buff[40];
|
2003-11-03 13:01:59 +01:00
|
|
|
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);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_simple::store_date(TIME *tm)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2003-03-15 11:56:03 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_DATE);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
2004-10-15 22:12:59 +02:00
|
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
|
|
int length= my_date_to_str(tm, buff);
|
|
|
|
return net_store_data(buff, (uint) length);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-08 12:06:05 +02:00
|
|
|
/*
|
|
|
|
TODO:
|
|
|
|
Second_part format ("%06") needs to change when
|
|
|
|
we support 0-6 decimals for time.
|
|
|
|
*/
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol_simple::store_time(TIME *tm)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-12-11 08:17:51 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
2003-03-15 11:56:03 +01:00
|
|
|
field_types[field_pos] == MYSQL_TYPE_TIME);
|
|
|
|
field_pos++;
|
2002-12-11 08:17:51 +01:00
|
|
|
#endif
|
|
|
|
char buff[40];
|
2003-11-03 13:01:59 +01:00
|
|
|
uint length;
|
2003-05-26 18:01:20 +02:00
|
|
|
uint day= (tm->year || tm->month) ? 0 : tm->day;
|
2003-11-03 13:01:59 +01:00
|
|
|
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);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
Functions to handle the binary protocol used with prepared statements
|
2003-01-03 12:52:53 +01:00
|
|
|
|
|
|
|
Data format:
|
|
|
|
|
2003-02-04 02:19:19 +01:00
|
|
|
[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
|
2002-12-11 08:17:51 +01:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
bool Protocol_prep::prepare_for_send(List<Item> *item_list)
|
|
|
|
{
|
2003-01-15 09:11:44 +01:00
|
|
|
Protocol::prepare_for_send(item_list);
|
2003-01-03 12:52:53 +01:00
|
|
|
bit_fields= (field_count+9)/8;
|
|
|
|
if (packet->alloc(bit_fields+1))
|
2002-12-11 08:17:51 +01:00
|
|
|
return 1;
|
|
|
|
/* prepare_for_resend will be called after this one */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Protocol_prep::prepare_for_resend()
|
|
|
|
{
|
2003-01-03 12:52:53 +01:00
|
|
|
packet->length(bit_fields+1);
|
|
|
|
bzero((char*) packet->ptr(), 1+bit_fields);
|
2002-12-11 08:17:51 +01:00
|
|
|
field_pos=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-25 00:03:49 +02:00
|
|
|
bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
|
2002-12-11 08:17:51 +01:00
|
|
|
{
|
2004-05-25 00:03:49 +02:00
|
|
|
CHARSET_INFO *tocs= thd->variables.character_set_results;
|
2002-12-11 08:17:51 +01:00
|
|
|
field_pos++;
|
2004-05-25 00:03:49 +02:00
|
|
|
return store_string_aux(from, length, fromcs, tocs);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
2003-04-07 10:52:48 +02:00
|
|
|
bool Protocol_prep::store(const char *from,uint length,
|
|
|
|
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
|
|
|
|
{
|
|
|
|
field_pos++;
|
2004-05-25 00:03:49 +02:00
|
|
|
return store_string_aux(from, length, fromcs, tocs);
|
2003-04-07 10:52:48 +02:00
|
|
|
}
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
bool Protocol_prep::store_null()
|
|
|
|
{
|
2003-01-03 12:52:53 +01:00
|
|
|
uint offset= (field_pos+2)/8+1, bit= (1 << ((field_pos+2) & 7));
|
2002-12-11 08:17:51 +01:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
char buff[1];
|
|
|
|
field_pos++;
|
|
|
|
buff[0]= (uchar) from;
|
2004-06-09 01:21:50 +02:00
|
|
|
return packet->append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_prep::store_short(longlong from)
|
|
|
|
{
|
|
|
|
field_pos++;
|
2004-06-09 01:21:50 +02:00
|
|
|
char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (!to)
|
|
|
|
return 1;
|
|
|
|
int2store(to, (int) from);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_prep::store_long(longlong from)
|
|
|
|
{
|
|
|
|
field_pos++;
|
2004-06-09 01:21:50 +02:00
|
|
|
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (!to)
|
|
|
|
return 1;
|
|
|
|
int4store(to, from);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
|
|
|
|
{
|
|
|
|
field_pos++;
|
2004-06-09 01:21:50 +02:00
|
|
|
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (!to)
|
|
|
|
return 1;
|
|
|
|
int8store(to, from);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-08 23:50:45 +01:00
|
|
|
bool Protocol_prep::store_decimal(const my_decimal *d)
|
|
|
|
{
|
2005-11-22 23:50:37 +01:00
|
|
|
#ifndef DBUG_OFF
|
2005-02-08 23:50:45 +01:00
|
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL);
|
|
|
|
field_pos++;
|
|
|
|
#endif
|
2005-02-19 17:58:27 +01:00
|
|
|
char buff[DECIMAL_MAX_STR_LENGTH];
|
|
|
|
String str(buff, sizeof(buff), &my_charset_bin);
|
|
|
|
(void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
|
2005-02-08 23:50:45 +01:00
|
|
|
return store(str.ptr(), str.length(), str.charset());
|
|
|
|
}
|
2002-12-11 08:17:51 +01:00
|
|
|
|
|
|
|
bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
|
|
|
|
{
|
|
|
|
field_pos++;
|
2004-06-09 01:21:50 +02:00
|
|
|
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (!to)
|
|
|
|
return 1;
|
|
|
|
float4store(to, from);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
|
|
|
|
{
|
|
|
|
field_pos++;
|
2004-06-09 01:21:50 +02:00
|
|
|
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (!to)
|
|
|
|
return 1;
|
|
|
|
float8store(to, from);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Protocol_prep::store(Field *field)
|
|
|
|
{
|
|
|
|
/*
|
2003-02-04 02:19:19 +01:00
|
|
|
We should not increment field_pos here as send_binary() will call another
|
2002-12-11 08:17:51 +01:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
char buff[12],*pos;
|
|
|
|
uint length;
|
|
|
|
field_pos++;
|
|
|
|
pos= buff+1;
|
2003-03-15 11:56:03 +01:00
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
int2store(pos, tm->year);
|
2003-01-04 08:37:16 +01:00
|
|
|
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;
|
2002-12-11 08:17:51 +01:00
|
|
|
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
|
2004-06-09 01:21:50 +02:00
|
|
|
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2004-06-09 01:21:50 +02:00
|
|
|
char buff[13], *pos;
|
2002-12-11 08:17:51 +01:00
|
|
|
uint length;
|
|
|
|
field_pos++;
|
|
|
|
pos= buff+1;
|
|
|
|
pos[0]= tm->neg ? 1 : 0;
|
2004-10-26 18:30:01 +02:00
|
|
|
if (tm->hour >= 24)
|
|
|
|
{
|
|
|
|
/* Fix if we come from Item::send */
|
|
|
|
uint days= tm->hour/24;
|
|
|
|
tm->hour-= days*24;
|
|
|
|
tm->day+= days;
|
|
|
|
}
|
2003-03-15 11:56:03 +01:00
|
|
|
int4store(pos+1, tm->day);
|
2003-01-04 08:37:16 +01:00
|
|
|
pos[5]= (uchar) tm->hour;
|
|
|
|
pos[6]= (uchar) tm->minute;
|
|
|
|
pos[7]= (uchar) tm->second;
|
|
|
|
int4store(pos+8, tm->second_part);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (tm->second_part)
|
2004-06-09 01:21:50 +02:00
|
|
|
length=12;
|
2002-12-11 08:17:51 +01:00
|
|
|
else if (tm->hour || tm->minute || tm->second || tm->day)
|
2003-01-04 08:37:16 +01:00
|
|
|
length=8;
|
2002-12-11 08:17:51 +01:00
|
|
|
else
|
|
|
|
length=0;
|
|
|
|
buff[0]=(char) length; // Length is stored first
|
2004-06-09 01:21:50 +02:00
|
|
|
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
|
2002-12-11 08:17:51 +01:00
|
|
|
}
|