mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
a260b15554
Base code and idea from a patch from by plinux at Taobao. The idea is that we mark all memory that are thread specific with MY_THREAD_SPECIFIC. Memory counting is done per thread in the my_malloc_size_cb_func callback function from my_malloc(). There are plenty of new asserts to ensure that for a debug server the counting is correct. Information_schema.processlist gets two new columns: MEMORY_USED and EXAMINED_ROWS. - The later is there mainly to show how query is progressing. The following changes in interfaces was needed to get this to work: - init_alloc_root() amd init_sql_alloc() has extra option so that one can mark memory with MY_THREAD_SPECIFIC - One now have to use alloc_root_set_min_malloc() to set min memory to be allocated by alloc_root() - my_init_dynamic_array() has extra option so that one can mark memory with MY_THREAD_SPECIFIC - my_net_init() has extra option so that one can mark memory with MY_THREAD_SPECIFIC - Added flag for hash_init() so that one can mark hash table to be thread specific. - Added flags to init_tree() so that one can mark tree to be thread specific. - Removed with_delete option to init_tree(). Now one should instead use MY_TREE_WITH_DELETE_FLAG. - Added flag to Warning_info::Warning_info() if the structure should be fully initialized. - String elements can now be marked as thread specific. - Internal HEAP tables are now marking it's memory as MY_THREAD_SPECIFIC. - Changed type of myf from int to ulong, as this is always a set of bit flags. Other things: - Removed calls to net_end() and thd->cleanup() as these are now done in ~THD() - We now also show EXAMINED_ROWS in SHOW PROCESSLIST - Added new variable 'memory_used' - Fixed bug where kill_threads_for_user() was using the wrong mem_root to allocate memory. - Removed calls to the obsoleted function init_dynamic_array() - Use set_current_thd() instead of my_pthread_setspecific_ptr(THR_THD,...) client/completion_hash.cc: Updated call to init_alloc_root() client/mysql.cc: Updated call to init_alloc_root() client/mysqlbinlog.cc: init_dynamic_array() -> my_init_dynamic_array() Updated call to init_alloc_root() client/mysqlcheck.c: Updated call to my_init_dynamic_array() client/mysqldump.c: Updated call to init_alloc_root() client/mysqltest.cc: Updated call to init_alloc_root() Updated call to my_init_dynamic_array() Fixed compiler warnings extra/comp_err.c: Updated call to my_init_dynamic_array() extra/resolve_stack_dump.c: Updated call to my_init_dynamic_array() include/hash.h: Added HASH_THREAD_SPECIFIC include/heap.h: Added flag is internal temporary table. include/my_dir.h: Safety fix: Ensure that MY_DONT_SORT and MY_WANT_STAT don't interfer with other mysys flags include/my_global.h: Changed type of myf from int to ulong, as this is always a set of bit flags. include/my_sys.h: Added MY_THREAD_SPECIFIC and MY_THREAD_MOVE Added malloc_flags to DYNAMIC_ARRAY Added extra mysys flag argument to my_init_dynamic_array() Removed deprecated functions init_dynamic_array() and my_init_dynamic_array.._ci Updated paramaters for init_alloc_root() include/my_tree.h: Added my_flags to allow one to use MY_THREAD_SPECIFIC with hash tables. Removed with_delete. One should now instead use MY_TREE_WITH_DELETE_FLAG Updated parameters to init_tree() include/myisamchk.h: Added malloc_flags to allow one to use MY_THREAD_SPECIFIC for checks. include/mysql.h: Added MYSQL_THREAD_SPECIFIC_MALLOC Used 'unused1' to mark memory as thread specific. include/mysql.h.pp: Updated file include/mysql_com.h: Used 'unused1' to mark memory as thread specific. Updated parameters for my_net_init() libmysql/libmysql.c: Updated call to init_alloc_root() to mark memory thread specific. libmysqld/emb_qcache.cc: Updated call to init_alloc_root() libmysqld/lib_sql.cc: Updated call to init_alloc_root() mysql-test/r/create.result: Updated results mysql-test/r/user_var.result: Updated results mysql-test/suite/funcs_1/datadict/processlist_priv.inc: Update to handle new format of SHOW PROCESSLIST mysql-test/suite/funcs_1/datadict/processlist_val.inc: Update to handle new format of SHOW PROCESSLIST mysql-test/suite/funcs_1/r/is_columns_is.result: Update to handle new format of SHOW PROCESSLIST mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result: Updated results mysql-test/suite/funcs_1/r/processlist_val_no_prot.result: Updated results mysql-test/t/show_explain.test: Fixed usage of debug variable so that one can run test with --debug mysql-test/t/user_var.test: Added test of memory_usage variable. mysys/array.c: Added extra my_flags option to init_dynamic_array() and init_dynamic_array2() so that one can mark memory with MY_THREAD_SPECIFIC All allocated memory is marked with the given my_flags. Removed obsolete function init_dynamic_array() mysys/default.c: Updated call to init_alloc_root() Updated call to my_init_dynamic_array() mysys/hash.c: Updated call to my_init_dynamic_array_ci(). Allocated memory is marked with MY_THREAD_SPECIFIC if HASH_THREAD_SPECIFIC is used. mysys/ma_dyncol.c: init_dynamic_array() -> my_init_dynamic_array() Added #if to get rid of compiler warnings mysys/mf_tempdir.c: Updated call to my_init_dynamic_array() mysys/my_alloc.c: Added extra parameter to init_alloc_root() so that one can mark memory with MY_THREAD_SPECIFIC Extend MEM_ROOT with a flag if memory is thread specific. This is stored in block_size, to keep the size of the MEM_ROOT object identical as before. Allocated memory is marked with MY_THREAD_SPECIFIC if used with init_alloc_root() mysys/my_chmod.c: Updated DBUG_PRINT because of change of myf type mysys/my_chsize.c: Updated DBUG_PRINT because of change of myf type mysys/my_copy.c: Updated DBUG_PRINT because of change of myf type mysys/my_create.c: Updated DBUG_PRINT because of change of myf type mysys/my_delete.c: Updated DBUG_PRINT because of change of myf type mysys/my_error.c: Updated DBUG_PRINT because of change of myf type mysys/my_fopen.c: Updated DBUG_PRINT because of change of myf type mysys/my_fstream.c: Updated DBUG_PRINT because of change of myf type mysys/my_getwd.c: Updated DBUG_PRINT because of change of myf type mysys/my_lib.c: Updated call to init_alloc_root() Updated call to my_init_dynamic_array() Updated DBUG_PRINT because of change of myf type mysys/my_lock.c: Updated DBUG_PRINT because of change of myf type mysys/my_malloc.c: Store at start of each allocated memory block the size of the block and if the block is thread specific. Call malloc_size_cb_func, if set, with the memory allocated/freed. Updated DBUG_PRINT because of change of myf type mysys/my_open.c: Updated DBUG_PRINT because of change of myf type mysys/my_pread.c: Updated DBUG_PRINT because of change of myf type mysys/my_read.c: Updated DBUG_PRINT because of change of myf type mysys/my_redel.c: Updated DBUG_PRINT because of change of myf type mysys/my_rename.c: Updated DBUG_PRINT because of change of myf type mysys/my_seek.c: Updated DBUG_PRINT because of change of myf type mysys/my_sync.c: Updated DBUG_PRINT because of change of myf type mysys/my_thr_init.c: Ensure that one can call my_thread_dbug_id() even if thread is not properly initialized. mysys/my_write.c: Updated DBUG_PRINT because of change of myf type mysys/mysys_priv.h: Updated parameters to sf_malloc and sf_realloc() mysys/safemalloc.c: Added checking that for memory marked with MY_THREAD_SPECIFIC that it's the same thread that is allocation and freeing the memory. Added sf_malloc_dbug_id() to allow MariaDB to specify which THD is handling the memory. Added my_flags arguments to sf_malloc() and sf_realloc() to be able to mark memory with MY_THREAD_SPECIFIC. Added sf_report_leaked_memory() to get list of memory not freed by a thread. mysys/tree.c: Added flags to init_tree() so that one can mark tree to be thread specific. Removed with_delete option to init_tree(). Now one should instead use MY_TREE_WITH_DELETE_FLAG. Updated call to init_alloc_root() All allocated memory is marked with the given malloc flags mysys/waiting_threads.c: Updated call to my_init_dynamic_array() sql-common/client.c: Updated call to init_alloc_root() and my_net_init() to mark memory thread specific. Updated call to my_init_dynamic_array(). Added MYSQL_THREAD_SPECIFIC_MALLOC so that client can mark memory as MY_THREAD_SPECIFIC. sql-common/client_plugin.c: Updated call to init_alloc_root() sql/debug_sync.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/event_scheduler.cc: Removed calls to net_end() as this is now done in ~THD() Call set_current_thd() to ensure that memory is assigned to right thread. sql/events.cc: my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/filesort.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/filesort_utils.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/ha_ndbcluster.cc: Updated call to init_alloc_root() Updated call to my_net_init() Removed calls to net_end() and thd->cleanup() as these are now done in ~THD() sql/ha_ndbcluster_binlog.cc: Updated call to my_net_init() Updated call to init_sql_alloc() Removed calls to net_end() and thd->cleanup() as these are now done in ~THD() sql/ha_partition.cc: Updated call to init_alloc_root() sql/handler.cc: Added MY_THREAD_SPECIFIC to allocated memory. Added missing call to my_dir_end() sql/item_func.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/item_subselect.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/item_sum.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/log.cc: More DBUG Updated call to init_alloc_root() sql/mdl.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/mysqld.cc: Added total_memory_used Updated call to init_alloc_root() Move mysql_cond_broadcast() before my_thread_end() Added mariadb_dbug_id() to count memory per THD instead of per thread. Added my_malloc_size_cb_func() callback function for my_malloc() to count memory. Move initialization of mysqld_server_started and mysqld_server_initialized earlier. Updated call to my_init_dynamic_array(). Updated call to my_net_init(). Call my_pthread_setspecific_ptr(THR_THD,...) to ensure that memory is assigned to right thread. Added status variable 'memory_used'. Updated call to init_alloc_root() my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/mysqld.h: Added set_current_thd() sql/net_serv.cc: Added new parameter to my_net_init() so that one can mark memory with MY_THREAD_SPECIFIC. Store in net->thread_specific_malloc if memory is thread specific. Mark memory to be thread specific if requested. sql/opt_range.cc: Updated call to my_init_dynamic_array() Updated call to init_sql_alloc() Added MY_THREAD_SPECIFIC to allocated memory. sql/opt_subselect.cc: Updated call to init_sql_alloc() to mark memory thread specific. sql/protocol.cc: Fixed compiler warning sql/records.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/rpl_filter.cc: Updated call to my_init_dynamic_array() sql/rpl_handler.cc: Updated call to my_init_dynamic_array2() sql/rpl_handler.h: Updated call to init_sql_alloc() sql/rpl_mi.cc: Updated call to my_init_dynamic_array() sql/rpl_tblmap.cc: Updated call to init_alloc_root() sql/rpl_utility.cc: Updated call to my_init_dynamic_array() sql/slave.cc: Initialize things properly before calling functions that allocate memory. Removed calls to net_end() as this is now done in ~THD() sql/sp_head.cc: Updated call to init_sql_alloc() Updated call to my_init_dynamic_array() Added parameter to warning_info() that it should be fully initialized. sql/sp_pcontext.cc: Updated call to my_init_dynamic_array() sql/sql_acl.cc: Updated call to init_sql_alloc() Updated call to my_init_dynamic_array() my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/sql_admin.cc: Added parameter to warning_info() that it should be fully initialized. sql/sql_analyse.h: Updated call to init_tree() to mark memory thread specific. sql/sql_array.h: Updated call to my_init_dynamic_array() to mark memory thread specific. sql/sql_audit.cc: Updated call to my_init_dynamic_array() sql/sql_base.cc: Updated call to init_sql_alloc() my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/sql_cache.cc: Updated comment sql/sql_class.cc: Added parameter to warning_info() that not initialize it until THD is fully created. Updated call to init_sql_alloc() Mark THD::user_vars has to be thread specific. Updated call to my_init_dynamic_array() Ensure that memory allocated by THD is assigned to the THD. More DBUG Always acll net_end() in ~THD() Assert that all memory signed to this THD is really deleted at ~THD. Fixed set_status_var_init() to not reset memory_used. my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/sql_class.h: Added MY_THREAD_SPECIFIC to allocated memory. Added malloc_size to THD to record allocated memory per THD. sql/sql_delete.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/sql_error.cc: Added 'initialize' parameter to Warning_info() to say if should allocate memory for it's structures. This is used by THD::THD() to not allocate memory until THD is ready. Added Warning_info::free_memory() sql/sql_error.h: Updated Warning_info() class. sql/sql_handler.cc: Updated call to init_alloc_root() to mark memory thread specific. sql/sql_insert.cc: More DBUG sql/sql_join_cache.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/sql_lex.cc: Updated call to my_init_dynamic_array() sql/sql_lex.h: Updated call to my_init_dynamic_array() sql/sql_load.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/sql_parse.cc: Removed calls to net_end() and thd->cleanup() as these are now done in ~THD() Ensure that examined_row_count() is reset before query. Fixed bug where kill_threads_for_user() was using the wrong mem_root to allocate memory. my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() Don't restore thd->status_var.memory_used when restoring thd->status_var sql/sql_plugin.cc: Updated call to init_alloc_root() Updated call to my_init_dynamic_array() Don't allocate THD on the stack, as this causes problems with valgrind when doing thd memory counting. my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/sql_prepare.cc: Added parameter to warning_info() that it should be fully initialized. Updated call to init_sql_alloc() to mark memory thread specific. sql/sql_reload.cc: my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/sql_select.cc: Updated call to my_init_dynamic_array() and init_sql_alloc() to mark memory thread specific. Added MY_THREAD_SPECIFIC to allocated memory. More DBUG sql/sql_servers.cc: Updated call to init_sql_alloc() to mark memory some memory thread specific. my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/sql_show.cc: Updated call to my_init_dynamic_array() Mark my_dir() memory thread specific. Use my_pthread_setspecific_ptr(THR_THD,...) to mark that allocated memory should be allocated to calling thread. More DBUG. Added malloc_size and examined_row_count to SHOW PROCESSLIST. Added MY_THREAD_SPECIFIC to allocated memory. Updated call to init_sql_alloc() Added parameter to warning_info() that it should be fully initialized. sql/sql_statistics.cc: Fixed compiler warning sql/sql_string.cc: String elements can now be marked as thread specific. sql/sql_string.h: String elements can now be marked as thread specific. sql/sql_table.cc: Updated call to init_sql_alloc() and my_malloc() to mark memory thread specific my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() Fixed compiler warning sql/sql_test.cc: Updated call to my_init_dynamic_array() to mark memory thread specific. sql/sql_trigger.cc: Updated call to init_sql_alloc() sql/sql_udf.cc: Updated call to init_sql_alloc() my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/sql_update.cc: Added MY_THREAD_SPECIFIC to allocated memory. sql/table.cc: Updated call to init_sql_alloc(). Mark memory used by temporary tables, that are not for slave threads, as MY_THREAD_SPECIFIC Updated call to init_sql_alloc() sql/thr_malloc.cc: Added my_flags argument to init_sql_alloc() to be able to mark memory as MY_THREAD_SPECIFIC. sql/thr_malloc.h: Updated prototype for init_sql_alloc() sql/tztime.cc: Updated call to init_sql_alloc() Updated call to init_alloc_root() to mark memory thread specific. my_pthread_setspecific_ptr(THR_THD,...) -> set_current_thd() sql/uniques.cc: Updated calls to init_tree(), my_init_dynamic_array() and my_malloc() to mark memory thread specific. sql/unireg.cc: Added MY_THREAD_SPECIFIC to allocated memory. storage/csv/ha_tina.cc: Updated call to init_alloc_root() storage/federated/ha_federated.cc: Updated call to init_alloc_root() Updated call to my_init_dynamic_array() Ensure that memory allocated by fedarated is registered for the system, not for the thread. storage/federatedx/federatedx_io_mysql.cc: Updated call to my_init_dynamic_array() storage/federatedx/ha_federatedx.cc: Updated call to init_alloc_root() Updated call to my_init_dynamic_array() storage/heap/ha_heap.cc: Added MY_THREAD_SPECIFIC to allocated memory. storage/heap/heapdef.h: Added parameter to hp_get_new_block() to be able to do thread specific memory tagging. storage/heap/hp_block.c: Added parameter to hp_get_new_block() to be able to do thread specific memory tagging. storage/heap/hp_create.c: - Internal HEAP tables are now marking it's memory as MY_THREAD_SPECIFIC. - Use MY_TREE_WITH_DELETE instead of removed option 'with_delete'. storage/heap/hp_open.c: Internal HEAP tables are now marking it's memory as MY_THREAD_SPECIFIC. storage/heap/hp_write.c: Added new parameter to hp_get_new_block() storage/maria/ma_bitmap.c: Updated call to my_init_dynamic_array() storage/maria/ma_blockrec.c: Updated call to my_init_dynamic_array() storage/maria/ma_check.c: Updated call to init_alloc_root() storage/maria/ma_ft_boolean_search.c: Updated calls to init_tree() and init_alloc_root() storage/maria/ma_ft_nlq_search.c: Updated call to init_tree() storage/maria/ma_ft_parser.c: Updated call to init_tree() Updated call to init_alloc_root() storage/maria/ma_loghandler.c: Updated call to my_init_dynamic_array() storage/maria/ma_open.c: Updated call to my_init_dynamic_array() storage/maria/ma_sort.c: Updated call to my_init_dynamic_array() storage/maria/ma_write.c: Updated calls to my_init_dynamic_array() and init_tree() storage/maria/maria_pack.c: Updated call to init_tree() storage/maria/unittest/sequence_storage.c: Updated call to my_init_dynamic_array() storage/myisam/ft_boolean_search.c: Updated call to init_tree() Updated call to init_alloc_root() storage/myisam/ft_nlq_search.c: Updated call to init_tree() storage/myisam/ft_parser.c: Updated call to init_tree() Updated call to init_alloc_root() storage/myisam/ft_stopwords.c: Updated call to init_tree() storage/myisam/mi_check.c: Updated call to init_alloc_root() storage/myisam/mi_write.c: Updated call to my_init_dynamic_array() Updated call to init_tree() storage/myisam/myisamlog.c: Updated call to init_tree() storage/myisam/myisampack.c: Updated call to init_tree() storage/myisam/sort.c: Updated call to my_init_dynamic_array() storage/myisammrg/ha_myisammrg.cc: Updated call to init_sql_alloc() storage/perfschema/pfs_check.cc: Rest current_thd storage/perfschema/pfs_instr.cc: Removed DBUG_ENTER/DBUG_VOID_RETURN as at this point my_thread_var is not allocated anymore, which can cause problems. support-files/compiler_warnings.supp: Disable compiler warning from offsetof macro.
1559 lines
44 KiB
C++
1559 lines
44 KiB
C++
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
|
|
Copyright (c) 2008, 2012, Monty Program 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; version 2 of the License.
|
|
|
|
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
/**
|
|
@file
|
|
|
|
Low level functions for storing data to be send to the MySQL client.
|
|
The actual communction is handled by the net_xxx functions in net_serv.cc
|
|
*/
|
|
|
|
#ifdef USE_PRAGMA_IMPLEMENTATION
|
|
#pragma implementation // gcc: Class implementation
|
|
#endif
|
|
|
|
#include "sql_priv.h"
|
|
#include "unireg.h" // REQUIRED: for other includes
|
|
#include "protocol.h"
|
|
#include "sql_class.h" // THD
|
|
#include <stdarg.h>
|
|
|
|
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
|
|
/* Declared non-static only because of the embedded library. */
|
|
bool net_send_error_packet(THD *, uint, const char *, const char *);
|
|
/* Declared non-static only because of the embedded library. */
|
|
bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *);
|
|
/* Declared non-static only because of the embedded library. */
|
|
bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
|
|
#ifndef EMBEDDED_LIBRARY
|
|
static bool write_eof_packet(THD *, NET *, uint, uint);
|
|
#endif
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
bool Protocol::net_store_data(const uchar *from, size_t length)
|
|
#else
|
|
bool Protocol_binary::net_store_data(const uchar *from, size_t 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;
|
|
uchar *to= net_store_length((uchar*) packet->ptr()+packet_length, length);
|
|
memcpy(to,from,length);
|
|
packet->length((uint) (to+length-(uchar*) packet->ptr()));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
net_store_data() - extended version with character set conversion.
|
|
|
|
It is optimized for short strings whose length after
|
|
conversion is garanteed to be less than 251, which accupies
|
|
exactly one byte to store length. It allows not to use
|
|
the "convert" member as a temporary buffer, conversion
|
|
is done directly to the "packet" member.
|
|
The limit 251 is good enough to optimize send_result_set_metadata()
|
|
because column, table, database names fit into this limit.
|
|
*/
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
bool Protocol::net_store_data(const uchar *from, size_t length,
|
|
CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
|
|
{
|
|
uint dummy_errors;
|
|
/* Calculate maxumum possible result length */
|
|
uint conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen;
|
|
|
|
if (conv_length > 250)
|
|
{
|
|
/*
|
|
For strings with conv_length greater than 250 bytes
|
|
we don't know how many bytes we will need to store length: one or two,
|
|
because we don't know result length until conversion is done.
|
|
For example, when converting from utf8 (mbmaxlen=3) to latin1,
|
|
conv_length=300 means that the result length can vary between 100 to 300.
|
|
length=100 needs one byte, length=300 needs to bytes.
|
|
|
|
Thus conversion directly to "packet" is not worthy.
|
|
Let's use "convert" as a temporary buffer.
|
|
*/
|
|
return (convert->copy((const char*) from, length, from_cs,
|
|
to_cs, &dummy_errors) ||
|
|
net_store_data((const uchar*) convert->ptr(), convert->length()));
|
|
}
|
|
|
|
ulong packet_length= packet->length();
|
|
ulong new_length= packet_length + conv_length + 1;
|
|
|
|
if (new_length > packet->alloced_length() && packet->realloc(new_length))
|
|
return 1;
|
|
|
|
char *length_pos= (char*) packet->ptr() + packet_length;
|
|
char *to= length_pos + 1;
|
|
|
|
to+= copy_and_convert(to, conv_length, to_cs,
|
|
(const char*) from, length, from_cs, &dummy_errors);
|
|
|
|
net_store_length((uchar*) length_pos, to - length_pos - 1);
|
|
packet->length((uint) (to - packet->ptr()));
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
Send a error string to client.
|
|
|
|
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.
|
|
|
|
@param thd Thread handler
|
|
@param sql_errno The error code to send
|
|
@param err A pointer to the error message
|
|
|
|
@return
|
|
@retval FALSE The message was sent to the client
|
|
@retval TRUE An error occurred and the message wasn't sent properly
|
|
*/
|
|
|
|
bool net_send_error(THD *thd, uint sql_errno, const char *err,
|
|
const char* sqlstate)
|
|
{
|
|
bool error;
|
|
DBUG_ENTER("net_send_error");
|
|
|
|
DBUG_ASSERT(!thd->spcont);
|
|
DBUG_ASSERT(sql_errno);
|
|
DBUG_ASSERT(err);
|
|
|
|
DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err));
|
|
|
|
if (sqlstate == NULL)
|
|
sqlstate= mysql_errno_to_sqlstate(sql_errno);
|
|
|
|
/*
|
|
It's one case when we can push an error even though there
|
|
is an OK or EOF already.
|
|
*/
|
|
thd->stmt_da->can_overwrite_status= TRUE;
|
|
|
|
/* Abort multi-result sets */
|
|
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
|
|
|
|
error= net_send_error_packet(thd, sql_errno, err, sqlstate);
|
|
|
|
thd->stmt_da->can_overwrite_status= FALSE;
|
|
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
/**
|
|
Return ok to the client.
|
|
|
|
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.
|
|
|
|
@param thd Thread handler
|
|
@param server_status The server status
|
|
@param statement_warn_count Total number of warnings
|
|
@param affected_rows Number of rows changed by statement
|
|
@param id Auto_increment id for first row (if used)
|
|
@param message Message to send to the client (Used by mysql_status)
|
|
|
|
@return
|
|
@retval FALSE The message was successfully sent
|
|
@retval TRUE An error occurred and the messages wasn't sent properly
|
|
|
|
*/
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
bool
|
|
net_send_ok(THD *thd,
|
|
uint server_status, uint statement_warn_count,
|
|
ulonglong affected_rows, ulonglong id, const char *message)
|
|
{
|
|
NET *net= &thd->net;
|
|
uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
|
|
bool error= FALSE;
|
|
DBUG_ENTER("net_send_ok");
|
|
|
|
if (! net->vio) // hack for re-parsing queries
|
|
{
|
|
DBUG_PRINT("info", ("vio present: NO"));
|
|
DBUG_RETURN(FALSE);
|
|
}
|
|
|
|
buff[0]=0; // No fields
|
|
pos=net_store_length(buff+1,affected_rows);
|
|
pos=net_store_length(pos, 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) (server_status & 0xffff),
|
|
(uint) statement_warn_count));
|
|
int2store(pos, server_status);
|
|
pos+=2;
|
|
|
|
/* We can only return up to 65535 warnings in two bytes */
|
|
uint tmp= min(statement_warn_count, 65535);
|
|
int2store(pos, tmp);
|
|
pos+= 2;
|
|
}
|
|
else if (net->return_status) // For 4.0 protocol
|
|
{
|
|
int2store(pos, server_status);
|
|
pos+=2;
|
|
}
|
|
thd->stmt_da->can_overwrite_status= TRUE;
|
|
|
|
if (message && message[0])
|
|
pos= net_store_data(pos, (uchar*) message, strlen(message));
|
|
error= my_net_write(net, buff, (size_t) (pos-buff));
|
|
if (!error)
|
|
error= net_flush(net);
|
|
|
|
|
|
thd->stmt_da->can_overwrite_status= FALSE;
|
|
DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
|
|
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
|
|
|
|
/**
|
|
Send eof (= end of result set) to the client.
|
|
|
|
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_MORE_RESULTS_EXISTS.
|
|
|
|
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.
|
|
|
|
@param thd Thread handler
|
|
@param server_status The server status
|
|
@param statement_warn_count Total number of warnings
|
|
|
|
@return
|
|
@retval FALSE The message was successfully sent
|
|
@retval TRUE An error occurred and the message wasn't sent properly
|
|
*/
|
|
|
|
bool
|
|
net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
|
|
{
|
|
NET *net= &thd->net;
|
|
bool error= FALSE;
|
|
DBUG_ENTER("net_send_eof");
|
|
/* Set to TRUE if no active vio, to work well in case of --init-file */
|
|
if (net->vio != 0)
|
|
{
|
|
thd->stmt_da->can_overwrite_status= TRUE;
|
|
error= write_eof_packet(thd, net, server_status, statement_warn_count);
|
|
if (!error)
|
|
error= net_flush(net);
|
|
thd->stmt_da->can_overwrite_status= FALSE;
|
|
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
|
|
}
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/**
|
|
Format EOF packet according to the current protocol and
|
|
write it to the network output buffer.
|
|
|
|
@param thd The thread handler
|
|
@param net The network handler
|
|
@param server_status The server status
|
|
@param statement_warn_count The number of warnings
|
|
|
|
|
|
@return
|
|
@retval FALSE The message was sent successfully
|
|
@retval TRUE An error occurred and the messages wasn't sent properly
|
|
*/
|
|
|
|
static bool write_eof_packet(THD *thd, NET *net,
|
|
uint server_status,
|
|
uint statement_warn_count)
|
|
{
|
|
bool error;
|
|
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= min(statement_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)
|
|
server_status&= ~SERVER_MORE_RESULTS_EXISTS;
|
|
int2store(buff + 3, server_status);
|
|
error= my_net_write(net, buff, 5);
|
|
}
|
|
else
|
|
error= my_net_write(net, eof_buff, 1);
|
|
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
@param thd Thread handler
|
|
@param sql_errno The error code to send
|
|
@param err A pointer to the error message
|
|
|
|
@return
|
|
@retval FALSE The message was successfully sent
|
|
@retval TRUE An error occurred and the messages wasn't sent properly
|
|
*/
|
|
|
|
bool net_send_error_packet(THD *thd, uint sql_errno, const char *err,
|
|
const char* sqlstate)
|
|
|
|
{
|
|
NET *net= &thd->net;
|
|
uint length;
|
|
/*
|
|
buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + MYSQL_ERRMSG_SIZE:512
|
|
*/
|
|
uint error;
|
|
char converted_err[MYSQL_ERRMSG_SIZE];
|
|
char buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *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_RETURN(FALSE);
|
|
}
|
|
|
|
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, sqlstate);
|
|
}
|
|
|
|
convert_error_message(converted_err, sizeof(converted_err),
|
|
thd->variables.character_set_results,
|
|
err, strlen(err), system_charset_info, &error);
|
|
/* Converted error message is always null-terminated. */
|
|
length= (uint) (strmake(pos, converted_err, MYSQL_ERRMSG_SIZE - 1) - buff);
|
|
|
|
DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) buff,
|
|
length));
|
|
}
|
|
|
|
#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.
|
|
*/
|
|
|
|
static uchar *net_store_length_fast(uchar *packet, uint length)
|
|
{
|
|
if (length < 251)
|
|
{
|
|
*packet=(uchar) length;
|
|
return packet+1;
|
|
}
|
|
*packet++=252;
|
|
int2store(packet,(uint) length);
|
|
return packet+2;
|
|
}
|
|
|
|
/**
|
|
Send the status of the current statement execution over network.
|
|
|
|
@param thd in fact, carries two parameters, NET for the transport and
|
|
Diagnostics_area as the source of status information.
|
|
|
|
In MySQL, there are two types of SQL statements: those that return
|
|
a result set and those that return status information only.
|
|
|
|
If a statement returns a result set, it consists of 3 parts:
|
|
- result set meta-data
|
|
- variable number of result set rows (can be 0)
|
|
- followed and terminated by EOF or ERROR packet
|
|
|
|
Once the client has seen the meta-data information, it always
|
|
expects an EOF or ERROR to terminate the result set. If ERROR is
|
|
received, the result set rows are normally discarded (this is up
|
|
to the client implementation, libmysql at least does discard them).
|
|
EOF, on the contrary, means "successfully evaluated the entire
|
|
result set". Since we don't know how many rows belong to a result
|
|
set until it's evaluated, EOF/ERROR is the indicator of the end
|
|
of the row stream. Note, that we can not buffer result set rows
|
|
on the server -- there may be an arbitrary number of rows. But
|
|
we do buffer the last packet (EOF/ERROR) in the Diagnostics_area and
|
|
delay sending it till the very end of execution (here), to be able to
|
|
change EOF to an ERROR if commit failed or some other error occurred
|
|
during the last cleanup steps taken after execution.
|
|
|
|
A statement that does not return a result set doesn't send result
|
|
set meta-data either. Instead it returns one of:
|
|
- OK packet
|
|
- ERROR packet.
|
|
Similarly to the EOF/ERROR of the previous statement type, OK/ERROR
|
|
packet is "buffered" in the diagnostics area and sent to the client
|
|
in the end of statement.
|
|
|
|
@note This method defines a template, but delegates actual
|
|
sending of data to virtual Protocol::send_{ok,eof,error}. This
|
|
allows for implementation of protocols that "intercept" ok/eof/error
|
|
messages, and store them in memory, etc, instead of sending to
|
|
the client.
|
|
|
|
@pre The diagnostics area is assigned or disabled. It can not be empty
|
|
-- we assume that every SQL statement or COM_* command
|
|
generates OK, ERROR, or EOF status.
|
|
|
|
@post The status information is encoded to protocol format and sent to the
|
|
client.
|
|
|
|
@return We conventionally return void, since the only type of error
|
|
that can happen here is a NET (transport) error, and that one
|
|
will become visible when we attempt to read from the NET the
|
|
next command.
|
|
Diagnostics_area::is_sent is set for debugging purposes only.
|
|
*/
|
|
|
|
void Protocol::end_statement()
|
|
{
|
|
DBUG_ENTER("Protocol::end_statement");
|
|
DBUG_ASSERT(! thd->stmt_da->is_sent);
|
|
bool error= FALSE;
|
|
|
|
/* Can not be true, but do not take chances in production. */
|
|
if (thd->stmt_da->is_sent)
|
|
DBUG_VOID_RETURN;
|
|
|
|
switch (thd->stmt_da->status()) {
|
|
case Diagnostics_area::DA_ERROR:
|
|
/* The query failed, send error to log and abort bootstrap. */
|
|
error= send_error(thd->stmt_da->sql_errno(),
|
|
thd->stmt_da->message(),
|
|
thd->stmt_da->get_sqlstate());
|
|
break;
|
|
case Diagnostics_area::DA_EOF:
|
|
error= send_eof(thd->server_status,
|
|
thd->stmt_da->statement_warn_count());
|
|
break;
|
|
case Diagnostics_area::DA_OK:
|
|
error= send_ok(thd->server_status,
|
|
thd->stmt_da->statement_warn_count(),
|
|
thd->stmt_da->affected_rows(),
|
|
thd->stmt_da->last_insert_id(),
|
|
thd->stmt_da->message());
|
|
break;
|
|
case Diagnostics_area::DA_DISABLED:
|
|
break;
|
|
case Diagnostics_area::DA_EMPTY:
|
|
default:
|
|
DBUG_ASSERT(0);
|
|
error= send_ok(thd->server_status, 0, 0, 0, NULL);
|
|
break;
|
|
}
|
|
if (!error)
|
|
thd->stmt_da->is_sent= TRUE;
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
/**
|
|
A default implementation of "OK" packet response to the client.
|
|
|
|
Currently this implementation is re-used by both network-oriented
|
|
protocols -- the binary and text one. They do not differ
|
|
in their OK packet format, which allows for a significant simplification
|
|
on client side.
|
|
*/
|
|
|
|
bool Protocol::send_ok(uint server_status, uint statement_warn_count,
|
|
ulonglong affected_rows, ulonglong last_insert_id,
|
|
const char *message)
|
|
{
|
|
DBUG_ENTER("Protocol::send_ok");
|
|
const bool retval=
|
|
net_send_ok(thd, server_status, statement_warn_count,
|
|
affected_rows, last_insert_id, message);
|
|
DBUG_RETURN(retval);
|
|
}
|
|
|
|
|
|
/**
|
|
A default implementation of "EOF" packet response to the client.
|
|
|
|
Binary and text protocol do not differ in their EOF packet format.
|
|
*/
|
|
|
|
bool Protocol::send_eof(uint server_status, uint statement_warn_count)
|
|
{
|
|
DBUG_ENTER("Protocol::send_eof");
|
|
const bool retval= net_send_eof(thd, server_status, statement_warn_count);
|
|
DBUG_RETURN(retval);
|
|
}
|
|
|
|
|
|
/**
|
|
A default implementation of "ERROR" packet response to the client.
|
|
|
|
Binary and text protocol do not differ in ERROR packet format.
|
|
*/
|
|
|
|
bool Protocol::send_error(uint sql_errno, const char *err_msg,
|
|
const char *sql_state)
|
|
{
|
|
DBUG_ENTER("Protocol::send_error");
|
|
const bool retval= net_send_error_packet(thd, sql_errno, err_msg, sql_state);
|
|
DBUG_RETURN(retval);
|
|
}
|
|
|
|
|
|
/**
|
|
Send a progress report to the client
|
|
|
|
What we send is:
|
|
header (255,255,255,1)
|
|
stage, max_stage as on byte integers
|
|
percentage withing the stage as percentage*1000
|
|
(that is, ratio*100000) as a 3 byte integer
|
|
proc_info as a string
|
|
*/
|
|
|
|
const uchar progress_header[2]= {(uchar) 255, (uchar) 255 };
|
|
|
|
void net_send_progress_packet(THD *thd)
|
|
{
|
|
uchar buff[200], *pos;
|
|
const char *proc_info= thd->proc_info ? thd->proc_info : "";
|
|
uint length= strlen(proc_info);
|
|
ulonglong progress;
|
|
DBUG_ENTER("net_send_progress_packet");
|
|
|
|
if (unlikely(!thd->net.vio))
|
|
DBUG_VOID_RETURN; // Socket is closed
|
|
|
|
pos= buff;
|
|
/*
|
|
Store number of strings first. This allows us to later expand the
|
|
progress indicator if needed.
|
|
*/
|
|
*pos++= (uchar) 1; // Number of strings
|
|
*pos++= (uchar) thd->progress.stage + 1;
|
|
/*
|
|
We have the max() here to avoid problems if max_stage is not set,
|
|
which may happen during automatic repair of table
|
|
*/
|
|
*pos++= (uchar) max(thd->progress.max_stage, thd->progress.stage + 1);
|
|
progress= 0;
|
|
if (thd->progress.max_counter)
|
|
progress= 100000ULL * thd->progress.counter / thd->progress.max_counter;
|
|
int3store(pos, progress); // Between 0 & 100000
|
|
pos+= 3;
|
|
pos= net_store_data(pos, (const uchar*) proc_info,
|
|
min(length, sizeof(buff)-7));
|
|
net_write_command(&thd->net, (uchar) 255, progress_header,
|
|
sizeof(progress_header), (uchar*) buff,
|
|
(uint) (pos - buff));
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Functions used by the protocol functions (like net_send_ok) to store
|
|
strings and numbers in the header result packet.
|
|
****************************************************************************/
|
|
|
|
/* The following will only be used for short strings < 65K */
|
|
|
|
uchar *net_store_data(uchar *to, const uchar *from, size_t length)
|
|
{
|
|
to=net_store_length_fast(to,length);
|
|
memcpy(to,from,length);
|
|
return to+length;
|
|
}
|
|
|
|
uchar *net_store_data(uchar *to,int32 from)
|
|
{
|
|
char buff[20];
|
|
uint length=(uint) (int10_to_str(from,buff,10)-buff);
|
|
to=net_store_length_fast(to,length);
|
|
memcpy(to,buff,length);
|
|
return to+length;
|
|
}
|
|
|
|
uchar *net_store_data(uchar *to,longlong from)
|
|
{
|
|
char buff[22];
|
|
uint length=(uint) (longlong10_to_str(from,buff,10)-buff);
|
|
to=net_store_length_fast(to,length);
|
|
memcpy(to,buff,length);
|
|
return to+length;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
Default Protocol functions
|
|
*****************************************************************************/
|
|
|
|
void Protocol::init(THD *thd_arg)
|
|
{
|
|
thd=thd_arg;
|
|
packet= &thd->packet;
|
|
convert= &thd->convert_buffer;
|
|
#ifndef DBUG_OFF
|
|
field_types= 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Finish the result set with EOF packet, as is expected by the client,
|
|
if there is an error evaluating the next row and a continue handler
|
|
for the error.
|
|
*/
|
|
|
|
void Protocol::end_partial_result_set(THD *thd_arg)
|
|
{
|
|
net_send_eof(thd_arg, thd_arg->server_status,
|
|
0 /* no warnings, we're inside SP */);
|
|
}
|
|
|
|
|
|
bool Protocol::flush()
|
|
{
|
|
#ifndef EMBEDDED_LIBRARY
|
|
bool error;
|
|
thd->stmt_da->can_overwrite_status= TRUE;
|
|
error= net_flush(&thd->net);
|
|
thd->stmt_da->can_overwrite_status= FALSE;
|
|
return error;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
/**
|
|
Send name and type of result to client.
|
|
|
|
Sum fields has table name empty and field_name.
|
|
|
|
@param THD Thread data object
|
|
@param list List of items to send to client
|
|
@param flag Bit mask with the following functions:
|
|
- 1 send number of rows
|
|
- 2 send default values
|
|
- 4 don't write eof packet
|
|
|
|
@retval
|
|
0 ok
|
|
@retval
|
|
1 Error (Note that in this case the error is not sent to the
|
|
client)
|
|
*/
|
|
bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
|
|
{
|
|
List_iterator_fast<Item> it(*list);
|
|
Item *item;
|
|
uchar buff[MAX_FIELD_WIDTH];
|
|
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
|
|
Protocol_text prot(thd);
|
|
String *local_packet= prot.storage_packet();
|
|
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
|
|
DBUG_ENTER("Protocol::send_result_set_metadata");
|
|
|
|
if (flags & SEND_NUM_ROWS)
|
|
{ // Packet with number of elements
|
|
uchar *pos= net_store_length(buff, list->elements);
|
|
if (my_net_write(&thd->net, buff, (size_t) (pos-buff)))
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
#ifndef DBUG_OFF
|
|
field_types= (enum_field_types*) thd->alloc(sizeof(field_types) *
|
|
list->elements);
|
|
uint count= 0;
|
|
#endif
|
|
|
|
/* We have to reallocate it here as a stored procedure may have reset it */
|
|
(void) local_packet->alloc(thd->variables.net_buffer_length);
|
|
|
|
while ((item=it++))
|
|
{
|
|
char *pos;
|
|
CHARSET_INFO *cs= system_charset_info;
|
|
Send_field field;
|
|
item->make_field(&field);
|
|
|
|
/* Keep things compatible for old clients */
|
|
if (field.type == MYSQL_TYPE_VARCHAR)
|
|
field.type= MYSQL_TYPE_VAR_STRING;
|
|
|
|
prot.prepare_for_resend();
|
|
|
|
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
|
{
|
|
if (prot.store(STRING_WITH_LEN("def"), 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
|
|
/* inject a NULL to test the client */
|
|
DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= (char) 0xfb;);
|
|
if (item->charset_for_protocol() == &my_charset_bin || thd_charset == NULL)
|
|
{
|
|
/* No conversion */
|
|
int2store(pos, item->charset_for_protocol()->number);
|
|
int4store(pos+2, field.length);
|
|
}
|
|
else
|
|
{
|
|
/* With conversion */
|
|
uint32 field_length, max_length;
|
|
int2store(pos, thd_charset->number);
|
|
/*
|
|
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.
|
|
|
|
When one has a LONG TEXT column with a single-byte
|
|
character set, and the connection character set is multi-byte, the
|
|
client may get fields longer than UINT_MAX32, due to
|
|
<character set column> -> <character set connection> conversion.
|
|
In that case column max length does not fit into the 4 bytes
|
|
reserved for it in the protocol.
|
|
*/
|
|
max_length= (field.type >= MYSQL_TYPE_TINY_BLOB &&
|
|
field.type <= MYSQL_TYPE_BLOB) ?
|
|
field.length / item->collation.collation->mbminlen :
|
|
field.length / item->collation.collation->mbmaxlen;
|
|
field_length= char_to_byte_length_safe(max_length,
|
|
thd_charset->mbmaxlen);
|
|
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();
|
|
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 (flags & SEND_DEFAULTS)
|
|
item->send(&prot, &tmp); // Send default value
|
|
if (prot.write())
|
|
DBUG_RETURN(1);
|
|
#ifndef DBUG_OFF
|
|
field_types[count++]= field.type;
|
|
#endif
|
|
}
|
|
|
|
if (flags & SEND_EOF)
|
|
{
|
|
/*
|
|
Mark the end of meta-data result set, and store thd->server_status,
|
|
to show that there is no cursor.
|
|
Send no warning information, as it will be sent at statement end.
|
|
*/
|
|
if (write_eof_packet(thd, &thd->net, thd->server_status,
|
|
thd->warning_info->statement_warn_count()))
|
|
DBUG_RETURN(1);
|
|
}
|
|
DBUG_RETURN(prepare_for_send(list->elements));
|
|
|
|
err:
|
|
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
|
|
MYF(0)); /* purecov: inspected */
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
}
|
|
|
|
|
|
bool Protocol::write()
|
|
{
|
|
DBUG_ENTER("Protocol::write");
|
|
DBUG_RETURN(my_net_write(&thd->net, (uchar*) packet->ptr(),
|
|
packet->length()));
|
|
}
|
|
#endif /* EMBEDDED_LIBRARY */
|
|
|
|
|
|
/**
|
|
Send one result set row.
|
|
|
|
@param row_items a collection of column values for that row
|
|
|
|
@return Error status.
|
|
@retval TRUE Error.
|
|
@retval FALSE Success.
|
|
*/
|
|
|
|
bool Protocol::send_result_set_row(List<Item> *row_items)
|
|
{
|
|
char buffer[MAX_FIELD_WIDTH];
|
|
String str_buffer(buffer, sizeof (buffer), &my_charset_bin);
|
|
List_iterator_fast<Item> it(*row_items);
|
|
|
|
DBUG_ENTER("Protocol::send_result_set_row");
|
|
|
|
for (Item *item= it++; item; item= it++)
|
|
{
|
|
if (item->send(this, &str_buffer))
|
|
{
|
|
// If we're out of memory, reclaim some, to help us recover.
|
|
this->free();
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
/* Item::send() may generate an error. If so, abort the loop. */
|
|
if (thd->is_error())
|
|
DBUG_RETURN(TRUE);
|
|
|
|
/*
|
|
Reset str_buffer to its original state, as it may have been altered in
|
|
Item::send().
|
|
*/
|
|
str_buffer.set(buffer, sizeof(buffer), &my_charset_bin);
|
|
}
|
|
|
|
DBUG_RETURN(FALSE);
|
|
}
|
|
|
|
|
|
/**
|
|
Send \\0 end terminated string.
|
|
|
|
@param from NullS or \\0 terminated string
|
|
|
|
@note
|
|
In most cases one should use store(from, length) instead of this function
|
|
|
|
@retval
|
|
0 ok
|
|
@retval
|
|
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_text::prepare_for_resend()
|
|
{
|
|
packet->length(0);
|
|
#ifndef DBUG_OFF
|
|
field_pos= 0;
|
|
#endif
|
|
}
|
|
|
|
bool Protocol_text::store_null()
|
|
{
|
|
#ifndef DBUG_OFF
|
|
field_pos++;
|
|
#endif
|
|
char buff[1];
|
|
buff[0]= (char)251;
|
|
return packet->append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
Auxilary function to convert string to the given character set
|
|
and store in network buffer.
|
|
*/
|
|
|
|
bool Protocol::store_string_aux(const char *from, size_t 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)
|
|
{
|
|
/* Store with conversion */
|
|
return net_store_data((uchar*) from, length, fromcs, tocs);
|
|
}
|
|
/* Store without conversion */
|
|
return net_store_data((uchar*) from, length);
|
|
}
|
|
|
|
|
|
bool Protocol_text::store(const char *from, size_t length,
|
|
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
|
|
field_types[field_pos] == MYSQL_TYPE_BIT ||
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL ||
|
|
(field_types[field_pos] >= MYSQL_TYPE_ENUM &&
|
|
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
|
field_pos++;
|
|
#endif
|
|
return store_string_aux(from, length, fromcs, tocs);
|
|
}
|
|
|
|
|
|
bool Protocol_text::store(const char *from, size_t length,
|
|
CHARSET_INFO *fromcs)
|
|
{
|
|
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
|
|
#ifndef DBUG_OFF
|
|
DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %.*s", field_pos,
|
|
field_count, (int) length, (length == 0 ? "" : from)));
|
|
DBUG_ASSERT(field_pos < field_count);
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
|
|
field_types[field_pos] == MYSQL_TYPE_BIT ||
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL ||
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDATE ||
|
|
(field_types[field_pos] >= MYSQL_TYPE_ENUM &&
|
|
field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
|
|
field_pos++;
|
|
#endif
|
|
return store_string_aux(from, length, fromcs, tocs);
|
|
}
|
|
|
|
|
|
bool Protocol_text::store_tiny(longlong from)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY);
|
|
field_pos++;
|
|
#endif
|
|
char buff[20];
|
|
return net_store_data((uchar*) buff,
|
|
(size_t) (int10_to_str((int) from, buff, -10) - buff));
|
|
}
|
|
|
|
|
|
bool Protocol_text::store_short(longlong from)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_YEAR ||
|
|
field_types[field_pos] == MYSQL_TYPE_SHORT);
|
|
field_pos++;
|
|
#endif
|
|
char buff[20];
|
|
return net_store_data((uchar*) buff,
|
|
(size_t) (int10_to_str((int) from, buff, -10) -
|
|
buff));
|
|
}
|
|
|
|
|
|
bool Protocol_text::store_long(longlong from)
|
|
{
|
|
#ifndef DBUG_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((uchar*) buff,
|
|
(size_t) (int10_to_str((long int)from, buff,
|
|
(from <0)?-10:10)-buff));
|
|
}
|
|
|
|
|
|
bool Protocol_text::store_longlong(longlong from, bool unsigned_flag)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_LONGLONG);
|
|
field_pos++;
|
|
#endif
|
|
char buff[22];
|
|
return net_store_data((uchar*) buff,
|
|
(size_t) (longlong10_to_str(from,buff,
|
|
unsigned_flag ? 10 : -10)-
|
|
buff));
|
|
}
|
|
|
|
|
|
bool Protocol_text::store_decimal(const my_decimal *d)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL);
|
|
field_pos++;
|
|
#endif
|
|
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);
|
|
return net_store_data((uchar*) str.ptr(), str.length());
|
|
}
|
|
|
|
|
|
bool Protocol_text::store(float from, uint32 decimals, String *buffer)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_FLOAT);
|
|
field_pos++;
|
|
#endif
|
|
buffer->set_real((double) from, decimals, thd->charset());
|
|
return net_store_data((uchar*) buffer->ptr(), buffer->length());
|
|
}
|
|
|
|
|
|
bool Protocol_text::store(double from, uint32 decimals, String *buffer)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_DOUBLE);
|
|
field_pos++;
|
|
#endif
|
|
buffer->set_real(from, decimals, thd->charset());
|
|
return net_store_data((uchar*) buffer->ptr(), buffer->length());
|
|
}
|
|
|
|
|
|
bool Protocol_text::store(Field *field)
|
|
{
|
|
if (field->is_null())
|
|
return store_null();
|
|
#ifndef DBUG_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;
|
|
#ifndef DBUG_OFF
|
|
TABLE *table= field->table;
|
|
my_bitmap_map *old_map= 0;
|
|
if (table->file)
|
|
old_map= dbug_tmp_use_all_columns(table, table->read_set);
|
|
#endif
|
|
|
|
field->val_str(&str);
|
|
#ifndef DBUG_OFF
|
|
if (old_map)
|
|
dbug_tmp_restore_column_map(table->read_set, old_map);
|
|
#endif
|
|
|
|
return store_string_aux(str.ptr(), str.length(), str.charset(), tocs);
|
|
}
|
|
|
|
|
|
bool Protocol_text::store(MYSQL_TIME *tm, int decimals)
|
|
{
|
|
#ifndef DBUG_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[MAX_DATE_STRING_REP_LENGTH];
|
|
uint length= my_datetime_to_str(tm, buff, decimals);
|
|
return net_store_data((uchar*) buff, length);
|
|
}
|
|
|
|
|
|
bool Protocol_text::store_date(MYSQL_TIME *tm)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_DATE);
|
|
field_pos++;
|
|
#endif
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
size_t length= my_date_to_str(tm, buff);
|
|
return net_store_data((uchar*) buff, length);
|
|
}
|
|
|
|
|
|
bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_TIME);
|
|
field_pos++;
|
|
#endif
|
|
char buff[MAX_DATE_STRING_REP_LENGTH];
|
|
uint length= my_time_to_str(tm, buff, decimals);
|
|
return net_store_data((uchar*) buff, length);
|
|
}
|
|
|
|
/**
|
|
Assign OUT-parameters to user variables.
|
|
|
|
@param sp_params List of PS/SP parameters (both input and output).
|
|
|
|
@return Error status.
|
|
@retval FALSE Success.
|
|
@retval TRUE Error.
|
|
*/
|
|
|
|
bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
|
|
{
|
|
DBUG_ASSERT(sp_params->elements ==
|
|
thd->lex->prepared_stmt_params.elements);
|
|
|
|
List_iterator_fast<Item_param> item_param_it(*sp_params);
|
|
List_iterator_fast<LEX_STRING> user_var_name_it(thd->lex->prepared_stmt_params);
|
|
|
|
while (true)
|
|
{
|
|
Item_param *item_param= item_param_it++;
|
|
LEX_STRING *user_var_name= user_var_name_it++;
|
|
|
|
if (!item_param || !user_var_name)
|
|
break;
|
|
|
|
if (!item_param->get_out_param_info())
|
|
continue; // It's an IN-parameter.
|
|
|
|
Item_func_set_user_var *suv=
|
|
new Item_func_set_user_var(*user_var_name, item_param);
|
|
/*
|
|
Item_func_set_user_var is not fixed after construction, call
|
|
fix_fields().
|
|
*/
|
|
if (suv->fix_fields(thd, NULL))
|
|
return TRUE;
|
|
|
|
if (suv->check(FALSE))
|
|
return TRUE;
|
|
|
|
if (suv->update())
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
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_binary::prepare_for_send(uint num_columns)
|
|
{
|
|
Protocol::prepare_for_send(num_columns);
|
|
bit_fields= (field_count+9)/8;
|
|
return packet->alloc(bit_fields+1);
|
|
|
|
/* prepare_for_resend will be called after this one */
|
|
}
|
|
|
|
|
|
void Protocol_binary::prepare_for_resend()
|
|
{
|
|
packet->length(bit_fields+1);
|
|
bzero((uchar*) packet->ptr(), 1+bit_fields);
|
|
field_pos=0;
|
|
}
|
|
|
|
|
|
bool Protocol_binary::store(const char *from, size_t length,
|
|
CHARSET_INFO *fromcs)
|
|
{
|
|
CHARSET_INFO *tocs= thd->variables.character_set_results;
|
|
field_pos++;
|
|
return store_string_aux(from, length, fromcs, tocs);
|
|
}
|
|
|
|
bool Protocol_binary::store(const char *from, size_t length,
|
|
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
|
|
{
|
|
field_pos++;
|
|
return store_string_aux(from, length, fromcs, tocs);
|
|
}
|
|
|
|
bool Protocol_binary::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_binary::store_tiny(longlong from)
|
|
{
|
|
char buff[1];
|
|
field_pos++;
|
|
buff[0]= (uchar) from;
|
|
return packet->append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
|
|
}
|
|
|
|
|
|
bool Protocol_binary::store_short(longlong from)
|
|
{
|
|
field_pos++;
|
|
char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC);
|
|
if (!to)
|
|
return 1;
|
|
int2store(to, (int) from);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Protocol_binary::store_long(longlong from)
|
|
{
|
|
field_pos++;
|
|
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
|
|
if (!to)
|
|
return 1;
|
|
int4store(to, from);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Protocol_binary::store_longlong(longlong from, bool unsigned_flag)
|
|
{
|
|
field_pos++;
|
|
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
|
|
if (!to)
|
|
return 1;
|
|
int8store(to, from);
|
|
return 0;
|
|
}
|
|
|
|
bool Protocol_binary::store_decimal(const my_decimal *d)
|
|
{
|
|
#ifndef DBUG_OFF
|
|
DBUG_ASSERT(field_types == 0 ||
|
|
field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL);
|
|
field_pos++;
|
|
#endif
|
|
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);
|
|
return store(str.ptr(), str.length(), str.charset());
|
|
}
|
|
|
|
bool Protocol_binary::store(float from, uint32 decimals, String *buffer)
|
|
{
|
|
field_pos++;
|
|
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
|
|
if (!to)
|
|
return 1;
|
|
float4store(to, from);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Protocol_binary::store(double from, uint32 decimals, String *buffer)
|
|
{
|
|
field_pos++;
|
|
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
|
|
if (!to)
|
|
return 1;
|
|
float8store(to, from);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Protocol_binary::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_binary::store(MYSQL_TIME *tm, int decimals)
|
|
{
|
|
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;
|
|
DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS ||
|
|
(decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS));
|
|
if (decimals != AUTO_SEC_PART_DIGITS)
|
|
tm->second_part= sec_part_truncate(tm->second_part, decimals);
|
|
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_BUFFER_EXTRA_ALLOC);
|
|
}
|
|
|
|
bool Protocol_binary::store_date(MYSQL_TIME *tm)
|
|
{
|
|
tm->hour= tm->minute= tm->second=0;
|
|
tm->second_part= 0;
|
|
return Protocol_binary::store(tm, 0);
|
|
}
|
|
|
|
|
|
bool Protocol_binary::store_time(MYSQL_TIME *tm, int decimals)
|
|
{
|
|
char buff[13], *pos;
|
|
uint length;
|
|
field_pos++;
|
|
pos= buff+1;
|
|
pos[0]= tm->neg ? 1 : 0;
|
|
if (tm->hour >= 24)
|
|
{
|
|
uint days= tm->hour/24;
|
|
tm->hour-= days*24;
|
|
tm->day+= days;
|
|
}
|
|
int4store(pos+1, tm->day);
|
|
pos[5]= (uchar) tm->hour;
|
|
pos[6]= (uchar) tm->minute;
|
|
pos[7]= (uchar) tm->second;
|
|
DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS ||
|
|
(decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS));
|
|
if (decimals != AUTO_SEC_PART_DIGITS)
|
|
tm->second_part= sec_part_truncate(tm->second_part, decimals);
|
|
int4store(pos+8, tm->second_part);
|
|
if (tm->second_part)
|
|
length=12;
|
|
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_BUFFER_EXTRA_ALLOC);
|
|
}
|
|
|
|
/**
|
|
Send a result set with OUT-parameter values by means of PS-protocol.
|
|
|
|
@param sp_params List of PS/SP parameters (both input and output).
|
|
|
|
@return Error status.
|
|
@retval FALSE Success.
|
|
@retval TRUE Error.
|
|
*/
|
|
|
|
bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
|
|
{
|
|
if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS))
|
|
{
|
|
/* The client does not support OUT-parameters. */
|
|
return FALSE;
|
|
}
|
|
|
|
List<Item> out_param_lst;
|
|
|
|
{
|
|
List_iterator_fast<Item_param> item_param_it(*sp_params);
|
|
|
|
while (true)
|
|
{
|
|
Item_param *item_param= item_param_it++;
|
|
|
|
if (!item_param)
|
|
break;
|
|
|
|
if (!item_param->get_out_param_info())
|
|
continue; // It's an IN-parameter.
|
|
|
|
if (out_param_lst.push_back(item_param))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!out_param_lst.elements)
|
|
return FALSE;
|
|
|
|
/*
|
|
We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it
|
|
is used in send_result_set_metadata().
|
|
*/
|
|
|
|
thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
|
|
|
|
/* Send meta-data. */
|
|
if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
|
|
return TRUE;
|
|
|
|
/* Send data. */
|
|
|
|
prepare_for_resend();
|
|
|
|
if (send_result_set_row(&out_param_lst))
|
|
return TRUE;
|
|
|
|
if (write())
|
|
return TRUE;
|
|
|
|
/* Restore THD::server_status. */
|
|
thd->server_status&= ~SERVER_PS_OUT_PARAMS;
|
|
|
|
/*
|
|
Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet
|
|
for sure.
|
|
*/
|
|
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
|
|
|
|
/* Send EOF-packet. */
|
|
net_send_eof(thd, thd->server_status, 0);
|
|
|
|
return FALSE;
|
|
}
|