From a5435ea78ab3d62223fd94ebd7c730f8ded30f1b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Oct 2004 11:32:52 +0400 Subject: [PATCH 01/52] Intermediate commit - just to make new files visible to bk in the new tree server-tools/instance-manager/Makefile.am: Fixed IM linking to avoid using both mysys and libmysql as the define the same symbols and therefore conflict server-tools/instance-manager/listener.cc: Added ability to listen network ports server-tools/instance-manager/listener.h: Various additions to the Listener_thread_args server-tools/instance-manager/log.cc: merge server-tools/instance-manager/log.h: merge server-tools/instance-manager/manager.cc: Fixes and additions to enable guardian functionality server-tools/instance-manager/manager.h: Changed manager() signature server-tools/instance-manager/mysqlmanager.cc: Various fixes server-tools/instance-manager/options.cc: Added handling of default values for new options in the Options struct. (such as default_user, default_password, monitoring_interval e.t.c) server-tools/instance-manager/options.h: Added new options to the Options struct sql/net_serv.cc: Added MYSQL_INSTANCE_MANAGER defines to enable alarm handling in the IM server-tools/instance-manager/buffer.cc: Simple implementation of variable-length buffer server-tools/instance-manager/command.cc: Abstract command. All commands are derived from Command class server-tools/instance-manager/commands.h: Interfaces for all commands we have server-tools/instance-manager/factory.cc: Commands factory. This class hides command instantiation. The idea is to handle various protocols this way. (different commands for different protocols server-tools/instance-manager/guardian.cc: Guardian thread implementation (monitor and restart instances in case of a failure server-tools/instance-manager/guardian.h: Guardian_thread and Guardian_thread_args class interface. The Guardian_thread is responsible for monitoring and restarting instances server-tools/instance-manager/instance.cc: Instance class contains methods and data to manage a single instance server-tools/instance-manager/instance.h: This file contains class an instance class interface. The class is responsible for starting/stopping an instance server-tools/instance-manager/instance_map.cc: The instance repository. This class is also responsible for initialization of Instance class objects. server-tools/instance-manager/instance_options.cc: The Instance_options class contains all methods to get and handle options of an instance server-tools/instance-manager/mysql_connection.cc: The class responsible for handling MySQL client/server protocol connections server-tools/instance-manager/mysql_manager_error.h: The list of Instance Manger-specific errors server-tools/instance-manager/parse.cc: Simple query parser server-tools/instance-manager/parse.h: Parser interface server-tools/instance-manager/protocol.cc: Here implemented functions used to handle mysql client/server protocol server-tools/instance-manager/protocol.h: Interface for MySQL client/server protocol server-tools/instance-manager/thread_registry.cc: Thread registry stores information about every thread. It's main function is to provide graceful shutdown for all threads. server-tools/instance-manager/user_map.h: User map contains hash with user names and passwords --- server-tools/instance-manager/Makefile.am | 94 +++- server-tools/instance-manager/buffer.cc | 91 ++++ server-tools/instance-manager/buffer.h | 57 +++ server-tools/instance-manager/command.cc | 43 ++ server-tools/instance-manager/command.h | 49 ++ server-tools/instance-manager/commands.cc | 183 +++++++ server-tools/instance-manager/commands.h | 129 +++++ server-tools/instance-manager/factory.cc | 60 +++ server-tools/instance-manager/factory.h | 45 ++ server-tools/instance-manager/guardian.cc | 180 +++++++ server-tools/instance-manager/guardian.h | 80 ++++ server-tools/instance-manager/instance.cc | 152 ++++++ server-tools/instance-manager/instance.h | 60 +++ server-tools/instance-manager/instance_map.cc | 450 ++++++++++++++++++ server-tools/instance-manager/instance_map.h | 76 +++ .../instance-manager/instance_options.cc | 212 +++++++++ .../instance-manager/instance_options.h | 76 +++ server-tools/instance-manager/listener.cc | 354 +++++++++++--- server-tools/instance-manager/listener.h | 29 +- server-tools/instance-manager/log.cc | 6 +- server-tools/instance-manager/log.h | 9 +- server-tools/instance-manager/manager.cc | 143 +++++- server-tools/instance-manager/manager.h | 6 +- server-tools/instance-manager/messages.cc | 73 +++ server-tools/instance-manager/messages.h | 26 + .../instance-manager/mysql_connection.cc | 388 +++++++++++++++ .../instance-manager/mysql_connection.h | 60 +++ .../instance-manager/mysql_manager_error.h | 27 ++ server-tools/instance-manager/mysqlmanager.cc | 67 +-- server-tools/instance-manager/options.cc | 88 +++- server-tools/instance-manager/options.h | 12 +- server-tools/instance-manager/parse.cc | 200 ++++++++ server-tools/instance-manager/parse.h | 23 + server-tools/instance-manager/priv.cc | 34 ++ server-tools/instance-manager/priv.h | 60 +++ server-tools/instance-manager/protocol.cc | 171 +++++++ server-tools/instance-manager/protocol.h | 43 ++ .../instance-manager/thread_registry.cc | 206 ++++++++ .../instance-manager/thread_registry.h | 116 +++++ server-tools/instance-manager/user_map.cc | 172 +++++++ server-tools/instance-manager/user_map.h | 45 ++ sql/net_serv.cc | 14 +- 42 files changed, 4261 insertions(+), 148 deletions(-) create mode 100644 server-tools/instance-manager/buffer.cc create mode 100644 server-tools/instance-manager/buffer.h create mode 100644 server-tools/instance-manager/command.cc create mode 100644 server-tools/instance-manager/command.h create mode 100644 server-tools/instance-manager/commands.cc create mode 100644 server-tools/instance-manager/commands.h create mode 100644 server-tools/instance-manager/factory.cc create mode 100644 server-tools/instance-manager/factory.h create mode 100644 server-tools/instance-manager/guardian.cc create mode 100644 server-tools/instance-manager/guardian.h create mode 100644 server-tools/instance-manager/instance.cc create mode 100644 server-tools/instance-manager/instance.h create mode 100644 server-tools/instance-manager/instance_map.cc create mode 100644 server-tools/instance-manager/instance_map.h create mode 100644 server-tools/instance-manager/instance_options.cc create mode 100644 server-tools/instance-manager/instance_options.h create mode 100644 server-tools/instance-manager/messages.cc create mode 100644 server-tools/instance-manager/messages.h create mode 100644 server-tools/instance-manager/mysql_connection.cc create mode 100644 server-tools/instance-manager/mysql_connection.h create mode 100644 server-tools/instance-manager/mysql_manager_error.h create mode 100644 server-tools/instance-manager/parse.cc create mode 100644 server-tools/instance-manager/parse.h create mode 100644 server-tools/instance-manager/priv.cc create mode 100644 server-tools/instance-manager/priv.h create mode 100644 server-tools/instance-manager/protocol.cc create mode 100644 server-tools/instance-manager/protocol.h create mode 100644 server-tools/instance-manager/thread_registry.cc create mode 100644 server-tools/instance-manager/thread_registry.h create mode 100644 server-tools/instance-manager/user_map.cc create mode 100644 server-tools/instance-manager/user_map.h diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 731c8503831..522ca6166cc 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -1,30 +1,98 @@ +# Copyright (C) 2004 MySQL AB +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + INCLUDES= -I$(top_srcdir)/include +DEFS= -DMYSQL_INSTANCE_MANAGER + # As all autoconf variables depend from ${prefix} and being resolved only when -# make is run, we can't put these defines to a header file (e.g. to +# make is run, we can not put these defines to a header file (e.g. to # default_options.h, generated from default_options.h.in) # See automake/autoconf docs for details -noinst_LIBRARIES= liboptions.a +noinst_LIBRARIES= liboptions.a libnet.a + liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_PID_FILE_NAME="$(localstatedir)/mysqlmanager.pid" \ -DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \ - -DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" + -DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \ + -DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \ + -DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \ + -DDEFAULT_USER="root" \ + -DDEFAULT_PASSWORD="" \ + -DDEFAULT_MONITORING_INTERVAL="5" \ + -DDEFAULT_PORT="3406" \ + -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ -liboptions_a_SOURCES= options.h options.cc +liboptions_a_SOURCES= options.h options.cc priv.h priv.cc -bin_PROGRAMS = mysqlmanager +# MySQL sometimes uses symlinks to reuse code +# All symlinked files are grouped in libnet.a -mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ - listener.h listener.cc \ - thread_repository.h thread_repository.cc +nodist_libnet_a_SOURCES= password.c pack.c sql_state.c net_serv.cc +nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -mysqlmanager_LDADD= liboptions.a \ - $(top_builddir)/mysys/libmysys.a \ - $(top_builddir)/strings/libmystrings.a \ - $(top_builddir)/dbug/libdbug.a +CLEANFILES= net_serv.cc password.c pack.c sql_state.c -tags: +net_serv.cc: Makefile + rm -f $(srcdir)/net_serv.cc + @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc $(srcdir)/net_serv.cc + +password.c: Makefile + rm -f $(srcdir)/password.c + @LN_CP_F@ $(top_srcdir)/sql/password.c $(srcdir)/password.c + +pack.c: Makefile + rm -f $(srcdir)/pack.c + @LN_CP_F@ $(top_srcdir)/sql-common/pack.c $(srcdir)/pack.c + +sql_state.c: Makefile + rm -f $(srcdir)/sql_state.c + @LN_CP_F@ $(top_srcdir)/sql/sql_state.c $(srcdir)/sql_state.c + +bin_PROGRAMS= mysqlmanager + +mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ + thread_registry.h thread_registry.cc \ + listener.h listener.cc \ + mysql_connection.h mysql_connection.cc \ + protocol.h protocol.cc \ + user_map.h user_map.cc \ + messages.h messages.cc \ + $(top_srcdir)/sql/sql_string.cc \ + command.h command.cc \ + commands.h commands.cc \ + factory.h factory.cc \ + instance.h instance.cc \ + instance_map.h instance_map.cc\ + instance_options.h instance_options.cc \ + buffer.h buffer.cc parse.cc parse.h \ + guardian.cc guardian.h common_structures.h \ + mysql_manager_error.h + +mysqlmanager_LDADD= liboptions.a \ + libnet.a \ + $(top_builddir)/vio/libvio.a \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/strings/libmystrings.a \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/libmysql/libmysqlclient.la + + +tags: ctags -R *.h *.cc # Don't update the files from bitkeeper diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc new file mode 100644 index 00000000000..39e255300cf --- /dev/null +++ b/server-tools/instance-manager/buffer.cc @@ -0,0 +1,91 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "buffer.h" +#include + + +/* + Puts the given string to the buffer. + + SYNOPSYS + put_to_buffer() + start_pos start position in the buffer + string string to be put in the buffer + len_arg the length of the string. This way we can avoid some + strlens. + + DESCRIPTION + + The method puts a string into the buffer, starting from start_pos . + In the case when the buffer is too small it reallocs the buffer. The + total size of the buffer is restricted with 16. + + RETURN + 0 - ok + 1 - The buffer came to 16Mb barrier +*/ + +int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) +{ + if (check_and_add(start_pos - buffer, len_arg)) + return 1; + + strnmov(start_pos, string, len_arg); + return 0; +} + + +/* + Checks whether the current buffer size is ok to put a string of the length + "len_arg" starting from "position" and reallocs it if no. + + SYNOPSYS + check_and_add() + position the number starting byte on the buffer to store a buffer + len_arg the length of the string. + + DESCRIPTION + + The method checks whether it is possible to pus a string of teh "len_arg" + length into the buffer, starting from "position" byte. In the case when the + buffer is too small it reallocs the buffer. The total size of the buffer is + restricted with 16 Mb. + + RETURN + 0 - ok + 1 - The buffer came to 16Mb barrier +*/ + +int Buffer::check_and_add(uint position, uint len_arg) +{ + if (position + len_arg >= MAX_BUFFER_SIZE) + return 1; + + if (position + len_arg>= buffer_size) + { + buffer= (char *) realloc(buffer, + min(MAX_BUFFER_SIZE, + max((uint) buffer_size*1.5, + position + len_arg))); + buffer_size= (uint) buffer_size*1.5; + } + return 0; +} diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h new file mode 100644 index 00000000000..8781abebd71 --- /dev/null +++ b/server-tools/instance-manager/buffer.h @@ -0,0 +1,57 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include + +#ifdef __GNUC__ +#pragma interface +#endif + +/* + This class is a simple implementation of the buffer of varying size. + It is used to store MySQL client-server protocol packets. This is why + the maximum buffer size if 16Mb. (See internals manual section + 7. MySQL Client/Server Protocol) +*/ + +class Buffer +{ +private: + enum { BUFFER_INITIAL_SIZE= 4096 }; + /* maximum buffer size is 16Mb */ + enum { MAX_BUFFER_SIZE= 16777216 }; + uint buffer_size; +public: + Buffer() + { + buffer=(char *) malloc(BUFFER_INITIAL_SIZE); + buffer_size= BUFFER_INITIAL_SIZE; + } + + ~Buffer() + { + free(buffer); + } + +public: + char *buffer; + int put_to_buffer(char *start_pos, const char *string, uint len_arg); + int check_and_add(uint position, uint len_arg); +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H */ diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc new file mode 100644 index 00000000000..71415a038f0 --- /dev/null +++ b/server-tools/instance-manager/command.cc @@ -0,0 +1,43 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "command.h" + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "protocol.h" +#include "instance_map.h" + +Command::Command(Command_factory *factory_arg) + :factory(factory_arg) +{} + +Command::~Command() +{} + +#ifdef __GNUC__ +FIX_GCC_LINKING_PROBLEM +#endif diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h new file mode 100644 index 00000000000..25b418ca8fe --- /dev/null +++ b/server-tools/instance-manager/command.h @@ -0,0 +1,49 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include + +/* Class responsible for allocation and deallocation of im classes. */ + +class Command_factory; + +/* + Command - entry point for any command. + GangOf4: 'Command' design pattern +*/ + +class Command +{ +public: + Command(Command_factory *factory_arg= 0); + virtual ~Command(); + + /* method of executing: */ + virtual int execute(struct st_net *net, ulong connection_id) = 0; + +protected: + Command_factory *factory; +}; + +#define CONST_STR(a) String(a,sizeof(a),&my_charset_latin1) + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc new file mode 100644 index 00000000000..e990f04216d --- /dev/null +++ b/server-tools/instance-manager/commands.cc @@ -0,0 +1,183 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "command.h" +#include "factory.h" +#include "commands.h" +#include "instance.h" +#include "instance_map.h" +#include "messages.h" + + +/* implementation for Show_instances: */ + +int Show_instances::execute(struct st_net *net, ulong connection_id) +{ + if (factory->instance_map.show_instances(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/* implementation for Flush_instances: */ + +int Flush_instances::execute(struct st_net *net, ulong connection_id) +{ + if (factory->instance_map.flush_instances()) + return ER_OUT_OF_RESOURCES; + + net_send_ok(net, connection_id); + return 0; +} + + +/* implementation for Show_instance_status: */ + +Show_instance_status::Show_instance_status(Command_factory *factory, + const char *name, uint len) + :Command(factory) +{ + Instance *instance; + + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->instance_map).find(name, len)) + { + instance_name= instance->options.instance_name; + } + else instance_name= NULL; +} + + +int Show_instance_status::execute(struct st_net *net, ulong connection_id) +{ + if (instance_name != NULL) + { + if (factory->instance_map.show_instance_status(net, instance_name)) + return ER_OUT_OF_RESOURCES; + return 0; + } + else + { + return ER_BAD_INSTANCE_NAME; + } +} + + +/* Implementation for Show_instance_options */ + +Show_instance_options::Show_instance_options(Command_factory *factory, + const char *name, uint len): + Command(factory) +{ + Instance *instance; + + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->instance_map).find(name, len)) + { + instance_name= instance->options.instance_name; + } + else instance_name= NULL; +} + + +int Show_instance_options::execute(struct st_net *net, ulong connection_id) +{ + if (instance_name != NULL) + { + if (factory->instance_map.show_instance_options(net, instance_name)) + return ER_OUT_OF_RESOURCES; + return 0; + } + else + { + return ER_BAD_INSTANCE_NAME; + } +} + + +/* Implementation for Start_instance */ + +Start_instance::Start_instance(Command_factory *factory, + const char *name, uint len) + :Command(factory) +{ + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->instance_map).find(name, len)) + instance_name= instance->options.instance_name; +} + + +int Start_instance::execute(struct st_net *net, ulong connection_id) +{ + uint err_code; + if (instance == 0) + { + return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ + } + else + { + if (err_code= instance->start()) + return err_code; + + if (instance->options.is_guarded != NULL) + factory->instance_map.guardian->guard(instance->options.instance_name, + instance->options.instance_name_len); + + net_send_ok(net, connection_id); + return 0; + } +} + + +/* Implementation for Stop_instance: */ + +Stop_instance::Stop_instance(Command_factory *factory, + const char *name, uint len) + :Command(factory) +{ + /* we make a search here, since we don't want t store the name */ + if (instance= (factory->instance_map).find(name, len)) + instance_name= instance->options.instance_name; +} + + +int Stop_instance::execute(struct st_net *net, ulong connection_id) +{ + uint err_code; + + if (instance == 0) + { + return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ + } + else + { + if (instance->options.is_guarded != NULL) + factory->instance_map.guardian-> + stop_guard(instance_name, instance->options.instance_name_len); + if (err_code= instance->stop()) + return err_code; + + net_send_ok(net, connection_id); + return 0; + } +} + + +int Syntax_error::execute(struct st_net *net, ulong connection_id) +{ + return ER_SYNTAX_ERROR; +} diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h new file mode 100644 index 00000000000..bab61f04b17 --- /dev/null +++ b/server-tools/instance-manager/commands.h @@ -0,0 +1,129 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "instance.h" +#include "my_global.h" + +/* + Print all instances of this instance manager. + Grammar: SHOW ISTANCES +*/ + +class Show_instances : public Command +{ +public: + Show_instances(Command_factory *factory): Command(factory) + {} + + int execute(struct st_net *net, ulong connection_id); +}; + + +/* + Reread configuration file and refresh instance map. + Grammar: FLUSH INSTANCES +*/ + +class Flush_instances : public Command +{ +public: + Flush_instances(Command_factory *factory): Command(factory) + {} + + int execute(struct st_net *net, ulong connection_id); +}; + + +/* + Print status of an instance. + Grammar: SHOW ISTANCE STATUS +*/ + +class Show_instance_status : public Command +{ +public: + + Show_instance_status(Command_factory *factory, const char *name, uint len); + + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Print options if chosen instance. + Grammar: SHOW INSTANCE OPTIONS +*/ + +class Show_instance_options : public Command +{ +public: + + Show_instance_options(Command_factory *factory, const char *name, uint len); + + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Start an instance. + Grammar: START INSTANCE +*/ + +class Start_instance : public Command +{ +public: + Start_instance(Command_factory *factory, const char *name, uint len); + + Instance *instance; + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Stop an instance. + Grammar: STOP INSTANCE +*/ + +class Stop_instance : public Command +{ +public: + Stop_instance(Command_factory *factory, const char *name, uint len); + + Instance *instance; + int execute(struct st_net *net, ulong connection_id); + const char *instance_name; +}; + + +/* + Syntax error command. +*/ + +class Syntax_error : public Command +{ +public: + Syntax_error() + {} + + int execute(struct st_net *net, ulong connection_id); +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMANDS_H */ diff --git a/server-tools/instance-manager/factory.cc b/server-tools/instance-manager/factory.cc new file mode 100644 index 00000000000..691aca0c7ea --- /dev/null +++ b/server-tools/instance-manager/factory.cc @@ -0,0 +1,60 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "factory.h" +#include "my_global.h" + +#include +#include + + +Show_instances *Command_factory::new_Show_instances() +{ + return new Show_instances(this); +} + +Flush_instances *Command_factory::new_Flush_instances() +{ + return new Flush_instances(this); +} + +Show_instance_status *Command_factory:: + new_Show_instance_status(const char *name, uint len) +{ + return new Show_instance_status(this, name, len); +} + +Show_instance_options *Command_factory:: + new_Show_instance_options(const char *name, uint len) +{ + return new Show_instance_options(this, name, len); +} + +Start_instance *Command_factory:: + new_Start_instance(const char *name, uint len) +{ + return new Start_instance(this, name, len); +} + +Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len) +{ + return new Stop_instance(this, name, len); +} + +Syntax_error *Command_factory::new_Syntax_error() +{ + return new Syntax_error(); +} diff --git a/server-tools/instance-manager/factory.h b/server-tools/instance-manager/factory.h new file mode 100644 index 00000000000..0a1b955d156 --- /dev/null +++ b/server-tools/instance-manager/factory.h @@ -0,0 +1,45 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_FACTORY_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_FACTORY_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "command.h" +#include "commands.h" +#include "instance_map.h" + +/* + This class could be used to handle various protocols. We could pass to + the parser various derived classes. I.e Mylsq_command_factory, + Http_command_factory e.t.c. Also see comment in the instance_map.cc +*/ + +class Command_factory +{ +public: + Command_factory(Instance_map &instance_map): instance_map(instance_map) + {} + + Show_instances *new_Show_instances (); + Show_instance_status *new_Show_instance_status (const char *name, uint len); + Show_instance_options *new_Show_instance_options (const char *name, uint len); + Start_instance *new_Start_instance (const char *name, uint len); + Stop_instance *new_Stop_instance (const char *name, uint len); + Flush_instances *new_Flush_instances (); + Syntax_error *new_Syntax_error (); + + Instance_map &instance_map; +}; +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_FACTORY_H */ diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc new file mode 100644 index 00000000000..ebc59d67906 --- /dev/null +++ b/server-tools/instance-manager/guardian.cc @@ -0,0 +1,180 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "guardian.h" +#include "instance_map.h" +#include + +C_MODE_START + +pthread_handler_decl(guardian, arg) +{ + Guardian_thread *guardian_thread= (Guardian_thread *) arg; + guardian_thread->run(); + return 0; +} + +C_MODE_END + + +Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg) : + Guardian_thread_args(thread_registry_arg, instance_map_arg, + monitoring_interval_arg), + thread_info(pthread_self()) +{ + pthread_mutex_init(&LOCK_guardian, 0); + thread_registry.register_thread(&thread_info); + init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); + guarded_instances= NULL; +} + + +Guardian_thread::~Guardian_thread() +{ + /* delay guardian destruction to the moment when no one needs it */ + pthread_mutex_lock(&LOCK_guardian); + free_root(&alloc, MYF(0)); + thread_registry.unregister_thread(&thread_info); + pthread_mutex_unlock(&LOCK_guardian); + pthread_mutex_destroy(&LOCK_guardian); +} + + +/* + Run guardian thread + + SYNOPSYS + run() + + DESCRIPTION + + Check for all guarded instances and restart them if needed. If everything + is fine go and sleep for some time. + + RETURN + The function return no value +*/ + +void Guardian_thread::run() +{ + Instance *instance; + LIST *loop; + int i=0; + + my_thread_init(); + + while (!thread_registry.is_shutdown()) + { + pthread_mutex_lock(&LOCK_guardian); + loop= guarded_instances; + while (loop != NULL) + { + instance= (Instance *) loop->data; + if (instance != NULL) + { + if (!instance->is_running()) + instance->start(); + } + loop= loop->next; + } + pthread_mutex_unlock(&LOCK_guardian); + sleep(monitoring_interval); + } + + my_thread_end(); +} + + +/* + Start instance guarding + + SYNOPSYS + guard() + instance_name the name of the instance to be guarded + name_len the length of the name + + DESCRIPTION + + The instance is added to the list of guarded instances. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Guardian_thread::guard(const char *instance_name, uint name_len) +{ + LIST *lst; + Instance *instance; + + lst= (LIST *) alloc_root(&alloc, sizeof(LIST)); + if (lst == NULL) return 1; + instance= instance_map->find(instance_name, name_len); + /* we store the pointers to instances from the instance_map's MEM_ROOT */ + lst->data= (void *) instance; + + pthread_mutex_lock(&LOCK_guardian); + guarded_instances= list_add(guarded_instances, lst); + pthread_mutex_unlock(&LOCK_guardian); + + return 0; +} + + +/* + TODO: perhaps it would make sense to create a pool of the LIST elements + elements and give them upon request. Now we are loosing a bit of memory when + guarded instance was stopped and then restarted (since we cannot free just + a piece of the MEM_ROOT). +*/ + +int Guardian_thread::stop_guard(const char *instance_name, uint name_len) +{ + LIST *lst; + Instance *instance; + + instance= instance_map->find(instance_name, name_len); + + lst= guarded_instances; + if (lst == NULL) return 1; + + pthread_mutex_lock(&LOCK_guardian); + while (lst != NULL) + { + /* + We compare only pointers, as we always use pointers from the + instance_map's MEM_ROOT. + */ + if ((Instance *) lst->data == instance) + { + guarded_instances= list_delete(guarded_instances, lst); + pthread_mutex_unlock(&LOCK_guardian); + return 0; + } + else lst= lst->next; + } + pthread_mutex_unlock(&LOCK_guardian); + /* if there is nothing to delete it is also fine */ + return 0; +} + diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h new file mode 100644 index 00000000000..5d0cdcd7c92 --- /dev/null +++ b/server-tools/instance-manager/guardian.h @@ -0,0 +1,80 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include + +#ifdef __GNUC__ +#pragma interface +#endif + +class Instance_map; + +#include "thread_registry.h" +#include "instance.h" + +C_MODE_START + +pthread_handler_decl(guardian, arg); + +C_MODE_END + + +struct Guardian_thread_args +{ + Thread_registry &thread_registry; + Instance_map *instance_map; + uint monitoring_interval; + + Guardian_thread_args(Thread_registry &thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg) : + thread_registry(thread_registry_arg), + instance_map(instance_map_arg), + monitoring_interval(monitoring_interval_arg) + {} +}; + + +/* + The guardian thread is responsible for monitoring and restarting of guarded + instances. +*/ + +class Guardian_thread: public Guardian_thread_args +{ +public: + Guardian_thread(Thread_registry &thread_registry_arg, + Instance_map *instance_map_arg, + uint monitoring_interval_arg); + ~Guardian_thread(); + void run(); + int init(); + int guard(const char *instance_name, uint name_len); + int stop_guard(const char *instance_name, uint name_len); + +private: + pthread_mutex_t LOCK_guardian; + Thread_info thread_info; + LIST *guarded_instances; + MEM_ROOT alloc; + enum { MEM_ROOT_BLOCK_SIZE= 512 }; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc new file mode 100644 index 00000000000..32e77429572 --- /dev/null +++ b/server-tools/instance-manager/instance.cc @@ -0,0 +1,152 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "instance.h" +#include "mysql_manager_error.h" +#include +#include +#include + + +/* + The method starts an instance. + + SYNOPSYS + start() + + RETURN + 0 ok + ER_CANNOT_START_INSTANCE Cannot start instance + ER_INSTANCE_ALREADY_STARTED The instance on the specified port/socket + is already started +*/ + +int Instance::start() +{ + + if (!is_running()) + { + switch (fork()) { + case 0: + if (fork()) /* zombie protection */ + exit(0); /* parent goes bye-bye */ + else + { + execv(options.mysqld_path, options.argv); + exit(1); + } + case -1: + return ER_CANNOT_START_INSTANCE; + default: + return 0; + } + } + + /* the instance is started already */ + return ER_INSTANCE_ALREADY_STARTED; +} + +int Instance::cleanup() +{ + /* + We cannot close connection in destructor, as mysql_close needs alarm + services which are definitely unavailaible at the time of destructor + call. + */ + if (is_connected) + mysql_close(&mysql); + return 0; +} + +Instance::~Instance() +{ + pthread_mutex_destroy(&LOCK_instance); +} + +bool Instance::is_running() +{ + pthread_mutex_lock(&LOCK_instance); + if (!is_connected) + { + mysql_init(&mysql); + if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user, + options.mysqld_password, + NullS, atoi(strchr(options.mysqld_port, '=') + 1), + strchr(options.mysqld_socket, '=') + 1, 0)) + { + is_connected= TRUE; + pthread_mutex_unlock(&LOCK_instance); + return TRUE; + } + mysql_close(&mysql); + pthread_mutex_unlock(&LOCK_instance); + return FALSE; + } + else if (!mysql_ping(&mysql)) + { + pthread_mutex_unlock(&LOCK_instance); + return TRUE; + } + pthread_mutex_unlock(&LOCK_instance); + return FALSE; +} + + +/* + Stop an instance. + + SYNOPSYS + stop() + + RETURN: + 0 ok + ER_INSTANCE_IS_NOT_STARTED Looks like the instance it is not started + ER_STOP_INSTANCE mysql_shutdown reported an error +*/ + +int Instance::stop() +{ + if (is_running()) + { + if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT)) + goto err; + + mysql_close(&mysql); + is_connected= FALSE; + return 0; + } + + return ER_INSTANCE_IS_NOT_STARTED; +err: + return ER_STOP_INSTANCE; +} + + +/* + We execute this function to initialize instance parameters. + Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY. +*/ + +int Instance::init(const char *name_arg) +{ + pthread_mutex_init(&LOCK_instance, 0); + + return options.init(name_arg); +} diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h new file mode 100644 index 00000000000..6733985116a --- /dev/null +++ b/server-tools/instance-manager/instance.h @@ -0,0 +1,60 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include +#include "instance_options.h" + +#ifdef __GNUC__ +#pragma interface +#endif + +class Instance +{ +public: + Instance(): is_connected(FALSE) + {} + ~Instance(); + + int init(const char *name); + + /* check if the instance is running and set up mysql connection if yes */ + bool is_running(); + int start(); + int stop(); + int cleanup(); + +public: + Instance_options options; + + /* connection to the instance */ + MYSQL mysql; + +private: + /* + Mutex protecting the instance. Currently we use it to avoid the + double start of the instance. This happens when the instance is starting + and we issue the start command once more. + */ + pthread_mutex_t LOCK_instance; + /* Here we store the state of the following connection */ + bool is_connected; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc new file mode 100644 index 00000000000..89731eb27b9 --- /dev/null +++ b/server-tools/instance-manager/instance_map.cc @@ -0,0 +1,450 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "instance_map.h" +#include "buffer.h" +#include "instance.h" +#include +#include +#include +#include + +/* + TODO: Currently there are some mysql-connection specific functions. + As we are going to suppost different types of connections, we shouldn't + have them here in future. To avoid it we could put such + connection-specific functions to the Command-derived class instead. + The command could be easily constructed for a specific connection if + we would provide a special factory for each connection. +*/ + +C_MODE_START + +/* Procedure needed for HASH initialization */ + +static byte* get_instance_key(const byte* u, uint* len, + my_bool __attribute__((unused)) t) +{ + const Instance *instance= (const Instance *) u; + *len= instance->options.instance_name_len; + return (byte *) instance->options.instance_name; +} + +static void delete_instance(void *u) +{ + Instance *instance= (Instance *) u; + delete instance; +} + +/* + The option handler to pass to the process_default_option_files finction. + + SYNOPSYS + process_option() + ctx Handler context. Here it is an instance_map structure. + group_name The name of the group the option belongs to. + option The very option to be processed. It is already + prepared to be used in argv (has -- prefix) + + DESCRIPTION + + This handler checks whether a group is an instance group and adds + an option to the appropriate instance class. If this is the first + occurence of an instance name, we'll also create the instance + with such name and add it to the instance map. + + RETURN + 0 - ok + 1 - error occured +*/ + +static int process_option(void * ctx, const char *group, const char *option) +{ + Instance_map *map= NULL; + Instance *instance= NULL; + static const char prefix[]= { 'm', 'y', 's', 'q', 'l', 'd' }; + + map = (Instance_map*) ctx; + if (strncmp(group, prefix, sizeof prefix) == 0 && + (my_isdigit(default_charset_info, group[sizeof prefix]))) + { + if ((instance= map->find(group, strlen(group))) == NULL) + { + if ((instance= new Instance) == 0) + goto err_new_instance; + if (instance->init(group)) + goto err; + if (map->add_instance(instance)) + goto err; + } + + if (instance->options.add_option(option)) + goto err; + } + + return 0; + +err: + delete instance; +err_new_instance: + return 1; +} + +C_MODE_END + + +Instance_map::Instance_map() +{ + hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_instance_key, delete_instance, 0); + pthread_mutex_init(&LOCK_instance_map, 0); +} + + +Instance_map::~Instance_map() +{ + pthread_mutex_lock(&LOCK_instance_map); + hash_free(&hash); + pthread_mutex_unlock(&LOCK_instance_map); + pthread_mutex_destroy(&LOCK_instance_map); +} + + +int Instance_map::flush_instances() +{ + int rc; + + pthread_mutex_lock(&LOCK_instance_map); + hash_free(&hash); + hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_instance_key, delete_instance, 0); + rc= load(); + pthread_mutex_unlock(&LOCK_instance_map); + return rc; +} + + +int Instance_map::show_instance_options(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, option; + LIST *field_list; + NAME_WITH_LENGTH name_field, option_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "option_name"; + name_field.length= 20; + name.data= &name_field; + option_field.name= (char *) "value"; + option_field.length= 20; + option.data= &option_field; + field_list= list_add(NULL, &option); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + if ((instance= find(instance_name, strlen(instance_name))) == NULL) + goto err; + store_to_string(&send_buff, (char *) "instance_name", &position); + store_to_string(&send_buff, (char *) instance_name, &position); + my_net_write(net, send_buff.buffer, (uint) position); + if (instance->options.mysqld_path != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "mysqld_path", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_path, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_user != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_user", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_user, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_password != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_password", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_password, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + /* loop through the options stored in DYNAMIC_ARRAY */ + for (int i= 0; i < instance->options.options_array.elements; i++) + { + char *tmp_option, *option_value; + get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); + option_value= strchr(tmp_option, '='); + /* split the option string into two parts */ + *option_value= 0; + position= 0; + store_to_string(&send_buff, tmp_option + 2, &position); + store_to_string(&send_buff, option_value + 1, &position); + /* join name and the value into the same option again */ + *option_value= '='; + my_net_write(net, send_buff.buffer, (uint) position); + } + } + + send_eof(net); + net_flush(net); + + return 0; + +err: + return 1; +} + +/* return the list of running guarded instances */ +int Instance_map::init_guardian() +{ + Instance *instance; + uint i= 0; + + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + if ((instance->options.is_guarded != NULL) && (instance->is_running())) + if (guardian->guard(instance->options.instance_name, + instance->options.instance_name_len)) + return 1; + i++; + } + + return 0; +} + + +/* + The method sends a list of instances in the instance map to the client. + + SYNOPSYS + show_instances() + net The network connection to the client. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_map::show_instances(struct st_net *net) +{ + Buffer send_buff; /* buffer for packets */ + LIST name, status; + NAME_WITH_LENGTH name_field, status_field; + LIST *field_list; + uint position=0; + + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + field_list= list_add(NULL, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + uint i= 0; + + pthread_mutex_lock(&LOCK_instance_map); + while (i < hash.records) + { + position= 0; + instance= (Instance *) hash_element(&hash, i); + store_to_string(&send_buff, instance->options.instance_name, &position); + if (instance->is_running()) + store_to_string(&send_buff, (char *) "online", &position); + else + store_to_string(&send_buff, (char *) "offline", &position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; + i++; + } + pthread_mutex_unlock(&LOCK_instance_map); + } + if (send_eof(net)) + goto err; + if (net_flush(net)) + goto err; + + return 0; +err: + return 1; +} + + +/* + The method sends a table with a status of requested instance to the client. + + SYNOPSYS + show_instance_status() + net The network connection to the client. + instance_name The name of the instance. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_map::show_instance_status(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, status, version; + LIST *field_list; + NAME_WITH_LENGTH name_field, status_field, version_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + version_field.name= (char *) "version"; + version_field.length= MAX_VERSION_LENGTH; + version.data= &version_field; + field_list= list_add(NULL, &version); + field_list= list_add(field_list, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + store_to_string(&send_buff, (char *) instance_name, &position); + if ((instance= find(instance_name, strlen(instance_name))) == NULL) + goto err; + if (instance->is_running()) + { + store_to_string(&send_buff, (char *) "online", &position); + store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position); + } + else + { + store_to_string(&send_buff, (char *) "offline", &position); + store_to_string(&send_buff, (char *) "unknown", &position); + } + + + my_net_write(net, send_buff.buffer, (uint) position); + } + + send_eof(net); + net_flush(net); + +err: + return 0; +} + + +int Instance_map::add_instance(Instance *instance) +{ + return my_hash_insert(&hash, (byte *) instance); +} + + +Instance * +Instance_map::find(const char *name, uint name_len) +{ + Instance *instance; + pthread_mutex_lock(&LOCK_instance_map); + instance= (Instance *) hash_search(&hash, (byte *) name, name_len); + pthread_mutex_unlock(&LOCK_instance_map); + return instance; +} + + +void Instance_map::complete_initialization() +{ + Instance *instance; + uint i= 0; + + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + instance->options.complete_initialization(mysqld_path, user, password); + i++; + } +} + + +int Instance_map::cleanup() +{ + Instance *instance; + uint i= 0; + + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + instance->cleanup(); + i++; + } +} + + +Instance * +Instance_map::find(uint instance_number) +{ + Instance *instance; + char name[80]; + + sprintf(name, "mysqld%i", instance_number); + pthread_mutex_lock(&LOCK_instance_map); + instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name)); + pthread_mutex_unlock(&LOCK_instance_map); + return instance; +} + + +/* load options from config files and create appropriate instance structures */ + +int Instance_map::load() +{ + int error; + + error= process_default_option_files("my", process_option, (void *) this); + + complete_initialization(); + + return error; +} diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h new file mode 100644 index 00000000000..965261f2920 --- /dev/null +++ b/server-tools/instance-manager/instance_map.h @@ -0,0 +1,76 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include + +#ifdef __GNUC__ +#pragma interface +#endif + +#include "protocol.h" +#include "guardian.h" + +class Instance; +extern int load_all_groups(char ***groups, const char *filename); +extern void free_groups(char **groups); + + +/* + Instance_map - stores all existing instances +*/ + +class Instance_map +{ +public: + /* returns a pointer to the instance or NULL, if there is no such instance */ + Instance *find(const char *name, uint name_len); + Instance *find(uint instance_number); + + int show_instances(struct st_net *net); + int show_instance_status(struct st_net *net, const char *instance_name); + int show_instance_options(struct st_net *net, const char *instance_name); + int flush_instances(); + int init_guardian(); + int cleanup(); + + Instance_map(); + ~Instance_map(); + + /* loads options from config files */ + int load(); + /* adds instance to internal hash */ + int add_instance(Instance *instance); + /* inits instances argv's after all options have been loaded */ + void complete_initialization(); + +public: + const char *mysqld_path; + /* user an password to shutdown MySQL */ + const char *user; + const char *password; + Guardian_thread *guardian; + +private: + enum { START_HASH_SIZE = 16 }; + pthread_mutex_t LOCK_instance_map; + HASH hash; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc new file mode 100644 index 00000000000..7586566de9d --- /dev/null +++ b/server-tools/instance-manager/instance_options.cc @@ -0,0 +1,212 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "instance_options.h" +#include +#include +#include +#include + +int Instance_options::complete_initialization(const char *default_path, + const char *default_user, + const char *default_password) +{ + /* we need to reserve space for the final zero + possible default options */ + if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1 + + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) + goto err; + + + if (mysqld_path == NULL) + { + if (!(mysqld_path= strdup_root(&alloc, default_path))) + goto err; + } + + /* this option must be first in the argv */ + if (add_to_argv(mysqld_path)) + goto err; + + /* the following options are not for argv */ + if (mysqld_user == NULL) + { + if (!(mysqld_user= strdup_root(&alloc, default_user))) + goto err; + } + + if (mysqld_password == NULL) + { + if (!(mysqld_password= strdup_root(&alloc, default_password))) + goto err; + } + + memcpy((gptr) (argv + filled_default_options), options_array.buffer, + options_array.elements*sizeof(char*)); + argv[filled_default_options + options_array.elements]= 0; + + return 0; + +err: + return 1; +} + + +/* + Assigns given value to the appropriate option from the class. + + SYNOPSYS + add_option() + option string with the option prefixed by -- + + DESCRIPTION + + The method is called from the option handling routine. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_options::add_option(const char* option) +{ + uint elements_count=0; + static const char socket[]= "--socket="; + static const char port[]= "--port="; + static const char datadir[]= "--datadir="; + static const char language[]= "--bind-address="; + static const char pid_file[]= "--pid-file="; + static const char path[]= "--mysqld_path="; + static const char user[]= "--admin_user="; + static const char password[]= "--admin_password="; + static const char guarded[]= "--guarded"; + char *tmp; + + if (!(tmp= strdup_root(&alloc, option))) + goto err; + + /* To get rid the final zero in a string we subtract 1 from sizeof value */ + if (strncmp(tmp, socket, sizeof socket - 1) == 0) + { + mysqld_socket= tmp; + goto add_options; + } + + if (strncmp(tmp, port, sizeof port - 1) == 0) + { + mysqld_port= tmp; + goto add_options; + } + + if (strncmp(tmp, datadir, sizeof datadir - 1) == 0) + { + mysqld_datadir= tmp; + goto add_options; + } + + if (strncmp(tmp, language, sizeof language - 1) == 0) + { + mysqld_bind_address= tmp; + goto add_options; + } + + if (strncmp(tmp, pid_file, sizeof pid_file - 1) == 0) + { + mysqld_pid_file= tmp; + goto add_options; + } + + /* + We don't need a prefix in the next three optios. + We also don't need to add them to argv array => + return instead of goto. + */ + + if (strncmp(tmp, path, sizeof path - 1) == 0) + { + mysqld_path= strchr(tmp, '=') + 1; + return 0; + } + + if (strncmp(tmp, user, sizeof user - 1) == 0) + { + mysqld_user= strchr(tmp, '=') + 1; + return 0; + } + + if (strncmp(tmp, password, sizeof password - 1) == 0) + { + mysqld_password= strchr(tmp, '=') + 1; + return 0; + } + + if (strncmp(tmp, guarded, sizeof guarded - 1) == 0) + { + is_guarded= tmp; + return 0; + } + +add_options: + insert_dynamic(&options_array,(gptr) &tmp); + return 0; + +err: + return 1; +} + +int Instance_options::add_to_argv(const char* option) +{ + DBUG_ASSERT(filled_default_options < (MAX_NUMBER_OF_DEFAULT_OPTIONS + 1)); + + if (option != NULL) + argv[filled_default_options++]= (char *) option; + return 0; +} + + +/* + We execute this function to initialize some options. + Return value: 0 - ok. 1 - unable to allocate memory. +*/ + +int Instance_options::init(const char *instance_name_arg) +{ + instance_name_len= strlen(instance_name_arg); + + init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); + + my_init_dynamic_array(&options_array, sizeof(char *), 0, 32); + + if (!(instance_name= strmake_root(&alloc, (char *) instance_name_arg, + instance_name_len))) + goto err; + + return 0; + +err: + return 1; +} + + +Instance_options::~Instance_options() +{ + free_root(&alloc, MYF(0)); + delete_dynamic(&options_array); +} + diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h new file mode 100644 index 00000000000..5034e775cd0 --- /dev/null +++ b/server-tools/instance-manager/instance_options.h @@ -0,0 +1,76 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include + +#ifdef __GNUC__ +#pragma interface +#endif + + +/* + This class contains options of an instance and methods to operate them. + + We do not provide this class with the means of synchronization as it is + supposed that options for instances are all loaded at once during the + instance_map initilization and we do not change them later. This way we + don't have to synchronize between threads. +*/ + +class Instance_options +{ +public: + Instance_options() : mysqld_socket(0), mysqld_datadir(0), + mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), + mysqld_user(0), mysqld_password(0), is_guarded(0), filled_default_options(0) + {} + ~Instance_options(); + /* fills in argv */ + int complete_initialization(const char *default_path, + const char *default_user, + const char *default_password); + + int add_option(const char* option); + int init(const char *instance_name_arg); + +public: + enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 3 }; + enum { MEM_ROOT_BLOCK_SIZE= 512 }; + char **argv; + /* We need the some options, so we store them as a separate pointers */ + const char *mysqld_socket; + const char *mysqld_datadir; + const char *mysqld_bind_address; + const char *mysqld_pid_file; + const char *mysqld_port; + uint instance_name_len; + const char *instance_name; + const char *mysqld_path; + const char *mysqld_user; + const char *mysqld_password; + const char *is_guarded; + DYNAMIC_ARRAY options_array; +private: + int add_to_argv(const char *option); +private: + uint filled_default_options; + MEM_ROOT alloc; +}; + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_OPTIONS_H */ diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 4bbfaf0b81a..749a31ea525 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -1,72 +1,306 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + #include "listener.h" -#include "thread_repository.h" + +#include +#include +#include +#include + +#include "thread_registry.h" +#include "options.h" +#include "instance_map.h" #include "log.h" +#include "mysql_connection.h" + + +/* + Listener_thread - incapsulates listening functionality +*/ + +class Listener_thread: public Listener_thread_args +{ +public: + Listener_thread(const Listener_thread_args &args); + ~Listener_thread(); + void run(); +private: + ulong total_connection_count; + Thread_info thread_info; +private: + void handle_new_mysql_connection(Vio *vio); +}; + + +Listener_thread::Listener_thread(const Listener_thread_args &args) : + Listener_thread_args(args.thread_registry, args.options, args.user_map, + args.instance_map) + ,total_connection_count(0) + ,thread_info(pthread_self()) +{ + thread_registry.register_thread(&thread_info); +} + + +Listener_thread::~Listener_thread() +{ + thread_registry.unregister_thread(&thread_info); +} + + +/* + Listener_thread::run() - listen all supported sockets and spawn a thread + to handle incoming connection. + Using 'die' in case of syscall failure is OK now - we don't hold any + resources and 'die' kills the signal thread automatically. To be rewritten + one day. + See also comments in mysqlmanager.cc to picture general Instance Manager + architecture. +*/ + +void Listener_thread::run() +{ + enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size + int flags; + int arg= 1; /* value to be set by setsockopt */ + /* I. prepare 'listen' sockets */ + + int ip_socket= socket(AF_INET, SOCK_STREAM, 0); + if (ip_socket == INVALID_SOCKET) + { + log_error("Listener_thead::run(): socket(AF_INET) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + struct sockaddr_in ip_socket_address; + memset(&ip_socket_address, 0, sizeof(ip_socket_address)); + + ulong im_bind_addr; + if (options.bind_address != 0) + { + if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE) + im_bind_addr= htonl(INADDR_ANY); + } + else im_bind_addr= htonl(INADDR_ANY); + uint im_port= options.port_number; + + ip_socket_address.sin_family= AF_INET; + ip_socket_address.sin_addr.s_addr = im_bind_addr; + + + ip_socket_address.sin_port= (unsigned short) + htons((unsigned short) im_port); + + setsockopt(ip_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &arg, sizeof(arg)); + if (bind(ip_socket, (struct sockaddr *) &ip_socket_address, + sizeof(ip_socket_address))) + { + log_error("Listener_thread::run(): bind(ip socket) failed, '%s'", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + if (listen(ip_socket, LISTEN_BACK_LOG_SIZE)) + { + log_error("Listener_thread::run(): listen(ip socket) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + flags= fcntl(ip_socket, F_GETFL, 0); + fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK); + + log_info("accepting connections on ip socket"); + + /*--------------------------------------------------------------*/ + int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); + if (unix_socket == INVALID_SOCKET) + { + log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + struct sockaddr_un unix_socket_address; + memset(&unix_socket_address, 0, sizeof(unix_socket_address)); + + unix_socket_address.sun_family= AF_UNIX; + strmake(unix_socket_address.sun_path, options.socket_file_name, + sizeof(unix_socket_address.sun_path)); + unlink(unix_socket_address.sun_path); // in case we have stale socket file + + { + /* + POSIX specifies default permissions for a pathname created by bind + to be 0777. We need everybody to have access to the socket. + */ + mode_t old_mask= umask(0); + if (bind(unix_socket, (struct sockaddr *) &unix_socket_address, + sizeof(unix_socket_address))) + { + log_error("Listener_thread::run(): bind(unix socket) failed, " + "socket file name is '%s', error '%s'", + unix_socket_address.sun_path, strerror(errno)); + thread_registry.request_shutdown(); + return; + } + umask(old_mask); + + if (listen(unix_socket, LISTEN_BACK_LOG_SIZE)) + { + log_error("Listener_thread::run(): listen(unix socket) failed, %s", + strerror(errno)); + thread_registry.request_shutdown(); + return; + } + + /* set the socket nonblocking */ + flags= fcntl(unix_socket, F_GETFL, 0); + fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK); + } + log_info("accepting connections on unix socket %s", + unix_socket_address.sun_path); + + /* II. Listen sockets and spawn childs */ + + { + int n= max(unix_socket, ip_socket) + 1; + fd_set read_fds; + + FD_ZERO(&read_fds); + FD_SET(unix_socket, &read_fds); + FD_SET(ip_socket, &read_fds); + + while (thread_registry.is_shutdown() == false) + { + fd_set read_fds_arg= read_fds; + int rc= select(n, &read_fds_arg, 0, 0, 0); + if (rc == -1 && errno != EINTR) + log_error("Listener_thread::run(): select() failed, %s", + strerror(errno)); + else + { + /* Assuming that rc > 0 as we asked to wait forever */ + if (FD_ISSET(unix_socket, &read_fds_arg)) + { + int client_fd= accept(unix_socket, 0, 0); + /* accept may return -1 (failure or spurious wakeup) */ + if (client_fd >= 0) // connection established + { + if (Vio *vio= vio_new(client_fd, VIO_TYPE_SOCKET, 1)) + handle_new_mysql_connection(vio); + else + { + shutdown(client_fd, SHUT_RDWR); + close(client_fd); + } + } + } + else + if (FD_ISSET(ip_socket, &read_fds_arg)) + { + int client_fd= accept(ip_socket, 0, 0); + /* accept may return -1 (failure or spurious wakeup) */ + if (client_fd >= 0) // connection established + { + if (Vio *vio= vio_new(client_fd, VIO_TYPE_TCPIP, 0)) + { + handle_new_mysql_connection(vio); + } + else + { + shutdown(client_fd, SHUT_RDWR); + close(client_fd); + } + } + } + } + } + } + + /* III. Release all resources and exit */ + + log_info("Listener_thread::run(): shutdown requested, exiting..."); + + close(unix_socket); + unlink(unix_socket_address.sun_path); +} + + +/* + Create new mysql connection. Created thread is responsible for deletion of + the Mysql_connection_thread_args and Vio instances passed to it. + SYNOPSYS + handle_new_mysql_connection() +*/ + +void Listener_thread::handle_new_mysql_connection(Vio *vio) +{ + if (Mysql_connection_thread_args *mysql_thread_args= + new Mysql_connection_thread_args(vio, thread_registry, user_map, + ++total_connection_count, + instance_map) + ) + { + /* + Initialize thread attributes to create detached thread; it seems + easier to do it ad-hoc than have a global variable for attributes. + */ + pthread_t mysql_thd_id; + pthread_attr_t mysql_thd_attr; + pthread_attr_init(&mysql_thd_attr); + pthread_attr_setdetachstate(&mysql_thd_attr, PTHREAD_CREATE_DETACHED); + if (pthread_create(&mysql_thd_id, &mysql_thd_attr, mysql_connection, + mysql_thread_args)) + { + delete mysql_thread_args; + vio_delete(vio); + log_error("handle_one_mysql_connection(): pthread_create(mysql) failed"); + } + pthread_attr_destroy(&mysql_thd_attr); + } + else + vio_delete(vio); +} + C_MODE_START + pthread_handler_decl(listener, arg) { - Thread_info info(pthread_self()); - Thread_repository &thread_repository= - ((Listener_thread_args *) arg)->thread_repository; - thread_repository.register_thread(&info); - - while (true) - { - log_info("listener is alive"); - sleep(2); - if (thread_repository.is_shutdown()) - break; - } - log_info("listener(): shutdown requested, exiting..."); - - thread_repository.unregister_thread(&info); + Listener_thread_args *args= (Listener_thread_args *) arg; + Listener_thread listener(*args); + listener.run(); + /* + args is a stack variable because listener thread lives as long as the + manager process itself + */ return 0; } + C_MODE_END -#if 0 - while (true) - { - } - /* - Dummy manager implementation: listens on a UNIX socket and - starts echo server in a dedicated thread for each accepted connection. - Enough to test startup/shutdown/options/logging of the instance manager. - */ - - int fd= socket(AF_UNIX, SOCK_STREAM, 0); - - if (!fd) - die("socket(): failed"); - - struct sockaddr_un address; - bzero(&address, sizeof(address)); - address.sun_family= AF_UNIX; - strcpy(address.sun_path, socket_path); - int opt= 1; - - if (unlink(socket_path) || - bind(fd, (struct sockaddr *) &address, sizeof(address)) || - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) - die("unlink | bind | setsockopt failed"); - - if (listen(fd, 5)) - die("listen() failed"); - - int client_fd; - while ((client_fd= accept(fd, 0, 0)) != -1); - { - printf("accepted\n"); - const char *message= "\n10hel"; - send(client_fd, message, strlen(message), 0); - - int sleep_seconds= argc > 1 && atoi(argv[1]) ? atoi(argv[1]) : 1; - printf("sleeping %d seconds\n", sleep_seconds); - sleep(sleep_seconds); - close(client_fd); - } - printf("accept(): failed\n"); - close(fd); -#endif diff --git a/server-tools/instance-manager/listener.h b/server-tools/instance-manager/listener.h index 9165e5a0ee7..7a8af49e2b3 100644 --- a/server-tools/instance-manager/listener.h +++ b/server-tools/instance-manager/listener.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,23 +23,34 @@ #include #include + C_MODE_START pthread_handler_decl(listener, arg); C_MODE_END -class Thread_repository; +class Thread_registry; +class Options; +class User_map; +class Instance_map; struct Listener_thread_args { - Thread_repository &thread_repository; - const char *socket_file_name; + Thread_registry &thread_registry; + const Options &options; + const User_map &user_map; + Instance_map &instance_map; - Listener_thread_args(Thread_repository &thread_repository_arg, - const char *socket_file_name_arg) : - thread_repository(thread_repository_arg), - socket_file_name(socket_file_name_arg) {} + Listener_thread_args(Thread_registry &thread_registry_arg, + const Options &options_arg, + const User_map &user_map_arg, + Instance_map &instance_map_arg) : + thread_registry(thread_registry_arg) + ,options(options_arg) + ,user_map(user_map_arg) + ,instance_map(instance_map_arg) + {} }; -#endif +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_LISTENER_H diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc index 7eb0f15b2a4..f89f5e425b8 100644 --- a/server-tools/instance-manager/log.cc +++ b/server-tools/instance-manager/log.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -139,12 +139,12 @@ void print_error(const char *format, ...) } /* - init_logs() + log_init() RETURN VALUE 0 ok !0 error */ - + void log_init() { /* diff --git a/server-tools/instance-manager/log.h b/server-tools/instance-manager/log.h index 7b69e516edb..a1441d5bd71 100644 --- a/server-tools/instance-manager/log.h +++ b/server-tools/instance-manager/log.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_LOG_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_LOG_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,11 +22,8 @@ Two logging streams are supported: error log and info log. Additionally libdbug may be used for debug information output. ANSI C buffered I/O is used to perform logging. - Logging may be performed in two modes: - - console application mode (default), stdout/stderr is used for logging - init_logs() must be called to initialize logging environment - - daemon mode, without controlling terminal, call - init_logs_in_daemon_mode() to initialize + Logging is performed via stdout/stder, so one can reopen them to point to + ordinary files. To initialize loggin environment log_init() must be called. Rationale: - no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 06e181d52d5..cb51197d52a 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,19 +17,68 @@ #include "manager.h" #include +#include +#include #include +#include -#include "thread_repository.h" +#include "thread_registry.h" #include "listener.h" +#include "instance_map.h" +#include "options.h" +#include "user_map.h" #include "log.h" +#include "guardian.h" -void manager(const char *socket_file_name) +/* + manager - entry point to the main instance manager process: start + listener thread, write pid file and enter into signal handling. + See also comments in mysqlmanager.cc to picture general Instance Manager + architecture. +*/ + +void manager(const Options &options) { - Thread_repository thread_repository; - Listener_thread_args listener_args(thread_repository, socket_file_name); + Thread_registry thread_registry; + /* + All objects created in the manager() function live as long as + thread_registry lives, and thread_registry is alive until there are + working threads. + */ + + User_map user_map; + Instance_map instance_map; + Guardian_thread guardian_thread(thread_registry, + &instance_map, + options.monitoring_interval); + + instance_map.mysqld_path= options.default_mysqld_path; + instance_map.user= options.default_admin_user; + instance_map.password= options.default_admin_password; + instance_map.guardian= &guardian_thread; + instance_map.load(); + + Listener_thread_args listener_args(thread_registry, options, user_map, + instance_map); + + + if (user_map.load(options.password_file_name)) + return; /* write pid file */ + if (FILE *pid_file= my_fopen(options.pid_file_name, + O_WRONLY | O_CREAT | O_BINARY, MYF(0))) + { + fprintf(pid_file, "%d\n", (int) getpid()); + my_fclose(pid_file, MYF(0)); + } + else + { + log_error("can't create pid file %s: errno=%d, %s", + options.pid_file_name, errno, strerror(errno)); + return; + } /* block signals */ sigset_t mask; @@ -37,26 +86,98 @@ void manager(const char *socket_file_name) sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGHUP); + /* + We want this signal to be blocked in all theads but the signal + one. It is needed for the thr_alarm subsystem to work. + */ + sigaddset(&mask,THR_SERVER_ALARM); /* all new threads will inherite this signal mask */ pthread_sigmask(SIG_BLOCK, &mask, NULL); + + /* create the listener */ { - /* create the listener */ pthread_t listener_thd_id; pthread_attr_t listener_thd_attr; + int rc; pthread_attr_init(&listener_thd_attr); pthread_attr_setdetachstate(&listener_thd_attr, PTHREAD_CREATE_DETACHED); - if (pthread_create(&listener_thd_id, &listener_thd_attr, listener, - &listener_args)) - die("manager(): pthread_create(listener) failed"); + rc= pthread_create(&listener_thd_id, &listener_thd_attr, listener, + &listener_args); + pthread_attr_destroy(&listener_thd_attr); + if (rc) + { + log_error("manager(): pthread_create(listener) failed"); + goto err; + } + } + + /* create guardian thread */ + { + pthread_t guardian_thd_id; + pthread_attr_t guardian_thd_attr; + int rc; + + pthread_attr_init(&guardian_thd_attr); + pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED); + rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian, + &guardian_thread); + pthread_attr_destroy(&guardian_thd_attr); + if (rc) + { + log_error("manager(): pthread_create(guardian) failed"); + goto err; + } + + } + /* To work nicely with LinuxThreads, the signal thread is the first thread in the process. */ int signo; - sigwait(&mask, &signo); - thread_repository.deliver_shutdown(); + bool shutdown_complete; + + shutdown_complete= FALSE; + /* + In our case the signal thread also implements functions of alarm thread. + Here we init alarm thread functionality. We suppose that we won't have + more then 10 alarms at the same time. + */ + init_thr_alarm(10); + /* + Now we can init the list of guarded instances. We have to do it after + alarm structures initialization as we have to use net_* functions while + making the list. And they in their turn need alarms for timeout suppport. + */ + instance_map.init_guardian(); + + while (!shutdown_complete) + { + sigwait(&mask, &signo); + switch (signo) + { + case THR_SERVER_ALARM: + process_alarm(signo); + break; + default: + thread_registry.deliver_shutdown(); + shutdown_complete= TRUE; + break; + } + } + +err: + /* delete the pid file */ + my_delete(options.pid_file_name, MYF(0)); + + /* close permanent connections to the running instances */ + instance_map.cleanup(); + + /* free alarm structures */ + end_thr_alarm(1); /* don't pthread_exit to kill all threads who did not shut down in time */ } + diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h index 2f30813180a..d73f4b35f18 100644 --- a/server-tools/instance-manager/manager.h +++ b/server-tools/instance-manager/manager.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,6 +16,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -void manager(const char *socket_file_name); +class Options; + +void manager(const Options &options); #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc new file mode 100644 index 00000000000..cc07352f58a --- /dev/null +++ b/server-tools/instance-manager/messages.cc @@ -0,0 +1,73 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "messages.h" + +#include +#include + +#include + + +static const char *mysqld_error_message(unsigned sql_errno) +{ + switch (sql_errno) { + case ER_HANDSHAKE_ERROR: + return "Bad handshake"; + case ER_OUT_OF_RESOURCES: + return "Out of memory; Check if mysqld or some other process" + " uses all available memory. If not you may have to use" + " 'ulimit' to allow mysqld to use more memory or you can" + " add more swap space"; + case ER_ACCESS_DENIED_ERROR: + return "Access denied. Bad username/password pair"; + case ER_NOT_SUPPORTED_AUTH_MODE: + return "Client does not support authentication protocol requested by" + " server; consider upgrading MySQL client"; + case ER_UNKNOWN_COM_ERROR: + return "Unknown command"; + case ER_SYNTAX_ERROR: + return "You have an error in your command syntax. Check the manual that" + " corresponds to your MySQL Instance Manager version for the right" + " syntax to use"; + case ER_BAD_INSTANCE_NAME: + return "Bad instance name. Check that the instance with such a name exists"; + case ER_INSTANCE_IS_NOT_STARTED: + return "Cannot stop instance. Perhaps the instance is not started or you" + " have specified wrong username/password in the config file"; + case ER_INSTANCE_ALREADY_STARTED: + return "The instance is already started"; + case ER_CANNOT_START_INSTANCE: + return "Cannot start instance. Possible reasons are wrong instance options" + " or resources shortage"; + case ER_STOP_INSTANCE: + return "Cannot stop instance"; + default: + DBUG_ASSERT(0); + return 0; + } +} + + +const char *message(unsigned sql_errno) +{ + return mysqld_error_message(sql_errno); +} + + +const char *errno_to_sqlstate(unsigned sql_errno) +{ + return mysql_errno_to_sqlstate(sql_errno); +} diff --git a/server-tools/instance-manager/messages.h b/server-tools/instance-manager/messages.h new file mode 100644 index 00000000000..4cc15b5efe4 --- /dev/null +++ b/server-tools/instance-manager/messages.h @@ -0,0 +1,26 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysqld_error.h" +#include "mysql_manager_error.h" + +const char *message(unsigned sql_errno); + +const char *errno_to_sqlstate(unsigned sql_errno); + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MESSAGES_H diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc new file mode 100644 index 00000000000..2a617675c26 --- /dev/null +++ b/server-tools/instance-manager/mysql_connection.cc @@ -0,0 +1,388 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include "mysql_connection.h" +#include "priv.h" + +#include +#include +#include +#include +#include + +#include "thread_registry.h" +#include "log.h" +#include "user_map.h" +#include "protocol.h" +#include "messages.h" +#include "command.h" +#include "factory.h" +#include "parse.h" + +Command *parse_command(Command_factory * factory, const char *text); + + +/* + MySQL connection - handle one connection with mysql command line client + See also comments in mysqlmanager.cc to picture general Instance Manager + architecture. + We use conventional technique to work with classes without exceptions: + class acquires all vital resource in init(); Thus if init() succeed, + a user must call cleanup(). All other methods are valid only between + init() and cleanup(). +*/ + +class Mysql_connection_thread: public Mysql_connection_thread_args +{ +public: + Mysql_connection_thread(const Mysql_connection_thread_args &args); + + int init(); + void cleanup(); + + void run(); + + ~Mysql_connection_thread(); +private: + Thread_info thread_info; + NET net; + struct rand_struct rand_st; + char scramble[SCRAMBLE_LENGTH + 1]; + uint status; + ulong client_capabilities; +private: + /* Names are conventionally the same as in mysqld */ + int check_connection(); + int check_user(const char *user, const char *password); + int do_command(); + int dispatch_command(enum enum_server_command command, + const char *text, uint len); +}; + + +Mysql_connection_thread::Mysql_connection_thread( + const Mysql_connection_thread_args &args) : + Mysql_connection_thread_args(args.vio, + args.thread_registry, + args.user_map, + args.connection_id, + args.instance_map) + ,thread_info(pthread_self()) +{ + thread_registry.register_thread(&thread_info); +} + + +/* + NET subsystem requieres its user to provide my_net_local_init extern + C function (exactly as declared below). my_net_local_init is called by + my_net_init and is supposed to set NET controlling variables. + See also priv.h for variables description. +*/ + +C_MODE_START + +void my_net_local_init(NET *net) +{ + net->max_packet= net_buffer_length; + net->read_timeout= net_read_timeout; + net->write_timeout= net_write_timeout; + net->retry_count= net_retry_count; + net->max_packet_size= max_allowed_packet; +} + +C_MODE_END + + +/* + Every resource, which we can fail to acquire, is allocated in init(). + This function is complementary to cleanup(). +*/ + +int Mysql_connection_thread::init() +{ + /* Allocate buffers for network I/O */ + if (my_net_init(&net, vio)) + return 1; + net.return_status= &status; + /* Initialize random number generator */ + { + ulong seed1= (ulong) &rand_st + rand(); + ulong seed2= rand() + time(0); + randominit(&rand_st, seed1, seed2); + } + /* Fill scramble - server's random message used for handshake */ + create_random_string(scramble, SCRAMBLE_LENGTH, &rand_st); + /* We don't support transactions, every query is atomic */ + status= SERVER_STATUS_AUTOCOMMIT; + return 0; +} + + +void Mysql_connection_thread::cleanup() +{ + net_end(&net); +} + + +Mysql_connection_thread::~Mysql_connection_thread() +{ + /* vio_delete closes the socket if necessary */ + vio_delete(vio); + thread_registry.unregister_thread(&thread_info); +} + + +void Mysql_connection_thread::run() +{ + log_info("accepted mysql connection %d", connection_id); + + my_thread_init(); + + if (check_connection()) + { + my_thread_end(); + return; + } + + log_info("connection %d is checked successfully", connection_id); + + vio_keepalive(vio, TRUE); + + while (!net.error && net.vio && !thread_registry.is_shutdown()) + { + if (do_command()) + break; + } + + my_thread_end(); +} + + +int Mysql_connection_thread::check_connection() +{ + ulong pkt_len=0; // to hold client reply length + + /* buffer for the first packet */ /* packet contains: */ + char buff[mysqlmanager_version_length + 1 + // server version, 0-ended + 4 + // connection id + SCRAMBLE_LENGTH + 2 + // scramble (in 2 pieces) + 18]; // server variables: flags, + // charset number, status, + char *pos= buff; + ulong server_flags; + + memcpy(pos, mysqlmanager_version, mysqlmanager_version_length + 1); + pos+= mysqlmanager_version_length + 1; + + int4store((uchar*) pos, connection_id); + pos+= 4; + + /* + Old clients does not understand long scrambles, but can ignore packet + tail: that's why first part of the scramble is placed here, and second + part at the end of packet (even though we don't support old clients, + we must follow standard packet format.) + */ + memcpy(pos, scramble, SCRAMBLE_LENGTH_323); + pos+= SCRAMBLE_LENGTH_323; + *pos++= '\0'; + + server_flags= CLIENT_LONG_FLAG | CLIENT_PROTOCOL_41 | + CLIENT_SECURE_CONNECTION; + + /* + 18-bytes long section for various flags/variables + + Every flag occupies a bit in first half of ulong; int2store will + gracefully pick up all flags. + */ + int2store(pos, server_flags); + pos+= 2; + *pos++= (char) default_charset_info->number; // global mysys variable + int2store(pos, status); // connection status + pos+= 2; + bzero(pos, 13); // not used now + pos+= 13; + + /* second part of the scramble, null-terminated */ + memcpy(pos, scramble + SCRAMBLE_LENGTH_323, + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1); + pos+= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1; + + /* write connection message and read reply */ + enum { MIN_HANDSHAKE_SIZE= 2 }; + if (net_write_command(&net, protocol_version, "", 0, buff, pos - buff) || + (pkt_len= my_net_read(&net)) == packet_error || + pkt_len < MIN_HANDSHAKE_SIZE) + { + net_send_error(&net, ER_HANDSHAKE_ERROR); + return 1; + } + + client_capabilities= uint2korr(net.read_pos); + if (!(client_capabilities & CLIENT_PROTOCOL_41)) + { + net_send_error_323(&net, ER_NOT_SUPPORTED_AUTH_MODE); + return 1; + } + client_capabilities|= ((ulong) uint2korr(net.read_pos + 2)) << 16; + + pos= (char *) net.read_pos + 32; + + /* At least one byte for username and one byte for password */ + if (pos >= (char *) net.read_pos + pkt_len + 2) + { + /*TODO add user and password handling in error messages*/ + net_send_error(&net, ER_HANDSHAKE_ERROR); + return 1; + } + + const char *user= pos; + const char *password= strend(user)+1; + ulong password_len= *password++; + if (password_len != SCRAMBLE_LENGTH) + { + net_send_error(&net, ER_ACCESS_DENIED_ERROR); + return 1; + } + if (user_map.authenticate(user, password-user-2, password, scramble)) + { + net_send_error(&net, ER_ACCESS_DENIED_ERROR); + return 1; + } + net_send_ok(&net, connection_id); + return 0; +} + + +int Mysql_connection_thread::check_user(const char *user, const char *password) +{ + return 0; +} + + +int Mysql_connection_thread::do_command() +{ + char *packet; + uint old_timeout; + ulong packet_length; + + /* We start to count packets from 0 for each new command */ + net.pkt_nr= 0; + + if ((packet_length=my_net_read(&net)) == packet_error) + { + /* Check if we can continue without closing the connection */ + if (net.error != 3) // what is 3 - find out + { + return 1; + } + if (thread_registry.is_shutdown()) + return 1; + net_send_error(&net, net.last_errno); + net.error= 0; + return 0; + } + else + { + if (thread_registry.is_shutdown()) + return 1; + packet= (char*) net.read_pos; + enum enum_server_command command= (enum enum_server_command) + (uchar) *packet; + log_info("connection %d: packet_length=%d, command=%d", + connection_id, packet_length, command); + return dispatch_command(command, packet + 1, packet_length - 1); + } +} + +int Mysql_connection_thread::dispatch_command(enum enum_server_command command, + const char *packet, uint len) +{ + switch (command) { + case COM_QUIT: // client exit + log_info("query for connection %d received quit command",connection_id); + return 1; + case COM_PING: + log_info("query for connection %d received ping command",connection_id); + net_send_ok(&net, connection_id); + break; + case COM_QUERY: + { + log_info("query for connection %d : ----\n%s\n-------------------------", + connection_id,packet); + Command_factory commands_factory(instance_map); + if (Command *command= parse_command(&commands_factory, packet)) + { + int res= 0; + log_info("query for connection %d successefully parsed",connection_id); + res= command->execute(&net, connection_id); + delete command; + if (!res) + { + log_info("query for connection %d executed ok",connection_id); + } + else + { + log_info("query for connection %d executed err=%d",connection_id,res); + net_send_error(&net, res); + return 0; + } + } + else + { + net_send_error(&net,ER_OUT_OF_RESOURCES); + return 0; + } + break; + } + default: + log_info("query for connection %d received unknown command",connection_id); + net_send_error(&net, ER_UNKNOWN_COM_ERROR); + break; + } + return 0; +} + + +C_MODE_START + +pthread_handler_decl(mysql_connection, arg) +{ + Mysql_connection_thread_args *args= (Mysql_connection_thread_args *) arg; + Mysql_connection_thread mysql_connection_thread(*args); + delete args; + if (mysql_connection_thread.init()) + log_info("mysql_connection(): error initializing thread"); + else + { + mysql_connection_thread.run(); + mysql_connection_thread.cleanup(); + } + return 0; +} + +C_MODE_END + + +/* + vim: fdm=marker +*/ diff --git a/server-tools/instance-manager/mysql_connection.h b/server-tools/instance-manager/mysql_connection.h new file mode 100644 index 00000000000..225f4a352ce --- /dev/null +++ b/server-tools/instance-manager/mysql_connection.h @@ -0,0 +1,60 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include +#include + + +C_MODE_START + +pthread_handler_decl(mysql_connection, arg); + +C_MODE_END + + +class Thread_registry; +class User_map; +class Instance_map; +struct st_vio; + +struct Mysql_connection_thread_args +{ + struct st_vio *vio; + Thread_registry &thread_registry; + const User_map &user_map; + ulong connection_id; + Instance_map &instance_map; + + Mysql_connection_thread_args(struct st_vio *vio_arg, + Thread_registry &thread_registry_arg, + const User_map &user_map_arg, + ulong connection_id_arg, + Instance_map &instance_map_arg) : + vio(vio_arg) + ,thread_registry(thread_registry_arg) + ,user_map(user_map_arg) + ,connection_id(connection_id_arg) + ,instance_map(instance_map_arg) + {} +}; + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_CONNECTION_H diff --git a/server-tools/instance-manager/mysql_manager_error.h b/server-tools/instance-manager/mysql_manager_error.h new file mode 100644 index 00000000000..2862e01649d --- /dev/null +++ b/server-tools/instance-manager/mysql_manager_error.h @@ -0,0 +1,27 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Definefile for instance manager error messagenumbers */ + +#define ER_BAD_INSTANCE_NAME 3000 +#define ER_INSTANCE_IS_NOT_STARTED 3001 +#define ER_INSTANCE_ALREADY_STARTED 3002 +#define ER_CANNOT_START_INSTANCE 3003 +#define ER_STOP_INSTANCE 3004 + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_MYSQL_MANAGER_ERROR_H */ diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index 0ec7e043ed5..bd8f3c6b870 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,20 +14,21 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "manager.h" +#include "options.h" +#include "log.h" + #include #include #include #include #include -#include #include +#include -#include "manager.h" -#include "options.h" -#include "log.h" /* - Few notes about the Instance Manager architecture: + Few notes about Instance Manager architecture: Instance Manager consisits of two processes: the angel process, and the instance manager process. Responsibilities of the angel process is to monitor the instance manager process, and restart it in case of @@ -35,19 +36,19 @@ '--run-as-service' is provided. The Instance Manager process consists of several subsystems (thread sets): - - the signal handling thread: it's responsibilities are to handle + - the signal handling thread: it's responsibilities are to handle user signals and propogate them to the other threads. All other threads - are accounted in the signal handler thread Thread Repository. - - the listener: listens all sockets. There is a listening + are accounted in the signal handler thread Thread Registry. + - the listener: listens all sockets. There is a listening socket for each (mysql, http, snmp, rendezvous (?)) subsystem. - mysql subsystem: Instance Manager acts like an ordinary MySQL Server, - but with very restricted command set. Each MySQL client connection is + but with very restricted command set. Each MySQL client connection is handled in a separate thread. All MySQL client connections threads constitute mysql subsystem. - - http subsystem: it is also possible to talk with Instance Manager via + - http subsystem: it is also possible to talk with Instance Manager via http. One thread per http connection is used. Threads are pooled. - 'snmp' connections (FIXME: I know nothing about it yet) - - rendezvous threads + - rendezvous threads */ static void init_environment(char *progname); @@ -70,11 +71,12 @@ int main(int argc, char *argv[]) options.load(argc, argv); if (options.run_as_service) { + /* forks, and returns only in child */ daemonize(options.log_file_name); + /* forks again, and returns only in child: parent becomes angel */ angel(options); } - else - manager(options.log_file_name); + manager(options); return 0; } @@ -90,6 +92,7 @@ static void init_environment(char *progname) MY_INIT(progname); log_init(); umask(0117); + srand(time(0)); } @@ -109,8 +112,8 @@ static void daemonize(const char *log_file_name) int fd; /* Become a session leader: setsid must succeed because child is - guaranteed not to be a process group leader (it belongs to the - process group of the parent.) + guaranteed not to be a process group leader (it belongs to the + process group of the parent.) The goal is not to have a controlling terminal. */ setsid(); @@ -121,7 +124,7 @@ static void daemonize(const char *log_file_name) close(STDIN_FILENO); - fd= open(log_file_name, O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY, + fd= open(log_file_name, O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (fd < 0) die("daemonize(): failed to open log file %s, %s", log_file_name, @@ -133,7 +136,7 @@ static void daemonize(const char *log_file_name) /* TODO: chroot() and/or chdir() here */ break; - default: + default: /* successfully exit from parent */ exit(0); } @@ -144,13 +147,13 @@ enum { CHILD_OK= 0, CHILD_NEED_RESPAWN, CHILD_EXIT_ANGEL }; static volatile sig_atomic_t child_status= CHILD_OK; -/* +/* Signal handler for SIGCHLD: reap child, analyze child exit status, and set child_status appropriately. */ void reap_child(int __attribute__((unused)) signo) -{ +{ int child_exit_status; /* As we have only one child, no need to cycle waitpid */ if (waitpid(0, &child_exit_status, WNOHANG) > 0) @@ -159,16 +162,14 @@ void reap_child(int __attribute__((unused)) signo) child_status= CHILD_NEED_RESPAWN; else /* - As we reap_child is not called for SIGSTOP, we should be here only + As reap_child is not called for SIGSTOP, we should be here only if the child exited normally. */ child_status= CHILD_EXIT_ANGEL; } } -/* Not static to reuse it in childs */ - -volatile sig_atomic_t is_terminated= 0; +static volatile sig_atomic_t is_terminated= 0; /* Signal handler for terminate signals - SIGTERM, SIGHUP, SIGINT. @@ -215,18 +216,18 @@ spawn: pid_t pid= fork(); switch (pid) { case -1: - die("angel(): fork failed, %s", strerror(errno)); + die("angel(): fork failed, %s", strerror(errno)); case 0: // child, success /* restore default actions for signals to let the manager work with signals as he wishes - */ + */ sigaction(SIGCHLD, &sa_chld_out, 0); sigaction(SIGTERM, &sa_term_out, 0); sigaction(SIGINT, &sa_int_out, 0); sigaction(SIGHUP, &sa_hup_out, 0); - - manager(options.socket_file_name); + /* Here we return to main, and fall into manager */ + break; default: // parent, success while (child_status == CHILD_OK && is_terminated == 0) sigsuspend(&zeromask); @@ -235,12 +236,18 @@ spawn: log_info("angel got signal %d (%s), exiting", is_terminated, sys_siglist[is_terminated]); else if (child_status == CHILD_NEED_RESPAWN) - { + { child_status= CHILD_OK; log_error("angel(): mysqlmanager exited abnormally: respawning..."); sleep(1); /* don't respawn too fast */ goto spawn; } - /* mysqlmanager successfully exited, let's silently evaporate */ + /* + mysqlmanager successfully exited, let's silently evaporate + If we return to main we fall into the manager() function, so let's + simply exit(). + */ + exit(0); } } + diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 5bb4b180030..01d83e2d994 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef __GNUC__ -#pragma implementation +#pragma implementation #endif #include "options.h" @@ -24,14 +24,22 @@ #include #include +#include "priv.h" #define QUOTE2(x) #x #define QUOTE(x) QUOTE2(x) - + char Options::run_as_service; const char *Options::log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME); const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); +const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); +const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); +const char *Options::default_admin_user= QUOTE(DEFAULT_USER); +const char *Options::default_admin_password= QUOTE(DEFAULT_PASSWORD); +const char *Options::bind_address= 0; /* No default value */ +uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; +uint Options::port_number= DEFAULT_PORT; /* List of options, accepted by the instance manager. @@ -42,9 +50,18 @@ enum options { OPT_LOG= 256, OPT_PID_FILE, OPT_SOCKET, - OPT_RUN_AS_SERVICE + OPT_PASSWORD_FILE, + OPT_MYSQLD_PATH, + OPT_RUN_AS_SERVICE, + OPT_USER, + OPT_PASSWORD, + OPT_DEFAULT_ADMIN_USER, + OPT_DEFAULT_ADMIN_PASSWORD, + OPT_MONITORING_INTERVAL, + OPT_PORT, + OPT_BIND_ADDRESS }; - + static struct my_option my_long_options[] = { { "help", '?', "Display this help and exit.", @@ -57,11 +74,48 @@ static struct my_option my_long_options[] = { "pid-file", OPT_PID_FILE, "Pid file to use.", (gptr *) &Options::pid_file_name, (gptr *) &Options::pid_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - + { "socket", OPT_SOCKET, "Socket file to use for connection.", (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "bind_address", OPT_BIND_ADDRESS, "Bind address to use for connection.", + (gptr *) &Options::bind_address, (gptr *) &Options::bind_address, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "port", OPT_PORT, "Port number to use for connections", + (gptr *) &Options::port_number, (gptr *) &Options::port_number, + 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users" + " and passwords here.", + (gptr *) &Options::password_file_name, + (gptr *) &Options::password_file_name, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "default_mysqld_path", OPT_MYSQLD_PATH, "Where to look for MySQL" + " Server binary.", + (gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "default_admin_user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL" + " instances.", + (gptr *) &Options::default_admin_user, + (gptr *) &Options::default_admin_user, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "default_admin_password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to" + "shutdown MySQL instances.", + (gptr *) &Options::default_admin_password, + (gptr *) &Options::default_admin_password, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + + { "monitoring_interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances" + " in seconds.", + (gptr *) &Options::monitoring_interval, + (gptr *) &Options::monitoring_interval, + 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "run-as-service", OPT_RUN_AS_SERVICE, "Daemonize and start angel process.", (gptr *) &Options::run_as_service, 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, @@ -74,15 +128,26 @@ static struct my_option my_long_options[] = static void version() { - static const char mysqlmanager_version[] = "0.1-alpha"; - printf("%s Ver %s for %s on %s\n", my_progname, mysqlmanager_version, + printf("%s Ver %s for %s on %s\n", my_progname, mysqlmanager_version, SYSTEM_TYPE, MACHINE_TYPE); } + +static const char *default_groups[]= { "mysql", "manager", 0 }; + + static void usage() { version(); + + printf("Copyright (C) 2003, 2004 MySQL AB\n" + "This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n" + "and you are welcome to modify and redistribute it under the GPL license\n"); + printf("Usage: %s [OPTIONS] \n", my_progname); + my_print_help(my_long_options); + print_defaults("my", default_groups); + my_print_variables(my_long_options); } C_MODE_START @@ -107,9 +172,9 @@ get_one_option(int optid, C_MODE_END -/* +/* - call load_defaults to load configuration file section - - call handle_options to assign defaults and command-line arguments + - call handle_options to assign defaults and command-line arguments to the class members if either of these function fail, exit the program May not return. @@ -117,6 +182,9 @@ C_MODE_END void Options::load(int argc, char **argv) { + /* config-file options are prepended to command-line ones */ + load_defaults("my", default_groups, &argc, &argv); + if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option)) exit(rc); } diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index b2eacf220d7..fc2b44c8b53 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -1,6 +1,6 @@ #ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H #define INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,13 +24,21 @@ Options - all possible options for the instance manager grouped in one struct. */ +#include -struct Options +struct Options { static char run_as_service; /* handle_options doesn't support bool */ static const char *log_file_name; static const char *pid_file_name; static const char *socket_file_name; + static const char *password_file_name; + static const char *default_mysqld_path; + static const char *default_admin_user; + static const char *default_admin_password; + static uint monitoring_interval; + static uint port_number; + static const char *bind_address; static void load(int argc, char **argv); }; diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc new file mode 100644 index 00000000000..09a60062946 --- /dev/null +++ b/server-tools/instance-manager/parse.cc @@ -0,0 +1,200 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "parse.h" +#include + +enum Token +{ + TOK_FLUSH = 0, + TOK_INSTANCE, + TOK_INSTANCES, + TOK_OPTIONS, + TOK_START, + TOK_STATUS, + TOK_STOP, + TOK_SHOW, + TOK_NOT_FOUND, // must be after all tokens + TOK_END +}; + +static const char *tokens[]= { + "FLUSH", + "INSTANCE", + "INSTANCES", + "OPTIONS", + "START", + "STATUS", + "STOP", + "SHOW", +}; + + +/* + tries to find next word in the text + if found, returns the beginning and puts word length to word_len argument. + if not found returns pointer to first non-space or to '\0', word_len == 0 +*/ + +inline void get_word(const char **text, uint *word_len) +{ + const char *word_end; + + /* skip space */ + while (my_isspace(default_charset_info, **text)) + ++(*text); + + word_end= *text; + + while (my_isalnum(default_charset_info, *word_end)) + ++word_end; + + *word_len= word_end - *text; +} + + +/* + Returns token no if word corresponds to some token, otherwise returns + TOK_NOT_FOUND +*/ + +inline Token find_token(const char *word, uint word_len) +{ + int i= 0; + do + { + if (strncasecmp(tokens[i], word, word_len) == 0) + break; + } + while (++i < TOK_NOT_FOUND); + return (Token) i; +} + + +Token get_token(const char **text, uint *word_len) +{ + get_word(text, word_len); + if (*word_len) + return find_token(*text, *word_len); + return TOK_END; +} + + +Token shift_token(const char **text, uint *word_len) +{ + Token save= get_token(text, word_len); + (*text)+= *word_len; + return save; +} + + +void print_token(const char *token, uint tok_len) +{ + for (uint i= 0; i < tok_len; ++i) + printf("%c", token[i]); +} + + +int get_text_id(const char **text, uint *word_len, const char **id) +{ + get_word(text, word_len); + if (word_len == 0) + return 1; + *id= *text; + return 0; +} + + +Command *parse_command(Command_factory *factory, const char *text) +{ + uint word_len; + const char *instance_name; + uint instance_name_len; + Command *command; + const char *saved_text= text; + + Token tok1= shift_token(&text, &word_len); + + switch (tok1) { + case TOK_START: // fallthrough + case TOK_STOP: + if (shift_token(&text, &word_len) != TOK_INSTANCE) + goto syntax_error; + get_word(&text, &word_len); + if (word_len == 0) + goto syntax_error; + instance_name= text; + instance_name_len= word_len; + text+= word_len; + /* it should be the end of command */ + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + + command= (tok1 == TOK_START) ? (Command *) + factory->new_Start_instance(instance_name, instance_name_len): + (Command *) + factory->new_Stop_instance(instance_name, instance_name_len); + break; + case TOK_FLUSH: + if (shift_token(&text, &word_len) != TOK_INSTANCES) + goto syntax_error; + + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + + command= factory->new_Flush_instances(); + break; + case TOK_SHOW: + switch (shift_token(&text, &word_len)) { + case TOK_INSTANCES: + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + command= factory->new_Show_instances(); + break; + case TOK_INSTANCE: + switch (Token tok2= shift_token(&text, &word_len)) { + case TOK_OPTIONS: + case TOK_STATUS: + get_text_id(&text, &instance_name_len, &instance_name); + text+= instance_name_len; + get_word(&text, &word_len); + if (word_len) + goto syntax_error; + command= (tok2 == TOK_STATUS) ? (Command *) + factory->new_Show_instance_status(instance_name, + instance_name_len): + (Command *) + factory->new_Show_instance_options(instance_name, + instance_name_len); + break; + default: + goto syntax_error; + } + break; + default: + goto syntax_error; + } + break; + default: +syntax_error: + command= factory->new_Syntax_error(); + } + return command; +} + diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h new file mode 100644 index 00000000000..236a9bee53a --- /dev/null +++ b/server-tools/instance-manager/parse.h @@ -0,0 +1,23 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "factory.h" + +Command *parse_command(Command_factory *factory, const char *text); + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */ diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc new file mode 100644 index 00000000000..e449df9f540 --- /dev/null +++ b/server-tools/instance-manager/priv.cc @@ -0,0 +1,34 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "priv.h" + +const char mysqlmanager_version[] = "0.2-alpha"; + +const int mysqlmanager_version_length= sizeof(mysqlmanager_version) - 1; + +const unsigned char protocol_version= PROTOCOL_VERSION; + +unsigned long net_buffer_length= 16384; + +unsigned long max_allowed_packet= 16384; + +unsigned long net_read_timeout= 30; // same as in mysqld + +unsigned long net_write_timeout= 60; // same as in mysqld + +unsigned long net_retry_count= 10; // same as in mysqld + diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h new file mode 100644 index 00000000000..73ee87552c8 --- /dev/null +++ b/server-tools/instance-manager/priv.h @@ -0,0 +1,60 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +extern const char mysqlmanager_version[]; +extern const int mysqlmanager_version_length; + +/* MySQL client-server protocol version: substituted from configure */ +extern const unsigned char protocol_version; + +/* + These variables are used in MySQL subsystem to work with mysql clients + To be moved to a config file/options one day. +*/ + + +/* Buffer length for TCP/IP and socket communication */ +extern unsigned long net_buffer_length; + + +/* Maximum allowed incoming/ougoung packet length */ +extern unsigned long max_allowed_packet; + + +/* + Number of seconds to wait for more data from a connection before aborting + the read +*/ +extern unsigned long net_read_timeout; + + +/* + Number of seconds to wait for a block to be written to a connection + before aborting the write. +*/ +extern unsigned long net_write_timeout; + + +/* + If a read on a communication port is interrupted, retry this many times + before giving up. +*/ +extern unsigned long net_retry_count; + + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc new file mode 100644 index 00000000000..6bb6c6d2fa0 --- /dev/null +++ b/server-tools/instance-manager/protocol.cc @@ -0,0 +1,171 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include +#include + +#include "messages.h" +#include "protocol.h" + +static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ + +int net_send_ok(struct st_net *net, unsigned long connection_id) +{ + char buff[1 + // packet type code + 9 + // affected rows count + 9 + // connection id + 2 + // thread return status + 2]; // warning count + + char *pos= buff; + enum { OK_PACKET_CODE= 0 }; + *pos++= OK_PACKET_CODE; + pos= net_store_length(pos, (ulonglong) 0); + pos= net_store_length(pos, (ulonglong) connection_id); + int2store(pos, *net->return_status); + pos+= 2; + /* We don't support warnings, so store 0 for total warning count */ + int2store(pos, 0); + pos+= 2; + + return my_net_write(net, buff, pos - buff) || net_flush(net); +} + + +int net_send_error(struct st_net *net, uint sql_errno) +{ + const char *err= message(sql_errno); + char buff[1 + // packet type code + 2 + // sql error number + 1 + SQLSTATE_LENGTH + // sql state + MYSQL_ERRMSG_SIZE]; // message + char *pos= buff; + + enum { ERROR_PACKET_CODE= 255 }; + *pos++= ERROR_PACKET_CODE; + int2store(pos, sql_errno); + pos+= 2; + /* The first # is to make the protocol backward compatible */ + *pos++= '#'; + memcpy(pos, errno_to_sqlstate(sql_errno), SQLSTATE_LENGTH); + pos+= SQLSTATE_LENGTH; + pos= strmake(pos, err, MYSQL_ERRMSG_SIZE - 1) + 1; + return my_net_write(net, buff, pos - buff) || net_flush(net); +} + + +int net_send_error_323(struct st_net *net, uint sql_errno) +{ + const char *err= message(sql_errno); + char buff[1 + // packet type code + 2 + // sql error number + MYSQL_ERRMSG_SIZE]; // message + char *pos= buff; + + enum { ERROR_PACKET_CODE= 255 }; + *pos++= ERROR_PACKET_CODE; + int2store(pos, sql_errno); + pos+= 2; + pos= strmake(pos, err, MYSQL_ERRMSG_SIZE - 1) + 1; + return my_net_write(net, buff, pos - buff) || net_flush(net); +} + +char *net_store_length(char *pkg, uint length) +{ + uchar *packet=(uchar*) pkg; + if (length < 251) + { + *packet=(uchar) length; + return (char*) packet+1; + } + *packet++=252; + int2store(packet,(uint) length); + return (char*) packet+2; +} + + +void store_to_string(Buffer *buf, const char *string, uint *position) +{ + char* currpos; + uint string_len; + + string_len= strlen(string); + buf->check_and_add(*position, 2); + currpos= net_store_length(buf->buffer + *position, string_len); + buf->put_to_buffer(currpos, string, string_len); + *position= *position + string_len + (currpos - buf->buffer - *position); +} + + +int send_eof(struct st_net *net) +{ + char buff[1 + /* eof packet code */ + 2 + /* warning count */ + 2]; /* server status */ + + buff[0]=254; + int2store(buff+1, 0); + int2store(buff+3, 0); + return my_net_write(net, buff, sizeof buff); +} + +int send_fields(struct st_net *net, LIST *fields) +{ + LIST *tmp= fields; + Buffer send_buff; + char small_buff[4]; + uint position= 0; + NAME_WITH_LENGTH *field; + + /* send the number of fileds */ + net_store_length(small_buff, (uint) list_length(fields)); + my_net_write(net, small_buff, (uint) 1); + + while (tmp) + { + position= 0; + field= (NAME_WITH_LENGTH *) tmp->data; + + store_to_string(&send_buff, (char *) "", &position); /* catalog name */ + store_to_string(&send_buff, (char *) "", &position); /* db name */ + store_to_string(&send_buff, (char *) "", &position); /* table name */ + store_to_string(&send_buff, (char *) "", &position); /* table name alias */ + store_to_string(&send_buff, field->name, &position); /* column name */ + store_to_string(&send_buff, field->name, &position); /* column name alias */ + send_buff.check_and_add(position, 12); + send_buff.buffer[position++]= 12; + int2store(send_buff.buffer + position, 1); /* charsetnr */ + int4store(send_buff.buffer + position + 2, field->length); /* field length */ + send_buff.buffer[position+6]= FIELD_TYPE_STRING; /* type */ + int2store(send_buff.buffer + position + 7, 0); /* flags */ + send_buff.buffer[position + 9]= (char) 0; /* decimals */ + send_buff.buffer[position + 10]= 0; + send_buff.buffer[position + 11]= 0; + position+= 12; + if (my_net_write(net, send_buff.buffer, (uint) position+1)) + goto err; + tmp= rest(tmp); + } + + if ( my_net_write(net, eof_buff, 1)) + goto err; + return 0; + +err: + return 1; +} diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h new file mode 100644 index 00000000000..7bce0e35b5b --- /dev/null +++ b/server-tools/instance-manager/protocol.h @@ -0,0 +1,43 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "buffer.h" +#include + +typedef struct field { + char *name; + uint length; +} NAME_WITH_LENGTH; + +struct st_net; + +int net_send_ok(struct st_net *net, unsigned long connection_id); + +int net_send_error(struct st_net *net, unsigned sql_errno); + +int net_send_error_323(struct st_net *net, unsigned sql_errno); + +int send_fields(struct st_net *net, LIST *fields); + +char *net_store_length(char *pkg, uint length); + +void store_to_string(Buffer *buf, const char *string, uint *position); + +int send_eof(struct st_net *net); + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PROTOCOL_H */ diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc new file mode 100644 index 00000000000..4037da71880 --- /dev/null +++ b/server-tools/instance-manager/thread_registry.cc @@ -0,0 +1,206 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "thread_registry.h" + +#include +#include +#include +#include "log.h" + + +/* Kick-off signal handler */ + +enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 }; + +static void handle_signal(int __attribute__((unused)) sig_no) +{ +} + + +/* + TODO: think about moving signal information (now it's shutdown_in_progress) + to Thread_info. It will reduce contention and allow signal deliverence to + a particular thread, not to the whole worker crew +*/ + +Thread_registry::Thread_registry() : + shutdown_in_progress(false) + ,sigwait_thread_pid(pthread_self()) +{ + pthread_mutex_init(&LOCK_thread_registry, 0); + pthread_cond_init(&COND_thread_registry_is_empty, 0); + + /* head is used by-value to simplify nodes inserting */ + head.next= head.prev= &head; +} + + +Thread_registry::~Thread_registry() +{ + /* Check that no one uses the repository. */ + pthread_mutex_lock(&LOCK_thread_registry); + + /* All threads must unregister */ + DBUG_ASSERT(head.next == &head); + + pthread_mutex_unlock(&LOCK_thread_registry); + pthread_cond_destroy(&COND_thread_registry_is_empty); + pthread_mutex_destroy(&LOCK_thread_registry); +} + + +/* + Set signal handler for kick-off thread, and insert a thread info to the + repository. New node is appended to the end of the list; head.prev always + points to the last node. +*/ + +void Thread_registry::register_thread(Thread_info *info) +{ + struct sigaction sa; + sa.sa_handler= handle_signal; + sa.sa_flags= 0; + sigemptyset(&sa.sa_mask); + sigaction(THREAD_KICK_OFF_SIGNAL, &sa, 0); + + info->current_cond= 0; + + pthread_mutex_lock(&LOCK_thread_registry); + info->next= &head; + info->prev= head.prev; + head.prev->next= info; + head.prev= info; + pthread_mutex_unlock(&LOCK_thread_registry); +} + + +/* + Unregister a thread from the repository and free Thread_info structure. + Every registered thread must unregister. Unregistering should be the last + thing a thread is doing, otherwise it could have no time to finalize. +*/ + +void Thread_registry::unregister_thread(Thread_info *info) +{ + pthread_mutex_lock(&LOCK_thread_registry); + info->prev->next= info->next; + info->next->prev= info->prev; + if (head.next == &head) + pthread_cond_signal(&COND_thread_registry_is_empty); + pthread_mutex_unlock(&LOCK_thread_registry); +} + + +/* + Check whether shutdown is in progress, and if yes, return immediately. + Else set info->current_cond and call pthread_cond_wait. When + pthread_cond_wait returns, unregister current cond and check the shutdown + status again. + RETURN VALUE + return value from pthread_cond_wait +*/ + +int Thread_registry::cond_wait(Thread_info *info, pthread_cond_t *cond, + pthread_mutex_t *mutex, bool *is_shutdown) +{ + pthread_mutex_lock(&LOCK_thread_registry); + *is_shutdown= shutdown_in_progress; + if (*is_shutdown) + { + pthread_mutex_unlock(&LOCK_thread_registry); + return 0; + } + info->current_cond= cond; + pthread_mutex_unlock(&LOCK_thread_registry); + /* sic: race condition here, cond can be signaled in deliver_shutdown */ + int rc= pthread_cond_wait(cond, mutex); + pthread_mutex_lock(&LOCK_thread_registry); + info->current_cond= 0; + *is_shutdown= shutdown_in_progress; + pthread_mutex_unlock(&LOCK_thread_registry); + return rc; +} + + +/* + Deliver shutdown message to the workers crew. + As it's impossible to avoid all race conditions, signal latecomers + again. +*/ + +void Thread_registry::deliver_shutdown() +{ + struct timespec shutdown_time; + set_timespec(shutdown_time, 1); + + pthread_mutex_lock(&LOCK_thread_registry); + shutdown_in_progress= true; + + /* to stop reading from the network we need to flush alarm queue */ + end_thr_alarm(0); + /* + We have to deliver final alarms this way, as the main thread has already + stopped alarm processing. + */ + process_alarm(THR_SERVER_ALARM); + for (Thread_info *info= head.next; info != &head; info= info->next) + { + pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); + /* + sic: race condition here, the thread may not yet fall into + pthread_cond_wait. + */ + if (info->current_cond) + pthread_cond_signal(info->current_cond); + } + /* + The common practice is to test predicate before pthread_cond_wait. + I don't do that here because the predicate is practically always false + before wait - is_shutdown's been just set, and the lock's still not + released - the only case when the predicate is false is when no other + threads exist. + */ + while (pthread_cond_timedwait(&COND_thread_registry_is_empty, + &LOCK_thread_registry, + &shutdown_time) != ETIMEDOUT && + head.next != &head) + ; + /* + If previous signals did not reach some threads, they must be sleeping + in pthread_cond_wait or in a blocking syscall. Wake them up: + every thread shall check signal variables after each syscall/cond_wait, + so this time everybody should be informed (presumably each worker can + get CPU during shutdown_time.) + */ + for (Thread_info *info= head.next; info != &head; info= info->next) + { + pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); + if (info->current_cond) + pthread_cond_signal(info->current_cond); + } + pthread_mutex_unlock(&LOCK_thread_registry); +} + + +void Thread_registry::request_shutdown() +{ + pthread_kill(sigwait_thread_pid, SIGTERM); +} diff --git a/server-tools/instance-manager/thread_registry.h b/server-tools/instance-manager/thread_registry.h new file mode 100644 index 00000000000..0836f44345d --- /dev/null +++ b/server-tools/instance-manager/thread_registry.h @@ -0,0 +1,116 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + A multi-threaded application shall nicely work with signals. + + This means it shall, first of all, shut down nicely on ``quit'' signals: + stop all running threads, cleanup and exit. + + Note, that a thread can't be shut down nicely if it doesn't want to be. + That's why to perform clean shutdown, all threads consituting a process + must observe certain rules. Here we use the rules, described in Butenhof + book 'Programming with POSIX threads', namely: + - all user signals are handled in 'signal thread' in synchronous manner + (by means of sigwait). To guarantee that the signal thread is the only who + can receive user signals, all threads block them, and signal thread is + the only who calls sigwait() with an apporpriate sigmask. + To propogate a signal to the workers the signal thread sets + a variable, corresponding to the signal. Additionally the signal thread + sends each worker an internal signal (by means of pthread_kill) to kick it + out from possible blocking syscall, and possibly pthread_cond_signal if + some thread is blocked in pthread_cond_[timed]wait. + - a worker handles only internal 'kick' signal (the handler does nothing). + In case when a syscall returns 'EINTR' the worker checks all + signal-related variables and behaves accordingly. + Also these variables shall be checked from time to time in long + CPU-bounded operations, and before/after pthread_cond_wait. (It's supposed + that a worker thread either waits in a syscall/conditional variable, or + computes something.) + - to guarantee signal deliverence, there should be some kind of feedback, + e. g. all workers shall account in the signal thread Thread Repository and + unregister from it on exit. + + Configuration reload (on SIGHUP) and thread timeouts/alarms can be handled + in manner, similar to ``quit'' signals. +*/ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include +#include + + +/* + Thread_info - repository entry for each worker thread + All entries comprise double-linked list like: + 0 -- entry -- entry -- entry - 0 + Double-linked list is used to unregister threads easy. +*/ + +class Thread_info +{ + pthread_cond_t *current_cond; + Thread_info *prev, *next; + pthread_t thread_id; + Thread_info() {} + friend class Thread_registry; +public: + Thread_info(pthread_t thread_id_arg) : thread_id(thread_id_arg) {} +}; + + +/* + Thread_registry - contains handles for each worker thread to deliver + signal information to workers. +*/ + +class Thread_registry +{ +public: + Thread_registry(); + ~Thread_registry(); + + void register_thread(Thread_info *info); + void unregister_thread(Thread_info *info); + void deliver_shutdown(); + void request_shutdown(); + inline bool is_shutdown(); + int cond_wait(Thread_info *info, pthread_cond_t *cond, + pthread_mutex_t *mutex, bool *is_shutdown); +private: + Thread_info head; + bool shutdown_in_progress; + pthread_mutex_t LOCK_thread_registry; + pthread_cond_t COND_thread_registry_is_empty; + pid_t sigwait_thread_pid; +}; + + +inline bool Thread_registry::is_shutdown() +{ + pthread_mutex_lock(&LOCK_thread_registry); + bool res= shutdown_in_progress; + pthread_mutex_unlock(&LOCK_thread_registry); + return res; +} + + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REGISTRY_H */ diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc new file mode 100644 index 00000000000..7fff324a521 --- /dev/null +++ b/server-tools/instance-manager/user_map.cc @@ -0,0 +1,172 @@ +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include "user_map.h" + +#include +#include + +#include "log.h" + +struct User +{ + char user[USERNAME_LENGTH + 1]; + uint8 user_length; + uint8 salt[SCRAMBLE_LENGTH]; + int init(const char *line); +}; + + +int User::init(const char *line) +{ + const char *name_begin, *name_end, *password; + + if (line[0] == '\'' || line[0] == '"') + { + name_begin= line + 1; + name_end= strchr(name_begin, line[0]); + if (name_end == 0 || name_end[1] != ':') + goto err; + password= name_end + 2; + } + else + { + name_begin= line; + name_end= strchr(name_begin, ':'); + if (name_end == 0) + goto err; + password= name_end + 1; + } + user_length= name_end - name_begin; + if (user_length > USERNAME_LENGTH) + goto err; + + /* assume that newline characater is present */ + if (strlen(password) != SCRAMBLED_PASSWORD_CHAR_LENGTH + 1) + goto err; + + memcpy(user, name_begin, user_length); + user[user_length]= 0; + get_salt_from_password(salt, password); + log_info("loaded user %s", user); + + return 0; +err: + log_error("error parsing user and password at line %d", line); + return 1; +} + + +C_MODE_START + +static byte* get_user_key(const byte* u, uint* len, + my_bool __attribute__((unused)) t) +{ + const User *user= (const User *) u; + *len= user->user_length; + return (byte *) user->user; +} + +static void delete_user(void *u) +{ + User *user= (User *) u; + delete user; +} + +C_MODE_END + + +User_map::User_map() +{ + enum { START_HASH_SIZE = 16 }; + hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_user_key, delete_user, 0); +} + +User_map::~User_map() +{ + hash_free(&hash); +} + + +/* + Load all users from the password file. Must be called once right after + construction. + In case of failure, puts error message to the log file and returns 1 +*/ + +int User_map::load(const char *password_file_name) +{ + FILE *file; + char line[USERNAME_LENGTH + SCRAMBLED_PASSWORD_CHAR_LENGTH + + 2 + /* for possible quotes */ + 1 + /* for ':' */ + 1 + /* for newline */ + 1]; /* for trailing zero */ + uint line_length; + User *user; + int rc= 1; + + if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0) + { + log_error("can't open password file %s: errno=%d, %s", password_file_name, + errno, strerror(errno)); + return 1; + } + + while (fgets(line, sizeof(line), file)) + { + /* skip comments and empty lines */ + if (line[0] == '#' || line[0] == '\n' && line[1] == '\0') + continue; + if ((user= new User) == 0) + goto done; + if (user->init(line) || my_hash_insert(&hash, (byte *) user)) + goto err_init_user; + } + if (feof(file)) + rc= 0; + goto done; +err_init_user: + delete user; +done: + my_fclose(file, MYF(0)); + return rc; +} + + +/* + Check if user exists and password is correct + RETURN VALUE + 0 - user found and password OK + 1 - password mismatch + 2 - user not found +*/ + +int User_map::authenticate(const char *user_name, uint length, + const char *scrambled_password, + const char *scramble) const +{ + const User *user= (const User *) hash_search((HASH *) &hash, + (byte *) user_name, length); + if (user) + return check_scramble(scrambled_password, scramble, user->salt); + return 2; +} diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h new file mode 100644 index 00000000000..acee0b3c02b --- /dev/null +++ b/server-tools/instance-manager/user_map.h @@ -0,0 +1,45 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H +/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma interface +#endif + +#include +#include +#include + +/* + User_map -- all users and passwords +*/ + +class User_map +{ +public: + User_map(); + ~User_map(); + + int load(const char *password_file_name); + int authenticate(const char *user_name, uint length, + const char *scrambled_password, + const char *scramble) const; +private: + HASH hash; +}; + +#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_USER_MAP_H diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 82a3b1bd520..78d254fccb1 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -78,11 +78,11 @@ my_bool net_flush(NET *net); can't normally do this the client should have a bigger max_allowed_packet. */ -#if defined(__WIN__) || !defined(MYSQL_SERVER) +#if (defined(__WIN__) || (!defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER))) /* The following is because alarms doesn't work on windows. */ #define NO_ALARM #endif - + #ifndef NO_ALARM #include "my_pthread.h" void sql_print_error(const char *format,...); @@ -665,6 +665,13 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, } #endif /* NO_ALARM */ +/* + If we are inside of the instance manageer, we need to simulate mysql + server for the following function. +*/ +#ifdef MYSQL_INSTANCE_MANAGER +#define MYSQL_SERVER +#endif /* Reads one packet to net->buff + net->where_b @@ -854,6 +861,9 @@ end: return(len); } +#ifdef MYSQL_INSTANCE_MANAGER +#undef MYSQL_SERVER +#endif /* Read a packet from the client/server and return it without the internal From a3d9a1eb066d7cca01bef104d065864f2a7c65ec Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 25 Oct 2004 14:23:31 +0400 Subject: [PATCH 02/52] minor post review fixes server-tools/instance-manager/buffer.cc: function renames server-tools/instance-manager/buffer.h: function renames server-tools/instance-manager/command.cc: unecessary headers removed server-tools/instance-manager/command.h: cleanup server-tools/instance-manager/commands.cc: cleanup server-tools/instance-manager/commands.h: cleanup server-tools/instance-manager/guardian.cc: cleanup server-tools/instance-manager/instance.cc: cleanup server-tools/instance-manager/instance_options.cc: cleanup server-tools/instance-manager/instance_options.h: cleanup server-tools/instance-manager/listener.cc: cleanup server-tools/instance-manager/manager.cc: cleanup server-tools/instance-manager/protocol.cc: cleanup --- server-tools/instance-manager/buffer.cc | 20 +++++----- server-tools/instance-manager/buffer.h | 4 +- server-tools/instance-manager/command.cc | 10 ----- server-tools/instance-manager/command.h | 4 +- server-tools/instance-manager/commands.cc | 12 +++--- server-tools/instance-manager/commands.h | 3 -- server-tools/instance-manager/guardian.cc | 39 ++++++++----------- server-tools/instance-manager/instance.cc | 4 +- .../instance-manager/instance_options.cc | 2 +- .../instance-manager/instance_options.h | 9 +++-- server-tools/instance-manager/listener.cc | 11 +++--- server-tools/instance-manager/manager.cc | 37 +++++++++++------- server-tools/instance-manager/protocol.cc | 6 +-- 13 files changed, 77 insertions(+), 84 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 39e255300cf..66267e4af18 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -26,15 +26,15 @@ Puts the given string to the buffer. SYNOPSYS - put_to_buffer() - start_pos start position in the buffer + append() + position start position in the buffer string string to be put in the buffer len_arg the length of the string. This way we can avoid some strlens. DESCRIPTION - The method puts a string into the buffer, starting from start_pos . + The method puts a string into the buffer, starting from position . In the case when the buffer is too small it reallocs the buffer. The total size of the buffer is restricted with 16. @@ -43,12 +43,12 @@ 1 - The buffer came to 16Mb barrier */ -int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) +int Buffer::append(char *position, const char *string, uint len_arg) { - if (check_and_add(start_pos - buffer, len_arg)) + if (reserve(position - buffer, len_arg)) return 1; - strnmov(start_pos, string, len_arg); + strnmov(position, string, len_arg); return 0; } @@ -58,7 +58,7 @@ int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) "len_arg" starting from "position" and reallocs it if no. SYNOPSYS - check_and_add() + reserve() position the number starting byte on the buffer to store a buffer len_arg the length of the string. @@ -74,7 +74,7 @@ int Buffer::put_to_buffer(char *start_pos, const char *string, uint len_arg) 1 - The buffer came to 16Mb barrier */ -int Buffer::check_and_add(uint position, uint len_arg) +int Buffer::reserve(uint position, uint len_arg) { if (position + len_arg >= MAX_BUFFER_SIZE) return 1; @@ -83,9 +83,9 @@ int Buffer::check_and_add(uint position, uint len_arg) { buffer= (char *) realloc(buffer, min(MAX_BUFFER_SIZE, - max((uint) buffer_size*1.5, + max((uint) (buffer_size*1.5), position + len_arg))); - buffer_size= (uint) buffer_size*1.5; + buffer_size= (uint) (buffer_size*1.5); } return 0; } diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 8781abebd71..dbf72e34bf0 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -50,8 +50,8 @@ public: public: char *buffer; - int put_to_buffer(char *start_pos, const char *string, uint len_arg); - int check_and_add(uint position, uint len_arg); + int append(char *start_pos, const char *string, uint len_arg); + int reserve(uint position, uint len_arg); }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_BUFFER_H */ diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc index 71415a038f0..87260f9e17b 100644 --- a/server-tools/instance-manager/command.cc +++ b/server-tools/instance-manager/command.cc @@ -20,16 +20,6 @@ #include "command.h" -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "protocol.h" -#include "instance_map.h" Command::Command(Command_factory *factory_arg) :factory(factory_arg) diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h index 25b418ca8fe..9b981ecd163 100644 --- a/server-tools/instance-manager/command.h +++ b/server-tools/instance-manager/command.h @@ -22,7 +22,7 @@ #include -/* Class responsible for allocation and deallocation of im classes. */ +/* Class responsible for allocation of im commands. */ class Command_factory; @@ -44,6 +44,4 @@ protected: Command_factory *factory; }; -#define CONST_STR(a) String(a,sizeof(a),&my_charset_latin1) - #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index e990f04216d..30d8f0794a0 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -54,11 +54,12 @@ Show_instance_status::Show_instance_status(Command_factory *factory, Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= factory->instance_map.find(name, len)) { instance_name= instance->options.instance_name; } - else instance_name= NULL; + else + instance_name= NULL; } @@ -90,7 +91,8 @@ Show_instance_options::Show_instance_options(Command_factory *factory, { instance_name= instance->options.instance_name; } - else instance_name= NULL; + else + instance_name= NULL; } @@ -116,7 +118,7 @@ Start_instance::Start_instance(Command_factory *factory, :Command(factory) { /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= factory->instance_map.find(name, len)) instance_name= instance->options.instance_name; } @@ -150,7 +152,7 @@ Stop_instance::Stop_instance(Command_factory *factory, :Command(factory) { /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= factory->instance_map.find(name, len)) instance_name= instance->options.instance_name; } diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index bab61f04b17..09df4fc9260 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -120,9 +120,6 @@ public: class Syntax_error : public Command { public: - Syntax_error() - {} - int execute(struct st_net *net, ulong connection_id); }; diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index ebc59d67906..f13b98cbf20 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -70,16 +70,13 @@ Guardian_thread::~Guardian_thread() Check for all guarded instances and restart them if needed. If everything is fine go and sleep for some time. - - RETURN - The function return no value */ void Guardian_thread::run() { Instance *instance; LIST *loop; - int i=0; + int i= 0; my_thread_init(); @@ -90,11 +87,8 @@ void Guardian_thread::run() while (loop != NULL) { instance= (Instance *) loop->data; - if (instance != NULL) - { - if (!instance->is_running()) - instance->start(); - } + /* instance-> start already checks whether instance is running */ + instance->start(); loop= loop->next; } pthread_mutex_unlock(&LOCK_guardian); @@ -124,17 +118,18 @@ void Guardian_thread::run() int Guardian_thread::guard(const char *instance_name, uint name_len) { - LIST *lst; + LIST *node; Instance *instance; - lst= (LIST *) alloc_root(&alloc, sizeof(LIST)); - if (lst == NULL) return 1; + node= (LIST *) alloc_root(&alloc, sizeof(LIST)); + if (node == NULL) + return 1; instance= instance_map->find(instance_name, name_len); /* we store the pointers to instances from the instance_map's MEM_ROOT */ - lst->data= (void *) instance; + node->data= (void *) instance; pthread_mutex_lock(&LOCK_guardian); - guarded_instances= list_add(guarded_instances, lst); + guarded_instances= list_add(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); return 0; @@ -150,28 +145,28 @@ int Guardian_thread::guard(const char *instance_name, uint name_len) int Guardian_thread::stop_guard(const char *instance_name, uint name_len) { - LIST *lst; + LIST *node; Instance *instance; instance= instance_map->find(instance_name, name_len); - lst= guarded_instances; - if (lst == NULL) return 1; - pthread_mutex_lock(&LOCK_guardian); - while (lst != NULL) + node= guarded_instances; + + while (node != NULL) { /* We compare only pointers, as we always use pointers from the instance_map's MEM_ROOT. */ - if ((Instance *) lst->data == instance) + if ((Instance *) node->data == instance) { - guarded_instances= list_delete(guarded_instances, lst); + guarded_instances= list_delete(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); return 0; } - else lst= lst->next; + else + node= node->next; } pthread_mutex_unlock(&LOCK_guardian); /* if there is nothing to delete it is also fine */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 32e77429572..689cea2d786 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -49,8 +49,8 @@ int Instance::start() exit(0); /* parent goes bye-bye */ else { - execv(options.mysqld_path, options.argv); - exit(1); + execv(options.mysqld_path, options.argv); + exit(1); } case -1: return ER_CANNOT_START_INSTANCE; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 7586566de9d..8311e4f7bc0 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -172,7 +172,7 @@ err: int Instance_options::add_to_argv(const char* option) { - DBUG_ASSERT(filled_default_options < (MAX_NUMBER_OF_DEFAULT_OPTIONS + 1)); + DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS); if (option != NULL) argv[filled_default_options++]= (char *) option; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 5034e775cd0..5bc46497d2a 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -36,9 +36,10 @@ class Instance_options { public: - Instance_options() : mysqld_socket(0), mysqld_datadir(0), - mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), - mysqld_user(0), mysqld_password(0), is_guarded(0), filled_default_options(0) + Instance_options() : + mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), + mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), mysqld_user(0), + mysqld_password(0), is_guarded(0), filled_default_options(0) {} ~Instance_options(); /* fills in argv */ @@ -50,7 +51,7 @@ public: int init(const char *instance_name_arg); public: - enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 3 }; + enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 1 }; enum { MEM_ROOT_BLOCK_SIZE= 512 }; char **argv; /* We need the some options, so we store them as a separate pointers */ diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 749a31ea525..ddd03726917 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -78,9 +78,9 @@ Listener_thread::~Listener_thread() void Listener_thread::run() { - enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size + enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size int flags; - int arg= 1; /* value to be set by setsockopt */ + int arg= 1; /* value to be set by setsockopt */ /* I. prepare 'listen' sockets */ int ip_socket= socket(AF_INET, SOCK_STREAM, 0); @@ -93,7 +93,7 @@ void Listener_thread::run() } struct sockaddr_in ip_socket_address; - memset(&ip_socket_address, 0, sizeof(ip_socket_address)); + bzero(&ip_socket_address, sizeof(ip_socket_address)); ulong im_bind_addr; if (options.bind_address != 0) @@ -101,7 +101,8 @@ void Listener_thread::run() if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE) im_bind_addr= htonl(INADDR_ANY); } - else im_bind_addr= htonl(INADDR_ANY); + else + im_bind_addr= htonl(INADDR_ANY); uint im_port= options.port_number; ip_socket_address.sin_family= AF_INET; @@ -144,7 +145,7 @@ void Listener_thread::run() } struct sockaddr_un unix_socket_address; - memset(&unix_socket_address, 0, sizeof(unix_socket_address)); + bzero(&unix_socket_address, sizeof(unix_socket_address)); unix_socket_address.sun_family= AF_UNIX; strmake(unix_socket_address.sun_path, options.socket_file_name, diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index cb51197d52a..11a3289adbf 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -30,6 +30,23 @@ #include "log.h" #include "guardian.h" +static int create_pid_file(const char *pid_file_name) +{ + if (FILE *pid_file= my_fopen(pid_file_name, + O_WRONLY | O_CREAT | O_BINARY, MYF(0))) + { + fprintf(pid_file, "%d\n", (int) getpid()); + my_fclose(pid_file, MYF(0)); + } + else + { + log_error("can't create pid file %s: errno=%d, %s", + pid_file_name, errno, strerror(errno)); + return 1; + } + return 0; +} + /* manager - entry point to the main instance manager process: start @@ -53,32 +70,24 @@ void manager(const Options &options) &instance_map, options.monitoring_interval); + Listener_thread_args listener_args(thread_registry, options, user_map, + instance_map); + instance_map.mysqld_path= options.default_mysqld_path; instance_map.user= options.default_admin_user; instance_map.password= options.default_admin_password; instance_map.guardian= &guardian_thread; - instance_map.load(); - Listener_thread_args listener_args(thread_registry, options, user_map, - instance_map); + if (instance_map.load()) + return; if (user_map.load(options.password_file_name)) return; /* write pid file */ - if (FILE *pid_file= my_fopen(options.pid_file_name, - O_WRONLY | O_CREAT | O_BINARY, MYF(0))) - { - fprintf(pid_file, "%d\n", (int) getpid()); - my_fclose(pid_file, MYF(0)); - } - else - { - log_error("can't create pid file %s: errno=%d, %s", - options.pid_file_name, errno, strerror(errno)); + if (create_pid_file(options.pid_file_name)) return; - } /* block signals */ sigset_t mask; diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index 6bb6c6d2fa0..f32d8558fbf 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -105,9 +105,9 @@ void store_to_string(Buffer *buf, const char *string, uint *position) uint string_len; string_len= strlen(string); - buf->check_and_add(*position, 2); + buf->reserve(*position, 2); currpos= net_store_length(buf->buffer + *position, string_len); - buf->put_to_buffer(currpos, string, string_len); + buf->append(currpos, string, string_len); *position= *position + string_len + (currpos - buf->buffer - *position); } @@ -147,7 +147,7 @@ int send_fields(struct st_net *net, LIST *fields) store_to_string(&send_buff, (char *) "", &position); /* table name alias */ store_to_string(&send_buff, field->name, &position); /* column name */ store_to_string(&send_buff, field->name, &position); /* column name alias */ - send_buff.check_and_add(position, 12); + send_buff.reserve(position, 12); send_buff.buffer[position++]= 12; int2store(send_buff.buffer + position, 1); /* charsetnr */ int4store(send_buff.buffer + position + 2, field->length); /* field length */ From 234ca309b9e1e49b6425fbf0dfd662e7d0f7b383 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 26 Oct 2004 23:22:12 +0400 Subject: [PATCH 03/52] Various post-review fixes server-tools/instance-manager/buffer.cc: simplified buffer interface server-tools/instance-manager/buffer.h: simplified buffer interface server-tools/instance-manager/command.cc: Command class now uses instance_map directly server-tools/instance-manager/command.h: Made Command to use instance_map directly (not through the factory, which is not needed here in fact) server-tools/instance-manager/commands.cc: Moved mysql client/server protocol-specific functions to the commands server-tools/instance-manager/commands.h: Added a comment for Syntax_error command, fixed classes to use instance map instead of the factory server-tools/instance-manager/factory.cc: Fixed factory to give appropriate class to the commands server-tools/instance-manager/guardian.cc: Fixed guardian to delay start of new instances monitoring. Moved guardian initialization to the class from Instance map. server-tools/instance-manager/guardian.h: interface fixed server-tools/instance-manager/instance.cc: added some loging server-tools/instance-manager/instance_map.cc: All non-instance map specific functions moved from the class. Added iterator for instance_map server-tools/instance-manager/instance_map.h: All non-instance map related functions moved from the class. Added iterator for instance_map. server-tools/instance-manager/listener.cc: Added FD_CLOEXEC flag to sockets, as we don't want instances to inherit them after exec. server-tools/instance-manager/manager.cc: use guardian method moved from the instance map server-tools/instance-manager/mysql_connection.cc: cleanup server-tools/instance-manager/protocol.cc: fix according to the changes in the Buffer class --- server-tools/instance-manager/buffer.cc | 9 +- server-tools/instance-manager/buffer.h | 4 +- server-tools/instance-manager/command.cc | 4 +- server-tools/instance-manager/command.h | 6 +- server-tools/instance-manager/commands.cc | 261 +++++++++++++++-- server-tools/instance-manager/commands.h | 23 +- server-tools/instance-manager/factory.cc | 12 +- server-tools/instance-manager/guardian.cc | 58 +++- server-tools/instance-manager/guardian.h | 10 +- server-tools/instance-manager/instance.cc | 2 + server-tools/instance-manager/instance_map.cc | 273 +++--------------- server-tools/instance-manager/instance_map.h | 25 +- server-tools/instance-manager/listener.cc | 7 + server-tools/instance-manager/manager.cc | 2 +- .../instance-manager/mysql_connection.cc | 4 +- server-tools/instance-manager/protocol.cc | 6 +- 16 files changed, 401 insertions(+), 305 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 66267e4af18..212260bf9e0 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -27,7 +27,7 @@ SYNOPSYS append() - position start position in the buffer + position start position in the buffer string string to be put in the buffer len_arg the length of the string. This way we can avoid some strlens. @@ -43,12 +43,12 @@ 1 - The buffer came to 16Mb barrier */ -int Buffer::append(char *position, const char *string, uint len_arg) +int Buffer::append(uint position, const char *string, uint len_arg) { - if (reserve(position - buffer, len_arg)) + if (reserve(position, len_arg)) return 1; - strnmov(position, string, len_arg); + strnmov(buffer + position, string, len_arg); return 0; } @@ -89,3 +89,4 @@ int Buffer::reserve(uint position, uint len_arg) } return 0; } + diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index dbf72e34bf0..66860bd67b5 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -35,7 +35,7 @@ private: enum { BUFFER_INITIAL_SIZE= 4096 }; /* maximum buffer size is 16Mb */ enum { MAX_BUFFER_SIZE= 16777216 }; - uint buffer_size; + size_t buffer_size; public: Buffer() { @@ -50,7 +50,7 @@ public: public: char *buffer; - int append(char *start_pos, const char *string, uint len_arg); + int append(uint position, const char *string, uint len_arg); int reserve(uint position, uint len_arg); }; diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc index 87260f9e17b..9f7d08d9fda 100644 --- a/server-tools/instance-manager/command.cc +++ b/server-tools/instance-manager/command.cc @@ -21,8 +21,8 @@ #include "command.h" -Command::Command(Command_factory *factory_arg) - :factory(factory_arg) +Command::Command(Instance_map *imap_arg) + :instance_map(imap_arg) {} Command::~Command() diff --git a/server-tools/instance-manager/command.h b/server-tools/instance-manager/command.h index 9b981ecd163..8ae4e33b92f 100644 --- a/server-tools/instance-manager/command.h +++ b/server-tools/instance-manager/command.h @@ -24,7 +24,7 @@ /* Class responsible for allocation of im commands. */ -class Command_factory; +class Instance_map; /* Command - entry point for any command. @@ -34,14 +34,14 @@ class Command_factory; class Command { public: - Command(Command_factory *factory_arg= 0); + Command(Instance_map *instance_map_arg= 0); virtual ~Command(); /* method of executing: */ virtual int execute(struct st_net *net, ulong connection_id) = 0; protected: - Command_factory *factory; + Instance_map *instance_map; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_COMMAND_H */ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 30d8f0794a0..357b9a47d4d 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -15,18 +15,81 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "command.h" -#include "factory.h" #include "commands.h" #include "instance.h" #include "instance_map.h" #include "messages.h" +#include "protocol.h" +#include "buffer.h" +#include /* implementation for Show_instances: */ + +/* + The method sends a list of instances in the instance map to the client. + + SYNOPSYS + Show_instances::do_command() + net The network connection to the client. + + RETURN + 0 - ok + 1 - error occured +*/ + +int Show_instances::do_command(struct st_net *net) +{ + Buffer send_buff; /* buffer for packets */ + LIST name, status; + NAME_WITH_LENGTH name_field, status_field; + LIST *field_list; + uint position=0; + + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + field_list= list_add(NULL, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + Imap_iterator iterator(instance_map); + + instance_map->lock(); + while (instance= iterator.next()) + { + position= 0; + store_to_string(&send_buff, instance->options.instance_name, &position); + if (instance->is_running()) + store_to_string(&send_buff, (char *) "online", &position); + else + store_to_string(&send_buff, (char *) "offline", &position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; + } + instance_map->unlock(); + } + if (send_eof(net)) + goto err; + if (net_flush(net)) + goto err; + + return 0; +err: + return 1; +} + + int Show_instances::execute(struct st_net *net, ulong connection_id) { - if (factory->instance_map.show_instances(net)) + if (do_command(net)) return ER_OUT_OF_RESOURCES; return 0; @@ -37,7 +100,7 @@ int Show_instances::execute(struct st_net *net, ulong connection_id) int Flush_instances::execute(struct st_net *net, ulong connection_id) { - if (factory->instance_map.flush_instances()) + if (instance_map->flush_instances()) return ER_OUT_OF_RESOURCES; net_send_ok(net, connection_id); @@ -47,14 +110,14 @@ int Flush_instances::execute(struct st_net *net, ulong connection_id) /* implementation for Show_instance_status: */ -Show_instance_status::Show_instance_status(Command_factory *factory, +Show_instance_status::Show_instance_status(Instance_map *imap_arg, const char *name, uint len) - :Command(factory) + :Command(imap_arg) { Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= factory->instance_map.find(name, len)) + if (instance= instance_map->find(name, len)) { instance_name= instance->options.instance_name; } @@ -63,11 +126,80 @@ Show_instance_status::Show_instance_status(Command_factory *factory, } +/* + The method sends a table with a status of requested instance to the client. + + SYNOPSYS + Show_instance_status::do_command() + net The network connection to the client. + instance_name The name of the instance. + + RETURN + 0 - ok + 1 - error occured +*/ + + +int Show_instance_status::do_command(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, status, version; + LIST *field_list; + NAME_WITH_LENGTH name_field, status_field, version_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "instance_name"; + name_field.length= 20; + name.data= &name_field; + status_field.name= (char *) "status"; + status_field.length= 20; + status.data= &status_field; + version_field.name= (char *) "version"; + version_field.length= MAX_VERSION_LENGTH; + version.data= &version_field; + field_list= list_add(NULL, &version); + field_list= list_add(field_list, &status); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + store_to_string(&send_buff, (char *) instance_name, &position); + if ((instance= instance_map->find(instance_name, strlen(instance_name))) == NULL) + goto err; + if (instance->is_running()) + { + store_to_string(&send_buff, (char *) "online", &position); + store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position); + } + else + { + store_to_string(&send_buff, (char *) "offline", &position); + store_to_string(&send_buff, (char *) "unknown", &position); + } + + + my_net_write(net, send_buff.buffer, (uint) position); + } + + send_eof(net); + net_flush(net); + +err: + return 0; +} + + int Show_instance_status::execute(struct st_net *net, ulong connection_id) { if (instance_name != NULL) { - if (factory->instance_map.show_instance_status(net, instance_name)) + if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; return 0; } @@ -80,14 +212,14 @@ int Show_instance_status::execute(struct st_net *net, ulong connection_id) /* Implementation for Show_instance_options */ -Show_instance_options::Show_instance_options(Command_factory *factory, +Show_instance_options::Show_instance_options(Instance_map *imap_arg, const char *name, uint len): - Command(factory) + Command(imap_arg) { Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= (factory->instance_map).find(name, len)) + if (instance= instance_map->find(name, len)) { instance_name= instance->options.instance_name; } @@ -96,11 +228,99 @@ Show_instance_options::Show_instance_options(Command_factory *factory, } +int Show_instance_options::do_command(struct st_net *net, + const char *instance_name) +{ + enum { MAX_VERSION_LENGTH= 40 }; + Buffer send_buff; /* buffer for packets */ + LIST name, option; + LIST *field_list; + NAME_WITH_LENGTH name_field, option_field; + uint position=0; + + /* create list of the fileds to be passed to send_fields */ + name_field.name= (char *) "option_name"; + name_field.length= 20; + name.data= &name_field; + option_field.name= (char *) "value"; + option_field.length= 20; + option.data= &option_field; + field_list= list_add(NULL, &option); + field_list= list_add(field_list, &name); + + send_fields(net, field_list); + + { + Instance *instance; + + if ((instance= instance_map-> + find(instance_name, strlen(instance_name))) == NULL) + goto err; + store_to_string(&send_buff, (char *) "instance_name", &position); + store_to_string(&send_buff, (char *) instance_name, &position); + my_net_write(net, send_buff.buffer, (uint) position); + if (instance->options.mysqld_path != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "mysqld_path", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_path, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_user != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_user", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_user, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + if (instance->options.mysqld_password != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "admin_password", &position); + store_to_string(&send_buff, + (char *) instance->options.mysqld_password, + &position); + my_net_write(net, send_buff.buffer, (uint) position); + } + + /* loop through the options stored in DYNAMIC_ARRAY */ + for (int i= 0; i < instance->options.options_array.elements; i++) + { + char *tmp_option, *option_value; + get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); + option_value= strchr(tmp_option, '='); + /* split the option string into two parts */ + *option_value= 0; + position= 0; + store_to_string(&send_buff, tmp_option + 2, &position); + store_to_string(&send_buff, option_value + 1, &position); + /* join name and the value into the same option again */ + *option_value= '='; + my_net_write(net, send_buff.buffer, (uint) position); + } + } + + send_eof(net); + net_flush(net); + + return 0; + +err: + return 1; +} + + int Show_instance_options::execute(struct st_net *net, ulong connection_id) { if (instance_name != NULL) { - if (factory->instance_map.show_instance_options(net, instance_name)) + if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; return 0; } @@ -113,12 +333,12 @@ int Show_instance_options::execute(struct st_net *net, ulong connection_id) /* Implementation for Start_instance */ -Start_instance::Start_instance(Command_factory *factory, +Start_instance::Start_instance(Instance_map *imap_arg, const char *name, uint len) - :Command(factory) + :Command(imap_arg) { /* we make a search here, since we don't want t store the name */ - if (instance= factory->instance_map.find(name, len)) + if (instance= instance_map->find(name, len)) instance_name= instance->options.instance_name; } @@ -136,8 +356,7 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) return err_code; if (instance->options.is_guarded != NULL) - factory->instance_map.guardian->guard(instance->options.instance_name, - instance->options.instance_name_len); + instance_map->guardian->guard(instance); net_send_ok(net, connection_id); return 0; @@ -147,12 +366,12 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) /* Implementation for Stop_instance: */ -Stop_instance::Stop_instance(Command_factory *factory, +Stop_instance::Stop_instance(Instance_map *imap_arg, const char *name, uint len) - :Command(factory) + :Command(imap_arg) { /* we make a search here, since we don't want t store the name */ - if (instance= factory->instance_map.find(name, len)) + if (instance= instance_map->find(name, len)) instance_name= instance->options.instance_name; } @@ -168,8 +387,8 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) else { if (instance->options.is_guarded != NULL) - factory->instance_map.guardian-> - stop_guard(instance_name, instance->options.instance_name_len); + instance_map->guardian-> + stop_guard(instance); if (err_code= instance->stop()) return err_code; diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index 09df4fc9260..8b53b21bbac 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -27,9 +27,10 @@ class Show_instances : public Command { public: - Show_instances(Command_factory *factory): Command(factory) + Show_instances(Instance_map *imap_arg): Command(imap_arg) {} + int do_command(struct st_net *net); int execute(struct st_net *net, ulong connection_id); }; @@ -42,7 +43,7 @@ public: class Flush_instances : public Command { public: - Flush_instances(Command_factory *factory): Command(factory) + Flush_instances(Instance_map *imap_arg): Command(imap_arg) {} int execute(struct st_net *net, ulong connection_id); @@ -58,8 +59,8 @@ class Show_instance_status : public Command { public: - Show_instance_status(Command_factory *factory, const char *name, uint len); - + Show_instance_status(Instance_map *imap_arg, const char *name, uint len); + int do_command(struct st_net *net, const char *instance_name); int execute(struct st_net *net, ulong connection_id); const char *instance_name; }; @@ -74,9 +75,10 @@ class Show_instance_options : public Command { public: - Show_instance_options(Command_factory *factory, const char *name, uint len); + Show_instance_options(Instance_map *imap_arg, const char *name, uint len); int execute(struct st_net *net, ulong connection_id); + int do_command(struct st_net *net, const char *instance_name); const char *instance_name; }; @@ -89,11 +91,11 @@ public: class Start_instance : public Command { public: - Start_instance(Command_factory *factory, const char *name, uint len); + Start_instance(Instance_map *imap_arg, const char *name, uint len); - Instance *instance; int execute(struct st_net *net, ulong connection_id); const char *instance_name; + Instance *instance; }; @@ -105,7 +107,7 @@ public: class Stop_instance : public Command { public: - Stop_instance(Command_factory *factory, const char *name, uint len); + Stop_instance(Instance_map *imap_arg, const char *name, uint len); Instance *instance; int execute(struct st_net *net, ulong connection_id); @@ -114,7 +116,10 @@ public: /* - Syntax error command. + Syntax error command. This command is issued if parser reported a syntax error. + We need it to distinguish the parse error and the situation when parser internal + error occured. E.g. parsing failed because we hadn't had enought memory. In the + latter case parse_command() should return an error. */ class Syntax_error : public Command diff --git a/server-tools/instance-manager/factory.cc b/server-tools/instance-manager/factory.cc index 691aca0c7ea..cde5d0564aa 100644 --- a/server-tools/instance-manager/factory.cc +++ b/server-tools/instance-manager/factory.cc @@ -23,35 +23,35 @@ Show_instances *Command_factory::new_Show_instances() { - return new Show_instances(this); + return new Show_instances(&instance_map); } Flush_instances *Command_factory::new_Flush_instances() { - return new Flush_instances(this); + return new Flush_instances(&instance_map); } Show_instance_status *Command_factory:: new_Show_instance_status(const char *name, uint len) { - return new Show_instance_status(this, name, len); + return new Show_instance_status(&instance_map, name, len); } Show_instance_options *Command_factory:: new_Show_instance_options(const char *name, uint len) { - return new Show_instance_options(this, name, len); + return new Show_instance_options(&instance_map, name, len); } Start_instance *Command_factory:: new_Start_instance(const char *name, uint len) { - return new Start_instance(this, name, len); + return new Start_instance(&instance_map, name, len); } Stop_instance *Command_factory::new_Stop_instance(const char *name, uint len) { - return new Stop_instance(this, name, len); + return new Stop_instance(&instance_map, name, len); } Syntax_error *Command_factory::new_Syntax_error() diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index f13b98cbf20..5a4d0bade74 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -21,6 +21,8 @@ #include "guardian.h" #include "instance_map.h" +#include "mysql_manager_error.h" +#include "log.h" #include C_MODE_START @@ -46,6 +48,7 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, thread_registry.register_thread(&thread_info); init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); guarded_instances= NULL; + starting_instances= NULL; } @@ -76,7 +79,6 @@ void Guardian_thread::run() { Instance *instance; LIST *loop; - int i= 0; my_thread_init(); @@ -88,9 +90,12 @@ void Guardian_thread::run() { instance= (Instance *) loop->data; /* instance-> start already checks whether instance is running */ - instance->start(); + if (instance->start() != ER_INSTANCE_ALREADY_STARTED) + log_info("guardian attempted to restart instance %s", + instance->options.instance_name); loop= loop->next; } + move_to_list(&starting_instances, &guarded_instances); pthread_mutex_unlock(&LOCK_guardian); sleep(monitoring_interval); } @@ -99,6 +104,24 @@ void Guardian_thread::run() } +int Guardian_thread::start() +{ + Instance *instance; + Imap_iterator iterator(instance_map); + + instance_map->lock(); + while (instance= iterator.next()) + { + if ((instance->options.is_guarded != NULL) && (instance->is_running())) + if (guard(instance)) + return 1; + } + instance_map->unlock(); + + return 0; +} + + /* Start instance guarding @@ -116,20 +139,38 @@ void Guardian_thread::run() 1 - error occured */ -int Guardian_thread::guard(const char *instance_name, uint name_len) + +int Guardian_thread::guard(Instance *instance) +{ + return add_instance_to_list(instance, &starting_instances); +} + + +void Guardian_thread::move_to_list(LIST **from, LIST **to) +{ + LIST *tmp; + + while (*from) + { + tmp= rest(*from); + *to= list_add(*to, *from); + *from= tmp; + } +} + + +int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) { LIST *node; - Instance *instance; node= (LIST *) alloc_root(&alloc, sizeof(LIST)); if (node == NULL) return 1; - instance= instance_map->find(instance_name, name_len); /* we store the pointers to instances from the instance_map's MEM_ROOT */ node->data= (void *) instance; pthread_mutex_lock(&LOCK_guardian); - guarded_instances= list_add(guarded_instances, node); + *list= list_add(*list, node); pthread_mutex_unlock(&LOCK_guardian); return 0; @@ -143,12 +184,9 @@ int Guardian_thread::guard(const char *instance_name, uint name_len) a piece of the MEM_ROOT). */ -int Guardian_thread::stop_guard(const char *instance_name, uint name_len) +int Guardian_thread::stop_guard(Instance *instance) { LIST *node; - Instance *instance; - - instance= instance_map->find(instance_name, name_len); pthread_mutex_lock(&LOCK_guardian); node= guarded_instances; diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 5d0cdcd7c92..0ae2161f1dc 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -66,13 +66,19 @@ public: ~Guardian_thread(); void run(); int init(); - int guard(const char *instance_name, uint name_len); - int stop_guard(const char *instance_name, uint name_len); + int start(); + int guard(Instance *instance); + int stop_guard(Instance *instance); + +private: + int add_instance_to_list(Instance *instance, LIST **list); + void move_to_list(LIST **from, LIST **to); private: pthread_mutex_t LOCK_guardian; Thread_info thread_info; LIST *guarded_instances; + LIST *starting_instances; MEM_ROOT alloc; enum { MEM_ROOT_BLOCK_SIZE= 512 }; }; diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 689cea2d786..1b9ce09d782 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -20,6 +20,7 @@ #include "instance.h" #include "mysql_manager_error.h" +#include "log.h" #include #include #include @@ -43,6 +44,7 @@ int Instance::start() if (!is_running()) { + log_info("trying to start instance %s", options.instance_name); switch (fork()) { case 0: if (fork()) /* zombie protection */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 89731eb27b9..aa81194b1d4 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -127,6 +127,18 @@ Instance_map::~Instance_map() } +int Instance_map::lock() +{ + pthread_mutex_lock(&LOCK_instance_map); +} + + +int Instance_map::unlock() +{ + pthread_mutex_unlock(&LOCK_instance_map); +} + + int Instance_map::flush_instances() { int rc; @@ -141,242 +153,6 @@ int Instance_map::flush_instances() } -int Instance_map::show_instance_options(struct st_net *net, - const char *instance_name) -{ - enum { MAX_VERSION_LENGTH= 40 }; - Buffer send_buff; /* buffer for packets */ - LIST name, option; - LIST *field_list; - NAME_WITH_LENGTH name_field, option_field; - uint position=0; - - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char *) "option_name"; - name_field.length= 20; - name.data= &name_field; - option_field.name= (char *) "value"; - option_field.length= 20; - option.data= &option_field; - field_list= list_add(NULL, &option); - field_list= list_add(field_list, &name); - - send_fields(net, field_list); - - { - Instance *instance; - - if ((instance= find(instance_name, strlen(instance_name))) == NULL) - goto err; - store_to_string(&send_buff, (char *) "instance_name", &position); - store_to_string(&send_buff, (char *) instance_name, &position); - my_net_write(net, send_buff.buffer, (uint) position); - if (instance->options.mysqld_path != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "mysqld_path", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_path, - &position); - my_net_write(net, send_buff.buffer, (uint) position); - } - - if (instance->options.mysqld_user != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "admin_user", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_user, - &position); - my_net_write(net, send_buff.buffer, (uint) position); - } - - if (instance->options.mysqld_password != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "admin_password", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_password, - &position); - my_net_write(net, send_buff.buffer, (uint) position); - } - - /* loop through the options stored in DYNAMIC_ARRAY */ - for (int i= 0; i < instance->options.options_array.elements; i++) - { - char *tmp_option, *option_value; - get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); - option_value= strchr(tmp_option, '='); - /* split the option string into two parts */ - *option_value= 0; - position= 0; - store_to_string(&send_buff, tmp_option + 2, &position); - store_to_string(&send_buff, option_value + 1, &position); - /* join name and the value into the same option again */ - *option_value= '='; - my_net_write(net, send_buff.buffer, (uint) position); - } - } - - send_eof(net); - net_flush(net); - - return 0; - -err: - return 1; -} - -/* return the list of running guarded instances */ -int Instance_map::init_guardian() -{ - Instance *instance; - uint i= 0; - - while (i < hash.records) - { - instance= (Instance *) hash_element(&hash, i); - if ((instance->options.is_guarded != NULL) && (instance->is_running())) - if (guardian->guard(instance->options.instance_name, - instance->options.instance_name_len)) - return 1; - i++; - } - - return 0; -} - - -/* - The method sends a list of instances in the instance map to the client. - - SYNOPSYS - show_instances() - net The network connection to the client. - - RETURN - 0 - ok - 1 - error occured -*/ - -int Instance_map::show_instances(struct st_net *net) -{ - Buffer send_buff; /* buffer for packets */ - LIST name, status; - NAME_WITH_LENGTH name_field, status_field; - LIST *field_list; - uint position=0; - - name_field.name= (char *) "instance_name"; - name_field.length= 20; - name.data= &name_field; - status_field.name= (char *) "status"; - status_field.length= 20; - status.data= &status_field; - field_list= list_add(NULL, &status); - field_list= list_add(field_list, &name); - - send_fields(net, field_list); - - { - Instance *instance; - uint i= 0; - - pthread_mutex_lock(&LOCK_instance_map); - while (i < hash.records) - { - position= 0; - instance= (Instance *) hash_element(&hash, i); - store_to_string(&send_buff, instance->options.instance_name, &position); - if (instance->is_running()) - store_to_string(&send_buff, (char *) "online", &position); - else - store_to_string(&send_buff, (char *) "offline", &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - i++; - } - pthread_mutex_unlock(&LOCK_instance_map); - } - if (send_eof(net)) - goto err; - if (net_flush(net)) - goto err; - - return 0; -err: - return 1; -} - - -/* - The method sends a table with a status of requested instance to the client. - - SYNOPSYS - show_instance_status() - net The network connection to the client. - instance_name The name of the instance. - - RETURN - 0 - ok - 1 - error occured -*/ - -int Instance_map::show_instance_status(struct st_net *net, - const char *instance_name) -{ - enum { MAX_VERSION_LENGTH= 40 }; - Buffer send_buff; /* buffer for packets */ - LIST name, status, version; - LIST *field_list; - NAME_WITH_LENGTH name_field, status_field, version_field; - uint position=0; - - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char *) "instance_name"; - name_field.length= 20; - name.data= &name_field; - status_field.name= (char *) "status"; - status_field.length= 20; - status.data= &status_field; - version_field.name= (char *) "version"; - version_field.length= MAX_VERSION_LENGTH; - version.data= &version_field; - field_list= list_add(NULL, &version); - field_list= list_add(field_list, &status); - field_list= list_add(field_list, &name); - - send_fields(net, field_list); - - { - Instance *instance; - - store_to_string(&send_buff, (char *) instance_name, &position); - if ((instance= find(instance_name, strlen(instance_name))) == NULL) - goto err; - if (instance->is_running()) - { - store_to_string(&send_buff, (char *) "online", &position); - store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position); - } - else - { - store_to_string(&send_buff, (char *) "offline", &position); - store_to_string(&send_buff, (char *) "unknown", &position); - } - - - my_net_write(net, send_buff.buffer, (uint) position); - } - - send_eof(net); - net_flush(net); - -err: - return 0; -} - - int Instance_map::add_instance(Instance *instance) { return my_hash_insert(&hash, (byte *) instance); @@ -448,3 +224,28 @@ int Instance_map::load() return error; } + + +Instance *Instance_map::get_instance(uint instance_number) +{ + if (instance_number < hash.records) + return (Instance *) hash_element(&hash, instance_number); + else + return NULL; +} + + +/*--- Implementaton of the Instance map iterator class (Imap_iterator) ---*/ + + +void Imap_iterator::go_to_first() +{ + current_instance=0; +} + + +Instance *Imap_iterator::next() +{ + return instance_map->get_instance(current_instance++); +} + diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 965261f2920..193376c6f23 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -43,12 +43,11 @@ public: Instance *find(const char *name, uint name_len); Instance *find(uint instance_number); - int show_instances(struct st_net *net); - int show_instance_status(struct st_net *net, const char *instance_name); - int show_instance_options(struct st_net *net, const char *instance_name); int flush_instances(); - int init_guardian(); int cleanup(); + int lock(); + int unlock(); + Instance *get_instance(uint instance_number); Instance_map(); ~Instance_map(); @@ -73,4 +72,22 @@ private: HASH hash; }; + +/* Instance_map iterator */ + +class Imap_iterator +{ +private: + uint current_instance; + Instance_map *instance_map; +public: + Imap_iterator(Instance_map *instance_map_arg) : + instance_map(instance_map_arg), current_instance(0) + {} + + void go_to_first(); + Instance *next(); +}; + + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index ddd03726917..4a13dc39ec7 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -129,8 +129,12 @@ void Listener_thread::run() thread_registry.request_shutdown(); return; } + /* set the socket nonblocking */ flags= fcntl(ip_socket, F_GETFL, 0); fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK); + /* make shure that instances won't be listening our sockets */ + flags= fcntl(ip_socket, F_GETFD, 0); + fcntl(ip_socket, F_SETFD, flags | FD_CLOEXEC); log_info("accepting connections on ip socket"); @@ -180,6 +184,9 @@ void Listener_thread::run() /* set the socket nonblocking */ flags= fcntl(unix_socket, F_GETFL, 0); fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK); + /* make shure that instances won't be listening our sockets */ + flags= fcntl(unix_socket, F_GETFD, 0); + fcntl(unix_socket, F_SETFD, flags | FD_CLOEXEC); } log_info("accepting connections on unix socket %s", unix_socket_address.sun_path); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 11a3289adbf..1c23aa602d4 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -161,7 +161,7 @@ void manager(const Options &options) alarm structures initialization as we have to use net_* functions while making the list. And they in their turn need alarms for timeout suppport. */ - instance_map.init_guardian(); + guardian_thread.start(); while (!shutdown_complete) { diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index 2a617675c26..0ebcb0eea8d 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -319,10 +319,10 @@ int Mysql_connection_thread::dispatch_command(enum enum_server_command command, { switch (command) { case COM_QUIT: // client exit - log_info("query for connection %d received quit command",connection_id); + log_info("query for connection %d received quit command", connection_id); return 1; case COM_PING: - log_info("query for connection %d received ping command",connection_id); + log_info("query for connection %d received ping command", connection_id); net_send_ok(&net, connection_id); break; case COM_QUERY: diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index f32d8558fbf..2f1f95a5f05 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -101,14 +101,14 @@ char *net_store_length(char *pkg, uint length) void store_to_string(Buffer *buf, const char *string, uint *position) { - char* currpos; + uint currpos; uint string_len; string_len= strlen(string); buf->reserve(*position, 2); - currpos= net_store_length(buf->buffer + *position, string_len); + currpos= (net_store_length(buf->buffer + *position, string_len) - buf->buffer); buf->append(currpos, string, string_len); - *position= *position + string_len + (currpos - buf->buffer - *position); + *position= *position + string_len + (currpos - *position); } From d97e8686f1aa3c880eb5baa9319b1e821d6d2bab Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Oct 2004 10:21:48 +0400 Subject: [PATCH 04/52] more review fixes server-tools/instance-manager/command.cc: member rename server-tools/instance-manager/commands.cc: rename server-tools/instance-manager/commands.h: member rename server-tools/instance-manager/guardian.cc: guardian fixed to use Instance_map::Iterator intead of Imap server-tools/instance-manager/instance_map.cc: removed get_instance method from Instance_map as it is needed only by Iterator which is made a friend class server-tools/instance-manager/instance_map.h: use Instance_map::Iterator instead if Imap_iterator (no need to add more names to the global namespace) --- server-tools/instance-manager/command.cc | 4 +-- server-tools/instance-manager/commands.cc | 18 +++++----- server-tools/instance-manager/commands.h | 12 +++---- server-tools/instance-manager/guardian.cc | 2 +- server-tools/instance-manager/instance_map.cc | 20 ++++------- server-tools/instance-manager/instance_map.h | 35 +++++++++---------- 6 files changed, 41 insertions(+), 50 deletions(-) diff --git a/server-tools/instance-manager/command.cc b/server-tools/instance-manager/command.cc index 9f7d08d9fda..818c4b0cae2 100644 --- a/server-tools/instance-manager/command.cc +++ b/server-tools/instance-manager/command.cc @@ -21,8 +21,8 @@ #include "command.h" -Command::Command(Instance_map *imap_arg) - :instance_map(imap_arg) +Command::Command(Instance_map *instance_map_arg) + :instance_map(instance_map_arg) {} Command::~Command() diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 357b9a47d4d..102a3df00e5 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -60,7 +60,7 @@ int Show_instances::do_command(struct st_net *net) { Instance *instance; - Imap_iterator iterator(instance_map); + Instance_map::Iterator iterator(instance_map); instance_map->lock(); while (instance= iterator.next()) @@ -110,9 +110,9 @@ int Flush_instances::execute(struct st_net *net, ulong connection_id) /* implementation for Show_instance_status: */ -Show_instance_status::Show_instance_status(Instance_map *imap_arg, +Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, const char *name, uint len) - :Command(imap_arg) + :Command(instance_map_arg) { Instance *instance; @@ -212,9 +212,9 @@ int Show_instance_status::execute(struct st_net *net, ulong connection_id) /* Implementation for Show_instance_options */ -Show_instance_options::Show_instance_options(Instance_map *imap_arg, +Show_instance_options::Show_instance_options(Instance_map *instance_map_arg, const char *name, uint len): - Command(imap_arg) + Command(instance_map_arg) { Instance *instance; @@ -333,9 +333,9 @@ int Show_instance_options::execute(struct st_net *net, ulong connection_id) /* Implementation for Start_instance */ -Start_instance::Start_instance(Instance_map *imap_arg, +Start_instance::Start_instance(Instance_map *instance_map_arg, const char *name, uint len) - :Command(imap_arg) + :Command(instance_map_arg) { /* we make a search here, since we don't want t store the name */ if (instance= instance_map->find(name, len)) @@ -366,9 +366,9 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) /* Implementation for Stop_instance: */ -Stop_instance::Stop_instance(Instance_map *imap_arg, +Stop_instance::Stop_instance(Instance_map *instance_map_arg, const char *name, uint len) - :Command(imap_arg) + :Command(instance_map_arg) { /* we make a search here, since we don't want t store the name */ if (instance= instance_map->find(name, len)) diff --git a/server-tools/instance-manager/commands.h b/server-tools/instance-manager/commands.h index 8b53b21bbac..cd13bf09549 100644 --- a/server-tools/instance-manager/commands.h +++ b/server-tools/instance-manager/commands.h @@ -27,7 +27,7 @@ class Show_instances : public Command { public: - Show_instances(Instance_map *imap_arg): Command(imap_arg) + Show_instances(Instance_map *instance_map_arg): Command(instance_map_arg) {} int do_command(struct st_net *net); @@ -43,7 +43,7 @@ public: class Flush_instances : public Command { public: - Flush_instances(Instance_map *imap_arg): Command(imap_arg) + Flush_instances(Instance_map *instance_map_arg): Command(instance_map_arg) {} int execute(struct st_net *net, ulong connection_id); @@ -59,7 +59,7 @@ class Show_instance_status : public Command { public: - Show_instance_status(Instance_map *imap_arg, const char *name, uint len); + Show_instance_status(Instance_map *instance_map_arg, const char *name, uint len); int do_command(struct st_net *net, const char *instance_name); int execute(struct st_net *net, ulong connection_id); const char *instance_name; @@ -75,7 +75,7 @@ class Show_instance_options : public Command { public: - Show_instance_options(Instance_map *imap_arg, const char *name, uint len); + Show_instance_options(Instance_map *instance_map_arg, const char *name, uint len); int execute(struct st_net *net, ulong connection_id); int do_command(struct st_net *net, const char *instance_name); @@ -91,7 +91,7 @@ public: class Start_instance : public Command { public: - Start_instance(Instance_map *imap_arg, const char *name, uint len); + Start_instance(Instance_map *instance_map_arg, const char *name, uint len); int execute(struct st_net *net, ulong connection_id); const char *instance_name; @@ -107,7 +107,7 @@ public: class Stop_instance : public Command { public: - Stop_instance(Instance_map *imap_arg, const char *name, uint len); + Stop_instance(Instance_map *instance_map_arg, const char *name, uint len); Instance *instance; int execute(struct st_net *net, ulong connection_id); diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 5a4d0bade74..c7c0f1b0a77 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -107,7 +107,7 @@ void Guardian_thread::run() int Guardian_thread::start() { Instance *instance; - Imap_iterator iterator(instance_map); + Instance_map::Iterator iterator(instance_map); instance_map->lock(); while (instance= iterator.next()) diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index aa81194b1d4..b70f622fa73 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -226,26 +226,20 @@ int Instance_map::load() } -Instance *Instance_map::get_instance(uint instance_number) -{ - if (instance_number < hash.records) - return (Instance *) hash_element(&hash, instance_number); - else - return NULL; -} +/*--- Implementaton of the Instance map iterator class ---*/ -/*--- Implementaton of the Instance map iterator class (Imap_iterator) ---*/ - - -void Imap_iterator::go_to_first() +void Instance_map::Iterator::go_to_first() { current_instance=0; } -Instance *Imap_iterator::next() +Instance *Instance_map::Iterator::next() { - return instance_map->get_instance(current_instance++); + if (current_instance < instance_map->hash.records) + return (Instance *) hash_element(&instance_map->hash, current_instance++); + else + return NULL; } diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 193376c6f23..21d8c5caa9f 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -38,6 +38,22 @@ extern void free_groups(char **groups); class Instance_map { + friend class Iterator; +public: + /* Instance_map iterator */ + class Iterator + { + private: + uint current_instance; + Instance_map *instance_map; + public: + Iterator(Instance_map *instance_map_arg) : + instance_map(instance_map_arg), current_instance(0) + {} + + void go_to_first(); + Instance *next(); + }; public: /* returns a pointer to the instance or NULL, if there is no such instance */ Instance *find(const char *name, uint name_len); @@ -47,7 +63,6 @@ public: int cleanup(); int lock(); int unlock(); - Instance *get_instance(uint instance_number); Instance_map(); ~Instance_map(); @@ -72,22 +87,4 @@ private: HASH hash; }; - -/* Instance_map iterator */ - -class Imap_iterator -{ -private: - uint current_instance; - Instance_map *instance_map; -public: - Imap_iterator(Instance_map *instance_map_arg) : - instance_map(instance_map_arg), current_instance(0) - {} - - void go_to_first(); - Instance *next(); -}; - - #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ From 3691a8a426787e8f7a9b902d6cb6ab28ec20697a Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Oct 2004 11:01:38 +0400 Subject: [PATCH 05/52] comment fixed to reflect the current state of things server-tools/instance-manager/guardian.cc: comment fixed --- server-tools/instance-manager/guardian.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index c7c0f1b0a77..ac3e6298ea1 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -127,12 +127,13 @@ int Guardian_thread::start() SYNOPSYS guard() - instance_name the name of the instance to be guarded - name_len the length of the name + instance the instance to be guarded DESCRIPTION - The instance is added to the list of guarded instances. + The instance is added to the list of starting instances. Then after one guardian + loop it is moved to the guarded instances list. Usually guard() is called after we + start an instance, so we need to give some time to the instance to start. RETURN 0 - ok From 7a3a757fd5169cda75c2130cdd30457f7c8d2ee8 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Nov 2004 10:11:03 +0300 Subject: [PATCH 06/52] post-review fixes server-tools/instance-manager/Makefile.am: Removed entry for deleted file server-tools/instance-manager/buffer.cc: cleanup server-tools/instance-manager/commands.cc: cleanup, added missing error handling server-tools/instance-manager/instance.cc: added waitpid in instance_start, added few checks server-tools/instance-manager/instance_map.cc: error handling for hash_init added server-tools/instance-manager/instance_map.h: Extended constructor server-tools/instance-manager/instance_options.cc: made add_option less bulky server-tools/instance-manager/instance_options.h: - server-tools/instance-manager/listener.cc: added missing close, fixed typo server-tools/instance-manager/manager.cc: moved some Instance_map initialization to costructor server-tools/instance-manager/protocol.cc: error handling added server-tools/instance-manager/protocol.h: store_to_string fixed to return a value server-tools/instance-manager/user_map.cc: error handling for hash_init added server-tools/instance-manager/user_map.h: added init() for User map (becouse of the hash_init check) --- server-tools/instance-manager/Makefile.am | 3 +- server-tools/instance-manager/buffer.cc | 7 +- server-tools/instance-manager/commands.cc | 31 +++-- server-tools/instance-manager/instance.cc | 21 +++- server-tools/instance-manager/instance_map.cc | 18 ++- server-tools/instance-manager/instance_map.h | 7 +- .../instance-manager/instance_options.cc | 115 +++++++----------- server-tools/instance-manager/listener.cc | 5 +- server-tools/instance-manager/manager.cc | 14 +-- server-tools/instance-manager/protocol.cc | 20 ++- server-tools/instance-manager/protocol.h | 2 +- server-tools/instance-manager/user_map.cc | 12 +- server-tools/instance-manager/user_map.h | 2 +- 13 files changed, 142 insertions(+), 115 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 522ca6166cc..71f2a5d66ce 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -80,8 +80,7 @@ mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ instance_map.h instance_map.cc\ instance_options.h instance_options.cc \ buffer.h buffer.cc parse.cc parse.h \ - guardian.cc guardian.h common_structures.h \ - mysql_manager_error.h + guardian.cc guardian.h mysql_manager_error.h mysqlmanager_LDADD= liboptions.a \ libnet.a \ diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 212260bf9e0..7e785f0450e 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -77,7 +77,7 @@ int Buffer::append(uint position, const char *string, uint len_arg) int Buffer::reserve(uint position, uint len_arg) { if (position + len_arg >= MAX_BUFFER_SIZE) - return 1; + goto err; if (position + len_arg>= buffer_size) { @@ -85,8 +85,13 @@ int Buffer::reserve(uint position, uint len_arg) min(MAX_BUFFER_SIZE, max((uint) (buffer_size*1.5), position + len_arg))); + if (buffer= NULL) + goto err; buffer_size= (uint) (buffer_size*1.5); } return 0; + +err: + return 1; } diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 102a3df00e5..50f7bea7daf 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -184,14 +184,17 @@ int Show_instance_status::do_command(struct st_net *net, } - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } send_eof(net); net_flush(net); -err: return 0; + +err: + return 1; } @@ -258,7 +261,8 @@ int Show_instance_options::do_command(struct st_net *net, goto err; store_to_string(&send_buff, (char *) "instance_name", &position); store_to_string(&send_buff, (char *) instance_name, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; if (instance->options.mysqld_path != NULL) { position= 0; @@ -266,7 +270,17 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_path, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; + } + + if (instance->options.is_guarded != NULL) + { + position= 0; + store_to_string(&send_buff, (char *) "guarded", &position); + store_to_string(&send_buff, "", &position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } if (instance->options.mysqld_user != NULL) @@ -276,7 +290,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_user, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } if (instance->options.mysqld_password != NULL) @@ -286,7 +301,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_password, &position); - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } /* loop through the options stored in DYNAMIC_ARRAY */ @@ -302,7 +318,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, option_value + 1, &position); /* join name and the value into the same option again */ *option_value= '='; - my_net_write(net, send_buff.buffer, (uint) position); + if (my_net_write(net, send_buff.buffer, (uint) position)) + goto err; } } diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 1b9ce09d782..2c041e31119 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -24,7 +24,7 @@ #include #include #include - +#include /* The method starts an instance. @@ -41,11 +41,12 @@ int Instance::start() { + pid_t pid; if (!is_running()) { log_info("trying to start instance %s", options.instance_name); - switch (fork()) { + switch (pid= fork()) { case 0: if (fork()) /* zombie protection */ exit(0); /* parent goes bye-bye */ @@ -57,6 +58,7 @@ int Instance::start() case -1: return ER_CANNOT_START_INSTANCE; default: + waitpid(pid, NULL, 0); return 0; } } @@ -77,21 +79,32 @@ int Instance::cleanup() return 0; } + Instance::~Instance() { pthread_mutex_destroy(&LOCK_instance); } + bool Instance::is_running() { + uint port; + const char *socket; + + if (options.mysqld_port) + port= atoi(strchr(options.mysqld_port, '=') + 1); + + if (options.mysqld_socket) + socket= strchr(options.mysqld_socket, '=') + 1; + pthread_mutex_lock(&LOCK_instance); if (!is_connected) { mysql_init(&mysql); if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user, options.mysqld_password, - NullS, atoi(strchr(options.mysqld_port, '=') + 1), - strchr(options.mysqld_socket, '=') + 1, 0)) + NullS, port, + socket, 0)) { is_connected= TRUE; pthread_mutex_unlock(&LOCK_instance); diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index b70f622fa73..12f0c799d50 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -110,14 +110,26 @@ err_new_instance: C_MODE_END -Instance_map::Instance_map() +Instance_map::Instance_map(const char *default_mysqld_path_arg, + const char *default_admin_user_arg, + const char *default_admin_password_arg) { - hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, - get_instance_key, delete_instance, 0); + mysqld_path= default_mysqld_path_arg; + user= default_admin_user_arg; + password= default_admin_password_arg; + pthread_mutex_init(&LOCK_instance_map, 0); } +int Instance_map::init() +{ + if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_instance_key, delete_instance, 0)) + return 1; + return 0; +} + Instance_map::~Instance_map() { pthread_mutex_lock(&LOCK_instance_map); diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 21d8c5caa9f..e1bfe6ab391 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -48,7 +48,7 @@ public: Instance_map *instance_map; public: Iterator(Instance_map *instance_map_arg) : - instance_map(instance_map_arg), current_instance(0) + current_instance(0), instance_map(instance_map_arg) {} void go_to_first(); @@ -63,8 +63,11 @@ public: int cleanup(); int lock(); int unlock(); + int init(); - Instance_map(); + Instance_map(const char *default_mysqld_path_arg, + const char *default_admin_user_arg, + const char *default_admin_password_arg); ~Instance_map(); /* loads options from config files */ diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 8311e4f7bc0..4f299252bac 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -86,90 +86,58 @@ err: int Instance_options::add_option(const char* option) { - uint elements_count=0; - static const char socket[]= "--socket="; - static const char port[]= "--port="; - static const char datadir[]= "--datadir="; - static const char language[]= "--bind-address="; - static const char pid_file[]= "--pid-file="; - static const char path[]= "--mysqld_path="; - static const char user[]= "--admin_user="; - static const char password[]= "--admin_password="; - static const char guarded[]= "--guarded"; char *tmp; + enum { SAVE_VALUE= 1, SAVE_WHOLE, SAVE_WHOLE_AND_ADD }; + struct selected_options_st + { + const char *name; + uint length; + const char **value; + uint type; + } options[]= + { + {"--socket=", 9, &mysqld_socket, SAVE_WHOLE_AND_ADD}, + {"--port=", 7, &mysqld_port, SAVE_WHOLE_AND_ADD}, + {"--datadir=", 10, &mysqld_datadir, SAVE_WHOLE_AND_ADD}, + {"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD}, + {"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD}, + {"--mysqld_path=", 14, &mysqld_path, SAVE_VALUE}, + {"--admin_user=", 13, &mysqld_user, SAVE_VALUE}, + {"--admin_password=", 17, &mysqld_password, SAVE_VALUE}, + {"--guarded", 9, &is_guarded, SAVE_WHOLE}, + {NULL, 0, NULL, 0} + }; + struct selected_options_st *selected_options; if (!(tmp= strdup_root(&alloc, option))) goto err; - /* To get rid the final zero in a string we subtract 1 from sizeof value */ - if (strncmp(tmp, socket, sizeof socket - 1) == 0) - { - mysqld_socket= tmp; - goto add_options; - } + for (selected_options= options; selected_options->name; selected_options++) + { + if (!strncmp(tmp, selected_options->name, selected_options->length)) + switch(selected_options->type){ + case SAVE_WHOLE_AND_ADD: + *(selected_options->value)= tmp; + insert_dynamic(&options_array,(gptr) &tmp); + return 0; + case SAVE_VALUE: + *(selected_options->value)= strchr(tmp, '=') + 1; + return 0; + case SAVE_WHOLE: + *(selected_options->value)= tmp; + return 0; + defaut: + break; + } + } - if (strncmp(tmp, port, sizeof port - 1) == 0) - { - mysqld_port= tmp; - goto add_options; - } - - if (strncmp(tmp, datadir, sizeof datadir - 1) == 0) - { - mysqld_datadir= tmp; - goto add_options; - } - - if (strncmp(tmp, language, sizeof language - 1) == 0) - { - mysqld_bind_address= tmp; - goto add_options; - } - - if (strncmp(tmp, pid_file, sizeof pid_file - 1) == 0) - { - mysqld_pid_file= tmp; - goto add_options; - } - - /* - We don't need a prefix in the next three optios. - We also don't need to add them to argv array => - return instead of goto. - */ - - if (strncmp(tmp, path, sizeof path - 1) == 0) - { - mysqld_path= strchr(tmp, '=') + 1; - return 0; - } - - if (strncmp(tmp, user, sizeof user - 1) == 0) - { - mysqld_user= strchr(tmp, '=') + 1; - return 0; - } - - if (strncmp(tmp, password, sizeof password - 1) == 0) - { - mysqld_password= strchr(tmp, '=') + 1; - return 0; - } - - if (strncmp(tmp, guarded, sizeof guarded - 1) == 0) - { - is_guarded= tmp; - return 0; - } - -add_options: - insert_dynamic(&options_array,(gptr) &tmp); return 0; err: return 1; } + int Instance_options::add_to_argv(const char* option) { DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS); @@ -191,7 +159,8 @@ int Instance_options::init(const char *instance_name_arg) init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); - my_init_dynamic_array(&options_array, sizeof(char *), 0, 32); + if (my_init_dynamic_array(&options_array, sizeof(char *), 0, 32)) + goto err; if (!(instance_name= strmake_root(&alloc, (char *) instance_name_arg, instance_name_len))) diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 4a13dc39ec7..cb650e7cf9d 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -132,7 +132,7 @@ void Listener_thread::run() /* set the socket nonblocking */ flags= fcntl(ip_socket, F_GETFL, 0); fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK); - /* make shure that instances won't be listening our sockets */ + /* make sure that instances won't be listening our sockets */ flags= fcntl(ip_socket, F_GETFD, 0); fcntl(ip_socket, F_SETFD, flags | FD_CLOEXEC); @@ -184,7 +184,7 @@ void Listener_thread::run() /* set the socket nonblocking */ flags= fcntl(unix_socket, F_GETFL, 0); fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK); - /* make shure that instances won't be listening our sockets */ + /* make sure that instances won't be listening our sockets */ flags= fcntl(unix_socket, F_GETFD, 0); fcntl(unix_socket, F_SETFD, flags | FD_CLOEXEC); } @@ -253,6 +253,7 @@ void Listener_thread::run() log_info("Listener_thread::run(): shutdown requested, exiting..."); close(unix_socket); + close(ip_socket); unlink(unix_socket_address.sun_path); } diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 1c23aa602d4..60b233bdaac 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -65,7 +65,9 @@ void manager(const Options &options) */ User_map user_map; - Instance_map instance_map; + Instance_map instance_map(options.default_mysqld_path, + options.default_admin_user, + options.default_admin_password); Guardian_thread guardian_thread(thread_registry, &instance_map, options.monitoring_interval); @@ -73,16 +75,10 @@ void manager(const Options &options) Listener_thread_args listener_args(thread_registry, options, user_map, instance_map); - instance_map.mysqld_path= options.default_mysqld_path; - instance_map.user= options.default_admin_user; - instance_map.password= options.default_admin_password; instance_map.guardian= &guardian_thread; - - if (instance_map.load()) - return; - - if (user_map.load(options.password_file_name)) + if (instance_map.init() || user_map.init() || instance_map.load() || + user_map.load(options.password_file_name)) return; /* write pid file */ diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index 2f1f95a5f05..581157ccd72 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -99,16 +99,22 @@ char *net_store_length(char *pkg, uint length) } -void store_to_string(Buffer *buf, const char *string, uint *position) +int store_to_string(Buffer *buf, const char *string, uint *position) { uint currpos; uint string_len; string_len= strlen(string); - buf->reserve(*position, 2); + if (buf->reserve(*position, 2)) + goto err; currpos= (net_store_length(buf->buffer + *position, string_len) - buf->buffer); - buf->append(currpos, string, string_len); + if (buf->append(currpos, string, string_len)) + goto err; *position= *position + string_len + (currpos - *position); + + return 0; +err: + return 1; } @@ -134,7 +140,8 @@ int send_fields(struct st_net *net, LIST *fields) /* send the number of fileds */ net_store_length(small_buff, (uint) list_length(fields)); - my_net_write(net, small_buff, (uint) 1); + if (my_net_write(net, small_buff, (uint) 1)) + goto err; while (tmp) { @@ -147,7 +154,8 @@ int send_fields(struct st_net *net, LIST *fields) store_to_string(&send_buff, (char *) "", &position); /* table name alias */ store_to_string(&send_buff, field->name, &position); /* column name */ store_to_string(&send_buff, field->name, &position); /* column name alias */ - send_buff.reserve(position, 12); + if (send_buff.reserve(position, 12)) + goto err; send_buff.buffer[position++]= 12; int2store(send_buff.buffer + position, 1); /* charsetnr */ int4store(send_buff.buffer + position + 2, field->length); /* field length */ @@ -162,7 +170,7 @@ int send_fields(struct st_net *net, LIST *fields) tmp= rest(tmp); } - if ( my_net_write(net, eof_buff, 1)) + if (my_net_write(net, eof_buff, 1)) goto err; return 0; diff --git a/server-tools/instance-manager/protocol.h b/server-tools/instance-manager/protocol.h index 7bce0e35b5b..1102f95ff8f 100644 --- a/server-tools/instance-manager/protocol.h +++ b/server-tools/instance-manager/protocol.h @@ -36,7 +36,7 @@ int send_fields(struct st_net *net, LIST *fields); char *net_store_length(char *pkg, uint length); -void store_to_string(Buffer *buf, const char *string, uint *position); +int store_to_string(Buffer *buf, const char *string, uint *position); int send_eof(struct st_net *net); diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 7fff324a521..b921152d858 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -93,13 +93,16 @@ static void delete_user(void *u) C_MODE_END -User_map::User_map() +int User_map::init() { enum { START_HASH_SIZE = 16 }; - hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, - get_user_key, delete_user, 0); + if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_user_key, delete_user, 0)) + return 1; + return 0; } + User_map::~User_map() { hash_free(&hash); @@ -134,7 +137,8 @@ int User_map::load(const char *password_file_name) while (fgets(line, sizeof(line), file)) { /* skip comments and empty lines */ - if (line[0] == '#' || line[0] == '\n' && line[1] == '\0') + if (line[0] == '#' || line[0] == '\n' && + (line[1] == '\0' || line[1] == '\r')) continue; if ((user= new User) == 0) goto done; diff --git a/server-tools/instance-manager/user_map.h b/server-tools/instance-manager/user_map.h index acee0b3c02b..dcd2fd1494d 100644 --- a/server-tools/instance-manager/user_map.h +++ b/server-tools/instance-manager/user_map.h @@ -31,9 +31,9 @@ class User_map { public: - User_map(); ~User_map(); + int init(); int load(const char *password_file_name); int authenticate(const char *user_name, uint length, const char *scrambled_password, From bb63229331d461baedecd0ef439914ff9458e441 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Nov 2004 02:42:22 +0300 Subject: [PATCH 07/52] linking problem fix server-tools/instance-manager/Makefile.am: fix that should solve the linking problem (libmysqlclient conflicts with libmysys) server-tools/instance-manager/instance_map.cc: propagae phtead_mutex_* functions from the wrapper --- server-tools/instance-manager/Makefile.am | 32 ++++++++++++------- server-tools/instance-manager/instance_map.cc | 4 +-- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 71f2a5d66ce..0c2c6230ac3 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -23,7 +23,7 @@ DEFS= -DMYSQL_INSTANCE_MANAGER # default_options.h, generated from default_options.h.in) # See automake/autoconf docs for details -noinst_LIBRARIES= liboptions.a libnet.a +noinst_LIBRARIES= liboptions.a libnet.a libalarm.a liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_PID_FILE_NAME="$(localstatedir)/mysqlmanager.pid" \ @@ -45,7 +45,14 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc nodist_libnet_a_SOURCES= password.c pack.c sql_state.c net_serv.cc nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -CLEANFILES= net_serv.cc password.c pack.c sql_state.c +nodist_libalarm_a_SOURCES= thr_alarm.c +nodist_libalarm_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER +libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.o \ + $(top_builddir)/mysys/queues.o \ + $(top_builddir)/mysys/my_new.o + + +CLEANFILES= net_serv.cc password.c pack.c sql_state.c thr_alarm.c net_serv.cc: Makefile rm -f $(srcdir)/net_serv.cc @@ -63,32 +70,35 @@ sql_state.c: Makefile rm -f $(srcdir)/sql_state.c @LN_CP_F@ $(top_srcdir)/sql/sql_state.c $(srcdir)/sql_state.c +thr_alarm.c: Makefile + rm -f $(srcdir)/thr_alarm.c + @LN_CP_F@ $(top_srcdir)/mysys/thr_alarm.c $(srcdir)/thr_alarm.c + bin_PROGRAMS= mysqlmanager -mysqlmanager_SOURCES= mysqlmanager.cc manager.h manager.cc log.h log.cc \ +mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ + manager.h manager.cc log.h log.cc \ thread_registry.h thread_registry.cc \ - listener.h listener.cc \ + listener.h listener.cc protocol.h protocol.cc \ mysql_connection.h mysql_connection.cc \ - protocol.h protocol.cc \ user_map.h user_map.cc \ messages.h messages.cc \ $(top_srcdir)/sql/sql_string.cc \ - command.h command.cc \ commands.h commands.cc \ factory.h factory.cc \ instance.h instance.cc \ instance_map.h instance_map.cc\ instance_options.h instance_options.cc \ buffer.h buffer.cc parse.cc parse.h \ - guardian.cc guardian.h mysql_manager_error.h + guardian.cc guardian.h common_structures.h \ + mysql_manager_error.h mysqlmanager_LDADD= liboptions.a \ libnet.a \ + libalarm.a \ $(top_builddir)/vio/libvio.a \ - $(top_builddir)/mysys/libmysys.a \ - $(top_builddir)/strings/libmystrings.a \ - $(top_builddir)/dbug/libdbug.a \ - $(top_builddir)/libmysql/libmysqlclient.la + $(top_builddir)/libmysql_r/libmysqlclient_r.la \ + $(top_builddir)/dbug/libdbug.a -lz tags: diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 12f0c799d50..3a0d408dc1c 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -141,13 +141,13 @@ Instance_map::~Instance_map() int Instance_map::lock() { - pthread_mutex_lock(&LOCK_instance_map); + return pthread_mutex_lock(&LOCK_instance_map); } int Instance_map::unlock() { - pthread_mutex_unlock(&LOCK_instance_map); + return pthread_mutex_unlock(&LOCK_instance_map); } From 598bdc38b1bcdb71a43528ded08c2c1aacd40074 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Nov 2004 11:24:14 +0300 Subject: [PATCH 08/52] protability fix server-tools/instance-manager/Makefile.am: portability fix BitKeeper/etc/ignore: Added server-tools/instance-manager/thr_alarm.c to the ignore list --- .bzrignore | 1 + server-tools/instance-manager/Makefile.am | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.bzrignore b/.bzrignore index c83b4ed4388..47ba7d06bc4 100644 --- a/.bzrignore +++ b/.bzrignore @@ -939,3 +939,4 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +server-tools/instance-manager/thr_alarm.c diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 0c2c6230ac3..d485a8798f5 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -47,9 +47,9 @@ nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER nodist_libalarm_a_SOURCES= thr_alarm.c nodist_libalarm_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.o \ - $(top_builddir)/mysys/queues.o \ - $(top_builddir)/mysys/my_new.o +libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.$(OBJEXT) \ + $(top_builddir)/mysys/queues.$(OBJEXT) \ + $(top_builddir)/mysys/my_new.$(OBJEXT) CLEANFILES= net_serv.cc password.c pack.c sql_state.c thr_alarm.c From 4f32ec18822f90ff118f4cbe655b80f977d7077f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Nov 2004 02:14:56 +0300 Subject: [PATCH 09/52] fix for the IM linking problem BitKeeper/etc/ignore: Added mysql-5.0.2-alpha.tar.gz server-tools/instance-manager/client.c server-tools/instance-manager/client_settings.h server-tools/instance-manager/errmsg.c to the ignore list server-tools/instance-manager/instance_map.cc: Comment updated server-tools/instance-manager/priv.cc: added variables needed by net_serv.cc server-tools/instance-manager/priv.h: declared variables needed by net_serv.cc sql/net_serv.cc: added some IM-specific defines --- .bzrignore | 4 ++ server-tools/instance-manager/Makefile.am | 54 ++++++++----------- server-tools/instance-manager/client_func.c | 32 +++++++++++ server-tools/instance-manager/instance_map.cc | 7 ++- server-tools/instance-manager/priv.cc | 5 ++ server-tools/instance-manager/priv.h | 4 ++ sql/net_serv.cc | 25 ++++----- 7 files changed, 81 insertions(+), 50 deletions(-) create mode 100644 server-tools/instance-manager/client_func.c diff --git a/.bzrignore b/.bzrignore index 47ba7d06bc4..dec7eb8ffdf 100644 --- a/.bzrignore +++ b/.bzrignore @@ -940,3 +940,7 @@ vio/test-sslclient vio/test-sslserver vio/viotest-ssl server-tools/instance-manager/thr_alarm.c +mysql-5.0.2-alpha.tar.gz +server-tools/instance-manager/client.c +server-tools/instance-manager/client_settings.h +server-tools/instance-manager/errmsg.c diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index d485a8798f5..4c3a772111a 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -16,14 +16,14 @@ INCLUDES= -I$(top_srcdir)/include -DEFS= -DMYSQL_INSTANCE_MANAGER +DEFS= -DMYSQL_INSTANCE_MANAGER -DMYSQL_SERVER # As all autoconf variables depend from ${prefix} and being resolved only when # make is run, we can not put these defines to a header file (e.g. to # default_options.h, generated from default_options.h.in) # See automake/autoconf docs for details -noinst_LIBRARIES= liboptions.a libnet.a libalarm.a +noinst_LIBRARIES= liboptions.a libnet.a liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_PID_FILE_NAME="$(localstatedir)/mysqlmanager.pid" \ @@ -34,7 +34,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_USER="root" \ -DDEFAULT_PASSWORD="" \ -DDEFAULT_MONITORING_INTERVAL="5" \ - -DDEFAULT_PORT="3406" \ + -DDEFAULT_PORT="33006" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc @@ -42,37 +42,28 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a -nodist_libnet_a_SOURCES= password.c pack.c sql_state.c net_serv.cc -nodist_libnet_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER +nodist_libnet_a_SOURCES= net_serv.cc client.c errmsg.c +libnet_a_LIBADD= $(top_builddir)/sql/password.$(OBJEXT) \ + $(top_builddir)/sql/pack.$(OBJEXT) \ + $(top_builddir)/sql/sql_state.$(OBJEXT) -nodist_libalarm_a_SOURCES= thr_alarm.c -nodist_libalarm_a_CPPFLAGS= $(CPPFLAGS) -DMYSQL_SERVER -libalarm_a_LIBADD= $(top_builddir)/mysys/mf_qsort2.$(OBJEXT) \ - $(top_builddir)/mysys/queues.$(OBJEXT) \ - $(top_builddir)/mysys/my_new.$(OBJEXT) - - -CLEANFILES= net_serv.cc password.c pack.c sql_state.c thr_alarm.c +CLEANFILES= net_serv.cc client.c client_settings.h errmsg.c net_serv.cc: Makefile rm -f $(srcdir)/net_serv.cc @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc $(srcdir)/net_serv.cc -password.c: Makefile - rm -f $(srcdir)/password.c - @LN_CP_F@ $(top_srcdir)/sql/password.c $(srcdir)/password.c +client.c: Makefile + rm -f $(srcdir)/client.c + @LN_CP_F@ $(top_srcdir)/sql-common/client.c $(srcdir)/client.c -pack.c: Makefile - rm -f $(srcdir)/pack.c - @LN_CP_F@ $(top_srcdir)/sql-common/pack.c $(srcdir)/pack.c +errmsg.c: Makefile + rm -f $(srcdir)/errmsg.c + @LN_CP_F@ $(top_srcdir)/libmysql/errmsg.c $(srcdir)/errmsg.c -sql_state.c: Makefile - rm -f $(srcdir)/sql_state.c - @LN_CP_F@ $(top_srcdir)/sql/sql_state.c $(srcdir)/sql_state.c - -thr_alarm.c: Makefile - rm -f $(srcdir)/thr_alarm.c - @LN_CP_F@ $(top_srcdir)/mysys/thr_alarm.c $(srcdir)/thr_alarm.c +client_settings.h: Makefile + rm -f $(srcdir)/client_settings.h + @LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h bin_PROGRAMS= mysqlmanager @@ -90,15 +81,16 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ instance_map.h instance_map.cc\ instance_options.h instance_options.cc \ buffer.h buffer.cc parse.cc parse.h \ - guardian.cc guardian.h common_structures.h \ - mysql_manager_error.h + guardian.cc guardian.h \ + mysql_manager_error.h client_func.c mysqlmanager_LDADD= liboptions.a \ libnet.a \ - libalarm.a \ $(top_builddir)/vio/libvio.a \ - $(top_builddir)/libmysql_r/libmysqlclient_r.la \ - $(top_builddir)/dbug/libdbug.a -lz + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/strings/libmystrings.a \ + $(top_builddir)/dbug/libdbug.a \ + @openssl_libs@ @ZLIB_LIBS@ tags: diff --git a/server-tools/instance-manager/client_func.c b/server-tools/instance-manager/client_func.c new file mode 100644 index 00000000000..a7ff1d27a8f --- /dev/null +++ b/server-tools/instance-manager/client_func.c @@ -0,0 +1,32 @@ +#include +#include +#include + +/* + Currently we cannot use libmysqlclient directly becouse of the linking + issues. Here we provide needed libmysqlclient functions. + TODO: to think how to use libmysqlclient code instead of copy&paste. + The other possible solution is to use simple_command directly. +*/ + +const char * STDCALL +mysql_get_server_info(MYSQL *mysql) +{ + return((char*) mysql->server_version); +} + +int STDCALL +mysql_ping(MYSQL *mysql) +{ + DBUG_ENTER("mysql_ping"); + DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0)); +} + +int STDCALL +mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) +{ + uchar level[1]; + DBUG_ENTER("mysql_shutdown"); + level[0]= (uchar) shutdown_level; + DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0)); +} diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 3a0d408dc1c..41b23c5dd00 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -27,10 +27,9 @@ #include /* - TODO: Currently there are some mysql-connection specific functions. - As we are going to suppost different types of connections, we shouldn't - have them here in future. To avoid it we could put such - connection-specific functions to the Command-derived class instead. + Note: As we are going to suppost different types of connections, + we shouldn't have connection-specific functions. To avoid it we could + put such functions to the Command-derived class instead. The command could be easily constructed for a specific connection if we would provide a special factory for each connection. */ diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc index e449df9f540..8112ebd41d8 100644 --- a/server-tools/instance-manager/priv.cc +++ b/server-tools/instance-manager/priv.cc @@ -32,3 +32,8 @@ unsigned long net_write_timeout= 60; // same as in mysqld unsigned long net_retry_count= 10; // same as in mysqld +/* needed by net_serv.cc */ +unsigned int test_flags= 0; +unsigned long bytes_sent = 0L, bytes_received = 0L; +unsigned long mysqld_net_retry_count = 10L; +unsigned long open_files_limit; diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 73ee87552c8..8014df7260c 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -56,5 +56,9 @@ extern unsigned long net_write_timeout; */ extern unsigned long net_retry_count; +extern unsigned int test_flags; +extern unsigned long bytes_sent, bytes_received; +extern unsigned long mysqld_net_retry_count; +extern unsigned long open_files_limit; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 0a728440e4d..ed4cc6a5942 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -78,11 +78,11 @@ my_bool net_flush(NET *net); can't normally do this the client should have a bigger max_allowed_packet. */ -#if (defined(__WIN__) || (!defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER))) +#if defined(__WIN__) || !defined(MYSQL_SERVER) /* The following is because alarms doesn't work on windows. */ #define NO_ALARM #endif - + #ifndef NO_ALARM #include "my_pthread.h" void sql_print_error(const char *format,...); @@ -93,7 +93,6 @@ void sql_print_error(const char *format,...); #include "thr_alarm.h" #ifdef MYSQL_SERVER -#define USE_QUERY_CACHE /* The following variables/functions should really not be declared extern, but as it's hard to include mysql_priv.h here, we have to @@ -102,9 +101,14 @@ void sql_print_error(const char *format,...); extern uint test_flags; extern ulong bytes_sent, bytes_received, net_big_packet_count; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; +#ifndef MYSQL_INSTANCE_MANAGER extern void query_cache_insert(NET *net, const char *packet, ulong length); +#define USE_QUERY_CACHE #define update_statistics(A) A -#else +#endif /* MYSQL_INSTANCE_MANGER */ +#endif /* defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER) */ + +#if !defined(MYSQL_SERVER) || defined(MYSQL_INSTANCE_MANAGER) #define update_statistics(A) #define thd_increment_bytes_sent() #endif @@ -453,7 +457,8 @@ net_real_write(NET *net,const char *packet,ulong len) my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); -#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) +#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) \ + && !defined(MYSQL_INSTANCE_MANAGER) if (net->query_cache_query != 0) query_cache_insert(net, packet, len); #endif @@ -663,13 +668,6 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, } #endif /* NO_ALARM */ -/* - If we are inside of the instance manageer, we need to simulate mysql - server for the following function. -*/ -#ifdef MYSQL_INSTANCE_MANAGER -#define MYSQL_SERVER -#endif /* Reads one packet to net->buff + net->where_b @@ -859,9 +857,6 @@ end: return(len); } -#ifdef MYSQL_INSTANCE_MANAGER -#undef MYSQL_SERVER -#endif /* Read a packet from the client/server and return it without the internal From 0157ffa5fd491b886586dbb0236fef8ae0af4d49 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Nov 2004 23:18:28 +0300 Subject: [PATCH 10/52] few more fixes - fix makefile and get rid of strncasecmp in favour of my_strnncoll server-tools/instance-manager/Makefile.am: one more makefile fix server-tools/instance-manager/parse.cc: get rid of non-portable strnacasecmp --- server-tools/instance-manager/Makefile.am | 2 +- server-tools/instance-manager/parse.cc | 27 ++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 4c3a772111a..d3702ba9464 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -42,7 +42,7 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a -nodist_libnet_a_SOURCES= net_serv.cc client.c errmsg.c +nodist_libnet_a_SOURCES= net_serv.cc client_settings.h client.c errmsg.c libnet_a_LIBADD= $(top_builddir)/sql/password.$(OBJEXT) \ $(top_builddir)/sql/pack.$(OBJEXT) \ $(top_builddir)/sql/sql_state.$(OBJEXT) diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc index 09a60062946..38e10c7f2f5 100644 --- a/server-tools/instance-manager/parse.cc +++ b/server-tools/instance-manager/parse.cc @@ -31,15 +31,21 @@ enum Token TOK_END }; -static const char *tokens[]= { - "FLUSH", - "INSTANCE", - "INSTANCES", - "OPTIONS", - "START", - "STATUS", - "STOP", - "SHOW", +struct tokens_st +{ + uint length; + const char *tok_name; +}; + +static struct tokens_st tokens[]= { + {5, "FLUSH"}, + {8, "INSTANCE"}, + {9, "INSTANCES"}, + {7, "OPTIONS"}, + {5, "START"}, + {6, "STATUS"}, + {4, "STOP"}, + {4, "SHOW"} }; @@ -76,7 +82,8 @@ inline Token find_token(const char *word, uint word_len) int i= 0; do { - if (strncasecmp(tokens[i], word, word_len) == 0) + if (my_strnncoll(default_charset_info, (const uchar *) tokens[i].tok_name, + tokens[i].length, (const uchar *) word, word_len) == 0) break; } while (++i < TOK_NOT_FOUND); From b5536bb962da6751bbc62ef9b7aba720bb04d018 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Nov 2004 14:53:30 +0300 Subject: [PATCH 11/52] Some minor IM fixes server-tools/instance-manager/Makefile.am: Makefile.am cleanup server-tools/instance-manager/commands.cc: cleanup server-tools/instance-manager/instance_map.cc: fix the problem caused thread deadlock (as load ends up in the find() call which now also locks an instance map mutex) server-tools/instance-manager/listener.cc: portability fix server-tools/instance-manager/manager.cc: Ignore SIGPIPE server-tools/instance-manager/mysqlmanager.cc: cleanup server-tools/instance-manager/options.cc: options renamed --- server-tools/instance-manager/Makefile.am | 17 +++++------------ server-tools/instance-manager/commands.cc | 2 +- server-tools/instance-manager/instance_map.cc | 2 +- server-tools/instance-manager/listener.cc | 1 + server-tools/instance-manager/manager.cc | 3 +++ server-tools/instance-manager/mysqlmanager.cc | 2 +- server-tools/instance-manager/options.cc | 10 +++++----- 7 files changed, 17 insertions(+), 20 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index d3702ba9464..050f9b9bfd2 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -42,25 +42,19 @@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a -nodist_libnet_a_SOURCES= net_serv.cc client_settings.h client.c errmsg.c +nodist_libnet_a_SOURCES= net_serv.cc client_settings.h libnet_a_LIBADD= $(top_builddir)/sql/password.$(OBJEXT) \ $(top_builddir)/sql/pack.$(OBJEXT) \ - $(top_builddir)/sql/sql_state.$(OBJEXT) + $(top_builddir)/sql/sql_state.$(OBJEXT) \ + $(top_builddir)/sql/mini_client_errors.$(OBJEXT)\ + $(top_builddir)/sql/client.$(OBJEXT) -CLEANFILES= net_serv.cc client.c client_settings.h errmsg.c +CLEANFILES= net_serv.cc client_settings.h net_serv.cc: Makefile rm -f $(srcdir)/net_serv.cc @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc $(srcdir)/net_serv.cc -client.c: Makefile - rm -f $(srcdir)/client.c - @LN_CP_F@ $(top_srcdir)/sql-common/client.c $(srcdir)/client.c - -errmsg.c: Makefile - rm -f $(srcdir)/errmsg.c - @LN_CP_F@ $(top_srcdir)/libmysql/errmsg.c $(srcdir)/errmsg.c - client_settings.h: Makefile rm -f $(srcdir)/client_settings.h @LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h @@ -74,7 +68,6 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ mysql_connection.h mysql_connection.cc \ user_map.h user_map.cc \ messages.h messages.cc \ - $(top_srcdir)/sql/sql_string.cc \ commands.h commands.cc \ factory.h factory.cc \ instance.h instance.cc \ diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 50f7bea7daf..2ac97382aa8 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -408,7 +408,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) stop_guard(instance); if (err_code= instance->stop()) return err_code; - + printf("instance was stopped\n"); net_send_ok(net, connection_id); return 0; } diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 41b23c5dd00..355b51269d5 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -158,8 +158,8 @@ int Instance_map::flush_instances() hash_free(&hash); hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_instance_key, delete_instance, 0); - rc= load(); pthread_mutex_unlock(&LOCK_instance_map); + rc= load(); return rc; } diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index cb650e7cf9d..15f57e7e595 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include "thread_registry.h" #include "options.h" diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 60b233bdaac..07d4f1ed33e 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -90,6 +90,7 @@ void manager(const Options &options) sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGPIPE); sigaddset(&mask, SIGHUP); /* We want this signal to be blocked in all theads but the signal @@ -159,6 +160,8 @@ void manager(const Options &options) */ guardian_thread.start(); + signal(SIGPIPE, SIG_IGN); + while (!shutdown_complete) { sigwait(&mask, &signo); diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index bd8f3c6b870..78fa3ac565b 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -179,7 +179,7 @@ static volatile sig_atomic_t is_terminated= 0; void terminate(int signo) { - is_terminated= signo; + is_terminated= signo; } diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 01d83e2d994..05493e10ad8 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -79,7 +79,7 @@ static struct my_option my_long_options[] = (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "bind_address", OPT_BIND_ADDRESS, "Bind address to use for connection.", + { "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.", (gptr *) &Options::bind_address, (gptr *) &Options::bind_address, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, @@ -93,24 +93,24 @@ static struct my_option my_long_options[] = (gptr *) &Options::password_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "default_mysqld_path", OPT_MYSQLD_PATH, "Where to look for MySQL" + { "default-mysqld-path", OPT_MYSQLD_PATH, "Where to look for MySQL" " Server binary.", (gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "default_admin_user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL" + { "default-admin-user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL" " instances.", (gptr *) &Options::default_admin_user, (gptr *) &Options::default_admin_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "default_admin_password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to" + { "default-admin-password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to" "shutdown MySQL instances.", (gptr *) &Options::default_admin_password, (gptr *) &Options::default_admin_password, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "monitoring_interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances" + { "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances" " in seconds.", (gptr *) &Options::monitoring_interval, (gptr *) &Options::monitoring_interval, From a37b15c6b013dcde0160bf0ff522e5a3422ba1f6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 01:17:52 +0200 Subject: [PATCH 12/52] Fixed that mysql-test-run --ps-protocol works with system_mysql_db_fix.test client/mysqltest.c: More debugging mysql-test/t/system_mysql_db_fix.test: Fixed that mysql-test-run --ps-protocol works --- client/mysqltest.c | 16 ++++++++++------ mysql-test/t/system_mysql_db_fix.test | 6 ++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index bc99d4d38f3..271068886d8 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -540,7 +540,6 @@ static void free_used_memory() mysql_server_end(); if (ps_protocol) ps_free_reg(); - my_end(MY_CHECK_ERROR); DBUG_VOID_RETURN; } @@ -558,6 +557,7 @@ static void die(const char* fmt, ...) } va_end(args); free_used_memory(); + my_end(MY_CHECK_ERROR); exit(1); } @@ -570,6 +570,7 @@ static void abort_not_supported_test() if (!silent) printf("skipped\n"); free_used_memory(); + my_end(MY_CHECK_ERROR); exit(2); } @@ -668,6 +669,7 @@ static int check_result(DYNAMIC_STRING* ds, const char* fname, { int error = 0; int res=dyn_string_cmp(ds, fname); + DBUG_ENTER("check_result"); if (res && require_option) abort_not_supported_test(); @@ -687,7 +689,7 @@ static int check_result(DYNAMIC_STRING* ds, const char* fname, } if (error) reject_dump(fname, ds->str, ds->length); - return error; + DBUG_RETURN(error); } @@ -1841,7 +1843,10 @@ int read_line(char* buf, int size) cur_file--; lineno--; if (cur_file == file_stack) + { + DBUG_PRINT("info", ("end of file")); DBUG_RETURN(1); + } continue; } @@ -2011,7 +2016,6 @@ int read_query(struct st_query** q_ptr) q->query_buf= q->query= 0; if (read_line(read_query_buf, sizeof(read_query_buf))) { - DBUG_PRINT("warning",("too long query")); DBUG_RETURN(1); } DBUG_PRINT("info", ("query: %s", read_query_buf)); @@ -3366,8 +3370,6 @@ int main(int argc, char **argv) my_bool require_file=0, q_send_flag=0, abort_flag= 0; char save_file[FN_REFLEN]; MY_INIT(argv[0]); - { - DBUG_ENTER("main"); /* Use all time until exit if no explicit 'start_timer' */ timer_start= timer_now(); @@ -3394,6 +3396,8 @@ int main(int argc, char **argv) *block_ok = 1; init_dynamic_string(&ds_res, "", 0, 65536); parse_args(argc, argv); + + DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : "")); if (mysql_server_init(embedded_server_arg_count, embedded_server_args, (char**) embedded_server_groups)) @@ -3659,9 +3663,9 @@ int main(int argc, char **argv) if (!got_end_timer) timer_output(); /* No end_timer cmd, end it */ free_used_memory(); + my_end(MY_CHECK_ERROR); exit(error ? 1 : 0); return error ? 1 : 0; /* Keep compiler happy */ - } } diff --git a/mysql-test/t/system_mysql_db_fix.test b/mysql-test/t/system_mysql_db_fix.test index 1122803fd8f..e34dbefbcba 100644 --- a/mysql-test/t/system_mysql_db_fix.test +++ b/mysql-test/t/system_mysql_db_fix.test @@ -9,6 +9,7 @@ use test; # create system tables as in mysql-3.20 +--disable_warnings CREATE TABLE db ( Host char(60) binary DEFAULT '' NOT NULL, Db char(32) binary DEFAULT '' NOT NULL, @@ -23,10 +24,12 @@ CREATE TABLE db ( KEY User (User) ) type=ISAM; +--enable-warnings INSERT INTO db VALUES ('%','test', '','Y','Y','Y','Y','Y','Y'); INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y'); +--disable_warnings CREATE TABLE host ( Host char(60) binary DEFAULT '' NOT NULL, Db char(32) binary DEFAULT '' NOT NULL, @@ -39,7 +42,9 @@ CREATE TABLE host ( PRIMARY KEY Host (Host,Db) ) type=ISAM; +--enable-warnings +--disable_warnings CREATE TABLE user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, @@ -56,6 +61,7 @@ CREATE TABLE user ( PRIMARY KEY Host (Host,User) ) type=ISAM; +--enable-warnings INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y'); INSERT INTO user VALUES ('localhost','', '','N','N','N','N','N','N','N','N','N'); From e6c14092204e73a1562c1beffc9406e57cce2021 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 20:13:33 +1100 Subject: [PATCH 13/52] Remove dead code ndb/src/ndbapi/TransporterFacade.cpp: Remove dead #if 0'd out condition with misleading comment BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted --- BitKeeper/etc/logging_ok | 1 + ndb/src/ndbapi/TransporterFacade.cpp | 61 ++++++++++++---------------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 11a49cee843..5140e43ee41 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -200,6 +200,7 @@ serg@sergbook.mylan serg@sergbook.mysql.com sergefp@mysql.com sinisa@rhols221.adsl.netsonic.fi +stewart@mysql.com tfr@beta.frontier86.ee tfr@indrek.tfr.cafe.ee tfr@sarvik.tfr.cafe.ee diff --git a/ndb/src/ndbapi/TransporterFacade.cpp b/ndb/src/ndbapi/TransporterFacade.cpp index dfb090c8416..f9dde92f17d 100644 --- a/ndb/src/ndbapi/TransporterFacade.cpp +++ b/ndb/src/ndbapi/TransporterFacade.cpp @@ -515,43 +515,32 @@ TransporterFacade::init(Uint32 nodeId, const ndb_mgm_configuration* props) iter.first(); theClusterMgr->init(iter); - /** - * Unless there is a "Name", the initiated transporter is within - * an NDB Cluster. (If "Name" is defined, then the transporter - * is used to connect to a different system, i.e. NDB Cluster.) - */ -#if 0 - if (!props->contains("Name")) { -#endif - iter.first(); - if(iter.find(CFG_NODE_ID, nodeId)){ - TRP_DEBUG( "Node info missing from config." ); - return false; - } - - Uint32 rank = 0; - if(!iter.get(CFG_NODE_ARBIT_RANK, &rank) && rank>0){ - theArbitMgr = new ArbitMgr(* this); - theArbitMgr->setRank(rank); - Uint32 delay = 0; - iter.get(CFG_NODE_ARBIT_DELAY, &delay); - theArbitMgr->setDelay(delay); - } - Uint32 scan_batch_size= 0; - if (!iter.get(CFG_MAX_SCAN_BATCH_SIZE, &scan_batch_size)) { - m_scan_batch_size= scan_batch_size; - } - Uint32 batch_byte_size= 0; - if (!iter.get(CFG_BATCH_BYTE_SIZE, &batch_byte_size)) { - m_batch_byte_size= batch_byte_size; - } - Uint32 batch_size= 0; - if (!iter.get(CFG_BATCH_SIZE, &batch_size)) { - m_batch_size= batch_size; - } -#if 0 + iter.first(); + if(iter.find(CFG_NODE_ID, nodeId)){ + TRP_DEBUG( "Node info missing from config." ); + return false; + } + + Uint32 rank = 0; + if(!iter.get(CFG_NODE_ARBIT_RANK, &rank) && rank>0){ + theArbitMgr = new ArbitMgr(* this); + theArbitMgr->setRank(rank); + Uint32 delay = 0; + iter.get(CFG_NODE_ARBIT_DELAY, &delay); + theArbitMgr->setDelay(delay); + } + Uint32 scan_batch_size= 0; + if (!iter.get(CFG_MAX_SCAN_BATCH_SIZE, &scan_batch_size)) { + m_scan_batch_size= scan_batch_size; + } + Uint32 batch_byte_size= 0; + if (!iter.get(CFG_BATCH_BYTE_SIZE, &batch_byte_size)) { + m_batch_byte_size= batch_byte_size; + } + Uint32 batch_size= 0; + if (!iter.get(CFG_BATCH_SIZE, &batch_size)) { + m_batch_size= batch_size; } -#endif if (!theTransporterRegistry->start_service(m_socket_server)){ ndbout_c("Unable to start theTransporterRegistry->start_service"); From 9be0b6106391189120985b89d0e07fa072118778 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 20:22:00 +1100 Subject: [PATCH 14/52] Correct and move comment ndb/src/ndbapi/TransporterFacade.hpp: Correct and move comment. --- ndb/src/ndbapi/TransporterFacade.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ndb/src/ndbapi/TransporterFacade.hpp b/ndb/src/ndbapi/TransporterFacade.hpp index 5680e3a6f03..dcdabee2fd4 100644 --- a/ndb/src/ndbapi/TransporterFacade.hpp +++ b/ndb/src/ndbapi/TransporterFacade.hpp @@ -43,10 +43,6 @@ extern "C" { void atexit_stop_instance(); } -/** - * Max number of Ndb objects in different threads. - * (Ndb objects should not be shared by different threads.) - */ class TransporterFacade { public: @@ -171,6 +167,10 @@ private: * Block number handling */ public: + /** + * Max number of Ndb objects. + * (Ndb objects should not be shared by different threads.) + */ STATIC_CONST( MAX_NO_THREADS = 4711 ); private: From 22b16624d5170bd6cc985be7476dc58e5e721b07 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 14:03:29 +0400 Subject: [PATCH 15/52] - VARCHAR(n) with binary character set is now displayed as VARBINARY(N). - CREATE TABLE t1 SELECT BINARY 'literal' now creates a VARBINARY() column, not a BINARY(). --- mysql-test/r/cast.result | 10 +++++----- mysql-test/r/ctype_utf8.result | 2 +- mysql-test/r/func_str.result | 2 +- mysql-test/r/ps_2myisam.result | 2 +- mysql-test/r/ps_3innodb.result | 2 +- mysql-test/r/ps_4heap.result | 2 +- mysql-test/r/ps_6bdb.result | 2 +- mysql-test/r/type_ranges.result | 2 +- mysql-test/r/union.result | 10 +++++----- sql/field.cc | 13 ++++++++++--- 10 files changed, 27 insertions(+), 20 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index d784460a68d..a893e6ee4ed 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -91,11 +91,11 @@ ab a ab a a show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `c1` varchar(2) NOT NULL default '', - `c2` varchar(2) NOT NULL default '', - `c3` varchar(2) NOT NULL default '', - `c4` varchar(2) NOT NULL default '', - `c5` varchar(2) NOT NULL default '' + `c1` varbinary(2) NOT NULL default '', + `c2` varbinary(2) NOT NULL default '', + `c3` varbinary(2) NOT NULL default '', + `c4` varbinary(2) NOT NULL default '', + `c5` varbinary(2) NOT NULL default '' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; select diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 79e871c4e79..79bca741fba 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -124,7 +124,7 @@ create table t1 select date_format("2004-01-19 10:10:10", "%Y-%m-%d"); show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `date_format("2004-01-19 10:10:10", "%Y-%m-%d")` binary(10) default NULL + `date_format("2004-01-19 10:10:10", "%Y-%m-%d")` varbinary(10) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 select * from t1; date_format("2004-01-19 10:10:10", "%Y-%m-%d") diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index e65ddde8aef..6a206d1c7d9 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -611,7 +611,7 @@ t1 CREATE TABLE `t1` ( `substring(_latin2'ab',1)` varchar(2) character set latin2 NOT NULL default '', `insert(_latin2'abcd',2,3,_latin2'ef')` varchar(6) character set latin2 NOT NULL default '', `replace(_latin2'abcd',_latin2'b',_latin2'B')` varchar(4) character set latin2 NOT NULL default '', - `encode('abcd','ab')` binary(4) NOT NULL default '' + `encode('abcd','ab')` varbinary(4) NOT NULL default '' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; select SUBSTR('abcdefg',3,2); diff --git a/mysql-test/r/ps_2myisam.result b/mysql-test/r/ps_2myisam.result index 1d1ee8a8561..347e1c39eb2 100644 --- a/mysql-test/r/ps_2myisam.result +++ b/mysql-test/r/ps_2myisam.result @@ -1775,7 +1775,7 @@ t5 CREATE TABLE `t5` ( `param03` double default NULL, `const04` varchar(3) NOT NULL default '', `param04` longtext, - `const05` binary(3) NOT NULL default '', + `const05` varbinary(3) NOT NULL default '', `param05` longblob, `const06` varchar(10) NOT NULL default '', `param06` longtext, diff --git a/mysql-test/r/ps_3innodb.result b/mysql-test/r/ps_3innodb.result index 888b8f57764..57ae4a3793c 100644 --- a/mysql-test/r/ps_3innodb.result +++ b/mysql-test/r/ps_3innodb.result @@ -1758,7 +1758,7 @@ t5 CREATE TABLE `t5` ( `param03` double default NULL, `const04` varchar(3) NOT NULL default '', `param04` longtext, - `const05` binary(3) NOT NULL default '', + `const05` varbinary(3) NOT NULL default '', `param05` longblob, `const06` varchar(10) NOT NULL default '', `param06` longtext, diff --git a/mysql-test/r/ps_4heap.result b/mysql-test/r/ps_4heap.result index d6e835adbc6..2f533f4c843 100644 --- a/mysql-test/r/ps_4heap.result +++ b/mysql-test/r/ps_4heap.result @@ -1759,7 +1759,7 @@ t5 CREATE TABLE `t5` ( `param03` double default NULL, `const04` varchar(3) NOT NULL default '', `param04` longtext, - `const05` binary(3) NOT NULL default '', + `const05` varbinary(3) NOT NULL default '', `param05` longblob, `const06` varchar(10) NOT NULL default '', `param06` longtext, diff --git a/mysql-test/r/ps_6bdb.result b/mysql-test/r/ps_6bdb.result index 3f9f3c61575..07034e01869 100644 --- a/mysql-test/r/ps_6bdb.result +++ b/mysql-test/r/ps_6bdb.result @@ -1758,7 +1758,7 @@ t5 CREATE TABLE `t5` ( `param03` double default NULL, `const04` varchar(3) NOT NULL default '', `param04` longtext, - `const05` binary(3) NOT NULL default '', + `const05` varbinary(3) NOT NULL default '', `param05` longblob, `const06` varchar(10) NOT NULL default '', `param06` longtext, diff --git a/mysql-test/r/type_ranges.result b/mysql-test/r/type_ranges.result index 4f2d2d7b54c..ef4f65eda1f 100644 --- a/mysql-test/r/type_ranges.result +++ b/mysql-test/r/type_ranges.result @@ -272,7 +272,7 @@ auto bigint(17) unsigned NULL PRI 0 select,insert,update,references t1 bigint(1) NULL 0 select,insert,update,references t2 varchar(1) latin1_swedish_ci select,insert,update,references t3 varchar(256) latin1_swedish_ci select,insert,update,references -t4 varchar(256) NULL select,insert,update,references +t4 varbinary(256) NULL select,insert,update,references t5 longtext latin1_swedish_ci select,insert,update,references t6 longblob NULL select,insert,update,references t7 char(0) latin1_swedish_ci select,insert,update,references diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index d4ce1e45bf0..1fbba8ebbb7 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -655,7 +655,7 @@ f show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `f` binary(24) default NULL + `f` varbinary(24) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 SELECT y from t2 UNION select da from t2; @@ -666,7 +666,7 @@ y show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `y` binary(10) default NULL + `y` varbinary(10) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 SELECT y from t2 UNION select dt from t2; @@ -677,7 +677,7 @@ y show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `y` binary(19) default NULL + `y` varbinary(19) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 SELECT da from t2 UNION select dt from t2; @@ -699,7 +699,7 @@ testc show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `dt` binary(19) default NULL + `dt` varbinary(19) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 SELECT dt from t2 UNION select sv from t2; @@ -710,7 +710,7 @@ testv show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `dt` binary(19) default NULL + `dt` varbinary(19) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 SELECT sc from t2 UNION select sv from t2; diff --git a/sql/field.cc b/sql/field.cc index 73f442a868e..dafb3dc25da 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4778,11 +4778,18 @@ void Field_varstring::sort_string(char *to,uint length) void Field_varstring::sql_type(String &res) const { + THD *thd= table->in_use; CHARSET_INFO *cs=res.charset(); - ulong length= cs->cset->snprintf(cs,(char*) res.ptr(), - res.alloced_length(),"varchar(%u)", - field_length / charset()->mbmaxlen); + ulong length; + + length= cs->cset->snprintf(cs,(char*) res.ptr(), + res.alloced_length(), "%s(%d)", + (has_charset() ? "varchar" : "varbinary"), + (int) field_length / charset()->mbmaxlen); res.length(length); + if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && + has_charset() && (charset()->state & MY_CS_BINSORT)) + res.append(" binary"); } From ff2c115ba8f048a117f9b3d03bd16b5883087826 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 14:33:09 +0200 Subject: [PATCH 16/52] row0mysql.c: Remove parameter from call to btr_search_validate(). buf0buf.c: Initialize member "index" of buf_block_t. buf0buf.h: Add member "index" to buf_block_t. btr0sea.h: Remove parameter of btr_search_validate() btr0sea.c: Make use of the added member "index" of buf_block_t. innobase/btr/btr0sea.c: Make use of the added member "index" of buf_block_t. innobase/include/btr0sea.h: Remove parameter of btr_search_validate() innobase/include/buf0buf.h: Add member "index" to buf_block_t. innobase/buf/buf0buf.c: Initialize member "index" of buf_block_t. innobase/row/row0mysql.c: Remove parameter from call to btr_search_validate(). --- innobase/btr/btr0sea.c | 22 +++++++++++++++++----- innobase/buf/buf0buf.c | 2 ++ innobase/include/btr0sea.h | 5 ++--- innobase/include/buf0buf.h | 2 ++ innobase/row/row0mysql.c | 2 +- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/innobase/btr/btr0sea.c b/innobase/btr/btr0sea.c index 40ccf56492f..468c5efd24d 100644 --- a/innobase/btr/btr0sea.c +++ b/innobase/btr/btr0sea.c @@ -411,6 +411,9 @@ btr_search_update_hash_ref( ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED) || rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ + ut_ad(buf_block_align(btr_cur_get_rec(cursor)) == block); + ut_a(!block->is_hashed || block->index == cursor->index); + if (block->is_hashed && (info->n_hash_potential > 0) && (block->curr_n_fields == info->n_fields) @@ -957,6 +960,7 @@ btr_search_drop_page_hash_index( ha_remove_all_nodes_to_page(table, page); block->is_hashed = FALSE; + block->index = NULL; rw_lock_x_unlock(&btr_search_latch); } @@ -1170,6 +1174,7 @@ btr_search_build_page_hash_index( block->curr_n_fields = n_fields; block->curr_n_bytes = n_bytes; block->curr_side = side; + block->index = index; for (i = 0; i < n_cached; i++) { @@ -1215,6 +1220,8 @@ btr_search_move_or_delete_hash_entries( ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX)); ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ + ut_a(!new_block->is_hashed || new_block->index == index); + ut_a(!block->is_hashed || block->index == index); rw_lock_s_lock(&btr_search_latch); @@ -1284,6 +1291,7 @@ btr_search_update_hash_on_delete( return; } + ut_a(block->index == cursor->index); ut_a(block->curr_n_fields + block->curr_n_bytes > 0); table = btr_search_sys->hash_index; @@ -1329,6 +1337,8 @@ btr_search_update_hash_node_on_insert( return; } + ut_a(block->index == cursor->index); + rw_lock_x_lock(&btr_search_latch); if ((cursor->flag == BTR_CUR_HASH) @@ -1394,6 +1404,8 @@ btr_search_update_hash_on_insert( return; } + ut_a(block->index == cursor->index); + tree_id = ((cursor->index)->tree)->id; n_fields = block->curr_n_fields; @@ -1499,10 +1511,9 @@ function_exit: Validates the search system. */ ibool -btr_search_validate( -/*================*/ +btr_search_validate(void) +/*=====================*/ /* out: TRUE if ok */ - dict_index_t* index) /* in: record descriptor */ { buf_block_t* block; page_t* page; @@ -1521,8 +1532,9 @@ btr_search_validate( while (node != NULL) { block = buf_block_align(node->data); page = buf_frame_align(node->data); - offsets = rec_reget_offsets((rec_t*) node->data, index, - offsets, block->curr_n_fields + offsets = rec_reget_offsets((rec_t*) node->data, + block->index, offsets, + block->curr_n_fields + (block->curr_n_bytes > 0), heap); if (!block->is_hashed diff --git a/innobase/buf/buf0buf.c b/innobase/buf/buf0buf.c index 2f8ce7507ba..8ca353aa5fb 100644 --- a/innobase/buf/buf0buf.c +++ b/innobase/buf/buf0buf.c @@ -460,6 +460,7 @@ buf_block_init( block->file_page_was_freed = FALSE; block->check_index_page_at_flush = FALSE; + block->index = NULL; block->in_free_list = FALSE; block->in_LRU_list = FALSE; @@ -1536,6 +1537,7 @@ buf_page_init( block->offset = offset; block->check_index_page_at_flush = FALSE; + block->index = NULL; block->lock_hash_val = lock_rec_hash(space, offset); block->lock_mutex = NULL; diff --git a/innobase/include/btr0sea.h b/innobase/include/btr0sea.h index 73cd95d1464..78e88a24083 100644 --- a/innobase/include/btr0sea.h +++ b/innobase/include/btr0sea.h @@ -130,10 +130,9 @@ btr_search_update_hash_on_delete( Validates the search system. */ ibool -btr_search_validate( -/*================*/ +btr_search_validate(void); +/*======================*/ /* out: TRUE if ok */ - dict_index_t* index); /* in: record descriptor */ /* Search info directions */ #define BTR_SEA_NO_DIRECTION 1 diff --git a/innobase/include/buf0buf.h b/innobase/include/buf0buf.h index b46b8ce40be..c116d7e0b3e 100644 --- a/innobase/include/buf0buf.h +++ b/innobase/include/buf0buf.h @@ -740,6 +740,8 @@ struct buf_block_struct{ buffer pool which are index pages, but this flag is not set because we do not keep track of all pages */ + dict_index_t* index; /* index for which the adaptive + hash index has been created */ /* 2. Page flushing fields */ UT_LIST_NODE_T(buf_block_t) flush_list; diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index be243b44488..fa584df15db 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -3398,7 +3398,7 @@ row_check_table_for_mysql( /* We validate also the whole adaptive hash index for all tables at every CHECK TABLE */ - if (!btr_search_validate(index)) { + if (!btr_search_validate()) { ret = DB_ERROR; } From 746799fb23aaef1705e9ee66de6a2cb77a8af2c8 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 14:34:58 +0200 Subject: [PATCH 17/52] Cset exclude: heikki@hundin.mysql.fi|ChangeSet|20041027124510|04970 innobase/btr/btr0sea.c: Exclude innobase/buf/buf0buf.c: Exclude innobase/buf/buf0lru.c: Exclude innobase/ha/ha0ha.c: Exclude innobase/include/buf0buf.h: Exclude innobase/include/ha0ha.h: Exclude innobase/include/hash0hash.h: Exclude --- innobase/btr/btr0sea.c | 74 +++++++++++++++++++-- innobase/buf/buf0buf.c | 1 - innobase/buf/buf0lru.c | 1 - innobase/ha/ha0ha.c | 125 ++++++++++------------------------- innobase/include/buf0buf.h | 8 +-- innobase/include/ha0ha.h | 18 ++--- innobase/include/hash0hash.h | 18 +---- 7 files changed, 114 insertions(+), 131 deletions(-) diff --git a/innobase/btr/btr0sea.c b/innobase/btr/btr0sea.c index 468c5efd24d..f2f5d09fa0e 100644 --- a/innobase/btr/btr0sea.c +++ b/innobase/btr/btr0sea.c @@ -439,8 +439,8 @@ btr_search_update_hash_ref( ha_insert_for_fold(btr_search_sys->hash_index, fold, rec); } -} - +} + /************************************************************************* Updates the search info. */ @@ -926,6 +926,17 @@ btr_search_drop_page_hash_index( { hash_table_t* table; buf_block_t* block; + ulint n_fields; + ulint n_bytes; + rec_t* rec; + rec_t* sup; + ulint fold; + ulint prev_fold; + dulint tree_id; + ulint n_cached; + ulint n_recs; + ulint* folds; + ulint i; #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); @@ -951,18 +962,73 @@ btr_search_drop_page_hash_index( || (block->buf_fix_count == 0)); #endif /* UNIV_SYNC_DEBUG */ - ut_a(block->curr_n_fields + block->curr_n_bytes > 0); + n_fields = block->curr_n_fields; + n_bytes = block->curr_n_bytes; + + ut_a(n_fields + n_bytes > 0); rw_lock_s_unlock(&btr_search_latch); + n_recs = page_get_n_recs(page); + + /* Calculate and cache fold values into an array for fast deletion + from the hash index */ + + folds = mem_alloc(n_recs * sizeof(ulint)); + + n_cached = 0; + + sup = page_get_supremum_rec(page); + + rec = page_get_infimum_rec(page); + rec = page_rec_get_next(rec); + + if (rec != sup) { + ut_a(n_fields <= rec_get_n_fields(rec)); + + if (n_bytes > 0) { + ut_a(n_fields < rec_get_n_fields(rec)); + } + } + + tree_id = btr_page_get_index_id(page); + + prev_fold = 0; + + while (rec != sup) { + /* FIXME: in a mixed tree, not all records may have enough + ordering fields: */ + + fold = rec_fold(rec, n_fields, n_bytes, tree_id); + + if (fold == prev_fold && prev_fold != 0) { + + goto next_rec; + } + + /* Remove all hash nodes pointing to this page from the + hash chain */ + + folds[n_cached] = fold; + n_cached++; +next_rec: + rec = page_rec_get_next(rec); + prev_fold = fold; + } + rw_lock_x_lock(&btr_search_latch); - ha_remove_all_nodes_to_page(table, page); + for (i = 0; i < n_cached; i++) { + + ha_remove_all_nodes_to_page(table, folds[i], page); + } block->is_hashed = FALSE; block->index = NULL; rw_lock_x_unlock(&btr_search_latch); + + mem_free(folds); } /************************************************************************ diff --git a/innobase/buf/buf0buf.c b/innobase/buf/buf0buf.c index 8ca353aa5fb..3930ea93889 100644 --- a/innobase/buf/buf0buf.c +++ b/innobase/buf/buf0buf.c @@ -466,7 +466,6 @@ buf_block_init( block->in_LRU_list = FALSE; block->n_pointers = 0; - block->hash_nodes = NULL; rw_lock_create(&(block->lock)); ut_ad(rw_lock_validate(&(block->lock))); diff --git a/innobase/buf/buf0lru.c b/innobase/buf/buf0lru.c index 985426a9e2b..8460a049d3e 100644 --- a/innobase/buf/buf0lru.c +++ b/innobase/buf/buf0lru.c @@ -823,7 +823,6 @@ buf_LRU_block_free_non_file_page( || (block->state == BUF_BLOCK_READY_FOR_USE)); ut_a(block->n_pointers == 0); - ut_a(block->hash_nodes == NULL); ut_a(!block->in_free_list); block->state = BUF_BLOCK_NOT_USED; diff --git a/innobase/ha/ha0ha.c b/innobase/ha/ha0ha.c index 49b59882626..ad1391ff83e 100644 --- a/innobase/ha/ha0ha.c +++ b/innobase/ha/ha0ha.c @@ -65,53 +65,10 @@ ha_create( return(table); } -/***************************************************************** -Removes an adaptive hash index node from the doubly linked list of hash nodes -for the buffer block. */ -UNIV_INLINE -void -ha_remove_buf_block_node( -/*=====================*/ - buf_block_t* block, /* in: buffer block */ - ha_node_t* node) /* in: an adaptive hash index node */ -{ - if (node == block->hash_nodes) { - block->hash_nodes = node->next_for_block; - } - - if (node->prev_for_block != NULL) { - (node->prev_for_block)->next_for_block = node->next_for_block; - } - - if (node->next_for_block != NULL) { - (node->next_for_block)->prev_for_block = node->prev_for_block; - } -} - -/***************************************************************** -Adds an adaptive hash index node to the start of the doubly linked list of -hash nodes for the buffer block. */ -UNIV_INLINE -void -ha_add_buf_block_node( -/*==================*/ - buf_block_t* block, /* in: buffer block */ - ha_node_t* node) /* in: an adaptive hash index node */ -{ - node->next_for_block = block->hash_nodes; - node->prev_for_block = NULL; - - block->hash_nodes = node; - - if (node->next_for_block != NULL) { - (node->next_for_block)->prev_for_block = node; - } -} - /***************************************************************** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node -is inserted. This function is only used in the adaptive hash index. */ +is inserted. */ ibool ha_insert_for_fold( @@ -127,7 +84,6 @@ ha_insert_for_fold( { hash_cell_t* cell; ha_node_t* node; - buf_block_t* block; ha_node_t* prev_node; buf_block_t* prev_block; ulint hash; @@ -136,9 +92,6 @@ ha_insert_for_fold( #ifdef UNIV_SYNC_DEBUG ut_ad(!table->mutexes || mutex_own(hash_get_mutex(table, fold))); #endif /* UNIV_SYNC_DEBUG */ - - block = buf_block_align(data); - hash = hash_calc_hash(fold, table); cell = hash_get_nth_cell(table, hash); @@ -151,15 +104,7 @@ ha_insert_for_fold( prev_block = buf_block_align(prev_node->data); ut_a(prev_block->n_pointers > 0); prev_block->n_pointers--; - - block->n_pointers++; - - if (prev_block != block) { - ha_remove_buf_block_node(prev_block, - prev_node); - ha_add_buf_block_node(block, - prev_node); - } + buf_block_align(data)->n_pointers++; } prev_node->data = data; @@ -186,9 +131,7 @@ ha_insert_for_fold( ha_node_set_data(node, data); if (table->adaptive) { - block->n_pointers++; - - ha_add_buf_block_node(block, node); + buf_block_align(data)->n_pointers++; } node->fold = fold; @@ -223,15 +166,9 @@ ha_delete_hash_node( hash_table_t* table, /* in: hash table */ ha_node_t* del_node) /* in: node to be deleted */ { - buf_block_t* block; - if (table->adaptive) { - block = buf_block_align(del_node->data); - - ut_a(block->n_pointers > 0); - - block->n_pointers--; - ha_remove_buf_block_node(block, del_node); + ut_a(buf_block_align(del_node->data)->n_pointers > 0); + buf_block_align(del_node->data)->n_pointers--; } HASH_DELETE_AND_COMPACT(ha_node_t, next, table, del_node); @@ -272,8 +209,6 @@ ha_search_and_update_if_found( void* data, /* in: pointer to the data */ void* new_data)/* in: new pointer to the data */ { - buf_block_t* old_block; - buf_block_t* block; ha_node_t* node; #ifdef UNIV_SYNC_DEBUG @@ -285,15 +220,8 @@ ha_search_and_update_if_found( if (node) { if (table->adaptive) { ut_a(buf_block_align(node->data)->n_pointers > 0); - - old_block = buf_block_align(node->data); - ut_a(old_block->n_pointers > 0); - old_block->n_pointers--; - ha_remove_buf_block_node(old_block, node); - - block = buf_block_align(new_data); - block->n_pointers++; - ha_add_buf_block_node(block, node); + buf_block_align(node->data)->n_pointers--; + buf_block_align(new_data)->n_pointers++; } node->data = new_data; @@ -308,25 +236,43 @@ void ha_remove_all_nodes_to_page( /*========================*/ hash_table_t* table, /* in: hash table */ + ulint fold, /* in: fold value */ page_t* page) /* in: buffer page */ { - buf_block_t* block; ha_node_t* node; - block = buf_block_align(page); - - node = block->hash_nodes; +#ifdef UNIV_SYNC_DEBUG + ut_ad(!table->mutexes || mutex_own(hash_get_mutex(table, fold))); +#endif /* UNIV_SYNC_DEBUG */ + node = ha_chain_get_first(table, fold); while (node) { - /* Remove the hash node */ + if (buf_frame_align(ha_node_get_data(node)) == page) { - ha_delete_hash_node(table, node); + /* Remove the hash node */ - node = block->hash_nodes; + ha_delete_hash_node(table, node); + + /* Start again from the first node in the chain + because the deletion may compact the heap of + nodes and move other nodes! */ + + node = ha_chain_get_first(table, fold); + } else { + node = ha_chain_get_next(node); + } } +#ifdef UNIV_DEBUG + /* Check that all nodes really got deleted */ - ut_a(block->n_pointers == 0); - ut_a(block->hash_nodes == NULL); + node = ha_chain_get_first(table, fold); + + while (node) { + ut_a(buf_frame_align(ha_node_get_data(node)) != page); + + node = ha_chain_get_next(node); + } +#endif } /***************************************************************** @@ -406,7 +352,6 @@ ha_print_info( n_bufs++; } - fprintf(file, ", node heap has %lu buffer(s)\n", - (ulong) n_bufs); + fprintf(file, ", node heap has %lu buffer(s)\n", (ulong) n_bufs); } } diff --git a/innobase/include/buf0buf.h b/innobase/include/buf0buf.h index c116d7e0b3e..8df1211327f 100644 --- a/innobase/include/buf0buf.h +++ b/innobase/include/buf0buf.h @@ -29,7 +29,6 @@ Created 11/5/1995 Heikki Tuuri #include "buf0types.h" #include "sync0rw.h" #include "hash0hash.h" -#include "ha0ha.h" #include "ut0byte.h" #include "os0proc.h" @@ -828,7 +827,7 @@ struct buf_block_struct{ records with the same prefix should be indexed in the hash index */ - /* The following 6 fields are protected by btr_search_latch: */ + /* The following 4 fields are protected by btr_search_latch: */ ibool is_hashed; /* TRUE if hash index has already been built on this page; note that it does @@ -845,11 +844,6 @@ struct buf_block_struct{ ulint curr_side; /* BTR_SEARCH_LEFT_SIDE or BTR_SEARCH_RIGHT_SIDE in hash indexing */ - ha_node_t* hash_nodes; /* a doubly linked list of hash nodes - for this buffer block; this points to - the first node in the list if any; - note that we do not use UT_LST_ macros - to manipulate this list */ /* 6. Debug fields */ #ifdef UNIV_SYNC_DEBUG rw_lock_t debug_latch; /* in the debug version, each thread diff --git a/innobase/include/ha0ha.h b/innobase/include/ha0ha.h index 5e3af9c1869..bdaecfcc57a 100644 --- a/innobase/include/ha0ha.h +++ b/innobase/include/ha0ha.h @@ -54,7 +54,7 @@ ha_create( /***************************************************************** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node -is inserted. This function is only used in the adaptive hash index. */ +is inserted. */ ibool ha_insert_for_fold( @@ -111,6 +111,7 @@ void ha_remove_all_nodes_to_page( /*========================*/ hash_table_t* table, /* in: hash table */ + ulint fold, /* in: fold value */ page_t* page); /* in: buffer page */ /***************************************************************** Validates a hash table. */ @@ -133,18 +134,9 @@ ha_print_info( typedef struct ha_node_struct ha_node_t; struct ha_node_struct { - ha_node_t* next; /* next chain node; NULL if none */ - void* data; /* pointer to the data */ - ulint fold; /* fold value for the data */ - ha_node_t* next_for_block;/* in an adaptive hash index - (btr0sea.c), a doubly linked list of hash - nodes for the buffer block; these nodes - contain pointers to index records on the - page; in the last node this field is NULL; - note that we do not use UT_LST_ macros - to manipulate this list */ - ha_node_t* prev_for_block;/* pointer to the previous node; in the - first node NULL */ + ha_node_t* next; /* next chain node or NULL if none */ + void* data; /* pointer to the data */ + ulint fold; /* fold value for the data */ }; #ifndef UNIV_NONINL diff --git a/innobase/include/hash0hash.h b/innobase/include/hash0hash.h index befe8a8d757..51315e40875 100644 --- a/innobase/include/hash0hash.h +++ b/innobase/include/hash0hash.h @@ -166,7 +166,7 @@ hash_get_n_cells( /*********************************************************************** Deletes a struct which is stored in the heap of the hash table, and compacts the heap. The fold value must be stored in the struct NODE in a field named -'fold'. This macro is only used for the adaptive hash index. */ +'fold'. */ #define HASH_DELETE_AND_COMPACT(TYPE, NAME, TABLE, NODE)\ do {\ @@ -191,23 +191,11 @@ do {\ /* Copy the top node in place of NODE */\ \ *(NODE) = *top_node111;\ -\ - /* Update the adaptive hash list of the buffer block that\ - corresponds to the top node */\ - if (top_node111->next_for_block != NULL) {\ - (top_node111->next_for_block)->prev_for_block = NODE;\ - }\ -\ - if (top_node111->prev_for_block != NULL) {\ - (top_node111->prev_for_block)->next_for_block = NODE;\ - } else {\ - buf_block_align(top_node111->data)->hash_nodes = NODE;\ - }\ -\ - /* Look for the hash pointer to the top node, to update it */\ \ cell111 = hash_get_nth_cell(TABLE,\ hash_calc_hash(top_node111->fold, TABLE));\ +\ + /* Look for the pointer to the top node, to update it */\ \ if (cell111->node == top_node111) {\ /* The top node is the first in the chain */\ From f9bd116c297f80613ed0e51750e20e5fcb623f75 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 15:20:58 +0200 Subject: [PATCH 18/52] InnoDB: Make btr_search_drop_page_hash_index() work with the compact record format without requiring the doubly linked list added by Heikki in ChangeSet@1.1627.16.2 innobase/btr/btr0sea.c: btr_search_drop_page_hash_index(): support the compact record format innobase/ibuf/ibuf0ibuf.c: ibuf_insert_to_index_page(): Remove unnecessary computation of field offsets. innobase/row/row0ins.c: row_ins_index_entry_low(): Remove unnecessary computation of field offsets. --- innobase/btr/btr0sea.c | 16 ++++++++++++---- innobase/ibuf/ibuf0ibuf.c | 7 +------ innobase/row/row0ins.c | 5 ++--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/innobase/btr/btr0sea.c b/innobase/btr/btr0sea.c index f2f5d09fa0e..c18fa9a923d 100644 --- a/innobase/btr/btr0sea.c +++ b/innobase/btr/btr0sea.c @@ -937,6 +937,8 @@ btr_search_drop_page_hash_index( ulint n_recs; ulint* folds; ulint i; + mem_heap_t* heap; + ulint* offsets; #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); @@ -984,10 +986,10 @@ btr_search_drop_page_hash_index( rec = page_rec_get_next(rec); if (rec != sup) { - ut_a(n_fields <= rec_get_n_fields(rec)); + ut_a(n_fields <= rec_get_n_fields(rec, block->index)); if (n_bytes > 0) { - ut_a(n_fields < rec_get_n_fields(rec)); + ut_a(n_fields < rec_get_n_fields(rec, block->index)); } } @@ -995,11 +997,15 @@ btr_search_drop_page_hash_index( prev_fold = 0; + heap = mem_heap_create(100); + offsets = NULL; + while (rec != sup) { /* FIXME: in a mixed tree, not all records may have enough ordering fields: */ - - fold = rec_fold(rec, n_fields, n_bytes, tree_id); + offsets = rec_reget_offsets(rec, block->index, + offsets, n_fields + (n_bytes > 0), heap); + fold = rec_fold(rec, offsets, n_fields, n_bytes, tree_id); if (fold == prev_fold && prev_fold != 0) { @@ -1016,6 +1022,8 @@ next_rec: prev_fold = fold; } + mem_heap_free(heap); + rw_lock_x_lock(&btr_search_latch); for (i = 0; i < n_cached; i++) { diff --git a/innobase/ibuf/ibuf0ibuf.c b/innobase/ibuf/ibuf0ibuf.c index c7ca03f9901..959c66159c5 100644 --- a/innobase/ibuf/ibuf0ibuf.c +++ b/innobase/ibuf/ibuf0ibuf.c @@ -2812,7 +2812,6 @@ ibuf_insert_to_index_page( rec_t* rec; page_t* bitmap_page; ulint old_bits; - mem_heap_t* heap; ut_ad(ibuf_inside()); ut_ad(dtuple_check_typed(entry)); @@ -2824,12 +2823,9 @@ ibuf_insert_to_index_page( goto dump; } - heap = mem_heap_create(100); rec = page_rec_get_next(page_get_infimum_rec(page)); - if (rec_offs_n_fields(rec_get_offsets(rec, index, ULINT_UNDEFINED, - heap)) != dtuple_get_n_fields(entry)) { - mem_heap_free(heap); + if (rec_get_n_fields(rec, index) != dtuple_get_n_fields(entry)) { fputs( "InnoDB: Trying to insert a record from the insert buffer to an index page\n" "InnoDB: but the number of fields does not match!\n", stderr); @@ -2847,7 +2843,6 @@ ibuf_insert_to_index_page( return; } - mem_heap_free(heap); low_match = page_cur_search(page, index, entry, PAGE_CUR_LE, &page_cur); diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 1c55005dcfa..a87a08fa3fe 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -1928,9 +1928,8 @@ row_ins_index_entry_low( buf_frame_align(btr_cur_get_rec(&cursor)))); if (!page_rec_is_supremum(first_rec)) { - offsets = rec_get_offsets(first_rec, index, - ULINT_UNDEFINED, heap); - ut_a(rec_offs_n_fields(offsets) == dtuple_get_n_fields(entry)); + ut_a(rec_get_n_fields(first_rec, index) + == dtuple_get_n_fields(entry)); } n_unique = dict_index_get_n_unique(index); From b80db00ef31f7f57d618bd1399c8743a101d11e7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 16:28:25 +0100 Subject: [PATCH 19/52] A fix for a crash in sp.test with -debug-max. When the joins were not cleaned up before the tables were closed, a JOIN_TAB still held a pointer to a meanwhile closed table and tried to close it again during item cleanup... --- sql/sp_head.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 114ff0d451a..31c6075b590 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -681,6 +681,8 @@ sp_head::execute_procedure(THD *thd, List *args) nctx->set_oindex(i, static_cast(it)->get_offset()); } } + // Clean up the joins before closing the tables. + thd->lex->unit.cleanup(); // Close tables opened for subselect in argument list close_thread_tables(thd); From c43c904fa52e19f60fc5d9a23448c2dd72f69ffe Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 17:58:00 +0100 Subject: [PATCH 20/52] ndb - Fixed bitfields of upto 31 bits ndb/include/util/Bitmask.hpp: Fixed bitfields of upto 31 bits ndb/src/common/util/Bitmask.cpp: Fixed bitfields of upto 31 bits --- ndb/include/util/Bitmask.hpp | 27 ++-- ndb/src/common/util/Bitmask.cpp | 214 ++++++++++++++++++++------------ 2 files changed, 151 insertions(+), 90 deletions(-) diff --git a/ndb/include/util/Bitmask.hpp b/ndb/include/util/Bitmask.hpp index b6bc6e3d5e3..2cd95190ed7 100644 --- a/ndb/include/util/Bitmask.hpp +++ b/ndb/include/util/Bitmask.hpp @@ -811,36 +811,45 @@ public: }; inline void -BitmaskImpl::getField(unsigned size, const Uint32 data[], +BitmaskImpl::getField(unsigned size, const Uint32 src[], unsigned pos, unsigned len, Uint32 dst[]) { assert(len > 0); assert(pos + len < (size << 5)); - Uint32 word = pos >> 5; + + src += (pos >> 5); Uint32 offset = pos & 31; + dst[0] = (* src >> offset) & (len >= 32 ? ~0 : (1 << len) - 1); + if(offset + len <= 32) { - dst[0] = (data[word] >> offset) & ((1 << len) - 1); return; } - getFieldImpl(data, pos, len, dst); + Uint32 used = (32 - offset); + assert(len > used); + getFieldImpl(src+1, used & 31, len-used, dst+(used >> 5)); } inline void -BitmaskImpl::setField(unsigned size, Uint32 data[], +BitmaskImpl::setField(unsigned size, Uint32 dst[], unsigned pos, unsigned len, const Uint32 src[]) { assert(len > 0); assert(pos + len < (size << 5)); - Uint32 word = pos >> 5; + + dst += (pos >> 5); Uint32 offset = pos & 31; - Uint32 mask = ((1 << len) - 1) << offset; - data[word] = (data[word] & ~mask) | ((src[0] << offset) & mask); + Uint32 mask = (len >= 32 ? ~0 : (1 << len) - 1) << offset; + + * dst = (* dst & ~mask) | ((*src << offset) & mask); + if(offset + len <= 32) { return; } - setFieldImpl(data, pos, len, src); + Uint32 used = (32 - offset); + assert(len > used); + setFieldImpl(dst+1, used & 31, len-used, src+(used >> 5)); } diff --git a/ndb/src/common/util/Bitmask.cpp b/ndb/src/common/util/Bitmask.cpp index 536620ce305..eecde77aad3 100644 --- a/ndb/src/common/util/Bitmask.cpp +++ b/ndb/src/common/util/Bitmask.cpp @@ -1,69 +1,59 @@ #include #include -#ifndef __TEST_BITMASK__ -void -BitmaskImpl::getFieldImpl(const Uint32 data[], - unsigned pos, unsigned l, Uint32 dst[]) +static +void print(const Uint32 src[], Uint32 len, Uint32 pos = 0) { - Uint32 word; - Uint32 offset; - int next_offset,i; - int len= l; - for(i=0,next_offset=0;len >0;i++) + printf("b'"); + for(int i = 0; i> 5; - offset = pos & 31; - - if(i%32==0) - dst[i/32]=0; - - if(!next_offset && (offset+len) > 32) - { - dst[i/32] = (data[word] >> offset) & ((1 << (32-offset)) - 1); - next_offset = 32-offset; - } + if(BitmaskImpl::get((pos + len + 31) >> 5, src, i+pos)) + printf("1"); else - { - dst[i/32]|= ((data[word] >> offset) & ((1 << len) - 1)) << next_offset; - next_offset = 0; - } - - if(len < 32-offset) - break; - - len-=32-offset; - pos+=32-offset; + printf("0"); + if((i & 7) == 7) + printf(" "); } } - -void -BitmaskImpl::setFieldImpl(Uint32 data[], - unsigned pos, unsigned l, const Uint32 src[]) -{ - Uint32 word; - Uint32 offset; - Uint32 mask; - int i=0,stored=0; - int len= l; - while(len>0) +#ifndef __TEST_BITMASK__ +void +BitmaskImpl::getFieldImpl(const Uint32 src[], + unsigned shift, unsigned len, Uint32 dst[]) +{ + assert(shift < 32); + + if(len <= (32 - shift)) { - ndbout_c("len: %d", len); - word = pos >> 5; - offset = pos & 31; - - if(offset+len > 32) - stored = 32-offset; - else - stored = len; - - mask = ((1 << stored) - 1) << (i+offset)%32; - data[word] = (data[word] & ~mask) | ((src[i/32] << (i+offset)%32) & mask); - - i+=stored; - len-=32-offset; - pos+=32-offset; + * dst++ |= ((* src) & ((1 << len) - 1)) << shift; + } + else + { + abort(); + while(len > 32) + { + * dst++ |= (* src) << shift; + * dst = (* src++) >> (32 - shift); + len -= 32; + } + } +} + +void +BitmaskImpl::setFieldImpl(Uint32 dst[], + unsigned shift, unsigned len, const Uint32 src[]) +{ + assert(shift < 32); + + Uint32 mask; + if(len < (32 - shift)) + { + mask = (1 << len) - 1; + * dst = (* dst & ~mask) | (((* src++) >> shift) & mask); + } + else + { + abort(); } } @@ -71,7 +61,7 @@ BitmaskImpl::setFieldImpl(Uint32 data[], #define DEBUG 0 #include -void do_test(int bitmask_size); +static void do_test(int bitmask_size); int main(int argc, char** argv) @@ -91,30 +81,86 @@ struct Alloc Vector data; }; -void require(bool b) +static void require(bool b) { if(!b) abort(); } -void +static int val_pos = 0; +static int val[] = { 384, 241, 32, + 1,1,1,1, 0,0,0,0, 1,1,1,1, 0,0,0,0, + 241 }; + +static int lrand() +{ +#if 0 + return val[val_pos++]; +#else + return rand(); +#endif +} + +static +void rand(Uint32 dst[], Uint32 len) +{ + for(int i = 0; i> 5, dst, i, (lrand() % 1000) > 500); +} + +static +void simple(int pos, int size) +{ + ndbout_c("simple pos: %d size: %d", pos, size); + Vector _mask; + Vector _src; + Vector _dst; + Uint32 sz32 = (size + pos + 32) >> 5; + const Uint32 sz = 4 * sz32; + + Uint32 zero = 0; + _mask.fill(sz32, zero); + _src.fill(sz32, zero); + _dst.fill(sz32, zero); + + Uint32 * src = _src.getBase(); + Uint32 * dst = _dst.getBase(); + Uint32 * mask = _mask.getBase(); + + memset(src, 0x0, sz); + memset(dst, 0x0, sz); + memset(mask, 0x0, sz); + rand(src, size); + BitmaskImpl::setField(sz32, mask, pos, size, src); + BitmaskImpl::getField(sz32, mask, pos, size, dst); + printf("src: "); print(src, size); printf("\n"); + printf("msk: "); print(mask, size, pos); printf("\n"); + printf("dst: "); print(dst, size); printf("\n"); + require(memcmp(src, dst, sz) == 0); +}; + +static void do_test(int bitmask_size) { +#if 0 + simple(rand() % 33, (rand() % 31)+1); +#else Vector alloc_list; bitmask_size = (bitmask_size + 31) & ~31; Uint32 sz32 = (bitmask_size >> 5); Vector alloc_mask; Vector test_mask; - Vector tmp; ndbout_c("Testing bitmask of size %d", bitmask_size); Uint32 zero = 0; alloc_mask.fill(sz32, zero); test_mask.fill(sz32, zero); - tmp.fill(sz32, zero); - for(int i = 0; i<1000; i++) + for(int i = 0; i<5000; i++) { - int pos = rand() % (bitmask_size - 1); + Vector tmp; + tmp.fill(sz32, zero); + + int pos = lrand() % (bitmask_size - 1); int free = 0; if(BitmaskImpl::get(sz32, alloc_mask.getBase(), pos)) { @@ -133,26 +179,26 @@ do_test(int bitmask_size) break; } } - if(DEBUG) - ndbout_c("freeing [ %d %d ]", min, max); - require(pos >= min && pos < max); BitmaskImpl::getField(sz32, test_mask.getBase(), min, max-min, tmp.getBase()); - if(memcmp(tmp.getBase(), alloc_list[j].data.getBase(), - ((max - min) + 31) >> 5) != 0) + if(DEBUG) { - printf("mask: "); + printf("freeing [ %d %d ]", min, max); + printf("- mask: "); + for(size_t k = 0; k<(((max - min)+31)>>5); k++) + printf("%.8x ", tmp.getBase()[k]); + + printf("save: "); size_t k; Alloc& a = alloc_list[j]; for(k = 0; k>5); k++) - printf("%.8x ", tmp.getBase()[k]); - printf("\n"); + } + int bytes = (max - min + 7) >> 3; + if(memcmp(tmp.getBase(), alloc_list[j].data.getBase(), bytes) != 0) + { abort(); } while(min < max) @@ -161,31 +207,36 @@ do_test(int bitmask_size) } else { + Vector tmp; + tmp.fill(sz32, zero); + // Bit was free // 1) Check how much space is avaiable - // 2) Create new allocation of random size - // 3) Fill data with random data + // 2) Create new allocation of lrandom size + // 3) Fill data with lrandom data // 4) Update alloc mask while(pos+free < bitmask_size && !BitmaskImpl::get(sz32, alloc_mask.getBase(), pos+free)) free++; - Uint32 sz = (rand() % free); sz = sz ? sz : 1; + Uint32 sz = (lrand() % free); + sz = sz ? sz : 1; + sz = (sz > 31) ? 31 : sz; Alloc a; a.pos = pos; a.size = sz; - a.data.fill(sz >> 5, zero); + a.data.fill(((sz+31)>> 5)-1, zero); if(DEBUG) - ndbout_c("pos %d -> alloc [ %d %d ]", pos, pos, pos+sz); + printf("pos %d -> alloc [ %d %d ]", pos, pos, pos+sz); for(size_t j = 0; j 500) + if((lrand() % 1000) > 500) BitmaskImpl::set((sz + 31) >> 5, a.data.getBase(), j); } if(DEBUG) { - printf("mask: "); + printf("- mask: "); size_t k; for(k = 0; k; From 377aedfb3715b87b1dc67dd37d1ab929368e58ce Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Dec 2004 22:55:34 +0100 Subject: [PATCH 21/52] ndb - Fix arbitrary sized bitfields ndb/src/common/util/Bitmask.cpp: Fix arbitrary sized bitfields --- ndb/src/common/util/Bitmask.cpp | 91 +++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/ndb/src/common/util/Bitmask.cpp b/ndb/src/common/util/Bitmask.cpp index eecde77aad3..2fce1bf2867 100644 --- a/ndb/src/common/util/Bitmask.cpp +++ b/ndb/src/common/util/Bitmask.cpp @@ -11,7 +11,7 @@ void print(const Uint32 src[], Uint32 len, Uint32 pos = 0) printf("1"); else printf("0"); - if((i & 7) == 7) + if((i & 31) == 31) printf(" "); } } @@ -19,42 +19,46 @@ void print(const Uint32 src[], Uint32 len, Uint32 pos = 0) #ifndef __TEST_BITMASK__ void BitmaskImpl::getFieldImpl(const Uint32 src[], - unsigned shift, unsigned len, Uint32 dst[]) + unsigned shiftL, unsigned len, Uint32 dst[]) { - assert(shift < 32); - - if(len <= (32 - shift)) + assert(shiftL < 32); + + unsigned shiftR = 32 - shiftL; + while(len >= 32) { - * dst++ |= ((* src) & ((1 << len) - 1)) << shift; - } - else - { - abort(); - while(len > 32) - { - * dst++ |= (* src) << shift; - * dst = (* src++) >> (32 - shift); - len -= 32; - } + * dst++ |= (* src) << shiftL; + * dst = shiftL ? (* src) >> shiftR : 0; + src++; + len -= 32; } + + * dst++ |= (* src) << shiftL; + * dst = shiftL ? ((* src) >> shiftR) & ((1 << len) - 1) : 0; } void BitmaskImpl::setFieldImpl(Uint32 dst[], - unsigned shift, unsigned len, const Uint32 src[]) + unsigned shiftL, unsigned len, const Uint32 src[]) { - assert(shift < 32); + /** + * + * abcd ef00 + * 00ab cdef + */ + assert(shiftL < 32); + unsigned shiftR = 32 - shiftL; - Uint32 mask; - if(len < (32 - shift)) + while(len >= 32) { - mask = (1 << len) - 1; - * dst = (* dst & ~mask) | (((* src++) >> shift) & mask); - } - else - { - abort(); + * dst = (* src++) >> shiftL; + * dst++ |= shiftL ? (* src) << shiftR : 0; + len -= 32; } + + Uint32 mask = ((1 << len) -1); + * dst = (* dst & ~mask); + * dst |= ((* src++) >> shiftL) & mask; + * dst |= shiftL ? ((* src) << shiftR) & mask : 0; } #else @@ -86,6 +90,19 @@ static void require(bool b) if(!b) abort(); } +static +bool cmp(const Uint32 b1[], const Uint32 b2[], Uint32 len) +{ + Uint32 sz32 = (len + 31) >> 5; + for(int i = 0; i alloc_list; bitmask_size = (bitmask_size + 31) & ~31; @@ -186,10 +203,9 @@ do_test(int bitmask_size) { printf("freeing [ %d %d ]", min, max); printf("- mask: "); - for(size_t k = 0; k<(((max - min)+31)>>5); k++) - printf("%.8x ", tmp.getBase()[k]); - - printf("save: "); + print(tmp.getBase(), max - min); + + printf(" save: "); size_t k; Alloc& a = alloc_list[j]; for(k = 0; k> 3; - if(memcmp(tmp.getBase(), alloc_list[j].data.getBase(), bytes) != 0) + if(!cmp(tmp.getBase(), alloc_list[j].data.getBase(), max - min)) { abort(); } @@ -221,7 +237,6 @@ do_test(int bitmask_size) Uint32 sz = (lrand() % free); sz = sz ? sz : 1; - sz = (sz > 31) ? 31 : sz; Alloc a; a.pos = pos; a.size = sz; @@ -237,9 +252,7 @@ do_test(int bitmask_size) if(DEBUG) { printf("- mask: "); - size_t k; - for(k = 0; k Date: Thu, 9 Dec 2004 11:29:17 +0100 Subject: [PATCH 22/52] bug#6995 - ndb don't store keys in normalized form instead save everythings as normal attributes ndb/src/kernel/blocks/backup/Backup.cpp: Remove special handling of keys, that was build to support tables where keys was only stored in ACC ndb/src/kernel/blocks/backup/Backup.hpp: Remove special handling of keys, that was build to support tables where keys was only stored in ACC ndb/src/kernel/blocks/backup/BackupInit.cpp: Remove special handling of keys, that was build to support tables where keys was only stored in ACC ndb/tools/restore/Restore.cpp: Remove special handling of keys, that was build to support tables where keys was only stored in ACC ndb/tools/restore/Restore.hpp: Remove special handling of keys, that was build to support tables where keys was only stored in ACC --- ndb/src/kernel/blocks/backup/Backup.cpp | 123 ++++---------------- ndb/src/kernel/blocks/backup/Backup.hpp | 40 +------ ndb/src/kernel/blocks/backup/BackupInit.cpp | 1 - ndb/tools/restore/Restore.cpp | 27 ----- ndb/tools/restore/Restore.hpp | 2 - 5 files changed, 24 insertions(+), 169 deletions(-) diff --git a/ndb/src/kernel/blocks/backup/Backup.cpp b/ndb/src/kernel/blocks/backup/Backup.cpp index e6fe63d9014..e7ec6c71196 100644 --- a/ndb/src/kernel/blocks/backup/Backup.cpp +++ b/ndb/src/kernel/blocks/backup/Backup.cpp @@ -1265,10 +1265,6 @@ Backup::createAttributeMask(TablePtr tabPtr, jam(); AttributePtr attr; table.attributes.getPtr(attr, i); - if(attr.p->data.key != 0){ - jam(); - continue; - } mask.set(i); } } @@ -2954,12 +2950,9 @@ Backup::parseTableDescription(Signal* signal, BackupRecordPtr ptr, Uint32 len) tabPtr.p->schemaVersion = tmpTab.TableVersion; tabPtr.p->noOfAttributes = tmpTab.NoOfAttributes; - tabPtr.p->noOfKeys = tmpTab.NoOfKeyAttr; tabPtr.p->noOfNull = 0; tabPtr.p->noOfVariable = 0; // Computed while iterating over attribs - tabPtr.p->sz_FixedKeys = 0; // Computed while iterating over attribs tabPtr.p->sz_FixedAttributes = 0; // Computed while iterating over attribs - tabPtr.p->variableKeyId = RNIL; // Computed while iterating over attribs tabPtr.p->triggerIds[0] = ILLEGAL_TRIGGER_ID; tabPtr.p->triggerIds[1] = ILLEGAL_TRIGGER_ID; tabPtr.p->triggerIds[2] = ILLEGAL_TRIGGER_ID; @@ -2994,7 +2987,6 @@ Backup::parseTableDescription(Signal* signal, BackupRecordPtr ptr, Uint32 len) attrPtr.p->data.nullable = tmp.AttributeNullableFlag; attrPtr.p->data.fixed = (tmp.AttributeArraySize != 0); - attrPtr.p->data.key = tmp.AttributeKeyFlag; attrPtr.p->data.sz32 = sz32; /** @@ -3002,50 +2994,26 @@ Backup::parseTableDescription(Signal* signal, BackupRecordPtr ptr, Uint32 len) * 1) Fixed * 2) Nullable * 3) Variable - * 4) Fixed key - * 5) Variable key */ - if(attrPtr.p->data.key == false) { + if(attrPtr.p->data.fixed == true && attrPtr.p->data.nullable == false) { jam(); - - if(attrPtr.p->data.fixed == true && attrPtr.p->data.nullable == false) { - jam(); - attrPtr.p->data.offset = tabPtr.p->sz_FixedAttributes; - tabPtr.p->sz_FixedAttributes += sz32; - }//if - - if(attrPtr.p->data.fixed == true && attrPtr.p->data.nullable == true) { - jam(); - attrPtr.p->data.offset = 0; - - attrPtr.p->data.offsetNull = tabPtr.p->noOfNull; - tabPtr.p->noOfNull++; - tabPtr.p->noOfVariable++; - }//if - - if(attrPtr.p->data.fixed == false) { - jam(); - tabPtr.p->noOfVariable++; - ndbrequire(0); - }//if - - } else if(attrPtr.p->data.key == true) { + attrPtr.p->data.offset = tabPtr.p->sz_FixedAttributes; + tabPtr.p->sz_FixedAttributes += sz32; + }//if + + if(attrPtr.p->data.fixed == true && attrPtr.p->data.nullable == true) { jam(); - ndbrequire(attrPtr.p->data.nullable == false); + attrPtr.p->data.offset = 0; - if(attrPtr.p->data.fixed == true) { // Fixed key - jam(); - tabPtr.p->sz_FixedKeys += sz32; - }//if - - if(attrPtr.p->data.fixed == false) { // Variable key - jam(); - attrPtr.p->data.offset = 0; - tabPtr.p->noOfVariable++; - ndbrequire(tabPtr.p->variableKeyId == RNIL); // Only one variable key - tabPtr.p->variableKeyId = attrPtr.i; - ndbrequire(0); - }//if + attrPtr.p->data.offsetNull = tabPtr.p->noOfNull; + tabPtr.p->noOfNull++; + tabPtr.p->noOfVariable++; + }//if + + if(attrPtr.p->data.fixed == false) { + jam(); + tabPtr.p->noOfVariable++; + ndbrequire(0); }//if it.next(); // Move Past EndOfAttribute @@ -3355,7 +3323,7 @@ Backup::execBACKUP_FRAGMENT_REQ(Signal* signal) Table & table = * tabPtr.p; ScanFragReq * req = (ScanFragReq *)signal->getDataPtrSend(); const Uint32 parallelism = 16; - const Uint32 attrLen = 5 + table.noOfAttributes - table.noOfKeys; + const Uint32 attrLen = 5 + table.noOfAttributes; req->senderData = filePtr.i; req->resultRef = reference(); @@ -3366,7 +3334,7 @@ Backup::execBACKUP_FRAGMENT_REQ(Signal* signal) req->tableId = table.tableId; ScanFragReq::setLockMode(req->requestInfo, 0); ScanFragReq::setHoldLockFlag(req->requestInfo, 0); - ScanFragReq::setKeyinfoFlag(req->requestInfo, 1); + ScanFragReq::setKeyinfoFlag(req->requestInfo, 0); ScanFragReq::setAttrLen(req->requestInfo,attrLen); req->transId1 = 0; req->transId2 = (BACKUP << 20) + (getOwnNodeId() << 8); @@ -3381,7 +3349,7 @@ Backup::execBACKUP_FRAGMENT_REQ(Signal* signal) signal->theData[2] = (BACKUP << 20) + (getOwnNodeId() << 8); // Return all - signal->theData[3] = table.noOfAttributes - table.noOfKeys; + signal->theData[3] = table.noOfAttributes; signal->theData[4] = 0; signal->theData[5] = 0; signal->theData[6] = 0; @@ -3393,10 +3361,6 @@ Backup::execBACKUP_FRAGMENT_REQ(Signal* signal) jam(); AttributePtr attr; table.attributes.getPtr(attr, i); - if(attr.p->data.key != 0) { - jam(); - continue; - }//if AttributeHeader::init(&signal->theData[dataPos], i, 0); dataPos++; @@ -3506,64 +3470,19 @@ Backup::execTRANSID_AI(Signal* signal) } } -void -Backup::execKEYINFO20(Signal* signal) -{ - jamEntry(); - - const Uint32 filePtrI = signal->theData[0]; - const Uint32 keyLen = signal->theData[1]; - //const Uint32 scanInfo = signal->theData[2]; - //const Uint32 transId1 = signal->theData[3]; - //const Uint32 transId2 = signal->theData[4]; - const Uint32 dataLen = signal->length() - 5; - - BackupFilePtr filePtr; - c_backupFilePool.getPtr(filePtr, filePtrI); - - OperationRecord & op = filePtr.p->operation; - - /** - * Unpack data - */ - ndbrequire(keyLen == dataLen); - const Uint32 * src = &signal->theData[5]; - const Uint32 klFixed = op.getFixedKeySize(); - ndbrequire(keyLen >= klFixed); - - Uint32 * dst = op.newKey(); - memcpy(dst, src, klFixed << 2); - - const Uint32 szLeft = (keyLen - klFixed); - if(szLeft > 0) { - jam(); - src += klFixed; - dst = op.newVariableKey(szLeft); - memcpy(dst, src, (szLeft << 2)); - ndbrequire(0); - }//if - - if(op.finished()){ - jam(); - op.newRecord(op.dst); - } -} - void Backup::OperationRecord::init(const TablePtr & ptr) { tablePtr = ptr.i; - noOfAttributes = (ptr.p->noOfAttributes - ptr.p->noOfKeys) + 1; - variableKeyId = ptr.p->variableKeyId; + noOfAttributes = ptr.p->noOfAttributes; sz_Bitmask = (ptr.p->noOfNull + 31) >> 5; - sz_FixedKeys = ptr.p->sz_FixedKeys; sz_FixedAttribs = ptr.p->sz_FixedAttributes; if(ptr.p->noOfVariable == 0) { jam(); - maxRecordSize = 1 + sz_Bitmask + sz_FixedKeys + sz_FixedAttribs; + maxRecordSize = 1 + sz_Bitmask + sz_FixedAttribs; } else { jam(); maxRecordSize = diff --git a/ndb/src/kernel/blocks/backup/Backup.hpp b/ndb/src/kernel/blocks/backup/Backup.hpp index 4dc2cd13ae0..9cc6255af11 100644 --- a/ndb/src/kernel/blocks/backup/Backup.hpp +++ b/ndb/src/kernel/blocks/backup/Backup.hpp @@ -76,7 +76,6 @@ protected: */ void execSCAN_HBREP(Signal* signal); void execTRANSID_AI(Signal* signal); - void execKEYINFO20(Signal* signal); void execSCAN_FRAGREF(Signal* signal); void execSCAN_FRAGCONF(Signal* signal); @@ -172,8 +171,8 @@ public: struct Data { Uint8 nullable; Uint8 fixed; - Uint8 key; - Uint8 unused; + Uint8 unused; + Uint8 unused2; Uint32 sz32; // No of 32 bit words Uint32 offset; // Relative DataFixedAttributes/DataFixedKeys Uint32 offsetNull; // In NullBitmask @@ -199,12 +198,9 @@ public: Uint32 frag_mask; Uint32 tableType; Uint32 noOfNull; - Uint32 noOfKeys; Uint32 noOfAttributes; Uint32 noOfVariable; - Uint32 sz_FixedKeys; Uint32 sz_FixedAttributes; - Uint32 variableKeyId; Uint32 triggerIds[3]; bool triggerAllocated[3]; @@ -224,7 +220,6 @@ public: * Once per table */ void init(const TablePtr & ptr); - inline Uint32 getFixedKeySize() const { return sz_FixedKeys; } /** * Once per fragment @@ -247,23 +242,19 @@ public: /** * Per attribute */ - Uint32 * newKey(); void nullAttribute(Uint32 nullOffset); Uint32 * newNullable(Uint32 attrId, Uint32 sz); Uint32 * newAttrib(Uint32 offset, Uint32 sz); Uint32 * newVariable(Uint32 id, Uint32 sz); - Uint32 * newVariableKey(Uint32 sz); private: Uint32* base; Uint32* dst_Length; Uint32* dst_Bitmask; - Uint32* dst_FixedKeys; Uint32* dst_FixedAttribs; BackupFormat::DataFile::VariableData* dst_VariableData; Uint32 noOfAttributes; // No of Attributes - Uint32 variableKeyId; // Id of variable key Uint32 attrLeft; // No of attributes left Uint32 opNoDone; @@ -289,7 +280,6 @@ public: * sizes of part */ Uint32 sz_Bitmask; - Uint32 sz_FixedKeys; Uint32 sz_FixedAttribs; public: @@ -628,7 +618,6 @@ Backup::OperationRecord::newRecord(Uint32 * p){ base = p; dst_Length = p; p += 1; dst_Bitmask = p; p += sz_Bitmask; - dst_FixedKeys = p; p += sz_FixedKeys; dst_FixedAttribs = p; p += sz_FixedAttribs; dst_VariableData = (BackupFormat::DataFile::VariableData*)p; BitmaskImpl::clear(sz_Bitmask, dst_Bitmask); @@ -645,14 +634,6 @@ Backup::OperationRecord::newAttrib(Uint32 offset, Uint32 sz){ return dst; } -inline -Uint32 * -Backup::OperationRecord::newKey(){ - attrLeft --; - attrSzLeft = 0; - return dst_FixedKeys; -} - inline void Backup::OperationRecord::nullAttribute(Uint32 offsetNull){ @@ -691,21 +672,6 @@ Backup::OperationRecord::newVariable(Uint32 id, Uint32 sz){ return dst; } -inline -Uint32 * -Backup::OperationRecord::newVariableKey(Uint32 sz){ - attrLeft--; - attrSzLeft = 0; - attrSzTotal += sz; - - dst = &dst_VariableData->Data[0]; - dst_VariableData->Sz = htonl(sz); - dst_VariableData->Id = htonl(variableKeyId); - - dst_VariableData = (BackupFormat::DataFile::VariableData *)(dst + sz); - return dst; -} - inline bool Backup::OperationRecord::finished(){ @@ -713,7 +679,7 @@ Backup::OperationRecord::finished(){ return false; } - opLen += attrSzTotal + sz_FixedKeys; + opLen += attrSzTotal; opNoDone++; scanStop = dst = (Uint32 *)dst_VariableData; diff --git a/ndb/src/kernel/blocks/backup/BackupInit.cpp b/ndb/src/kernel/blocks/backup/BackupInit.cpp index 8daad05558b..a02e068687b 100644 --- a/ndb/src/kernel/blocks/backup/BackupInit.cpp +++ b/ndb/src/kernel/blocks/backup/BackupInit.cpp @@ -126,7 +126,6 @@ Backup::Backup(const Configuration & conf) : addRecSignal(GSN_SCAN_HBREP, &Backup::execSCAN_HBREP); addRecSignal(GSN_TRANSID_AI, &Backup::execTRANSID_AI); - addRecSignal(GSN_KEYINFO20, &Backup::execKEYINFO20); addRecSignal(GSN_SCAN_FRAGREF, &Backup::execSCAN_FRAGREF); addRecSignal(GSN_SCAN_FRAGCONF, &Backup::execSCAN_FRAGCONF); diff --git a/ndb/tools/restore/Restore.cpp b/ndb/tools/restore/Restore.cpp index 6e2fcaed3af..de769b4c880 100644 --- a/ndb/tools/restore/Restore.cpp +++ b/ndb/tools/restore/Restore.cpp @@ -334,27 +334,6 @@ RestoreDataIterator::getNextTuple(int & res) Uint32 *buf_ptr = (Uint32*)_buf_ptr, *ptr = buf_ptr; ptr += m_currentTable->m_nullBitmaskSize; Uint32 i; - for(i= 0; i < m_currentTable->m_fixedKeys.size(); i++){ - assert(ptr < buf_ptr + dataLength); - - const Uint32 attrId = m_currentTable->m_fixedKeys[i]->attrId; - - AttributeData * attr_data = m_tuple.getData(attrId); - const AttributeDesc * attr_desc = m_tuple.getDesc(attrId); - - const Uint32 sz = attr_desc->getSizeInWords(); - - attr_data->null = false; - attr_data->void_value = ptr; - - if(!Twiddle(attr_desc, attr_data)) - { - res = -1; - return NULL; - } - ptr += sz; - } - for(i = 0; i < m_currentTable->m_fixedAttribs.size(); i++){ assert(ptr < buf_ptr + dataLength); @@ -699,12 +678,6 @@ void TableS::createAttr(NdbDictionary::Column *column) if (d->m_column->getAutoIncrement()) m_auto_val_id= d->attrId; - if(d->m_column->getPrimaryKey() /* && not variable */) - { - m_fixedKeys.push_back(d); - return; - } - if(!d->m_column->getNullable()) { m_fixedAttribs.push_back(d); diff --git a/ndb/tools/restore/Restore.hpp b/ndb/tools/restore/Restore.hpp index 82fcdcdb183..a7d4fc174c7 100644 --- a/ndb/tools/restore/Restore.hpp +++ b/ndb/tools/restore/Restore.hpp @@ -123,8 +123,6 @@ class TableS { Uint32 schemaVersion; Uint32 backupVersion; Vector allAttributesDesc; - Vector m_fixedKeys; - //Vector m_variableKey; Vector m_fixedAttribs; Vector m_variableAttribs; From d25261e00953ba309e59f969c9ef282ea898c325 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 12:06:11 +0100 Subject: [PATCH 23/52] ndb - bit field micro optimization ndb/src/common/util/Bitmask.cpp: A litter smarter handling of <<32 --- ndb/src/common/util/Bitmask.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ndb/src/common/util/Bitmask.cpp b/ndb/src/common/util/Bitmask.cpp index 2fce1bf2867..dde6111c32b 100644 --- a/ndb/src/common/util/Bitmask.cpp +++ b/ndb/src/common/util/Bitmask.cpp @@ -24,16 +24,16 @@ BitmaskImpl::getFieldImpl(const Uint32 src[], assert(shiftL < 32); unsigned shiftR = 32 - shiftL; + unsigned undefined = shiftL ? ~0 : 0; while(len >= 32) { * dst++ |= (* src) << shiftL; - * dst = shiftL ? (* src) >> shiftR : 0; - src++; + * dst = ((* src++) >> shiftR) & undefined; len -= 32; } * dst++ |= (* src) << shiftL; - * dst = shiftL ? ((* src) >> shiftR) & ((1 << len) - 1) : 0; + * dst = ((* src) >> shiftR) & ((1 << len) - 1) & undefined; } void @@ -47,18 +47,18 @@ BitmaskImpl::setFieldImpl(Uint32 dst[], */ assert(shiftL < 32); unsigned shiftR = 32 - shiftL; - + unsigned undefined = shiftL ? ~0 : 0; while(len >= 32) { * dst = (* src++) >> shiftL; - * dst++ |= shiftL ? (* src) << shiftR : 0; + * dst++ |= ((* src) << shiftR) & undefined; len -= 32; } Uint32 mask = ((1 << len) -1); * dst = (* dst & ~mask); * dst |= ((* src++) >> shiftL) & mask; - * dst |= shiftL ? ((* src) << shiftR) & mask : 0; + * dst |= ((* src) << shiftR) & mask & undefined; } #else From f67fe154dd4025ccf0e1f74b0841410c80f21a46 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 13:36:23 +0100 Subject: [PATCH 24/52] bug#6995 - fixed so that ndb_restore 4.1.8 can read files created by 4.1.7 as bugfix changed binary format ndb/tools/restore/Restore.cpp: Fixed so that version 4.1.8 of restore can read files created by 4.1.7 ndb/tools/restore/Restore.hpp: Fixed so that version 4.1.8 of restore can read files created by 4.1.7 ndb/tools/restore/main.cpp: Fixed so that version 4.1.8 of restore can read files created by 4.1.7 --- ndb/tools/restore/Restore.cpp | 35 +++++++++++++++++++++++++++++++---- ndb/tools/restore/Restore.hpp | 4 +++- ndb/tools/restore/main.cpp | 9 ++++++++- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/ndb/tools/restore/Restore.cpp b/ndb/tools/restore/Restore.cpp index de769b4c880..277cdc72532 100644 --- a/ndb/tools/restore/Restore.cpp +++ b/ndb/tools/restore/Restore.cpp @@ -192,14 +192,15 @@ RestoreMetaData::readGCPEntry() { return true; } -TableS::TableS(NdbTableImpl* tableImpl) +TableS::TableS(Uint32 version, NdbTableImpl* tableImpl) : m_dictTable(tableImpl) { m_dictTable = tableImpl; m_noOfNullable = m_nullBitmaskSize = 0; m_auto_val_id= ~(Uint32)0; m_max_auto_val= 0; - + backupVersion = version; + for (int i = 0; i < tableImpl->getNoOfColumns(); i++) createAttr(tableImpl->getColumn(i)); } @@ -226,11 +227,10 @@ RestoreMetaData::parseTableDescriptor(const Uint32 * data, Uint32 len) debug << "parseTableInfo " << tableImpl->getName() << " done" << endl; - TableS * table = new TableS(tableImpl); + TableS * table = new TableS(m_fileHeader.NdbVersion, tableImpl); if(table == NULL) { return false; } - table->setBackupVersion(m_fileHeader.NdbVersion); debug << "Parsed table id " << table->getTableId() << endl; debug << "Parsed table #attr " << table->getNoOfAttributes() << endl; @@ -334,6 +334,27 @@ RestoreDataIterator::getNextTuple(int & res) Uint32 *buf_ptr = (Uint32*)_buf_ptr, *ptr = buf_ptr; ptr += m_currentTable->m_nullBitmaskSize; Uint32 i; + for(i= 0; i < m_currentTable->m_fixedKeys.size(); i++){ + assert(ptr < buf_ptr + dataLength); + + const Uint32 attrId = m_currentTable->m_fixedKeys[i]->attrId; + + AttributeData * attr_data = m_tuple.getData(attrId); + const AttributeDesc * attr_desc = m_tuple.getDesc(attrId); + + const Uint32 sz = attr_desc->getSizeInWords(); + + attr_data->null = false; + attr_data->void_value = ptr; + + if(!Twiddle(attr_desc, attr_data)) + { + res = -1; + return NULL; + } + ptr += sz; + } + for(i = 0; i < m_currentTable->m_fixedAttribs.size(); i++){ assert(ptr < buf_ptr + dataLength); @@ -678,6 +699,12 @@ void TableS::createAttr(NdbDictionary::Column *column) if (d->m_column->getAutoIncrement()) m_auto_val_id= d->attrId; + if(d->m_column->getPrimaryKey() && backupVersion <= MAKE_VERSION(4,1,7)) + { + m_fixedKeys.push_back(d); + return; + } + if(!d->m_column->getNullable()) { m_fixedAttribs.push_back(d); diff --git a/ndb/tools/restore/Restore.hpp b/ndb/tools/restore/Restore.hpp index a7d4fc174c7..e0b06c1774c 100644 --- a/ndb/tools/restore/Restore.hpp +++ b/ndb/tools/restore/Restore.hpp @@ -123,6 +123,8 @@ class TableS { Uint32 schemaVersion; Uint32 backupVersion; Vector allAttributesDesc; + Vector m_fixedKeys; + //Vector m_variableKey; Vector m_fixedAttribs; Vector m_variableAttribs; @@ -138,7 +140,7 @@ class TableS { public: class NdbDictionary::Table* m_dictTable; - TableS (class NdbTableImpl* dictTable); + TableS (Uint32 version, class NdbTableImpl* dictTable); ~TableS(); Uint32 getTableId() const { diff --git a/ndb/tools/restore/main.cpp b/ndb/tools/restore/main.cpp index 482212911cb..575cd9b3b2f 100644 --- a/ndb/tools/restore/main.cpp +++ b/ndb/tools/restore/main.cpp @@ -247,11 +247,18 @@ main(int argc, char** argv) ndbout << "Failed to read " << metaData.getFilename() << endl << endl; return -1; } + + const BackupFormat::FileHeader & tmp = metaData.getFileHeader(); + const Uint32 version = tmp.NdbVersion; + + ndbout << "Ndb version in backup files: " + << getVersionString(version, 0) << endl; + /** * check wheater we can restore the backup (right version). */ int res = metaData.loadContent(); - + if (res == 0) { ndbout_c("Restore: Failed to load content"); From b1d69736235e9497781425ad2c5feb41bf5d0e08 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 14:17:58 +0100 Subject: [PATCH 25/52] use new ndb bitformat to shrink storage --- sql/ha_ndbcluster.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index f838bfcd8c3..929d50e077e 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3331,7 +3331,10 @@ static int create_ndb_column(NDBCOL &col, col.setCharset(cs); } if (field->pack_length() == 0) - col.setLength(1); // currently ndb does not support size 0 + { + col.setType(NDBCOL::Bit); + col.setLength(1); + } else col.setLength(field->pack_length()); break; From 7945222c28e3a6a4e6087c7c99ea34c3a990e46f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 15:29:55 +0200 Subject: [PATCH 26/52] InnoDB: Changed interface to rec_get_offsets(), to reduce the use of memory heaps. This changeset plugs also a few memory leaks that were introduced with the compact InnoDB table format. innobase/btr/btr0btr.c: Changed interface to rec_get_offsets() innobase/btr/btr0cur.c: Changed interface to rec_get_offsets() innobase/btr/btr0pcur.c: Changed interface to rec_get_offsets() innobase/btr/btr0sea.c: Changed interface to rec_get_offsets() innobase/include/rem0rec.h: Changed interface to rec_get_offsets() innobase/include/rem0rec.ic: Changed interface to rec_get_offsets() innobase/lock/lock0lock.c: Changed interface to rec_get_offsets() innobase/page/page0cur.c: Changed interface to rec_get_offsets() innobase/page/page0page.c: Changed interface to rec_get_offsets() innobase/rem/rem0rec.c: Changed interface to rec_get_offsets() innobase/row/row0ins.c: Changed interface to rec_get_offsets() innobase/row/row0mysql.c: Changed interface to rec_get_offsets() innobase/row/row0purge.c: Changed interface to rec_get_offsets() innobase/row/row0row.c: Changed interface to rec_get_offsets() innobase/row/row0sel.c: Changed interface to rec_get_offsets() innobase/row/row0umod.c: Changed interface to rec_print() innobase/row/row0undo.c: Changed interface to rec_get_offsets() innobase/row/row0upd.c: Changed interface to rec_get_offsets() innobase/row/row0vers.c: Changed interface to rec_get_offsets() innobase/trx/trx0rec.c: Changed interface to rec_get_offsets() --- innobase/btr/btr0btr.c | 165 ++++++++++++------------ innobase/btr/btr0cur.c | 182 +++++++++++++++----------- innobase/btr/btr0pcur.c | 9 +- innobase/btr/btr0sea.c | 171 ++++++++++++------------ innobase/include/rem0rec.h | 73 ++++++----- innobase/include/rem0rec.ic | 83 ++++++++++-- innobase/lock/lock0lock.c | 66 ++++++---- innobase/page/page0cur.c | 138 +++++++++++--------- innobase/page/page0page.c | 72 ++++++----- innobase/rem/rem0rec.c | 164 +++++++++++------------ innobase/row/row0ins.c | 140 ++++++++------------ innobase/row/row0mysql.c | 17 +-- innobase/row/row0purge.c | 14 +- innobase/row/row0row.c | 49 ++++--- innobase/row/row0sel.c | 250 +++++++++++++++++++----------------- innobase/row/row0umod.c | 6 +- innobase/row/row0undo.c | 12 +- innobase/row/row0upd.c | 92 +++++++------ innobase/row/row0vers.c | 33 ++--- innobase/trx/trx0rec.c | 32 +++-- 20 files changed, 940 insertions(+), 828 deletions(-) diff --git a/innobase/btr/btr0btr.c b/innobase/btr/btr0btr.c index c911124e705..9da422e927f 100644 --- a/innobase/btr/btr0btr.c +++ b/innobase/btr/btr0btr.c @@ -567,7 +567,8 @@ btr_page_get_father_for_rec( btr_cur_t cursor; rec_t* node_ptr; dict_index_t* index; - ulint* offsets; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(mtr_memo_contains(mtr, dict_tree_get_lock(tree), MTR_MEMO_X_LOCK)); @@ -591,7 +592,8 @@ btr_page_get_father_for_rec( BTR_CONT_MODIFY_TREE, &cursor, 0, mtr); node_ptr = btr_cur_get_rec(&cursor); - offsets = rec_get_offsets(node_ptr, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, index, offsets, + ULINT_UNDEFINED, &heap); if (btr_node_ptr_get_child_page_no(node_ptr, offsets) != buf_frame_get_page_no(page)) { @@ -609,13 +611,13 @@ btr_page_get_father_for_rec( (ulong) btr_node_ptr_get_child_page_no(node_ptr, offsets), (ulong) buf_frame_get_page_no(page)); - offsets = rec_reget_offsets(page_rec_get_next( + offsets = rec_get_offsets(page_rec_get_next( page_get_infimum_rec(page)), index, - offsets, ULINT_UNDEFINED, heap); + offsets, ULINT_UNDEFINED, &heap); page_rec_print(page_rec_get_next(page_get_infimum_rec(page)), offsets); - offsets = rec_reget_offsets(node_ptr, index, offsets, - ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, index, offsets, + ULINT_UNDEFINED, &heap); page_rec_print(node_ptr, offsets); fputs( @@ -1231,7 +1233,7 @@ btr_page_get_sure_split_rec( ins_rec = btr_cur_get_rec(cursor); rec = page_get_infimum_rec(page); - heap = mem_heap_create(100); + heap = NULL; offsets = NULL; /* We start to include records to the left half, and when the @@ -1256,8 +1258,8 @@ btr_page_get_sure_split_rec( /* Include tuple */ incl_data += insert_size; } else { - offsets = rec_reget_offsets(rec, cursor->index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, cursor->index, + offsets, ULINT_UNDEFINED, &heap); incl_data += rec_offs_size(offsets); } @@ -1280,12 +1282,16 @@ btr_page_get_sure_split_rec( next_rec = page_rec_get_next(rec); } if (next_rec != page_get_supremum_rec(page)) { - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(next_rec); } } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(rec); } } @@ -1367,8 +1373,8 @@ btr_page_insert_fits( /* In this loop we calculate the amount of reserved space after rec is removed from page. */ - offs = rec_reget_offsets(rec, cursor->index, offs, - ULINT_UNDEFINED, heap); + offs = rec_get_offsets(rec, cursor->index, offs, + ULINT_UNDEFINED, &heap); total_data -= rec_offs_size(offs); total_n_recs--; @@ -1459,7 +1465,7 @@ btr_attach_half_pages( ut_a(page_is_comp(page) == page_is_comp(new_page)); /* Create a memory heap where the data tuple is stored */ - heap = mem_heap_create(100); + heap = mem_heap_create(1024); /* Based on split direction, decide upper and lower pages */ if (direction == FSP_DOWN) { @@ -1478,7 +1484,7 @@ btr_attach_half_pages( btr_node_ptr_set_child_page_no(node_ptr, rec_get_offsets(node_ptr, UT_LIST_GET_FIRST(tree->tree_indexes), - ULINT_UNDEFINED, heap), + NULL, ULINT_UNDEFINED, &heap), lower_page_no, mtr); mem_heap_empty(heap); } else { @@ -1656,8 +1662,8 @@ func_start: thus reducing the tree latch contention. */ if (split_rec) { - offsets = rec_reget_offsets(split_rec, cursor->index, - offsets, n_uniq, heap); + offsets = rec_get_offsets(split_rec, cursor->index, offsets, + n_uniq, &heap); insert_will_fit = btr_page_insert_fits(cursor, split_rec, offsets, tuple, heap); @@ -1700,8 +1706,8 @@ func_start: insert_page = right_page; } else { - offsets = rec_reget_offsets(first_rec, cursor->index, - offsets, n_uniq, heap); + offsets = rec_get_offsets(first_rec, cursor->index, + offsets, n_uniq, &heap); if (cmp_dtuple_rec(tuple, first_rec, offsets) >= 0) { @@ -2091,14 +2097,18 @@ btr_compress( if (is_left) { btr_node_ptr_delete(tree, page, mtr); } else { - mem_heap_t* heap = mem_heap_create(100); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; /* Replace the address of the old child node (= page) with the address of the merge page to the right */ btr_node_ptr_set_child_page_no(node_ptr, rec_get_offsets(node_ptr, cursor->index, - ULINT_UNDEFINED, heap), right_page_no, mtr); - mem_heap_free(heap); + offsets_, ULINT_UNDEFINED, &heap), + right_page_no, mtr); + if (heap) { + mem_heap_free(heap); + } btr_node_ptr_delete(tree, merge_page, mtr); } @@ -2312,8 +2322,8 @@ btr_print_recursive( page_t* page, /* in: index page */ ulint width, /* in: print this many entries from start and end */ - mem_heap_t* heap, /* in: heap for rec_reget_offsets() */ - ulint** offsets,/* in/out: buffer for rec_reget_offsets() */ + mem_heap_t** heap, /* in/out: heap for rec_get_offsets() */ + ulint** offsets,/* in/out: buffer for rec_get_offsets() */ mtr_t* mtr) /* in: mtr */ { page_cur_t cursor; @@ -2350,8 +2360,8 @@ btr_print_recursive( node_ptr = page_cur_get_rec(&cursor); - *offsets = rec_reget_offsets(node_ptr, index, - *offsets, ULINT_UNDEFINED, heap); + *offsets = rec_get_offsets(node_ptr, index, *offsets, + ULINT_UNDEFINED, heap); child = btr_node_ptr_get_child(node_ptr, *offsets, &mtr2); btr_print_recursive(tree, child, width, @@ -2362,8 +2372,6 @@ btr_print_recursive( page_cur_move_to_next(&cursor); i++; } - - mem_heap_free(heap); } /****************************************************************** @@ -2378,8 +2386,10 @@ btr_print_tree( { mtr_t mtr; page_t* root; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] + = { 100, }; + ulint* offsets = offsets_; fputs("--------------------------\n" "INDEX TREE PRINT\n", stderr); @@ -2388,8 +2398,10 @@ btr_print_tree( root = btr_root_get(tree, &mtr); - btr_print_recursive(tree, root, width, heap, &offsets, &mtr); - mem_heap_free(heap); + btr_print_recursive(tree, root, width, &heap, &offsets, &mtr); + if (heap) { + mem_heap_free(heap); + } mtr_commit(&mtr); @@ -2435,7 +2447,7 @@ btr_check_node_ptr( ut_a(cmp_dtuple_rec(node_ptr_tuple, node_ptr, rec_get_offsets(node_ptr, dict_tree_find_index(tree, node_ptr), - ULINT_UNDEFINED, heap)) == 0); + NULL, ULINT_UNDEFINED, &heap)) == 0); mem_heap_free(heap); @@ -2476,8 +2488,10 @@ btr_index_rec_validate( ulint n; ulint i; page_t* page; - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] + = { 100, }; + ulint* offsets = offsets_; page = buf_frame_align(rec); @@ -2496,22 +2510,17 @@ btr_index_rec_validate( fprintf(stderr, "InnoDB: has %lu fields, should have %lu\n", (ulong) rec_get_n_fields_old(rec), (ulong) n); - if (!dump_on_error) { + if (dump_on_error) { + buf_page_print(page); - return(FALSE); + fputs("InnoDB: corrupt record ", stderr); + rec_print_old(stderr, rec); + putc('\n', stderr); } - - buf_page_print(page); - - fputs("InnoDB: corrupt record ", stderr); - rec_print_old(stderr, rec); - putc('\n', stderr); - return(FALSE); } - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); for (i = 0; i < n; i++) { dtype_t* type = dict_index_get_nth_type(index, i); @@ -2536,23 +2545,23 @@ btr_index_rec_validate( "InnoDB: field %lu len is %lu, should be %lu\n", (ulong) i, (ulong) len, (ulong) dtype_get_fixed_size(type)); - if (!dump_on_error) { - mem_heap_free(heap); - return(FALSE); + if (dump_on_error) { + buf_page_print(page); + + fputs("InnoDB: corrupt record ", stderr); + rec_print_new(stderr, rec, offsets); + putc('\n', stderr); + } + if (heap) { + mem_heap_free(heap); } - - buf_page_print(page); - - fputs("InnoDB: corrupt record ", stderr); - rec_print(stderr, rec, offsets); - putc('\n', stderr); - - mem_heap_free(heap); return(FALSE); } } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(TRUE); } @@ -2677,8 +2686,8 @@ btr_validate_level( page_cur_move_to_next(&cursor); node_ptr = page_cur_get_rec(&cursor); - offsets = rec_reget_offsets(node_ptr, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, index, offsets, + ULINT_UNDEFINED, &heap); page = btr_node_ptr_get_child(node_ptr, offsets, &mtr); } @@ -2722,10 +2731,10 @@ loop: rec = page_rec_get_prev(page_get_supremum_rec(page)); right_rec = page_rec_get_next( page_get_infimum_rec(right_page)); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); - offsets2 = rec_reget_offsets(right_rec, index, - offsets2, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, + offsets, ULINT_UNDEFINED, &heap); + offsets2 = rec_get_offsets(right_rec, index, + offsets2, ULINT_UNDEFINED, &heap); if (cmp_rec_rec(rec, right_rec, offsets, offsets2, dict_index_get_n_fields(index), index) >= 0) { @@ -2740,16 +2749,12 @@ loop: fputs("InnoDB: record ", stderr); rec = page_rec_get_prev(page_get_supremum_rec(page)); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); - rec_print(stderr, rec, offsets); + rec_print(stderr, rec, index); putc('\n', stderr); fputs("InnoDB: record ", stderr); rec = page_rec_get_next(page_get_infimum_rec( right_page)); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); - rec_print(stderr, rec, offsets); + rec_print(stderr, rec, index); putc('\n', stderr); ret = FALSE; @@ -2768,8 +2773,8 @@ loop: node_ptr = btr_page_get_father_node_ptr(tree, page, &mtr); father_page = buf_frame_align(node_ptr); - offsets = rec_reget_offsets(node_ptr, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, index, + offsets, ULINT_UNDEFINED, &heap); if (btr_node_ptr_get_child_page_no(node_ptr, offsets) != buf_frame_get_page_no(page) @@ -2785,7 +2790,7 @@ loop: buf_page_print(page); fputs("InnoDB: node ptr ", stderr); - rec_print(stderr, node_ptr, offsets); + rec_print_new(stderr, node_ptr, offsets); fprintf(stderr, "\n" "InnoDB: node ptr child page n:o %lu\n", @@ -2796,9 +2801,7 @@ loop: rec = btr_page_get_father_for_rec(tree, page, page_rec_get_prev(page_get_supremum_rec(page)), &mtr); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); - rec_print(stderr, rec, offsets); + rec_print(stderr, rec, index); putc('\n', stderr); ret = FALSE; @@ -2806,8 +2809,8 @@ loop: } if (btr_page_get_level(page, &mtr) > 0) { - offsets = rec_reget_offsets(node_ptr, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, index, + offsets, ULINT_UNDEFINED, &heap); node_ptr_tuple = dict_tree_build_node_ptr( tree, @@ -2829,11 +2832,9 @@ loop: fputs("InnoDB: Error: node ptrs differ" " on levels > 0\n" "InnoDB: node ptr ", stderr); - rec_print(stderr, node_ptr, offsets); + rec_print_new(stderr, node_ptr, offsets); fputs("InnoDB: first rec ", stderr); - offsets = rec_reget_offsets(first_rec, index, - offsets, ULINT_UNDEFINED, heap); - rec_print(stderr, first_rec, offsets); + rec_print(stderr, first_rec, index); putc('\n', stderr); ret = FALSE; diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c index f5e146172ed..a3083e0e545 100644 --- a/innobase/btr/btr0cur.c +++ b/innobase/btr/btr0cur.c @@ -274,8 +274,9 @@ btr_cur_search_to_nth_level( #ifdef BTR_CUR_ADAPT btr_search_t* info; #endif - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; /* Currently, PAGE_CUR_LE is the only search mode used for searches ending to upper levels */ @@ -395,8 +396,6 @@ btr_cur_search_to_nth_level( break; } - heap = mem_heap_create(100); - offsets = NULL; /* Loop and search until we arrive at the desired level */ for (;;) { @@ -431,7 +430,9 @@ retry_page_get: cursor->thr)) { /* Insertion to the insert buffer succeeded */ cursor->flag = BTR_CUR_INSERT_TO_IBUF; - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return; } @@ -517,13 +518,15 @@ retry_page_get: guess = NULL; node_ptr = page_cur_get_rec(page_cursor); - offsets = rec_reget_offsets(node_ptr, cursor->index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, cursor->index, offsets, + ULINT_UNDEFINED, &heap); /* Go to the child node */ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } if (level == 0) { cursor->low_match = low_match; @@ -574,8 +577,9 @@ btr_cur_open_at_index_side( rec_t* node_ptr; ulint estimate; ulint savepoint; - mem_heap_t* heap; - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; estimate = latch_mode & BTR_ESTIMATE; latch_mode = latch_mode & ~BTR_ESTIMATE; @@ -600,7 +604,6 @@ btr_cur_open_at_index_side( page_no = dict_tree_get_page(tree); height = ULINT_UNDEFINED; - heap = mem_heap_create(100); for (;;) { page = buf_page_get_gen(space, page_no, RW_NO_LATCH, NULL, @@ -670,13 +673,15 @@ btr_cur_open_at_index_side( height--; node_ptr = page_cur_get_rec(page_cursor); - offsets = rec_reget_offsets(node_ptr, cursor->index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, cursor->index, offsets, + ULINT_UNDEFINED, &heap); /* Go to the child node */ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /************************************************************************** @@ -697,8 +702,9 @@ btr_cur_open_at_rnd_pos( ulint space; ulint height; rec_t* node_ptr; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; tree = index->tree; @@ -747,13 +753,15 @@ btr_cur_open_at_rnd_pos( height--; node_ptr = page_cur_get_rec(page_cursor); - offsets = rec_reget_offsets(node_ptr, cursor->index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(node_ptr, cursor->index, offsets, + ULINT_UNDEFINED, &heap); /* Go to the child node */ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /*==================== B-TREE INSERT =========================*/ @@ -1242,11 +1250,14 @@ btr_cur_upd_lock_and_undo( err = DB_SUCCESS; if (!(flags & BTR_NO_LOCKING_FLAG)) { - mem_heap_t* heap = mem_heap_create(100); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; err = lock_clust_rec_modify_check_and_lock(flags, rec, index, - rec_get_offsets(rec, index, ULINT_UNDEFINED, heap), - thr); - mem_heap_free(heap); + rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap), thr); + if (heap) { + mem_heap_free(heap); + } if (err != DB_SUCCESS) { return(err); @@ -1374,7 +1385,7 @@ btr_cur_parse_update_in_place( /* We do not need to reserve btr_search_latch, as the page is only being recovered, and there cannot be a hash index to it. */ - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); if (!(flags & BTR_KEEP_SYS_FLAG)) { row_upd_rec_sys_fields_in_recovery(rec, offsets, @@ -1413,18 +1424,19 @@ btr_cur_update_in_place( dulint roll_ptr = ut_dulint_zero; trx_t* trx; ibool was_delete_marked; - mem_heap_t* heap; - const ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; rec = btr_cur_get_rec(cursor); index = cursor->index; trx = thr_get_trx(thr); heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(trx, index, "update "); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); } /* Do lock checking and undo logging */ @@ -1432,7 +1444,9 @@ btr_cur_update_in_place( thr, &roll_ptr); if (err != DB_SUCCESS) { - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(err); } @@ -1477,7 +1491,9 @@ btr_cur_update_in_place( btr_cur_unmark_extern_fields(rec, mtr, offsets); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(DB_SUCCESS); } @@ -1526,11 +1542,11 @@ btr_cur_optimistic_update( index = cursor->index; heap = mem_heap_create(1024); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(thr_get_trx(thr), index, "update "); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); } ut_ad(mtr_memo_contains(mtr, buf_block_align(page), @@ -1646,8 +1662,8 @@ btr_cur_optimistic_update( /* The new inserted record owns its possible externally stored fields */ - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); btr_cur_unmark_extern_fields(rec, mtr, offsets); } @@ -1801,7 +1817,7 @@ btr_cur_pessimistic_update( } heap = mem_heap_create(1024); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); trx = thr_get_trx(thr); @@ -1836,8 +1852,8 @@ btr_cur_pessimistic_update( ext_vect = mem_heap_alloc(heap, sizeof(ulint) * dict_index_get_n_fields(index)); ut_ad(!cursor->index->table->comp || !rec_get_node_ptr_flag(rec)); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); n_ext_vect = btr_push_update_extern_fields(ext_vect, offsets, update); if (rec_get_converted_size(index, new_entry) >= @@ -1877,8 +1893,8 @@ btr_cur_pessimistic_update( ut_a(rec || optim_err != DB_UNDERFLOW); if (rec) { - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); lock_rec_restore_from_page_infimum(rec, page); rec_set_field_extern_bits(rec, index, @@ -1918,8 +1934,7 @@ btr_cur_pessimistic_update( ut_a(dummy_big_rec == NULL); rec_set_field_extern_bits(rec, index, ext_vect, n_ext_vect, mtr); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (!rec_get_deleted_flag(rec, rec_offs_comp(offsets))) { /* The new inserted record owns its possible externally @@ -2048,12 +2063,15 @@ btr_cur_parse_del_mark_set_clust_rec( rec = page + offset; if (!(flags & BTR_KEEP_SYS_FLAG)) { - mem_heap_t* heap = mem_heap_create(100); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; row_upd_rec_sys_fields_in_recovery(rec, - rec_get_offsets(rec, index, - ULINT_UNDEFINED, heap), + rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap), pos, trx_id, roll_ptr); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /* We do not need to reserve btr_search_latch, as the page @@ -2089,17 +2107,17 @@ btr_cur_del_mark_set_clust_rec( ulint err; rec_t* rec; trx_t* trx; - mem_heap_t* heap; - const ulint* offsets; - + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + rec = btr_cur_get_rec(cursor); index = cursor->index; - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(thr_get_trx(thr), index, "del mark "); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); } ut_ad(index->type & DICT_CLUSTERED); @@ -2110,7 +2128,9 @@ btr_cur_del_mark_set_clust_rec( if (err != DB_SUCCESS) { - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(err); } @@ -2119,7 +2139,9 @@ btr_cur_del_mark_set_clust_rec( &roll_ptr); if (err != DB_SUCCESS) { - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(err); } @@ -2143,7 +2165,9 @@ btr_cur_del_mark_set_clust_rec( btr_cur_del_mark_set_clust_rec_log(flags, rec, index, val, trx, roll_ptr, mtr); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(DB_SUCCESS); } @@ -2246,12 +2270,9 @@ btr_cur_del_mark_set_sec_rec( rec = btr_cur_get_rec(cursor); if (btr_cur_print_record_ops && thr) { - mem_heap_t* heap = mem_heap_create(100); btr_cur_trx_report(thr_get_trx(thr), cursor->index, "del mark "); - rec_print(stderr, rec, rec_get_offsets(rec, cursor->index, - ULINT_UNDEFINED, heap)); - mem_heap_free(heap); + rec_print(stderr, rec, cursor->index); } err = lock_sec_rec_modify_check_and_lock(flags, rec, cursor->index, @@ -2375,9 +2396,10 @@ btr_cur_optimistic_delete( { page_t* page; ulint max_ins_size; - mem_heap_t* heap; rec_t* rec; - const ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(mtr_memo_contains(mtr, buf_block_align(btr_cur_get_page(cursor)), MTR_MEMO_PAGE_X_FIX)); @@ -2387,9 +2409,9 @@ btr_cur_optimistic_delete( ut_ad(btr_page_get_level(page, mtr) == 0); - heap = mem_heap_create(100); rec = btr_cur_get_rec(cursor); - offsets = rec_get_offsets(rec, cursor->index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, cursor->index, offsets, + ULINT_UNDEFINED, &heap); if (!rec_offs_any_extern(offsets) && btr_cur_can_delete_without_compress( @@ -2406,11 +2428,15 @@ btr_cur_optimistic_delete( ibuf_update_free_bits_low(cursor->index, page, max_ins_size, mtr); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(TRUE); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(FALSE); } @@ -2487,8 +2513,9 @@ btr_cur_pessimistic_delete( : !rec_get_1byte_offs_flag(rec)) { btr_rec_free_externally_stored_fields(cursor->index, rec, rec_get_offsets(rec, cursor->index, - ULINT_UNDEFINED, heap), + NULL, ULINT_UNDEFINED, &heap), in_rollback, mtr); + mem_heap_empty(heap); } if ((page_get_n_recs(page) < 2) @@ -2772,13 +2799,14 @@ btr_estimate_number_of_different_key_vals( ulint j; ulint add_on; mtr_t mtr; - mem_heap_t* heap; - ulint* offsets1 = 0; - ulint* offsets2 = 0; + mem_heap_t* heap = NULL; + ulint offsets1_[100] = { 100, }; + ulint offsets2_[100] = { 100, }; + ulint* offsets1 = offsets1_; + ulint* offsets2 = offsets2_; n_cols = dict_index_get_n_unique(index); - heap = mem_heap_create(100); n_diff = mem_alloc((n_cols + 1) * sizeof(ib_longlong)); for (j = 0; j <= n_cols; j++) { @@ -2813,10 +2841,10 @@ btr_estimate_number_of_different_key_vals( rec_t* next_rec = page_rec_get_next(rec); matched_fields = 0; matched_bytes = 0; - offsets1 = rec_reget_offsets(rec, index, - offsets1, ULINT_UNDEFINED, heap); - offsets2 = rec_reget_offsets(next_rec, index, - offsets2, n_cols, heap); + offsets1 = rec_get_offsets(rec, index, offsets1, + ULINT_UNDEFINED, &heap); + offsets2 = rec_get_offsets(next_rec, index, offsets2, + n_cols, &heap); cmp_rec_rec_with_match(rec, next_rec, offsets1, offsets2, @@ -2856,8 +2884,8 @@ btr_estimate_number_of_different_key_vals( } } - offsets1 = rec_reget_offsets(rec, index, - offsets1, ULINT_UNDEFINED, heap); + offsets1 = rec_get_offsets(rec, index, offsets1, + ULINT_UNDEFINED, &heap); total_external_size += btr_rec_get_externally_stored_len(rec, offsets1); @@ -2901,7 +2929,9 @@ btr_estimate_number_of_different_key_vals( } mem_free(n_diff); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /*================== EXTERNAL STORAGE OF BIG FIELDS ===================*/ diff --git a/innobase/btr/btr0pcur.c b/innobase/btr/btr0pcur.c index 7df8e53cd07..ad7613e0655 100644 --- a/innobase/btr/btr0pcur.c +++ b/innobase/btr/btr0pcur.c @@ -262,9 +262,10 @@ btr_pcur_restore_position( heap = mem_heap_create(256); offsets1 = rec_get_offsets(cursor->old_rec, - index, ULINT_UNDEFINED, heap); - offsets2 = rec_get_offsets(rec, - index, ULINT_UNDEFINED, heap); + index, NULL, + cursor->old_n_fields, &heap); + offsets2 = rec_get_offsets(rec, index, NULL, + cursor->old_n_fields, &heap); ut_ad(cmp_rec_rec(cursor->old_rec, rec, offsets1, offsets2, @@ -310,7 +311,7 @@ btr_pcur_restore_position( && 0 == cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor), rec_get_offsets(btr_pcur_get_rec(cursor), btr_pcur_get_btr_cur(cursor)->index, - ULINT_UNDEFINED, heap))) { + NULL, ULINT_UNDEFINED, &heap))) { /* We have to store the NEW value for the modify clock, since the cursor can now be on a different page! But we can retain diff --git a/innobase/btr/btr0sea.c b/innobase/btr/btr0sea.c index c18fa9a923d..dc712f650e7 100644 --- a/innobase/btr/btr0sea.c +++ b/innobase/btr/btr0sea.c @@ -419,7 +419,9 @@ btr_search_update_hash_ref( && (block->curr_n_fields == info->n_fields) && (block->curr_n_bytes == info->n_bytes) && (block->curr_side == info->side)) { - mem_heap_t* heap; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + rec = btr_cur_get_rec(cursor); if (!page_rec_is_user_rec(rec)) { @@ -428,11 +430,13 @@ btr_search_update_hash_ref( } tree_id = ((cursor->index)->tree)->id; - heap = mem_heap_create(100); fold = rec_fold(rec, rec_get_offsets(rec, cursor->index, - ULINT_UNDEFINED, heap), block->curr_n_fields, + offsets_, ULINT_UNDEFINED, &heap), + block->curr_n_fields, block->curr_n_bytes, tree_id); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ @@ -547,8 +551,10 @@ btr_search_check_guess( ulint match; ulint bytes; int cmp; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + ibool success = FALSE; n_unique = dict_index_get_n_unique_in_tree(cursor->index); @@ -560,47 +566,43 @@ btr_search_check_guess( match = 0; bytes = 0; - offsets = rec_get_offsets(rec, cursor->index, n_unique, heap); + offsets = rec_get_offsets(rec, cursor->index, offsets, + n_unique, &heap); cmp = page_cmp_dtuple_rec_with_match(tuple, rec, offsets, &match, &bytes); if (mode == PAGE_CUR_GE) { if (cmp == 1) { - mem_heap_free(heap); - return(FALSE); + goto exit_func; } cursor->up_match = match; if (match >= n_unique) { - mem_heap_free(heap); - return(TRUE); + success = TRUE; + goto exit_func; } } else if (mode == PAGE_CUR_LE) { if (cmp == -1) { - mem_heap_free(heap); - return(FALSE); + goto exit_func; } cursor->low_match = match; } else if (mode == PAGE_CUR_G) { if (cmp != -1) { - mem_heap_free(heap); - return(FALSE); + goto exit_func; } } else if (mode == PAGE_CUR_L) { if (cmp != 1) { - mem_heap_free(heap); - return(FALSE); + goto exit_func; } } if (can_only_compare_to_cursor_rec) { /* Since we could not determine if our guess is right just by looking at the record under the cursor, return FALSE */ - mem_heap_free(heap); - return(FALSE); + goto exit_func; } match = 0; @@ -613,28 +615,21 @@ btr_search_check_guess( prev_rec = page_rec_get_prev(rec); if (prev_rec == page_get_infimum_rec(page)) { - mem_heap_free(heap); - return(btr_page_get_prev(page, mtr) == FIL_NULL); + success = btr_page_get_prev(page, mtr) == FIL_NULL; + goto exit_func; } - offsets = rec_reget_offsets(prev_rec, cursor->index, - offsets, n_unique, heap); + offsets = rec_get_offsets(prev_rec, cursor->index, offsets, + n_unique, &heap); cmp = page_cmp_dtuple_rec_with_match(tuple, prev_rec, offsets, &match, &bytes); - mem_heap_free(heap); if (mode == PAGE_CUR_GE) { - if (cmp != 1) { - - return(FALSE); - } + success = cmp == 1; } else { - if (cmp == -1) { - - return(FALSE); - } + success = cmp != -1; } - return(TRUE); + goto exit_func; } ut_ad(rec != page_get_supremum_rec(page)); @@ -642,39 +637,30 @@ btr_search_check_guess( next_rec = page_rec_get_next(rec); if (next_rec == page_get_supremum_rec(page)) { - mem_heap_free(heap); - if (btr_page_get_next(page, mtr) == FIL_NULL) { cursor->up_match = 0; - - return(TRUE); + success = TRUE; } - return(FALSE); + goto exit_func; } - offsets = rec_reget_offsets(next_rec, cursor->index, - offsets, n_unique, heap); + offsets = rec_get_offsets(next_rec, cursor->index, offsets, + n_unique, &heap); cmp = page_cmp_dtuple_rec_with_match(tuple, next_rec, offsets, &match, &bytes); - mem_heap_free(heap); - if (mode == PAGE_CUR_LE) { - if (cmp != -1) { - - return(FALSE); - } - + success = cmp == -1; cursor->up_match = match; } else { - if (cmp == 1) { - - return(FALSE); - } + success = cmp != 1; } - - return(TRUE); +exit_func: + if (heap) { + mem_heap_free(heap); + } + return(success); } /********************************************************************** @@ -997,14 +983,14 @@ btr_search_drop_page_hash_index( prev_fold = 0; - heap = mem_heap_create(100); + heap = NULL; offsets = NULL; while (rec != sup) { /* FIXME: in a mixed tree, not all records may have enough ordering fields: */ - offsets = rec_reget_offsets(rec, block->index, - offsets, n_fields + (n_bytes > 0), heap); + offsets = rec_get_offsets(rec, block->index, + offsets, n_fields + (n_bytes > 0), &heap); fold = rec_fold(rec, offsets, n_fields, n_bytes, tree_id); if (fold == prev_fold && prev_fold != 0) { @@ -1022,7 +1008,9 @@ next_rec: prev_fold = fold; } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } rw_lock_x_lock(&btr_search_latch); @@ -1109,8 +1097,9 @@ btr_search_build_page_hash_index( ulint* folds; rec_t** recs; ulint i; - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(index); @@ -1161,7 +1150,6 @@ btr_search_build_page_hash_index( folds = mem_alloc(n_recs * sizeof(ulint)); recs = mem_alloc(n_recs * sizeof(rec_t*)); - heap = mem_heap_create(100); n_cached = 0; @@ -1172,7 +1160,8 @@ btr_search_build_page_hash_index( rec = page_get_infimum_rec(page); rec = page_rec_get_next(rec); - offsets = rec_get_offsets(rec, index, n_fields + (n_bytes > 0), heap); + offsets = rec_get_offsets(rec, index, offsets, + n_fields + (n_bytes > 0), &heap); if (rec != sup) { ut_a(n_fields <= rec_offs_n_fields(offsets)); @@ -1208,8 +1197,8 @@ btr_search_build_page_hash_index( break; } - offsets = rec_reget_offsets(next_rec, index, - offsets, n_fields + (n_bytes > 0), heap); + offsets = rec_get_offsets(next_rec, index, offsets, + n_fields + (n_bytes > 0), &heap); next_fold = rec_fold(next_rec, offsets, n_fields, n_bytes, tree_id); @@ -1260,7 +1249,9 @@ exit_func: mem_free(folds); mem_free(recs); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /************************************************************************ @@ -1350,7 +1341,8 @@ btr_search_update_hash_on_delete( ulint fold; dulint tree_id; ibool found; - mem_heap_t* heap; + ulint offsets_[100] = { 100, }; + mem_heap_t* heap = NULL; rec = btr_cur_get_rec(cursor); @@ -1371,11 +1363,12 @@ btr_search_update_hash_on_delete( table = btr_search_sys->hash_index; tree_id = cursor->index->tree->id; - heap = mem_heap_create(100); - fold = rec_fold(rec, rec_get_offsets(rec, cursor->index, - ULINT_UNDEFINED, heap), block->curr_n_fields, + fold = rec_fold(rec, rec_get_offsets(rec, cursor->index, offsets_, + ULINT_UNDEFINED, &heap), block->curr_n_fields, block->curr_n_bytes, tree_id); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } rw_lock_x_lock(&btr_search_latch); found = ha_search_and_delete_if_found(table, fold, rec); @@ -1457,9 +1450,10 @@ btr_search_update_hash_on_insert( ulint n_fields; ulint n_bytes; ulint side; - ibool locked = FALSE; - mem_heap_t* heap; - ulint* offsets; + ibool locked = FALSE; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; table = btr_search_sys->hash_index; @@ -1490,21 +1484,20 @@ btr_search_update_hash_on_insert( next_rec = page_rec_get_next(ins_rec); page = buf_frame_align(rec); - heap = mem_heap_create(100); - offsets = rec_get_offsets(ins_rec, cursor->index, - ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(ins_rec, cursor->index, offsets, + ULINT_UNDEFINED, &heap); ins_fold = rec_fold(ins_rec, offsets, n_fields, n_bytes, tree_id); if (next_rec != page_get_supremum_rec(page)) { - offsets = rec_reget_offsets(next_rec, cursor->index, - offsets, n_fields + (n_bytes > 0), heap); + offsets = rec_get_offsets(next_rec, cursor->index, offsets, + n_fields + (n_bytes > 0), &heap); next_fold = rec_fold(next_rec, offsets, n_fields, n_bytes, tree_id); } if (rec != page_get_infimum_rec(page)) { - offsets = rec_reget_offsets(rec, cursor->index, - offsets, n_fields + (n_bytes > 0), heap); + offsets = rec_get_offsets(rec, cursor->index, offsets, + n_fields + (n_bytes > 0), &heap); fold = rec_fold(rec, offsets, n_fields, n_bytes, tree_id); } else { if (side == BTR_SEARCH_LEFT_SIDE) { @@ -1575,7 +1568,9 @@ check_next_rec: } function_exit: - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } if (locked) { rw_lock_x_unlock(&btr_search_latch); } @@ -1595,8 +1590,9 @@ btr_search_validate(void) ulint n_page_dumps = 0; ibool ok = TRUE; ulint i; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; rw_lock_x_lock(&btr_search_latch); @@ -1606,10 +1602,10 @@ btr_search_validate(void) while (node != NULL) { block = buf_block_align(node->data); page = buf_frame_align(node->data); - offsets = rec_reget_offsets((rec_t*) node->data, + offsets = rec_get_offsets((rec_t*) node->data, block->index, offsets, block->curr_n_fields - + (block->curr_n_bytes > 0), heap); + + (block->curr_n_bytes > 0), &heap); if (!block->is_hashed || node->fold != rec_fold((rec_t*)(node->data), @@ -1635,7 +1631,8 @@ btr_search_validate(void) btr_page_get_index_id(page))); fputs("InnoDB: Record ", stderr); - rec_print(stderr, (rec_t*)node->data, offsets); + rec_print_new(stderr, (rec_t*)node->data, + offsets); fprintf(stderr, "\nInnoDB: on that page." "Page mem address %p, is hashed %lu, n fields %lu, n bytes %lu\n" "side %lu\n", @@ -1659,7 +1656,9 @@ btr_search_validate(void) } rw_lock_x_unlock(&btr_search_latch); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(ok); } diff --git a/innobase/include/rem0rec.h b/innobase/include/rem0rec.h index d450df82311..ab89b912523 100644 --- a/innobase/include/rem0rec.h +++ b/innobase/include/rem0rec.h @@ -67,6 +67,16 @@ rec_get_n_fields_old( /* out: number of data fields */ rec_t* rec); /* in: physical record */ /********************************************************** +The following function is used to get the number of fields +in a record. */ +UNIV_INLINE +ulint +rec_get_n_fields( +/*=============*/ + /* out: number of data fields */ + rec_t* rec, /* in: physical record */ + dict_index_t* index); /* in: record descriptor */ +/********************************************************** The following function is used to get the number of records owned by the previous directory record. */ UNIV_INLINE @@ -188,45 +198,28 @@ rec_get_1byte_offs_flag( rec_t* rec); /* in: physical record */ /********************************************************** The following function determines the offsets to each field -in the record. The offsets are returned in an array of -ulint, with [0] being the number of fields (n), [1] being the -extra size (if REC_OFFS_COMPACT is set, the record is in the new -format), and [2]..[n+1] being the offsets past the end of -fields 0..n, or to the beginning of fields 1..n+1. When the -high-order bit of the offset at [n+1] is set (REC_OFFS_SQL_NULL), -the field n is NULL. When the second high-order bit of the offset -at [n+1] is set (REC_OFFS_EXTERNAL), the field n is being stored -externally. */ +in the record. It can reuse a previously allocated array. */ ulint* -rec_get_offsets( -/*============*/ - /* out: the offsets */ - rec_t* rec, /* in: physical record */ - dict_index_t* index, /* in: record descriptor */ - ulint n_fields,/* in: maximum number of initialized fields - (ULINT_UNDEFINED if all fields) */ - mem_heap_t* heap); /* in: memory heap */ -/********************************************************** -The following function determines the offsets to each field -in the record. It differs from rec_get_offsets() by trying to -reuse a previously returned array. */ - -ulint* -rec_reget_offsets( -/*==============*/ +rec_get_offsets_func( +/*=================*/ /* out: the new offsets */ rec_t* rec, /* in: physical record */ dict_index_t* index, /* in: record descriptor */ - ulint* offsets,/* in: array of offsets - from rec_get_offsets() - or rec_reget_offsets(), or NULL */ + ulint* offsets,/* in: array consisting of offsets[0] + allocated elements, or an array from + rec_get_offsets(), or NULL */ ulint n_fields,/* in: maximum number of initialized fields (ULINT_UNDEFINED if all fields) */ - mem_heap_t* heap); /* in: memory heap */ + mem_heap_t** heap, /* in/out: memory heap */ + const char* file, /* in: file name where called */ + ulint line); /* in: line number where called */ + +#define rec_get_offsets(rec,index,offsets,n,heap) \ + rec_get_offsets_func(rec,index,offsets,n,heap,__FILE__,__LINE__) /**************************************************************** -Validates offsets returned by rec_get_offsets() or rec_reget_offsets(). */ +Validates offsets returned by rec_get_offsets(). */ UNIV_INLINE ibool rec_offs_validate( @@ -234,8 +227,7 @@ rec_offs_validate( /* out: TRUE if valid */ rec_t* rec, /* in: record or NULL */ dict_index_t* index, /* in: record descriptor or NULL */ - const ulint* offsets);/* in: array returned by rec_get_offsets() - or rec_reget_offsets() */ + const ulint* offsets);/* in: array returned by rec_get_offsets() */ /**************************************************************** Updates debug data in offsets, in order to avoid bogus rec_offs_validate() failures. */ @@ -243,10 +235,9 @@ UNIV_INLINE void rec_offs_make_valid( /*================*/ - const rec_t* rec, /* in: record */ - const dict_index_t* index,/* in: record descriptor */ - ulint* offsets);/* in: array returned by rec_get_offsets() - or rec_reget_offsets() */ + rec_t* rec, /* in: record */ + dict_index_t* index,/* in: record descriptor */ + ulint* offsets);/* in: array returned by rec_get_offsets() */ /**************************************************************** The following function is used to get a pointer to the nth @@ -551,7 +542,15 @@ rec_print_old( /*==========*/ FILE* file, /* in: file where to print */ rec_t* rec); /* in: physical record */ +/******************************************************************* +Prints a physical record. */ +void +rec_print_new( +/*==========*/ + FILE* file, /* in: file where to print */ + rec_t* rec, /* in: physical record */ + const ulint* offsets);/* in: array returned by rec_get_offsets() */ /******************************************************************* Prints a physical record. */ @@ -560,7 +559,7 @@ rec_print( /*======*/ FILE* file, /* in: file where to print */ rec_t* rec, /* in: physical record */ - const ulint* offsets);/* in: array returned by rec_get_offsets() */ + dict_index_t* index); /* in: record descriptor */ #define REC_INFO_BITS 6 /* This is single byte bit-field */ diff --git a/innobase/include/rem0rec.ic b/innobase/include/rem0rec.ic index 8443b5fa07d..db938aa9fa5 100644 --- a/innobase/include/rem0rec.ic +++ b/innobase/include/rem0rec.ic @@ -681,9 +681,11 @@ rec_2_get_field_end_info( } #ifdef UNIV_DEBUG -# define REC_OFFS_HEADER_SIZE 3 +/* Length of the rec_get_offsets() header */ +# define REC_OFFS_HEADER_SIZE 4 #else /* UNIV_DEBUG */ -# define REC_OFFS_HEADER_SIZE 1 +/* Length of the rec_get_offsets() header */ +# define REC_OFFS_HEADER_SIZE 2 #endif /* UNIV_DEBUG */ /* Get the base address of offsets. The extra_size is stored at @@ -691,8 +693,40 @@ this position, and following positions hold the end offsets of the fields. */ #define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE) +/************************************************************** +The following function returns the number of allocated elements +for an array of offsets. */ +UNIV_INLINE +ulint +rec_offs_get_n_alloc( +/*=================*/ + /* out: number of elements */ + const ulint* offsets)/* in: array for rec_get_offsets() */ +{ + ulint n_alloc; + ut_ad(offsets); + n_alloc = offsets[0]; + ut_ad(n_alloc > 0); + return(n_alloc); +} + +/************************************************************** +The following function sets the number of allocated elements +for an array of offsets. */ +UNIV_INLINE +void +rec_offs_set_n_alloc( +/*=================*/ + ulint* offsets, /* in: array for rec_get_offsets() */ + ulint n_alloc) /* in: number of elements */ +{ + ut_ad(offsets); + ut_ad(n_alloc > 0); + offsets[0] = n_alloc; +} + /**************************************************************** -Validates offsets returned by rec_get_offsets() or rec_reget_offsets(). */ +Validates offsets returned by rec_get_offsets(). */ UNIV_INLINE ibool rec_offs_validate( @@ -705,16 +739,16 @@ rec_offs_validate( ulint i = rec_offs_n_fields(offsets); ulint last = ULINT_MAX; ibool comp = (*rec_offs_base(offsets) & REC_OFFS_COMPACT) != 0; - ut_a(offsets); + if (rec) { - ut_ad((ulint) rec == offsets[1]); + ut_ad((ulint) rec == offsets[2]); if (!comp) { ut_a(rec_get_n_fields_old(rec) >= i); } } if (index) { ulint max_n_fields; - ut_ad((ulint) index == offsets[2]); + ut_ad((ulint) index == offsets[3]); max_n_fields = ut_max( dict_index_get_n_fields(index), dict_index_get_n_unique_in_tree(index) + 1); @@ -734,7 +768,9 @@ rec_offs_validate( ut_error; } } - ut_a(i <= max_n_fields); + /* index->n_def == 0 for dummy indexes if !comp */ + ut_a(!comp || index->n_def); + ut_a(!index->n_def || i <= max_n_fields); } while (i--) { ulint curr = rec_offs_base(offsets)[1 + i] & REC_OFFS_MASK; @@ -750,17 +786,17 @@ UNIV_INLINE void rec_offs_make_valid( /*================*/ - const rec_t* rec __attribute__((unused)), + rec_t* rec __attribute__((unused)), /* in: record */ - const dict_index_t* index __attribute__((unused)), + dict_index_t* index __attribute__((unused)), /* in: record descriptor */ ulint* offsets __attribute__((unused))) - /* in: array returned by rec_get_offsets() - or rec_reget_offsets() */ + /* in: array returned by rec_get_offsets() */ { #ifdef UNIV_DEBUG - offsets[1] = (ulint) rec; - offsets[2] = (ulint) index; + ut_ad(rec_get_n_fields(rec, index) >= rec_offs_n_fields(offsets)); + offsets[2] = (ulint) rec; + offsets[3] = (ulint) index; #endif /* UNIV_DEBUG */ } @@ -1146,12 +1182,31 @@ rec_offs_n_fields( { ulint n_fields; ut_ad(offsets); - n_fields = offsets[0]; + n_fields = offsets[1]; ut_ad(n_fields > 0); ut_ad(n_fields <= REC_MAX_N_FIELDS); + ut_ad(n_fields + REC_OFFS_HEADER_SIZE + <= rec_offs_get_n_alloc(offsets)); return(n_fields); } +/************************************************************** +The following function sets the number of fields in offsets. */ +UNIV_INLINE +void +rec_offs_set_n_fields( +/*==================*/ + ulint* offsets, /* in: array returned by rec_get_offsets() */ + ulint n_fields) /* in: number of fields */ +{ + ut_ad(offsets); + ut_ad(n_fields > 0); + ut_ad(n_fields <= REC_MAX_N_FIELDS); + ut_ad(n_fields + REC_OFFS_HEADER_SIZE + <= rec_offs_get_n_alloc(offsets)); + offsets[1] = n_fields; +} + /************************************************************** The following function returns the data size of a physical record, that is the sum of field lengths. SQL null fields diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index d2d16a1ae4e..961210dbd06 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -424,7 +424,7 @@ lock_check_trx_id_sanity( /* out: TRUE if ok */ dulint trx_id, /* in: trx id */ rec_t* rec, /* in: user record */ - dict_index_t* index, /* in: clustered index */ + dict_index_t* index, /* in: index */ const ulint* offsets, /* in: rec_get_offsets(rec, index) */ ibool has_kernel_mutex)/* in: TRUE if the caller owns the kernel mutex */ @@ -445,7 +445,7 @@ lock_check_trx_id_sanity( fputs(" InnoDB: Error: transaction id associated" " with record\n", stderr); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); fputs("InnoDB: in ", stderr); dict_index_name_print(stderr, NULL, index); fprintf(stderr, "\n" @@ -4073,10 +4073,9 @@ lock_rec_print( ulint page_no; ulint i; mtr_t mtr; - mem_heap_t* heap; - ulint* offsets = NULL; - - heap = mem_heap_create(100); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex)); @@ -4157,9 +4156,9 @@ lock_rec_print( if (page) { rec_t* rec = page_find_rec_with_heap_no(page, i); - offsets = rec_reget_offsets(rec, lock->index, - offsets, ULINT_UNDEFINED, heap); - rec_print(file, rec, offsets); + offsets = rec_get_offsets(rec, lock->index, + offsets, ULINT_UNDEFINED, &heap); + rec_print_new(file, rec, offsets); } putc('\n', file); @@ -4167,9 +4166,11 @@ lock_rec_print( } mtr_commit(&mtr); - mem_heap_free(heap); -} - + if (heap) { + mem_heap_free(heap); + } +} + /************************************************************************* Calculates the number of record lock structs in the record lock hash table. */ static @@ -4562,12 +4563,13 @@ lock_rec_validate_page( page_t* page; lock_t* lock; rec_t* rec; - ulint nth_lock = 0; - ulint nth_bit = 0; + ulint nth_lock = 0; + ulint nth_bit = 0; ulint i; mtr_t mtr; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; #ifdef UNIV_SYNC_DEBUG ut_ad(!mutex_own(&kernel_mutex)); @@ -4607,8 +4609,8 @@ loop: index = lock->index; rec = page_find_rec_with_heap_no(page, i); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); fprintf(stderr, "Validating %lu %lu\n", (ulong) space, (ulong) page_no); @@ -4635,7 +4637,9 @@ function_exit: mtr_commit(&mtr); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(TRUE); } @@ -4811,11 +4815,15 @@ lock_rec_insert_check_and_lock( #ifdef UNIV_DEBUG { - mem_heap_t* heap = mem_heap_create(100); - const ulint* offsets = rec_get_offsets(next_rec, index, - ULINT_UNDEFINED, heap); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + const ulint* offsets = rec_get_offsets( + next_rec, index, offsets_, + ULINT_UNDEFINED, &heap); ut_ad(lock_rec_queue_validate(next_rec, index, offsets)); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } #endif /* UNIV_DEBUG */ @@ -4954,11 +4962,14 @@ lock_sec_rec_modify_check_and_lock( #ifdef UNIV_DEBUG { - mem_heap_t* heap = mem_heap_create(100); - const ulint* offsets = rec_get_offsets(rec, index, - ULINT_UNDEFINED, heap); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + const ulint* offsets = rec_get_offsets( + rec, index, offsets_, ULINT_UNDEFINED, &heap); ut_ad(lock_rec_queue_validate(rec, index, offsets)); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } #endif /* UNIV_DEBUG */ @@ -5067,7 +5078,6 @@ lock_clust_rec_read_check_and_lock( ut_ad(page_rec_is_user_rec(rec) || page_rec_is_supremum(rec)); ut_ad(gap_mode == LOCK_ORDINARY || gap_mode == LOCK_GAP || gap_mode == LOCK_REC_NOT_GAP); - ut_ad(index->type & DICT_CLUSTERED); ut_ad(rec_offs_validate(rec, index, offsets)); if (flags & BTR_NO_LOCKING_FLAG) { diff --git a/innobase/page/page0cur.c b/innobase/page/page0cur.c index 8def8474d9a..53c3c573b8e 100644 --- a/innobase/page/page0cur.c +++ b/innobase/page/page0cur.c @@ -29,6 +29,7 @@ UNIV_INLINE ibool page_cur_try_search_shortcut( /*=========================*/ + /* out: TRUE on success */ page_t* page, /* in: index page */ dict_index_t* index, /* in: record descriptor */ dtuple_t* tuple, /* in: data tuple */ @@ -56,14 +57,15 @@ page_cur_try_search_shortcut( #ifdef UNIV_SEARCH_DEBUG page_cur_t cursor2; #endif - mem_heap_t* heap; - ulint* offsets; + ibool success = FALSE; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(dtuple_check_typed(tuple)); rec = page_header_get_ptr(page, PAGE_LAST_INSERT); - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, - dtuple_get_n_fields(tuple), heap); + offsets = rec_get_offsets(rec, index, offsets, + dtuple_get_n_fields(tuple), &heap); ut_ad(rec); ut_ad(page_rec_is_user_rec(rec)); @@ -78,21 +80,17 @@ page_cur_try_search_shortcut( cmp = page_cmp_dtuple_rec_with_match(tuple, rec, offsets, &low_match, &low_bytes); if (cmp == -1) { - - mem_heap_free(heap); - return(FALSE); + goto exit_func; } next_rec = page_rec_get_next(rec); - offsets = rec_reget_offsets(next_rec, index, offsets, - dtuple_get_n_fields(tuple), heap); + offsets = rec_get_offsets(next_rec, index, offsets, + dtuple_get_n_fields(tuple), &heap); cmp = page_cmp_dtuple_rec_with_match(tuple, next_rec, offsets, &up_match, &up_bytes); if (cmp != -1) { - - mem_heap_free(heap); - return(FALSE); + goto exit_func; } cursor->rec = rec; @@ -127,8 +125,12 @@ page_cur_try_search_shortcut( #ifdef UNIV_SEARCH_PERF_STAT page_cur_short_succ++; #endif - mem_heap_free(heap); - return(TRUE); + success = TRUE; +exit_func: + if (heap) { + mem_heap_free(heap); + } + return(success); } #endif @@ -226,8 +228,9 @@ page_cur_search_with_match( ulint dbg_matched_fields; ulint dbg_matched_bytes; #endif - mem_heap_t* heap; - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(page && tuple && iup_matched_fields && iup_matched_bytes && ilow_matched_fields && ilow_matched_bytes && cursor); @@ -262,8 +265,6 @@ page_cur_search_with_match( /*#endif */ #endif - heap = mem_heap_create(100); - /* The following flag does not work for non-latin1 char sets because cmp_full_field does not tell how many bytes matched */ ut_a(mode != PAGE_CUR_LE_OR_EXTENDS); @@ -298,8 +299,8 @@ page_cur_search_with_match( low_matched_fields, low_matched_bytes, up_matched_fields, up_matched_bytes); - offsets = rec_reget_offsets(mid_rec, index, offsets, - dtuple_get_n_fields_cmp(tuple), heap); + offsets = rec_get_offsets(mid_rec, index, offsets, + dtuple_get_n_fields_cmp(tuple), &heap); cmp = cmp_dtuple_rec_with_match(tuple, mid_rec, offsets, &cur_matched_fields, @@ -310,8 +311,8 @@ page_cur_search_with_match( low_matched_bytes = cur_matched_bytes; } else if (cmp == -1) { - offsets = rec_reget_offsets(mid_rec, index, - offsets, dtuple_get_n_fields_cmp(tuple), heap); + offsets = rec_get_offsets(mid_rec, index, offsets, + dtuple_get_n_fields_cmp(tuple), &heap); if (mode == PAGE_CUR_LE_OR_EXTENDS && page_cur_rec_field_extends(tuple, mid_rec, @@ -353,8 +354,8 @@ page_cur_search_with_match( low_matched_fields, low_matched_bytes, up_matched_fields, up_matched_bytes); - offsets = rec_reget_offsets(mid_rec, index, - offsets, dtuple_get_n_fields_cmp(tuple), heap); + offsets = rec_get_offsets(mid_rec, index, offsets, + dtuple_get_n_fields_cmp(tuple), &heap); cmp = cmp_dtuple_rec_with_match(tuple, mid_rec, offsets, &cur_matched_fields, @@ -365,8 +366,8 @@ page_cur_search_with_match( low_matched_bytes = cur_matched_bytes; } else if (cmp == -1) { - offsets = rec_reget_offsets(mid_rec, index, - offsets, dtuple_get_n_fields_cmp(tuple), heap); + offsets = rec_get_offsets(mid_rec, index, offsets, + dtuple_get_n_fields_cmp(tuple), &heap); if (mode == PAGE_CUR_LE_OR_EXTENDS && page_cur_rec_field_extends(tuple, mid_rec, @@ -398,8 +399,8 @@ page_cur_search_with_match( dbg_matched_fields = 0; dbg_matched_bytes = 0; - offsets = rec_reget_offsets(low_rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(low_rec, index, offsets, + ULINT_UNDEFINED, &heap); dbg_cmp = page_cmp_dtuple_rec_with_match(tuple, low_rec, offsets, &dbg_matched_fields, &dbg_matched_bytes); @@ -422,8 +423,8 @@ page_cur_search_with_match( dbg_matched_fields = 0; dbg_matched_bytes = 0; - offsets = rec_reget_offsets(up_rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(up_rec, index, offsets, + ULINT_UNDEFINED, &heap); dbg_cmp = page_cmp_dtuple_rec_with_match(tuple, up_rec, offsets, &dbg_matched_fields, &dbg_matched_bytes); @@ -453,7 +454,9 @@ page_cur_search_with_match( *iup_matched_bytes = up_matched_bytes; *ilow_matched_fields = low_matched_fields; *ilow_matched_bytes = low_matched_bytes; - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /*************************************************************** @@ -519,22 +522,26 @@ page_cur_insert_rec_write_log( ut_a(rec_size < UNIV_PAGE_SIZE); { - mem_heap_t* heap; + mem_heap_t* heap = NULL; + ulint cur_offs_[100] = { 100, }; + ulint ins_offs_[100] = { 100, }; + ulint* cur_offs; ulint* ins_offs; - heap = mem_heap_create(100); - cur_offs = rec_get_offsets(cursor_rec, index, - ULINT_UNDEFINED, heap); - ins_offs = rec_get_offsets(insert_rec, index, - ULINT_UNDEFINED, heap); + cur_offs = rec_get_offsets(cursor_rec, index, cur_offs_, + ULINT_UNDEFINED, &heap); + ins_offs = rec_get_offsets(insert_rec, index, ins_offs_, + ULINT_UNDEFINED, &heap); extra_size = rec_offs_extra_size(ins_offs); cur_extra_size = rec_offs_extra_size(cur_offs); ut_ad(rec_size == rec_offs_size(ins_offs)); cur_rec_size = rec_offs_size(cur_offs); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } ins_ptr = insert_rec - extra_size; @@ -668,8 +675,9 @@ page_cur_parse_insert_rec( byte* ptr2 = ptr; ulint info_bits = 0; /* remove warning */ page_cur_t cursor; - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; if (!is_short) { /* Read the cursor rec offset as a 2-byte ulint */ @@ -756,8 +764,8 @@ page_cur_parse_insert_rec( cursor_rec = page + offset; } - heap = mem_heap_create(100); - offsets = rec_get_offsets(cursor_rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(cursor_rec, index, offsets, + ULINT_UNDEFINED, &heap); if (extra_info_yes == 0) { info_bits = rec_get_info_bits(cursor_rec, index->table->comp); @@ -816,6 +824,10 @@ page_cur_parse_insert_rec( mem_free(buf); } + if (heap) { + mem_heap_free(heap); + } + return(ptr + end_seg_len); } @@ -850,8 +862,9 @@ page_cur_insert_rec_low( inserted record */ rec_t* owner_rec; ulint n_owned; - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ibool comp = index->table->comp; ut_ad(cursor && mtr); @@ -865,14 +878,12 @@ page_cur_insert_rec_low( ut_ad(cursor->rec != page_get_supremum_rec(page)); - heap = mem_heap_create(100); - /* 1. Get the size of the physical record in the page */ if (tuple != NULL) { - offsets = NULL; rec_size = rec_get_converted_size(index, tuple); } else { - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); rec_size = rec_offs_size(offsets); } @@ -880,7 +891,9 @@ page_cur_insert_rec_low( insert_buf = page_mem_alloc(page, rec_size, index, &heap_no); if (insert_buf == NULL) { - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(NULL); } @@ -888,13 +901,15 @@ page_cur_insert_rec_low( if (tuple != NULL) { insert_rec = rec_convert_dtuple_to_rec(insert_buf, index, tuple); + offsets = rec_get_offsets(insert_rec, index, offsets, + ULINT_UNDEFINED, &heap); } else { insert_rec = rec_copy(insert_buf, rec, offsets); + ut_ad(rec_offs_validate(rec, index, offsets)); + rec_offs_make_valid(insert_rec, index, offsets); } ut_ad(insert_rec); - offsets = rec_reget_offsets(insert_rec, index, - offsets, ULINT_UNDEFINED, heap); ut_ad(rec_size == rec_offs_size(offsets)); /* 4. Insert the record in the linked list of records */ @@ -966,7 +981,9 @@ page_cur_insert_rec_low( page_cur_insert_rec_write_log(insert_rec, rec_size, current_rec, index, mtr); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(insert_rec); } @@ -1068,9 +1085,10 @@ page_copy_rec_list_end_to_created_page( ulint log_mode; byte* log_ptr; ulint log_data_len; - ibool comp = page_is_comp(page); - mem_heap_t* heap; - ulint* offsets = NULL; + ibool comp = page_is_comp(page); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(page_dir_get_n_heap(new_page) == 2); ut_ad(page != new_page); @@ -1117,8 +1135,8 @@ page_copy_rec_list_end_to_created_page( /* should be do ... until, comment by Jani */ while (rec != page_get_supremum_rec(page)) { - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); insert_rec = rec_copy(heap_top, rec, offsets); rec_set_next_offs(prev_rec, comp, insert_rec - new_page); @@ -1170,7 +1188,9 @@ page_copy_rec_list_end_to_created_page( slot_index--; } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } log_data_len = dyn_array_get_data_size(&(mtr->log)) - log_data_len; diff --git a/innobase/page/page0page.c b/innobase/page/page0page.c index 38b1e503c8f..67c7bd936d1 100644 --- a/innobase/page/page0page.c +++ b/innobase/page/page0page.c @@ -227,10 +227,12 @@ page_mem_alloc( rec = page_header_get_ptr(page, PAGE_FREE); if (rec) { - mem_heap_t* heap - = mem_heap_create(100); - const ulint* offsets - = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); if (rec_offs_size(offsets) >= need) { page_header_set_ptr(page, PAGE_FREE, @@ -245,11 +247,15 @@ page_mem_alloc( *heap_no = rec_get_heap_no(rec, page_is_comp(page)); block = rec_get_start(rec, offsets); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(block); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /* Could not find space from the free list, try top of heap */ @@ -374,7 +380,8 @@ page_create( rec_set_n_owned(infimum_rec, comp, 1); rec_set_heap_no(infimum_rec, comp, 0); - offsets = rec_get_offsets(infimum_rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(infimum_rec, index, NULL, + ULINT_UNDEFINED, &heap); heap_top = rec_get_end(infimum_rec, offsets); @@ -396,8 +403,8 @@ page_create( rec_set_n_owned(supremum_rec, comp, 1); rec_set_heap_no(supremum_rec, comp, 1); - offsets = rec_reget_offsets(supremum_rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(supremum_rec, index, offsets, + ULINT_UNDEFINED, &heap); heap_top = rec_get_end(supremum_rec, offsets); ut_ad(heap_top == @@ -711,8 +718,9 @@ page_delete_rec_list_end( last_rec = page_rec_get_prev(sup); if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) { - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; /* Calculate the sum of sizes and the number of records */ size = 0; n_recs = 0; @@ -720,8 +728,8 @@ page_delete_rec_list_end( while (rec2 != sup) { ulint s; - offsets = rec_reget_offsets(rec2, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec2, index, offsets, + ULINT_UNDEFINED, &heap); s = rec_offs_size(offsets); ut_ad(rec2 - page + s - rec_offs_extra_size(offsets) < UNIV_PAGE_SIZE); @@ -732,7 +740,9 @@ page_delete_rec_list_end( rec2 = page_rec_get_next(rec2); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } ut_ad(size < UNIV_PAGE_SIZE); @@ -1213,7 +1223,7 @@ page_rec_print( ibool comp = page_is_comp(buf_frame_align(rec)); ut_a(comp == rec_offs_comp(offsets)); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); fprintf(stderr, " n_owned: %lu; heap_no: %lu; next rec: %lu\n", (ulong) rec_get_n_owned(rec, comp), @@ -1276,11 +1286,11 @@ page_print_list( page_cur_t cur; ulint count; ulint n_recs; - mem_heap_t* heap; - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_a(page_is_comp(page) == index->table->comp); - heap = mem_heap_create(100); fprintf(stderr, "--------------------------------\n" @@ -1292,8 +1302,8 @@ page_print_list( page_cur_set_before_first(page, &cur); count = 0; for (;;) { - offsets = rec_reget_offsets(cur.rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(cur.rec, index, offsets, + ULINT_UNDEFINED, &heap); page_rec_print(cur.rec, offsets); if (count == pr_n) { @@ -1314,8 +1324,8 @@ page_print_list( page_cur_move_to_next(&cur); if (count + pr_n >= n_recs) { - offsets = rec_reget_offsets(cur.rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(cur.rec, index, offsets, + ULINT_UNDEFINED, &heap); page_rec_print(cur.rec, offsets); } count++; @@ -1326,7 +1336,9 @@ page_print_list( "--------------------------------\n", (ulong) (count + 1)); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /******************************************************************* @@ -1680,7 +1692,7 @@ page_validate( goto func_exit2; } - heap = mem_heap_create(UNIV_PAGE_SIZE); + heap = mem_heap_create(UNIV_PAGE_SIZE + 200); /* The following buffer is used to check that the records in the page record heap do not overlap */ @@ -1720,8 +1732,8 @@ page_validate( for (;;) { rec = cur.rec; - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); if (comp && page_rec_is_user_rec(rec) && rec_get_node_ptr_flag(rec) @@ -1744,9 +1756,9 @@ page_validate( (ulong) buf_frame_get_page_no(page)); dict_index_name_print(stderr, NULL, index); fputs("\nInnoDB: previous record ", stderr); - rec_print(stderr, old_rec, old_offsets); + rec_print_new(stderr, old_rec, old_offsets); fputs("\nInnoDB: record ", stderr); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); putc('\n', stderr); goto func_exit; @@ -1852,8 +1864,8 @@ page_validate( rec = page_header_get_ptr(page, PAGE_FREE); while (rec != NULL) { - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); if (!page_rec_validate(rec, offsets)) { goto func_exit; diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c index e4fa213480f..74876ad9402 100644 --- a/innobase/rem/rem0rec.c +++ b/innobase/rem/rem0rec.c @@ -284,86 +284,25 @@ rec_init_offsets( /********************************************************** The following function determines the offsets to each field -in the record. The offsets are returned in an array of -ulint, with [0] being the number of fields (n), [1] being the -extra size (if REC_OFFS_COMPACT is set, the record is in the new -format), and [2]..[n+1] being the offsets past the end of -fields 0..n, or to the beginning of fields 1..n+1. When the -high-order bit of the offset at [n+1] is set (REC_OFFS_SQL_NULL), -the field n is NULL. When the second high-order bit of the offset -at [n+1] is set (REC_OFFS_EXTERNAL), the field n is being stored -externally. */ +in the record. It can reuse a previously returned array. */ ulint* -rec_get_offsets( -/*============*/ - /* out: the offsets */ - rec_t* rec, /* in: physical record */ - dict_index_t* index, /* in: record descriptor */ - ulint n_fields,/* in: maximum number of initialized fields - (ULINT_UNDEFINED if all fields) */ - mem_heap_t* heap) /* in: memory heap */ -{ - ulint* offsets; - ulint n; - - ut_ad(rec); - ut_ad(index); - ut_ad(heap); - - if (index->table->comp) { - switch (rec_get_status(rec)) { - case REC_STATUS_ORDINARY: - n = dict_index_get_n_fields(index); - break; - case REC_STATUS_NODE_PTR: - n = dict_index_get_n_unique_in_tree(index) + 1; - break; - case REC_STATUS_INFIMUM: - case REC_STATUS_SUPREMUM: - /* infimum or supremum record */ - n = 1; - break; - default: - ut_error; - return(NULL); - } - } else { - n = rec_get_n_fields_old(rec); - } - - if (n_fields < n) { - n = n_fields; - } - - offsets = mem_heap_alloc(heap, - (n + (1 + REC_OFFS_HEADER_SIZE)) * sizeof(ulint)); - - offsets[0] = n; - - rec_init_offsets(rec, index, offsets); - return(offsets); -} - -/********************************************************** -The following function determines the offsets to each field -in the record. It differs from rec_get_offsets() by trying to -reuse a previously returned array. */ - -ulint* -rec_reget_offsets( -/*==============*/ +rec_get_offsets_func( +/*=================*/ /* out: the new offsets */ rec_t* rec, /* in: physical record */ dict_index_t* index, /* in: record descriptor */ - ulint* offsets,/* in: array of offsets - from rec_get_offsets() - or rec_reget_offsets(), or NULL */ + ulint* offsets,/* in: array consisting of offsets[0] + allocated elements, or an array from + rec_get_offsets(), or NULL */ ulint n_fields,/* in: maximum number of initialized fields (ULINT_UNDEFINED if all fields) */ - mem_heap_t* heap) /* in: memory heap */ + mem_heap_t** heap, /* in/out: memory heap */ + const char* file, /* in: file name where called */ + ulint line) /* in: line number where called */ { ulint n; + ulint size; ut_ad(rec); ut_ad(index); @@ -394,13 +333,18 @@ rec_reget_offsets( n = n_fields; } - if (!offsets || rec_offs_n_fields(offsets) < n) { - offsets = mem_heap_alloc(heap, - (n + (1 + REC_OFFS_HEADER_SIZE)) * sizeof(ulint)); + size = (n + (1 + REC_OFFS_HEADER_SIZE)) * sizeof(ulint); + + if (!offsets || rec_offs_get_n_alloc(offsets) < size) { + if (!*heap) { + *heap = mem_heap_create_func(size, + NULL, MEM_HEAP_DYNAMIC, file, line); + } + offsets = mem_heap_alloc(*heap, size); + rec_offs_set_n_alloc(offsets, size); } - offsets[0] = n; - + rec_offs_set_n_fields(offsets, n); rec_init_offsets(rec, index, offsets); return(offsets); } @@ -722,14 +666,16 @@ rec_get_size( rec_t* rec, /* in: physical record */ dict_index_t* index) /* in: record descriptor */ { - mem_heap_t* heap - = mem_heap_create(100); - ulint* offsets - = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); - ulint size - = rec_offs_size(offsets); + mem_heap_t* heap = NULL; + ulint offsets_[100 + REC_OFFS_HEADER_SIZE] + = { 100, }; + ulint* offsets = rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap); + ulint size = rec_offs_size(offsets); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(size); } @@ -1032,10 +978,15 @@ rec_convert_dtuple_to_rec( #ifdef UNIV_DEBUG { - mem_heap_t* heap = mem_heap_create(100); - ut_ad(rec_validate(rec, - rec_get_offsets(rec, index, ULINT_UNDEFINED, heap))); - mem_heap_free(heap); + mem_heap_t* heap = NULL; + ulint offsets_[100 + REC_OFFS_HEADER_SIZE] + = { 100, }; + const ulint* offsets = rec_get_offsets(rec, index, + offsets_, ULINT_UNDEFINED, &heap); + ut_ad(rec_validate(rec, offsets)); + if (heap) { + mem_heap_free(heap); + } } #endif /* UNIV_DEBUG */ return(rec); @@ -1059,9 +1010,11 @@ rec_copy_prefix_to_dtuple( ulint len; byte* buf = NULL; ulint i; - ulint* offsets; + ulint offsets_[100 + REC_OFFS_HEADER_SIZE] + = { 100, }; + ulint* offsets = offsets_; - offsets = rec_get_offsets(rec, index, n_fields, heap); + offsets = rec_get_offsets(rec, index, offsets, n_fields, &heap); ut_ad(rec_validate(rec, offsets)); ut_ad(dtuple_check_typed(tuple)); @@ -1406,8 +1359,8 @@ rec_print_old( Prints a physical record. */ void -rec_print( -/*======*/ +rec_print_new( +/*==========*/ FILE* file, /* in: file where to print */ rec_t* rec, /* in: physical record */ const ulint* offsets)/* in: array returned by rec_get_offsets() */ @@ -1416,6 +1369,8 @@ rec_print( ulint len; ulint i; + ut_ad(rec_offs_validate(rec, NULL, offsets)); + if (!rec_offs_comp(offsets)) { rec_print_old(file, rec); return; @@ -1453,3 +1408,30 @@ rec_print( rec_validate(rec, offsets); } + +/******************************************************************* +Prints a physical record. */ + +void +rec_print( +/*======*/ + FILE* file, /* in: file where to print */ + rec_t* rec, /* in: physical record */ + dict_index_t* index) /* in: record descriptor */ +{ + ut_ad(index); + + if (!index->table->comp) { + rec_print_old(file, rec); + return; + } else { + mem_heap_t* heap = NULL; + ulint offsets_[100 + REC_OFFS_HEADER_SIZE] + = { 100, }; + rec_print_new(file, rec, rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap)); + if (heap) { + mem_heap_free(heap); + } + } +} diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index a87a08fa3fe..969c3341be3 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -590,16 +590,8 @@ row_ins_foreign_report_err( fputs(", in index ", ef); ut_print_name(ef, trx, foreign->foreign_index->name); if (rec) { - mem_heap_t* heap; - ulint* offsets; - - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, foreign->foreign_index, - ULINT_UNDEFINED, heap); - fputs(", there is a record:\n", ef); - rec_print(ef, rec, offsets); - mem_heap_free(heap); + rec_print(ef, rec, foreign->foreign_index); } else { fputs(", the record is not available\n", ef); } @@ -654,16 +646,7 @@ row_ins_foreign_report_add_err( } if (rec) { - mem_heap_t* heap; - ulint* offsets; - - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, foreign->foreign_index, - ULINT_UNDEFINED, heap); - - rec_print(ef, rec, offsets); - - mem_heap_free(heap); + rec_print(ef, rec, foreign->foreign_index); } putc('\n', ef); @@ -734,7 +717,8 @@ row_ins_foreign_check_on_constraint( ulint i; trx_t* trx; mem_heap_t* tmp_heap = NULL; - ulint* offsets; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_a(thr && foreign && pcur && mtr); @@ -880,14 +864,10 @@ row_ins_foreign_check_on_constraint( fputs("\n" "InnoDB: record ", stderr); - offsets = rec_get_offsets(rec, index, - ULINT_UNDEFINED, tmp_heap); - rec_print(stderr, rec, offsets); + rec_print(stderr, rec, index); fputs("\n" "InnoDB: clustered record ", stderr); - offsets = rec_reget_offsets(clust_rec, clust_index, - offsets, ULINT_UNDEFINED, tmp_heap); - rec_print(stderr, clust_rec, offsets); + rec_print(stderr, clust_rec, clust_index); fputs("\n" "InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); @@ -906,11 +886,8 @@ row_ins_foreign_check_on_constraint( we already have a normal shared lock on the appropriate gap if the search criterion was not unique */ - if (!tmp_heap) { - tmp_heap = mem_heap_create(256); - } - offsets = rec_get_offsets(clust_rec, clust_index, - ULINT_UNDEFINED, tmp_heap); + offsets = rec_get_offsets(clust_rec, clust_index, offsets, + ULINT_UNDEFINED, &tmp_heap); err = lock_clust_rec_read_check_and_lock(0, clust_rec, clust_index, offsets, LOCK_X, LOCK_REC_NOT_GAP, thr); } @@ -1152,11 +1129,10 @@ row_ins_check_foreign_constraint( ulint err; ulint i; mtr_t mtr; - trx_t* trx = thr_get_trx(thr); - mem_heap_t* heap; - ulint* offsets = NULL; - - heap = mem_heap_create(100); + trx_t* trx = thr_get_trx(thr); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; run_again: #ifdef UNIV_SYNC_DEBUG @@ -1168,8 +1144,7 @@ run_again: if (trx->check_foreigns == FALSE) { /* The user has suppressed foreign key checks currently for this session */ - mem_heap_free(heap); - return(DB_SUCCESS); + goto exit_func; } /* If any of the foreign key fields in entry is SQL NULL, we @@ -1180,8 +1155,7 @@ run_again: if (UNIV_SQL_NULL == dfield_get_len( dtuple_get_nth_field(entry, i))) { - mem_heap_free(heap); - return(DB_SUCCESS); + goto exit_func; } } @@ -1205,8 +1179,7 @@ run_again: another, and the user has problems predicting in which order they are performed. */ - mem_heap_free(heap); - return(DB_SUCCESS); + goto exit_func; } } @@ -1219,8 +1192,6 @@ run_again: } if (check_table == NULL) { - mem_heap_free(heap); - if (check_ref) { FILE* ef = dict_foreign_err_file; mutex_enter(&dict_foreign_err_mutex); @@ -1242,10 +1213,10 @@ run_again: fputs(" does not currently exist!\n", ef); mutex_exit(&dict_foreign_err_mutex); - return(DB_NO_REFERENCED_ROW); + err = DB_NO_REFERENCED_ROW; } - return(DB_SUCCESS); + goto exit_func; } ut_a(check_table && check_index); @@ -1291,8 +1262,8 @@ run_again: goto next_rec; } - offsets = rec_reget_offsets(rec, check_index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, check_index, + offsets, ULINT_UNDEFINED, &heap); if (rec == page_get_supremum_rec(buf_frame_align(rec))) { @@ -1424,7 +1395,10 @@ do_possible_lock_wait: err = trx->error_state; } - mem_heap_free(heap); +exit_func: + if (heap) { + mem_heap_free(heap); + } return(err); } @@ -1565,8 +1539,9 @@ row_ins_scan_sec_index_for_duplicate( ibool moved; mtr_t mtr; trx_t* trx; - mem_heap_t* heap; - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; n_unique = dict_index_get_n_unique(index); @@ -1582,7 +1557,6 @@ row_ins_scan_sec_index_for_duplicate( } } - heap = mem_heap_create(100); mtr_start(&mtr); /* Store old value on n_fields_cmp */ @@ -1608,8 +1582,8 @@ row_ins_scan_sec_index_for_duplicate( trx = thr_get_trx(thr); ut_ad(trx); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); if (innobase_query_is_replace()) { @@ -1662,7 +1636,9 @@ next_rec: } } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } mtr_commit(&mtr); /* Restore old value */ @@ -1692,7 +1668,11 @@ row_ins_duplicate_error_in_clust( rec_t* rec; page_t* page; ulint n_unique; - trx_t* trx = thr_get_trx(thr); + trx_t* trx = thr_get_trx(thr); + mem_heap_t*heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + UT_NOT_USED(mtr); @@ -1720,12 +1700,8 @@ row_ins_duplicate_error_in_clust( page = buf_frame_align(rec); if (rec != page_get_infimum_rec(page)) { - mem_heap_t* heap; - ulint* offsets; - - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, cursor->index, - ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, cursor->index, offsets, + ULINT_UNDEFINED, &heap); /* We set a lock on the possible duplicate: this is needed in logical logging of MySQL to make @@ -1750,17 +1726,15 @@ row_ins_duplicate_error_in_clust( } if (err != DB_SUCCESS) { - mem_heap_free(heap); - return(err); + goto func_exit; } if (row_ins_dupl_error_with_rec(rec, entry, cursor->index, offsets)) { trx->error_info = cursor->index; - mem_heap_free(heap); - return(DB_DUPLICATE_KEY); + err = DB_DUPLICATE_KEY; + goto func_exit; } - mem_heap_free(heap); } } @@ -1770,12 +1744,8 @@ row_ins_duplicate_error_in_clust( page = buf_frame_align(rec); if (rec != page_get_supremum_rec(page)) { - mem_heap_t* heap; - ulint* offsets; - - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, cursor->index, - ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, cursor->index, offsets, + ULINT_UNDEFINED, &heap); /* The manual defines the REPLACE semantics that it is either an INSERT or DELETE(s) for duplicate key @@ -1795,15 +1765,14 @@ row_ins_duplicate_error_in_clust( } if (err != DB_SUCCESS) { - mem_heap_free(heap); - return(err); + goto func_exit; } if (row_ins_dupl_error_with_rec(rec, entry, cursor->index, offsets)) { trx->error_info = cursor->index; - mem_heap_free(heap); - return(DB_DUPLICATE_KEY); + err = DB_DUPLICATE_KEY; + goto func_exit; } mem_heap_free(heap); } @@ -1812,7 +1781,9 @@ row_ins_duplicate_error_in_clust( /* This should never happen */ } - return(DB_SUCCESS); + err = DB_SUCCESS; +func_exit: + return(err); } /******************************************************************* @@ -1894,8 +1865,9 @@ row_ins_index_entry_low( ulint n_unique; big_rec_t* big_rec = NULL; mtr_t mtr; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; log_free_check(); @@ -2023,8 +1995,8 @@ function_exit: btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, &mtr); rec = btr_cur_get_rec(&cursor); - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); err = btr_store_big_rec_extern_fields(index, rec, offsets, big_rec, &mtr); @@ -2038,7 +2010,9 @@ function_exit: mtr_commit(&mtr); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(err); } diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index fa584df15db..4a65cbff8b5 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -3215,18 +3215,19 @@ row_scan_and_check_index( ulint* n_rows) /* out: number of entries seen in the current consistent read */ { - mem_heap_t* heap; - dtuple_t* prev_entry = NULL; + dtuple_t* prev_entry = NULL; ulint matched_fields; ulint matched_bytes; byte* buf; ulint ret; rec_t* rec; - ibool is_ok = TRUE; + ibool is_ok = TRUE; int cmp; ibool contains_null; ulint i; - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; *n_rows = 0; @@ -3268,8 +3269,8 @@ loop: matched_fields = 0; matched_bytes = 0; - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); cmp = cmp_dtuple_rec_with_match(prev_entry, rec, offsets, &matched_fields, &matched_bytes); @@ -3299,7 +3300,7 @@ loop: dtuple_print(stderr, prev_entry); fputs("\n" "InnoDB: record ", stderr); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); putc('\n', stderr); is_ok = FALSE; } else if ((index->type & DICT_UNIQUE) @@ -3313,7 +3314,7 @@ loop: } mem_heap_empty(heap); - offsets = NULL; + offsets = offsets_; prev_entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap); diff --git a/innobase/row/row0purge.c b/innobase/row/row0purge.c index 109d0f3b976..8897a1a872f 100644 --- a/innobase/row/row0purge.c +++ b/innobase/row/row0purge.c @@ -100,7 +100,8 @@ row_purge_remove_clust_if_poss_low( ulint err; mtr_t mtr; rec_t* rec; - mem_heap_t* heap; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; index = dict_table_get_first_index(node->table); @@ -120,19 +121,22 @@ row_purge_remove_clust_if_poss_low( } rec = btr_pcur_get_rec(pcur); - heap = mem_heap_create(100); if (0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(rec, index, rec_get_offsets( - rec, index, ULINT_UNDEFINED, heap)))) { - mem_heap_free(heap); + rec, index, offsets_, ULINT_UNDEFINED, &heap)))) { + if (heap) { + mem_heap_free(heap); + } /* Someone else has modified the record later: do not remove */ btr_pcur_commit_specify_mtr(pcur, &mtr); return(TRUE); } - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, &mtr); diff --git a/innobase/row/row0row.c b/innobase/row/row0row.c index 9cf285a519d..43d0cd41b0a 100644 --- a/innobase/row/row0row.c +++ b/innobase/row/row0row.c @@ -202,17 +202,16 @@ row_build( ulint row_len; byte* buf; ulint i; - mem_heap_t* tmp_heap; + mem_heap_t* tmp_heap = NULL; + ulint offsets_[100] = { 100, }; ut_ad(index && rec && heap); ut_ad(index->type & DICT_CLUSTERED); if (!offsets) { - tmp_heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, - ULINT_UNDEFINED, tmp_heap); + offsets = rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &tmp_heap); } else { - tmp_heap = NULL; ut_ad(rec_offs_validate(rec, index, offsets)); } @@ -296,13 +295,14 @@ row_rec_to_index_entry( ulint len; ulint rec_len; byte* buf; - mem_heap_t* tmp_heap; - ulint* offsets; + mem_heap_t* tmp_heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(rec && heap && index); - tmp_heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, tmp_heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &tmp_heap); if (type == ROW_COPY_DATA) { /* Take a copy of rec to heap */ @@ -334,7 +334,9 @@ row_rec_to_index_entry( } ut_ad(dtuple_check_typed(entry)); - mem_heap_free(tmp_heap); + if (tmp_heap) { + mem_heap_free(tmp_heap); + } return(entry); } @@ -374,13 +376,14 @@ row_build_row_ref( byte* buf; ulint clust_col_prefix_len; ulint i; - mem_heap_t* tmp_heap; - ulint* offsets; - + mem_heap_t* tmp_heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + ut_ad(index && rec && heap); - tmp_heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, tmp_heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &tmp_heap); if (type == ROW_COPY_DATA) { /* Take a copy of rec to heap */ @@ -433,7 +436,9 @@ row_build_row_ref( } ut_ad(dtuple_check_typed(ref)); - mem_heap_free(tmp_heap); + if (tmp_heap) { + mem_heap_free(tmp_heap); + } return(ref); } @@ -464,8 +469,9 @@ row_build_row_ref_in_tuple( ulint pos; ulint clust_col_prefix_len; ulint i; - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_a(ref && index && rec); @@ -486,8 +492,7 @@ row_build_row_ref_in_tuple( goto notfound; } - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); ref_len = dict_index_get_n_unique(clust_index); @@ -526,7 +531,9 @@ row_build_row_ref_in_tuple( } ut_ad(dtuple_check_typed(ref)); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /*********************************************************************** diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 2b40b62e5bc..a3d844d1dac 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -78,14 +78,19 @@ row_sel_sec_rec_is_for_clust_rec( ulint n; ulint i; dtype_t* cur_type; - mem_heap_t* heap; - ulint* clust_offs; - ulint* sec_offs; + mem_heap_t* heap = NULL; + ulint clust_offsets_[100] + = { 100, }; + ulint sec_offsets_[10] + = { 10, }; + ulint* clust_offs = clust_offsets_; + ulint* sec_offs = sec_offsets_; + ibool is_equal = TRUE; - heap = mem_heap_create(100); - clust_offs = rec_get_offsets(clust_rec, clust_index, - ULINT_UNDEFINED, heap); - sec_offs = rec_get_offsets(sec_rec, sec_index, ULINT_UNDEFINED, heap); + clust_offs = rec_get_offsets(clust_rec, clust_index, clust_offs, + ULINT_UNDEFINED, &heap); + sec_offs = rec_get_offsets(sec_rec, sec_index, sec_offs, + ULINT_UNDEFINED, &heap); n = dict_index_get_n_ordering_defined_by_user(sec_index); @@ -113,13 +118,16 @@ row_sel_sec_rec_is_for_clust_rec( if (0 != cmp_data_data(dict_col_get_type(col), clust_field, clust_len, sec_field, sec_len)) { - mem_heap_free(heap); - return(FALSE); + is_equal = FALSE; + goto func_exit; } } - mem_heap_free(heap); - return(TRUE); +func_exit: + if (heap) { + mem_heap_free(heap); + } + return(is_equal); } /************************************************************************* @@ -612,13 +620,13 @@ row_sel_get_clust_rec( rec_t* clust_rec; rec_t* old_vers; ulint err; - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; - heap = mem_heap_create(100); offsets = rec_get_offsets(rec, btr_pcur_get_btr_cur(&plan->pcur)->index, - ULINT_UNDEFINED, heap); + offsets, ULINT_UNDEFINED, &heap); row_build_row_ref_fast(plan->clust_ref, plan->clust_map, rec, offsets); @@ -654,8 +662,8 @@ row_sel_get_clust_rec( goto func_exit; } - offsets = rec_reget_offsets(clust_rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(clust_rec, index, offsets, + ULINT_UNDEFINED, &heap); if (!node->read_view) { /* Try to place a lock on the index record */ @@ -677,8 +685,7 @@ row_sel_get_clust_rec( if (err != DB_SUCCESS) { - mem_heap_free(heap); - return(err); + goto err_exit; } } else { /* This is a non-locking consistent read: if necessary, fetch @@ -693,8 +700,7 @@ row_sel_get_clust_rec( clust_rec, &old_vers, mtr); if (err != DB_SUCCESS) { - mem_heap_free(heap); - return(err); + goto err_exit; } clust_rec = old_vers; @@ -731,9 +737,12 @@ row_sel_get_clust_rec( UT_LIST_GET_FIRST(plan->columns)); func_exit: *out_rec = clust_rec; - - mem_heap_free(heap); - return(DB_SUCCESS); + err = DB_SUCCESS; +err_exit: + if (heap) { + mem_heap_free(heap); + } + return(err); } /************************************************************************* @@ -975,8 +984,10 @@ row_sel_try_search_shortcut( { dict_index_t* index; rec_t* rec; - mem_heap_t* heap; - ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + ulint ret; index = plan->index; @@ -1010,43 +1021,46 @@ row_sel_try_search_shortcut( /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (index->type & DICT_CLUSTERED) { if (!lock_clust_rec_cons_read_sees(rec, index, offsets, node->read_view)) { - mem_heap_free(heap); - return(SEL_RETRY); + ret = SEL_RETRY; + goto func_exit; } } else if (!lock_sec_rec_cons_read_sees(rec, index, node->read_view)) { - mem_heap_free(heap); - return(SEL_RETRY); + ret = SEL_RETRY; + goto func_exit; } /* Test deleted flag. Fetch the columns needed in test conditions. */ row_sel_fetch_columns(index, rec, offsets, UT_LIST_GET_FIRST(plan->columns)); - mem_heap_free(heap); if (rec_get_deleted_flag(rec, plan->table->comp)) { - return(SEL_EXHAUSTED); + ret = SEL_EXHAUSTED; + goto func_exit; } /* Test the rest of search conditions */ if (!row_sel_test_other_conds(plan)) { - return(SEL_EXHAUSTED); + ret = SEL_EXHAUSTED; + goto func_exit; } ut_ad(plan->pcur.latch_mode == node->latch_mode); plan->n_rows_fetched++; - +func_exit: + if (heap) { + mem_heap_free(heap); + } return(SEL_FOUND); } @@ -1095,8 +1109,9 @@ row_sel( to the next non-clustered record */ ulint found_flag; ulint err; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(thr->run_node == node); @@ -1253,8 +1268,8 @@ rec_loop: rec_t* next_rec = page_rec_get_next(rec); ulint lock_type; - offsets = rec_reget_offsets(next_rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(next_rec, index, offsets, + ULINT_UNDEFINED, &heap); if (srv_locks_unsafe_for_binlog) { lock_type = LOCK_REC_NOT_GAP; @@ -1295,8 +1310,8 @@ rec_loop: not used. */ ulint lock_type; - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); if (srv_locks_unsafe_for_binlog) { lock_type = LOCK_REC_NOT_GAP; @@ -1369,8 +1384,7 @@ rec_loop: /* PHASE 3: Get previous version in a consistent read */ cons_read_requires_clust_rec = FALSE; - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (consistent_read) { /* This is a non-locking consistent read: if necessary, fetch @@ -1403,8 +1417,8 @@ rec_loop: } rec = old_vers; - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); } } else if (!lock_sec_rec_cons_read_sees(rec, index, node->read_view)) { @@ -1635,8 +1649,8 @@ next_table_no_mtr: rw_lock_s_unlock(&btr_search_latch); } - mem_heap_free(heap); - return(DB_SUCCESS); + err = DB_SUCCESS; + goto func_exit; } node->fetch_table++; @@ -1669,7 +1683,7 @@ table_exhausted: table_exhausted_no_mtr: if (node->fetch_table == 0) { - mem_heap_free(heap); + err = DB_SUCCESS; if (node->is_aggregate && !node->aggregate_already_fetched) { @@ -1683,7 +1697,7 @@ table_exhausted_no_mtr: rw_lock_s_unlock(&btr_search_latch); } - return(DB_SUCCESS); + goto func_exit; } node->state = SEL_NODE_NO_MORE_ROWS; @@ -1694,7 +1708,7 @@ table_exhausted_no_mtr: rw_lock_s_unlock(&btr_search_latch); } - return(DB_SUCCESS); + goto func_exit; } node->fetch_table--; @@ -1718,8 +1732,8 @@ stop_for_a_while: mtr_commit(&mtr); ut_ad(sync_thread_levels_empty_gen(TRUE)); - mem_heap_free(heap); - return(DB_SUCCESS); + err = DB_SUCCESS; + goto func_exit; commit_mtr_for_a_while: /* Stores the cursor position and commits &mtr; this is used if @@ -1754,7 +1768,10 @@ lock_wait_or_error: ut_ad(sync_thread_levels_empty_gen(TRUE)); - mem_heap_free(heap); +func_exit: + if (heap) { + mem_heap_free(heap); + } return(err); } @@ -2197,7 +2214,7 @@ row_sel_store_row_id_to_prebuilt( fprintf(stderr, "\n" "InnoDB: Field number %lu, record:\n", (ulong) dict_index_get_sys_col_pos(index, DATA_ROW_ID)); - rec_print(stderr, index_rec, offsets); + rec_print_new(stderr, index_rec, offsets); putc('\n', stderr); ut_error; } @@ -2496,8 +2513,9 @@ row_sel_get_clust_rec_for_mysql( rec_t* old_vers; ulint err; trx_t* trx; - mem_heap_t* heap = mem_heap_create(100); - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; *out_rec = NULL; trx = thr_get_trx(thr); @@ -2537,14 +2555,10 @@ row_sel_get_clust_rec_for_mysql( dict_index_name_print(stderr, trx, sec_index); fputs("\n" "InnoDB: sec index record ", stderr); - offsets = rec_get_offsets(rec, sec_index, - ULINT_UNDEFINED, heap); - rec_print(stderr, rec, offsets); + rec_print(stderr, rec, sec_index); fputs("\n" "InnoDB: clust index record ", stderr); - offsets = rec_reget_offsets(clust_rec, clust_index, - offsets, ULINT_UNDEFINED, heap); - rec_print(stderr, clust_rec, offsets); + rec_print(stderr, clust_rec, clust_index); putc('\n', stderr); trx_print(stderr, trx); @@ -2557,8 +2571,8 @@ row_sel_get_clust_rec_for_mysql( goto func_exit; } - offsets = rec_get_offsets(clust_rec, clust_index, - ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(clust_rec, clust_index, offsets, + ULINT_UNDEFINED, &heap); if (prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a lock on the index record; we are searching @@ -2571,8 +2585,7 @@ row_sel_get_clust_rec_for_mysql( LOCK_REC_NOT_GAP, thr); if (err != DB_SUCCESS) { - mem_heap_free(heap); - return(err); + goto err_exit; } } else { /* This is a non-locking consistent read: if necessary, fetch @@ -2594,8 +2607,7 @@ row_sel_get_clust_rec_for_mysql( if (err != DB_SUCCESS) { - mem_heap_free(heap); - return(err); + goto err_exit; } clust_rec = old_vers; @@ -2637,8 +2649,12 @@ func_exit: btr_pcur_store_position(prebuilt->clust_pcur, mtr); } - mem_heap_free(heap); - return(DB_SUCCESS); + err = DB_SUCCESS; +err_exit: + if (heap) { + mem_heap_free(heap); + } + return(err); } /************************************************************************ @@ -2809,8 +2825,8 @@ row_sel_try_search_shortcut_for_mysql( /* out: SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ rec_t** out_rec,/* out: record if found */ row_prebuilt_t* prebuilt,/* in: prebuilt struct */ - ulint** offsets,/* in/out: for rec_reget_offsets(*out_rec) */ - mem_heap_t* heap, /* in: heap for rec_reget_offsets() */ + ulint** offsets,/* in/out: for rec_get_offsets(*out_rec) */ + mem_heap_t** heap, /* in/out: heap for rec_get_offsets() */ mtr_t* mtr) /* in: started mtr */ { dict_index_t* index = prebuilt->index; @@ -2849,8 +2865,8 @@ row_sel_try_search_shortcut_for_mysql( /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ - *offsets = rec_reget_offsets(rec, index, - *offsets, ULINT_UNDEFINED, heap); + *offsets = rec_get_offsets(rec, index, *offsets, + ULINT_UNDEFINED, heap); if (!lock_clust_rec_cons_read_sees(rec, index, *offsets, trx->read_view)) { @@ -2915,7 +2931,6 @@ row_search_for_mysql( ibool moved; ibool cons_read_requires_clust_rec; ibool was_lock_wait; - ulint ret; ulint shortcut; ibool unique_search = FALSE; ibool unique_search_from_clust_index = FALSE; @@ -2931,8 +2946,9 @@ row_search_for_mysql( ulint cnt = 0; ulint next_offs; mtr_t mtr; - mem_heap_t* heap; - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_ad(index && pcur && search_tuple); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); @@ -3023,9 +3039,8 @@ row_search_for_mysql( prebuilt->n_rows_fetched++; srv_n_rows_read++; - trx->op_info = ""; - - return(DB_SUCCESS); + err = DB_SUCCESS; + goto func_exit; } if (prebuilt->fetch_cache_first > 0 @@ -3034,9 +3049,9 @@ row_search_for_mysql( /* The previous returned row was popped from the fetch cache, but the cache was not full at the time of the popping: no more rows can exist in the result set */ - - trx->op_info = ""; - return(DB_RECORD_NOT_FOUND); + + err = DB_RECORD_NOT_FOUND; + goto func_exit; } prebuilt->n_rows_fetched++; @@ -3080,13 +3095,12 @@ row_search_for_mysql( if (direction != 0 && !prebuilt->used_in_HANDLER) { - trx->op_info = ""; - return(DB_RECORD_NOT_FOUND); + err = DB_RECORD_NOT_FOUND; + goto func_exit; } } mtr_start(&mtr); - heap = mem_heap_create(100); /*-------------------------------------------------------------*/ /* PHASE 2: Try fast adaptive hash index search if possible */ @@ -3132,7 +3146,7 @@ row_search_for_mysql( } #endif shortcut = row_sel_try_search_shortcut_for_mysql(&rec, - prebuilt, &offsets, heap, &mtr); + prebuilt, &offsets, &heap, &mtr); if (shortcut == SEL_FOUND) { #ifdef UNIV_SEARCH_DEBUG ut_a(0 == cmp_dtuple_rec(search_tuple, @@ -3163,12 +3177,10 @@ row_search_for_mysql( trx->has_search_latch = FALSE; } - trx->op_info = ""; - /* NOTE that we do NOT store the cursor position */ - mem_heap_free(heap); - return(DB_SUCCESS); + err = DB_SUCCESS; + goto func_exit; } else if (shortcut == SEL_EXHAUSTED) { @@ -3186,13 +3198,11 @@ row_search_for_mysql( trx->has_search_latch = FALSE; } - trx->op_info = ""; - /* NOTE that we do NOT store the cursor position */ - mem_heap_free(heap); - return(DB_RECORD_NOT_FOUND); + err = DB_RECORD_NOT_FOUND; + goto func_exit; } shortcut_fails_too_big_rec: mtr_commit(&mtr); @@ -3334,9 +3344,9 @@ rec_loop: we do not lock gaps. Supremum record is really a gap and therefore we do not set locks there. */ - if (srv_locks_unsafe_for_binlog == FALSE) { - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + if (!srv_locks_unsafe_for_binlog) { + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); err = sel_set_rec_lock(rec, index, offsets, prebuilt->select_lock_type, LOCK_ORDINARY, thr); @@ -3406,8 +3416,7 @@ rec_loop: } } - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (srv_force_recovery > 0) { if (!rec_validate(rec, offsets) @@ -3464,7 +3473,7 @@ rec_loop: btr_pcur_store_position(pcur, &mtr); - ret = DB_RECORD_NOT_FOUND; + err = DB_RECORD_NOT_FOUND; /* ut_print_name(stderr, index->name); fputs(" record not found 3\n", stderr); */ @@ -3498,7 +3507,7 @@ rec_loop: btr_pcur_store_position(pcur, &mtr); - ret = DB_RECORD_NOT_FOUND; + err = DB_RECORD_NOT_FOUND; /* ut_print_name(stderr, index->name); fputs(" record not found 4\n", stderr); */ @@ -3644,11 +3653,11 @@ rec_loop: if (prebuilt->need_to_access_clustered) { ut_ad(rec == clust_rec || index == clust_index); - offsets = rec_reget_offsets(rec, clust_index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, clust_index, offsets, + ULINT_UNDEFINED, &heap); } else { - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); } /* We found a qualifying row */ @@ -3694,8 +3703,8 @@ rec_loop: } if (prebuilt->clust_index_was_generated) { - offsets = rec_reget_offsets(index_rec, index, offsets, - ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(index_rec, index, offsets, + ULINT_UNDEFINED, &heap); row_sel_store_row_id_to_prebuilt(prebuilt, index_rec, index, offsets); } @@ -3717,7 +3726,7 @@ got_row: btr_pcur_store_position(pcur, &mtr); } - ret = DB_SUCCESS; + err = DB_SUCCESS; goto normal_return; @@ -3756,9 +3765,9 @@ next_rec: btr_pcur_store_position(pcur, &mtr); if (match_mode != 0) { - ret = DB_RECORD_NOT_FOUND; + err = DB_RECORD_NOT_FOUND; } else { - ret = DB_END_OF_INDEX; + err = DB_END_OF_INDEX; } goto normal_return; @@ -3797,10 +3806,7 @@ lock_wait_or_error: /* fputs("Using ", stderr); dict_index_name_print(stderr, index); fprintf(stderr, " cnt %lu ret value %lu err\n", cnt, err); */ - trx->op_info = ""; - - mem_heap_free(heap); - return(err); + goto func_exit; normal_return: /*-------------------------------------------------------------*/ @@ -3811,20 +3817,22 @@ normal_return: if (prebuilt->n_fetch_cached > 0) { row_sel_pop_cached_row_for_mysql(buf, prebuilt); - ret = DB_SUCCESS; + err = DB_SUCCESS; } /* fputs("Using ", stderr); dict_index_name_print(stderr, index); fprintf(stderr, " cnt %lu ret value %lu err\n", cnt, err); */ - if (ret == DB_SUCCESS) { + if (err == DB_SUCCESS) { srv_n_rows_read++; } +func_exit: trx->op_info = ""; - - mem_heap_free(heap); - return(ret); + if (heap) { + mem_heap_free(heap); + } + return(err); } /*********************************************************************** diff --git a/innobase/row/row0umod.c b/innobase/row/row0umod.c index ee9066a0d6f..1cade0f304f 100644 --- a/innobase/row/row0umod.c +++ b/innobase/row/row0umod.c @@ -430,7 +430,6 @@ row_undo_mod_del_unmark_sec_and_undo_update( found = row_search_index_entry(index, entry, mode, &pcur, &mtr); if (!found) { - heap = mem_heap_create(100); fputs("InnoDB: error in sec index entry del undo in\n" "InnoDB: ", stderr); dict_index_name_print(stderr, trx, index); @@ -439,14 +438,11 @@ row_undo_mod_del_unmark_sec_and_undo_update( dtuple_print(stderr, entry); fputs("\n" "InnoDB: record ", stderr); - rec_print(stderr, btr_pcur_get_rec(&pcur), - rec_get_offsets(btr_pcur_get_rec(&pcur), - index, ULINT_UNDEFINED, heap)); + rec_print(stderr, btr_pcur_get_rec(&pcur), index); putc('\n', stderr); trx_print(stderr, trx); fputs("\n" "InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); - mem_heap_free(heap); } else { btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur); diff --git a/innobase/row/row0undo.c b/innobase/row/row0undo.c index 42f5ef94854..d994eab9873 100644 --- a/innobase/row/row0undo.c +++ b/innobase/row/row0undo.c @@ -151,8 +151,9 @@ row_undo_search_clust_to_pcur( mtr_t mtr; ibool ret; rec_t* rec; - mem_heap_t* heap; - const ulint* offsets; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; mtr_start(&mtr); @@ -163,8 +164,8 @@ row_undo_search_clust_to_pcur( rec = btr_pcur_get_rec(&(node->pcur)); - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, clust_index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, clust_index, offsets, + ULINT_UNDEFINED, &heap); if (!found || 0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(rec, clust_index, offsets))) { @@ -188,6 +189,9 @@ row_undo_search_clust_to_pcur( btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); + if (heap) { + mem_heap_free(heap); + } return(ret); } diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c index e080d0ba577..e4013633bed 100644 --- a/innobase/row/row0upd.c +++ b/innobase/row/row0upd.c @@ -700,6 +700,7 @@ row_upd_build_sec_rec_difference_binary( upd_t* update; ulint n_diff; ulint i; + ulint offsets_[10] = { 10, }; const ulint* offsets; /* This function is used only for a secondary index */ @@ -708,7 +709,8 @@ row_upd_build_sec_rec_difference_binary( update = upd_create(dtuple_get_n_fields(entry), heap); n_diff = 0; - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap); for (i = 0; i < dtuple_get_n_fields(entry); i++) { @@ -775,6 +777,7 @@ row_upd_build_difference_binary( ulint trx_id_pos; ibool extern_bit; ulint i; + ulint offsets_[100] = { 100, }; const ulint* offsets; /* This function is used only for a clustered index */ @@ -787,7 +790,8 @@ row_upd_build_difference_binary( roll_ptr_pos = dict_index_get_sys_col_pos(index, DATA_ROLL_PTR); trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap); for (i = 0; i < dtuple_get_n_fields(entry); i++) { @@ -1182,7 +1186,8 @@ row_upd_store_row( dict_index_t* clust_index; upd_t* update; rec_t* rec; - mem_heap_t* heap; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; const ulint* offsets; ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES); @@ -1196,8 +1201,8 @@ row_upd_store_row( rec = btr_pcur_get_rec(node->pcur); - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, clust_index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, clust_index, offsets_, + ULINT_UNDEFINED, &heap); node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets, node->heap); node->ext_vec = mem_heap_alloc(node->heap, sizeof(ulint) @@ -1210,7 +1215,9 @@ row_upd_store_row( node->n_ext_vec = btr_push_update_extern_fields(node->ext_vec, offsets, update); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } } /*************************************************************** @@ -1263,8 +1270,7 @@ row_upd_sec_index_entry( dtuple_print(stderr, entry); fputs("\n" "InnoDB: record ", stderr); - rec_print(stderr, rec, - rec_get_offsets(rec, index, ULINT_UNDEFINED, heap)); + rec_print(stderr, rec, index); putc('\n', stderr); trx_print(stderr, trx); @@ -1364,7 +1370,7 @@ row_upd_clust_rec_by_insert( a foreign key constraint */ mtr_t* mtr) /* in: mtr; gets committed here */ { - mem_heap_t* heap; + mem_heap_t* heap = NULL; btr_pcur_t* pcur; btr_cur_t* btr_cur; trx_t* trx; @@ -1379,15 +1385,14 @@ row_upd_clust_rec_by_insert( table = node->table; pcur = node->pcur; btr_cur = btr_pcur_get_btr_cur(pcur); - heap = mem_heap_create(500); if (node->state != UPD_NODE_INSERT_CLUSTERED) { + ulint offsets_[100] = { 100, }; err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG, btr_cur, TRUE, thr, mtr); if (err != DB_SUCCESS) { mtr_commit(mtr); - mem_heap_free(heap); return(err); } @@ -1398,8 +1403,8 @@ row_upd_clust_rec_by_insert( btr_cur_mark_extern_inherited_fields(btr_cur_get_rec(btr_cur), rec_get_offsets(btr_cur_get_rec(btr_cur), - dict_table_get_first_index(table), - ULINT_UNDEFINED, heap), node->update, mtr); + dict_table_get_first_index(table), offsets_, + ULINT_UNDEFINED, &heap), node->update, mtr); if (check_ref) { /* NOTE that the following call loses the position of pcur ! */ @@ -1408,7 +1413,9 @@ row_upd_clust_rec_by_insert( index, thr, mtr); if (err != DB_SUCCESS) { mtr_commit(mtr); - + if (heap) { + mem_heap_free(heap); + } return(err); } } @@ -1417,6 +1424,9 @@ row_upd_clust_rec_by_insert( mtr_commit(mtr); + if (!heap) { + heap = mem_heap_create(500); + } node->state = UPD_NODE_INSERT_CLUSTERED; entry = row_build_index_entry(node->row, index, heap); @@ -1516,17 +1526,20 @@ row_upd_clust_rec( mtr_commit(mtr); if (err == DB_SUCCESS && big_rec) { - mem_heap_t* heap; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; rec_t* rec; mtr_start(mtr); - heap = mem_heap_create(100); rec = btr_cur_get_rec(btr_cur); ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr)); err = btr_store_big_rec_extern_fields(index, rec, - rec_get_offsets(rec, index, ULINT_UNDEFINED, heap), + rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap), big_rec, mtr); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } mtr_commit(mtr); } @@ -1611,7 +1624,8 @@ row_upd_clust_step( mtr_t* mtr; mtr_t mtr_buf; rec_t* rec; - mem_heap_t* heap; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; const ulint* offsets; index = dict_table_get_first_index(node->table); @@ -1670,33 +1684,31 @@ row_upd_clust_step( } rec = btr_pcur_get_rec(pcur); - heap = mem_heap_create(100); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap); if (!node->has_clust_rec_x_lock) { err = lock_clust_rec_modify_check_and_lock(0, rec, index, offsets, thr); if (err != DB_SUCCESS) { mtr_commit(mtr); - mem_heap_free(heap); - return(err); + goto exit_func; } } /* NOTE: the following function calls will also commit mtr */ if (node->is_delete) { - mem_heap_free(heap); err = row_upd_del_mark_clust_rec(node, index, thr, check_ref, mtr); - if (err != DB_SUCCESS) { - - return(err); + if (err == DB_SUCCESS) { + node->state = UPD_NODE_UPDATE_ALL_SEC; + node->index = dict_table_get_next_index(index); + } + exit_func: + if (heap) { + mem_heap_free(heap); } - - node->state = UPD_NODE_UPDATE_ALL_SEC; - node->index = dict_table_get_next_index(index); - return(err); } @@ -1710,13 +1722,14 @@ row_upd_clust_step( UT_LIST_GET_FIRST(node->columns)); row_upd_eval_new_vals(node->update); } - - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } + if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) { err = row_upd_clust_rec(node, index, thr, mtr); - return(err); } @@ -1968,7 +1981,8 @@ row_upd_in_place_in_select( btr_pcur_t* pcur; btr_cur_t* btr_cur; ulint err; - mem_heap_t* heap; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; ut_ad(sel_node->select_will_do_update); ut_ad(sel_node->latch_mode == BTR_MODIFY_LEAF); @@ -1984,11 +1998,13 @@ row_upd_in_place_in_select( /* Copy the necessary columns from clust_rec and calculate the new values to set */ - heap = mem_heap_create(100); row_upd_copy_columns(btr_pcur_get_rec(pcur), rec_get_offsets( - btr_pcur_get_rec(pcur), btr_cur->index, ULINT_UNDEFINED, heap), + btr_pcur_get_rec(pcur), btr_cur->index, offsets_, + ULINT_UNDEFINED, &heap), UT_LIST_GET_FIRST(node->columns)); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } row_upd_eval_new_vals(node->update); ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur), diff --git a/innobase/row/row0vers.c b/innobase/row/row0vers.c index 5281dbd67d7..9ccaf32f2c2 100644 --- a/innobase/row/row0vers.c +++ b/innobase/row/row0vers.c @@ -100,32 +100,25 @@ row_vers_impl_x_locked_off_kernel( } heap = mem_heap_create(1024); - clust_offsets = rec_get_offsets(clust_rec, clust_index, - ULINT_UNDEFINED, heap); + clust_offsets = rec_get_offsets(clust_rec, clust_index, NULL, + ULINT_UNDEFINED, &heap); trx_id = row_get_rec_trx_id(clust_rec, clust_index, clust_offsets); mtr_s_lock(&(purge_sys->latch), &mtr); mutex_enter(&kernel_mutex); + trx = NULL; if (!trx_is_active(trx_id)) { /* The transaction that modified or inserted clust_rec is no longer active: no implicit lock on rec */ - - mem_heap_free(heap); - mtr_commit(&mtr); - - return(NULL); + goto exit_func; } if (!lock_check_trx_id_sanity(trx_id, clust_rec, clust_index, clust_offsets, TRUE)) { /* Corruption noticed: try to avoid a crash by returning */ - - mem_heap_free(heap); - mtr_commit(&mtr); - - return(NULL); + goto exit_func; } comp = index->table->comp; @@ -166,7 +159,8 @@ row_vers_impl_x_locked_off_kernel( if (prev_version) { clust_offsets = rec_get_offsets(prev_version, - clust_index, ULINT_UNDEFINED, heap); + clust_index, NULL, + ULINT_UNDEFINED, &heap); row = row_build(ROW_COPY_POINTERS, clust_index, prev_version, clust_offsets, heap); entry = row_build_index_entry(row, index, heap); @@ -250,6 +244,7 @@ row_vers_impl_x_locked_off_kernel( version = prev_version; }/* for (;;) */ +exit_func: mtr_commit(&mtr); mem_heap_free(heap); @@ -330,8 +325,8 @@ row_vers_old_has_index_entry( comp = index->table->comp; ut_ad(comp == page_is_comp(buf_frame_align(rec))); heap = mem_heap_create(1024); - clust_offsets = rec_get_offsets(rec, clust_index, - ULINT_UNDEFINED, heap); + clust_offsets = rec_get_offsets(rec, clust_index, NULL, + ULINT_UNDEFINED, &heap); if (also_curr && !rec_get_deleted_flag(rec, comp)) { row = row_build(ROW_COPY_POINTERS, clust_index, @@ -371,7 +366,7 @@ row_vers_old_has_index_entry( } clust_offsets = rec_get_offsets(prev_version, clust_index, - ULINT_UNDEFINED, heap); + NULL, ULINT_UNDEFINED, &heap); if (!rec_get_deleted_flag(prev_version, comp)) { row = row_build(ROW_COPY_POINTERS, clust_index, @@ -438,7 +433,7 @@ row_vers_build_for_consistent_read( #endif /* UNIV_SYNC_DEBUG */ heap = mem_heap_create(1024); - offsets = rec_get_offsets(rec, index, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); ut_ad(!read_view_sees_trx_id(view, row_get_rec_trx_id(rec, index, offsets))); @@ -466,8 +461,8 @@ row_vers_build_for_consistent_read( break; } - offsets = rec_get_offsets(prev_version, index, - ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(prev_version, index, NULL, + ULINT_UNDEFINED, &heap); prev_trx_id = row_get_rec_trx_id(prev_version, index, offsets); if (read_view_sees_trx_id(view, prev_trx_id)) { diff --git a/innobase/trx/trx0rec.c b/innobase/trx/trx0rec.c index 484d4f62744..12a0512da53 100644 --- a/innobase/trx/trx0rec.c +++ b/innobase/trx/trx0rec.c @@ -1010,8 +1010,9 @@ trx_undo_report_row_operation( ibool is_insert; trx_rseg_t* rseg; mtr_t mtr; - mem_heap_t* heap; - ulint* offsets = NULL; + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; ut_a(index->type & DICT_CLUSTERED); @@ -1066,8 +1067,6 @@ trx_undo_report_row_operation( mtr_start(&mtr); - heap = mem_heap_create(100); - for (;;) { undo_page = buf_page_get_gen(undo->space, page_no, RW_X_LATCH, undo->guess_page, @@ -1084,8 +1083,8 @@ trx_undo_report_row_operation( index, clust_entry, &mtr); } else { - offsets = rec_reget_offsets(rec, index, - offsets, ULINT_UNDEFINED, heap); + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); offset = trx_undo_page_report_modify(undo_page, trx, index, rec, offsets, update, cmpl_info, &mtr); } @@ -1129,7 +1128,9 @@ trx_undo_report_row_operation( mutex_exit(&(trx->undo_mutex)); mtr_commit(&mtr); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(DB_OUT_OF_FILE_SPACE); } } @@ -1146,7 +1147,9 @@ trx_undo_report_row_operation( *roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no, offset); - mem_heap_free(heap); + if (heap) { + mem_heap_free(heap); + } return(DB_SUCCESS); } @@ -1266,7 +1269,6 @@ trx_undo_prev_version_build( ibool dummy_extern; byte* buf; ulint err; - ulint* index_offsets = NULL; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ @@ -1282,12 +1284,10 @@ trx_undo_prev_version_build( "InnoDB: Submit a detailed bug report to" " http://bugs.mysql.com\n" "InnoDB: index record ", index->name); - index_offsets = rec_get_offsets(index_rec, index, - ULINT_UNDEFINED, heap); - rec_print(stderr, index_rec, index_offsets); + rec_print(stderr, index_rec, index); fputs("\n" "InnoDB: record version ", stderr); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); putc('\n', stderr); return(DB_ERROR); } @@ -1353,12 +1353,10 @@ trx_undo_prev_version_build( ut_print_buf(stderr, undo_rec, 150); fputs("\n" "InnoDB: index record ", stderr); - index_offsets = rec_get_offsets(index_rec, index, - ULINT_UNDEFINED, heap); - rec_print(stderr, index_rec, index_offsets); + rec_print(stderr, index_rec, index); fputs("\n" "InnoDB: record version ", stderr); - rec_print(stderr, rec, offsets); + rec_print_new(stderr, rec, offsets); fprintf(stderr, "\n" "InnoDB: Record trx id %lu %lu, update rec trx id %lu %lu\n" "InnoDB: Roll ptr in rec %lu %lu, in update rec %lu %lu\n", From 40c2a8870b7dea27917430d995a0acb91465b892 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 14:44:10 +0100 Subject: [PATCH 27/52] Changing the default of libmysqlclient : it's now NO reconnection. All our programs which use mysql_real_connect() and mysql_connect() are updated accordingly, though I have deliberately made mysqlimport not reconnect anymore (already true for mysqldump >= 4.1.8). All Connector devs have been warned about the change I'm doing here - which was agreed with Monty, and fixes BUG#2555. VC++Files/libmysqltest/mytest.c: explicit mention of reconnect (no behaviour change) VC++Files/mysqlmanager/mysqlmanagerview.cpp: explicit mention of reconnect (no behaviour change) VC++Files/test1/mysql_thr.c: explicit mention of reconnect (no behaviour change) VC++Files/winmysqladmin/main.cpp: explicit mention of reconnect (no behaviour change) client/mysql.cc: explicit mention of reconnect if embedded (no behaviour change) client/mysqladmin.cc: explicit mention of reconnect (no behaviour change) client/mysqlbinlog.cc: explicit mention of reconnect (no behaviour change) client/mysqlcheck.c: explicit mention of reconnect (no behaviour change) client/mysqlimport.c: explicit mention of NO reconnect (behaviour change). As most time is passed in LOAD DATA INFILE, and as it does not make sense to reconnect after a partly failed LOAD... And as mysqlimport sometimes does LOCK TABLES where we mustn't reconnect... client/mysqlshow.c: explicit mention of reconnect (no behaviour change) client/mysqltest.c: explicit mention of reconnect (no behaviour change). Normally we should not reconnect (it's not good to have silent reconnection in the middle of a test), but 5.0 is too touchy to change it now. I'm marking it TODO. libmysql/libmysql.c: explicit mention of reconnect (no behaviour change) libmysqld/examples/builder-sample/emb_samples.cpp: explicit mention of reconnect (no behaviour change) ndb/test/ndbapi/flex_bench_mysql.cpp: explicit mention of reconnect (no behaviour change) ndb/tools/restore/consumer_restorem.cpp: explicit mention of reconnect (no behaviour change) sql-common/client.c: Changing the default of libmysqlclient : it's now NO reconnection. sql/repl_failsafe.cc: explicit mention of reconnect (no behaviour change) sql/slave.cc: explicit mention of reconnect (no behaviour change) tests/client_test.c: explicit mention of reconnect (no behaviour change) tests/connect_test.c: explicit mention of reconnect (no behaviour change) tests/deadlock_test.c: explicit mention of reconnect (no behaviour change) tests/insert_test.c: explicit mention of reconnect (no behaviour change) tests/list_test.c: explicit mention of reconnect (no behaviour change) tests/select_test.c: explicit mention of reconnect (no behaviour change) tests/showdb_test.c: explicit mention of reconnect (no behaviour change) tests/ssl_test.c: explicit mention of reconnect (no behaviour change) tests/thread_test.c: explicit mention of reconnect (no behaviour change) tools/mysqlmanager.c: explicit mention of reconnect (no behaviour change) --- VC++Files/libmysqltest/mytest.c | 1 + VC++Files/mysqlmanager/mysqlmanagerview.cpp | 5 +++++ VC++Files/test1/mysql_thr.c | 1 + VC++Files/winmysqladmin/main.cpp | 2 ++ client/mysql.cc | 2 ++ client/mysqladmin.cc | 1 + client/mysqlbinlog.cc | 1 + client/mysqlcheck.c | 1 + client/mysqlimport.c | 1 + client/mysqlshow.c | 1 + client/mysqltest.c | 1 + libmysql/libmysql.c | 4 ++++ .../examples/builder-sample/emb_samples.cpp | 1 + ndb/test/ndbapi/flex_bench_mysql.cpp | 2 ++ ndb/tools/restore/consumer_restorem.cpp | 1 + sql-common/client.c | 19 ++++++++++++++++++- sql/repl_failsafe.cc | 1 + sql/slave.cc | 1 + tests/client_test.c | 7 +++++++ tests/connect_test.c | 1 + tests/deadlock_test.c | 1 + tests/insert_test.c | 1 + tests/list_test.c | 1 + tests/select_test.c | 1 + tests/showdb_test.c | 1 + tests/ssl_test.c | 1 + tests/thread_test.c | 1 + tools/mysqlmanager.c | 3 +++ 28 files changed, 63 insertions(+), 1 deletion(-) diff --git a/VC++Files/libmysqltest/mytest.c b/VC++Files/libmysqltest/mytest.c index 9af8c486e40..a1dc13db39f 100644 --- a/VC++Files/libmysqltest/mytest.c +++ b/VC++Files/libmysqltest/mytest.c @@ -91,6 +91,7 @@ main( int argc, char * argv[] ) mysql_real_connect( myData, NULL, NULL, NULL, NULL, MYSQL_PORT, NULL, 0 ) ) { + myData->reconnect= 1; if ( mysql_select_db( myData, szDB ) < 0 ) { printf( "Can't select the %s database !\n", szDB ) ; mysql_close( myData ) ; diff --git a/VC++Files/mysqlmanager/mysqlmanagerview.cpp b/VC++Files/mysqlmanager/mysqlmanagerview.cpp index 1d4756e7d7a..f39e0a9963e 100644 --- a/VC++Files/mysqlmanager/mysqlmanagerview.cpp +++ b/VC++Files/mysqlmanager/mysqlmanagerview.cpp @@ -551,6 +551,7 @@ void CMySqlManagerView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) PostMessage(WM_COMMAND,IDM_TOOLS_SERVER_PROPERTIES); return; } + mysql.reconnect= 1; if (!(result=mysql_list_processes(&mysql))) { return; @@ -576,6 +577,7 @@ void CMySqlManagerView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) ); return; } + mysql.reconnect= 1; if (!(result=mysql_list_dbs(&mysql,0))) { } @@ -603,6 +605,7 @@ void CMySqlManagerView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) ); return; } + mysql.reconnect= 1; CResourceDatabase* pRes = (CResourceDatabase*) pResource; CString strDB = pResource->GetDisplayName(); strDB.TrimRight(); @@ -641,6 +644,7 @@ void CMySqlManagerView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) ); return; } + mysql.reconnect= 1; HTREEITEM hParent = m_pTree->GetParentItem(hItem); memset( &item, 0, sizeof(TV_ITEM) ); item.hItem = hParent; @@ -714,6 +718,7 @@ void CMySqlManagerView::OnRefresh() { return; } + mysql.reconnect= 1; memset( &item, 0, sizeof(TV_ITEM) ); item.hItem = hParent; item.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_CHILDREN | TVIF_PARAM ; diff --git a/VC++Files/test1/mysql_thr.c b/VC++Files/test1/mysql_thr.c index fac5c37a9af..c2743cb8e4c 100644 --- a/VC++Files/test1/mysql_thr.c +++ b/VC++Files/test1/mysql_thr.c @@ -167,6 +167,7 @@ pthread_handler_decl(test_thread,arg) perror(""); goto end; } + mysql.reconnect= 1; if (mysql_query(&mysql,"select 1") < 0) { fprintf(stderr,"Query failed (%s)\n",mysql_error(&mysql)); diff --git a/VC++Files/winmysqladmin/main.cpp b/VC++Files/winmysqladmin/main.cpp index dfb2004a780..150bc669c74 100644 --- a/VC++Files/winmysqladmin/main.cpp +++ b/VC++Files/winmysqladmin/main.cpp @@ -1337,6 +1337,7 @@ void __fastcall TForm1::IsMySQLInit(void) } } + MySQL->reconnect= 1; } @@ -1348,6 +1349,7 @@ void __fastcall TForm1::IsMySQLInit(void) MySQL = mysql_init(MySQL); if(mysql_real_connect(MySQL,host,user,password , 0, 0, NULL, 0)) IsConnect = true; + MySQL->reconnect= 1; } } } diff --git a/client/mysql.cc b/client/mysql.cc index 8e9dd84c8f0..cb3a56972fa 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -2792,6 +2792,8 @@ sql_real_connect(char *host,char *database,char *user,char *password, connected=1; #ifndef EMBEDDED_LIBRARY mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens +#else + mysql.reconnect= 1; #endif #ifdef HAVE_READLINE build_completion_hash(rehash, 1); diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 924af3a9977..21e8f6ab3e4 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -425,6 +425,7 @@ static my_bool sql_connect(MYSQL *mysql, uint wait) if (mysql_real_connect(mysql,host,user,opt_password,NullS,tcp_port, unix_port, 0)) { + mysql->reconnect= 1; if (info) { fputs("\n",stderr); diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 1c10ece92dd..7036deab2fe 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -753,6 +753,7 @@ static MYSQL* safe_connect() mysql_options(local_mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0)) die("failed on connect: %s", mysql_error(local_mysql)); + local_mysql->reconnect= 1; return local_mysql; } diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index c670b84db44..980046fe6e6 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -628,6 +628,7 @@ static int dbConnect(char *host, char *user, char *passwd) DBerror(&mysql_connection, "when trying to connect"); return 1; } + mysql_connection.reconnect= 1; return 0; } /* dbConnect */ diff --git a/client/mysqlimport.c b/client/mysqlimport.c index fae84be610a..3552f03fb27 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -388,6 +388,7 @@ static MYSQL *db_connect(char *host, char *database, char *user, char *passwd) ignore_errors=0; /* NO RETURN FROM db_error */ db_error(&mysql_connection); } + mysql_connection.reconnect= 0; if (verbose) fprintf(stdout, "Selecting database %s\n", database); if (mysql_select_db(sock, database)) diff --git a/client/mysqlshow.c b/client/mysqlshow.c index ee478058cdc..8171cb95268 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -125,6 +125,7 @@ int main(int argc, char **argv) fprintf(stderr,"%s: %s\n",my_progname,mysql_error(&mysql)); exit(1); } + mysql.reconnect= 1; switch (argc) { diff --git a/client/mysqltest.c b/client/mysqltest.c index 4f55320e4f3..27ba5bad6c7 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1627,6 +1627,7 @@ int safe_connect(MYSQL* con, const char* host, const char* user, } sleep(CON_RETRY_SLEEP); } + con->reconnect= 1; /* TODO: change this to 0 in future versions */ return con_error; } diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 3e98d8399ff..2355f9f6617 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -317,6 +317,7 @@ my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, DBUG_ENTER("mysql_master_send_query"); if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0)) DBUG_RETURN(1); + master->reconnect= 1; mysql->last_used_con = master; DBUG_RETURN(simple_command(master, COM_QUERY, q, length, 1)); } @@ -351,6 +352,7 @@ my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, if (!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0, 0,0,0,0)) DBUG_RETURN(1); + slave_to_use->reconnect= 1; DBUG_RETURN(simple_command(slave_to_use, COM_QUERY, q, length, 1)); } @@ -448,6 +450,7 @@ static my_bool get_slaves_from_master(MYSQL* mysql) expand_error(mysql, CR_PROBE_MASTER_CONNECT); DBUG_RETURN(1); } + mysql->reconnect= 1; if (mysql_query(mysql, "SHOW SLAVE HOSTS") || !(res = mysql_store_result(mysql))) @@ -615,6 +618,7 @@ mysql_connect(MYSQL *mysql,const char *host, if (mysql->free_me) my_free((gptr) mysql,MYF(0)); } + mysql->reconnect= 1; DBUG_RETURN(res); } } diff --git a/libmysqld/examples/builder-sample/emb_samples.cpp b/libmysqld/examples/builder-sample/emb_samples.cpp index 4dfde111f84..411de26149b 100644 --- a/libmysqld/examples/builder-sample/emb_samples.cpp +++ b/libmysqld/examples/builder-sample/emb_samples.cpp @@ -109,6 +109,7 @@ bool __fastcall TForm1::connect_server() ret_value = true; is_server_started = true; } + MySQL->reconnect= 1; return ret_value; } //--------------------------------------------------------------------------- diff --git a/ndb/test/ndbapi/flex_bench_mysql.cpp b/ndb/test/ndbapi/flex_bench_mysql.cpp index c8d4d85bedf..ad84390a9e5 100644 --- a/ndb/test/ndbapi/flex_bench_mysql.cpp +++ b/ndb/test/ndbapi/flex_bench_mysql.cpp @@ -397,6 +397,7 @@ NDB_COMMAND(flexBench, "flexBench", "flexBench", "flexbench", 65535) ndbout << "Connect failed" <options.port; if (!unix_socket) unix_socket=mysql->options.unix_socket; + + /* + By default we don't reconnect because it could silently corrupt data (after + reconnection you potentially lose table locks, user variables, session + variables (transactions but they are specifically dealt with in + mysql_reconnect()). + This is a change: < 5.0.3 mysql->reconnect was set to 1 by default. + How this change impacts existing apps: + - existing apps which relyed on the default will see a behaviour change; + they will have to set reconnect=1 after mysql_real_connect(). + - existing apps which explicitely asked for reconnection (the only way they + could do it was by setting mysql.reconnect to 1 after mysql_real_connect()) + will not see a behaviour change. + - existing apps which explicitely asked for no reconnection + (mysql.reconnect=0) will not see a behaviour change. + */ + mysql->reconnect= 0; - mysql->reconnect=1; /* Reconnect as default */ mysql->server_status=SERVER_STATUS_AUTOCOMMIT; /* @@ -2161,6 +2177,7 @@ my_bool mysql_reconnect(MYSQL *mysql) strmov(mysql->net.sqlstate, tmp_mysql.net.sqlstate); DBUG_RETURN(1); } + tmp_mysql.reconnect= 1; tmp_mysql.free_me= mysql->free_me; /* Don't free options as these are now used in tmp_mysql */ bzero((char*) &mysql->options,sizeof(mysql->options)); diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index f759be59ffb..b7575f3a44e 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -709,6 +709,7 @@ int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi) if (!mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0, mi->port, 0, 0)) DBUG_RETURN(1); + mysql->reconnect= 1; DBUG_RETURN(0); } diff --git a/sql/slave.cc b/sql/slave.cc index 1e38d92ebc5..9ddbe7d05de 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -4394,6 +4394,7 @@ replication resumed in log '%s' at position %s", mi->user, thd->set_active_vio(mysql->net.vio); #endif } + mysql->reconnect= 1; DBUG_PRINT("exit",("slave_was_killed: %d", slave_was_killed)); DBUG_RETURN(slave_was_killed); } diff --git a/tests/client_test.c b/tests/client_test.c index aac0a92df65..7392f05d820 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -243,6 +243,7 @@ static void client_connect() fprintf(stdout, "\n Check the connection options using --help or -?\n"); exit(1); } + mysql->reconnect= 1; if (!opt_silent) fprintf(stdout, " OK"); @@ -1050,6 +1051,7 @@ static my_bool thread_query(char *query) error= 1; goto end; } + l_mysql->reconnect= 1; if (mysql_query(l_mysql, (char *)query)) { fprintf(stderr, "Query failed (%s)\n", mysql_error(l_mysql)); @@ -4474,6 +4476,7 @@ static void test_stmt_close() myerror("connection failed"); exit(1); } + lmysql->reconnect= 1; if (!opt_silent) fprintf(stdout, " OK"); @@ -5365,6 +5368,7 @@ DROP TABLE IF EXISTS test_multi_tab"; fprintf(stdout, "\n connection failed(%s)", mysql_error(mysql_local)); exit(1); } + mysql_local->reconnect= 1; rc= mysql_query(mysql_local, query); myquery(rc); @@ -5473,6 +5477,7 @@ static void test_prepare_multi_statements() fprintf(stderr, "\n connection failed(%s)", mysql_error(mysql_local)); exit(1); } + mysql_local->reconnect= 1; strmov(query, "select 1; select 'another value'"); stmt= mysql_simple_prepare(mysql_local, query); check_stmt_r(stmt); @@ -7004,6 +7009,7 @@ static void test_prepare_grant() mysql_close(lmysql); exit(1); } + lmysql->reconnect= 1; if (!opt_silent) fprintf(stdout, " OK"); @@ -7439,6 +7445,7 @@ static void test_drop_temp() mysql_close(lmysql); exit(1); } + lmysql->reconnect= 1; if (!opt_silent) fprintf(stdout, " OK"); diff --git a/tests/connect_test.c b/tests/connect_test.c index fd81ad635ad..c68ade9f78f 100644 --- a/tests/connect_test.c +++ b/tests/connect_test.c @@ -46,6 +46,7 @@ int main(int argc, char **argv) perror(""); exit(1); } + sock->reconnect= 1; if (mysql_select_db(sock,"test")) { diff --git a/tests/deadlock_test.c b/tests/deadlock_test.c index 65a0df5c215..ab8158e0cd8 100644 --- a/tests/deadlock_test.c +++ b/tests/deadlock_test.c @@ -227,6 +227,7 @@ int main() !mysql_real_connect(&sel, host, user, pass, db, 0,0,0 ) || !mysql_real_connect(&del_ins, host, user, pass, db, 0,0,0 )) die("Error in mysql_real_connect(): %s", mysql_error(&lock)); + lock.reconnect= sel.reconnect= del_ins.reconnect= 1; permute(order, num_queries); printf("count = %d\n", count); diff --git a/tests/insert_test.c b/tests/insert_test.c index 052c12bfdf0..2b659e9eecb 100644 --- a/tests/insert_test.c +++ b/tests/insert_test.c @@ -40,6 +40,7 @@ int main(int argc, char **argv) perror(""); exit(1); } + mysql.reconnect= 1; num = atoi(argv[2]); count = 0; diff --git a/tests/list_test.c b/tests/list_test.c index 06bf16d2751..1d50e703133 100644 --- a/tests/list_test.c +++ b/tests/list_test.c @@ -43,6 +43,7 @@ int main(int argc, char **argv) perror(""); exit(1); } + mysql.reconnect= 1; if (mysql_select_db(sock,argv[1]) < 0) { diff --git a/tests/select_test.c b/tests/select_test.c index ee2a9192865..64c4fec5167 100644 --- a/tests/select_test.c +++ b/tests/select_test.c @@ -44,6 +44,7 @@ int main(int argc, char **argv) perror(""); exit(1); } + mysql.reconnect= 1; count = 0; num = atoi(argv[2]); diff --git a/tests/showdb_test.c b/tests/showdb_test.c index df2b3037c00..08229fc51ee 100644 --- a/tests/showdb_test.c +++ b/tests/showdb_test.c @@ -45,6 +45,7 @@ int main(int argc, char **argv) perror(""); exit(1); } + mysql.reconnect= 1; count = 0; num = atoi(argv[2]); diff --git a/tests/ssl_test.c b/tests/ssl_test.c index b18e493c267..85f490cb02e 100644 --- a/tests/ssl_test.c +++ b/tests/ssl_test.c @@ -51,6 +51,7 @@ int main(int argc, char **argv) perror(""); exit(1); } + mysql.reconnect= 1; count = 0; num = atoi(argv[2]); while (count < num) diff --git a/tests/thread_test.c b/tests/thread_test.c index 06f335fe1a6..f8577857d0a 100644 --- a/tests/thread_test.c +++ b/tests/thread_test.c @@ -55,6 +55,7 @@ unsigned __stdcall test_thread(void *arg __attribute__((unused))) perror(""); goto end; } + mysql.reconnect= 1; if (verbose) { putchar('*'); fflush(stdout); } for (count=0 ; count < number_of_tests ; count++) { diff --git a/tools/mysqlmanager.c b/tools/mysqlmanager.c index bb0a76d6c49..1be0242c505 100644 --- a/tools/mysqlmanager.c +++ b/tools/mysqlmanager.c @@ -877,7 +877,10 @@ static void manager_exec_connect(struct manager_exec* e) { if (mysql_real_connect(&e->mysql,e->con_host,e->con_user,e->con_pass,0, e->con_port,e->con_sock,0)) + { + e->mysql.reconnect= 1; return; + } sleep(1); } e->error="Could not connect to MySQL server withing the number of tries"; From 3ef3fa73604c1087fd1e9b6cad546f71b0feeb43 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 15:31:51 +0100 Subject: [PATCH 28/52] add debug print for bit field --- sql/ha_ndbcluster.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 929d50e077e..ae3f78fa5b8 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2244,6 +2244,11 @@ void ha_ndbcluster::print_results() fprintf(DBUG_FILE, "Var\t'%.*s'", field->pack_length(), value); break; } + case NdbDictionary::Column::Bit: { + const char *value= (char *) field->ptr; + fprintf(DBUG_FILE, "Bit\t'%.*s'", field->pack_length(), value); + break; + } case NdbDictionary::Column::Datetime: { Uint64 value= (Uint64) *field->ptr; fprintf(DBUG_FILE, "Datetime\t%llu", value); From 4cf675a21d5210f62a7907f464a06406e33468ba Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 16:19:24 +0100 Subject: [PATCH 29/52] ndb - Fix printout/bugs for bit column ndb/include/kernel/AttributeHeader.hpp: Add pseudo column for row size ndb/include/ndbapi/NdbDictionary.hpp: Add pseudo column for row size ndb/include/util/Bitmask.hpp: Remove assert which was not needed ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp: Missing break ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp: 1) New pseudo column ROW_SIZE 2) Fix bug in Bit columns ndb/src/ndbapi/NdbDictionary.cpp: Fix prinout of bit column ndb/src/ndbapi/NdbDictionaryImpl.cpp: Bug fix Bit column ndb/src/ndbapi/NdbRecAttr.cpp: Fix prinout of bit column ndb/test/src/HugoCalculator.cpp: Bit enable Hugo ndb/test/src/HugoOperations.cpp: Bit enable Hugo ndb/test/src/NDBT_Tables.cpp: Bit enable Hugo --- ndb/include/kernel/AttributeHeader.hpp | 2 + ndb/include/ndbapi/NdbDictionary.hpp | 1 + ndb/include/util/Bitmask.hpp | 2 - ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp | 1 + ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp | 46 ++++++------------- ndb/src/ndbapi/NdbDictionary.cpp | 5 +- ndb/src/ndbapi/NdbDictionaryImpl.cpp | 11 +++++ ndb/src/ndbapi/NdbRecAttr.cpp | 3 ++ ndb/test/src/HugoCalculator.cpp | 32 +++++++------ ndb/test/src/HugoOperations.cpp | 14 ++---- ndb/test/src/NDBT_Tables.cpp | 2 +- 11 files changed, 57 insertions(+), 62 deletions(-) diff --git a/ndb/include/kernel/AttributeHeader.hpp b/ndb/include/kernel/AttributeHeader.hpp index b807b4ef4f1..035b31f14e0 100644 --- a/ndb/include/kernel/AttributeHeader.hpp +++ b/ndb/include/kernel/AttributeHeader.hpp @@ -38,6 +38,8 @@ public: STATIC_CONST( ROW_COUNT = 0xFFFD ); STATIC_CONST( COMMIT_COUNT = 0xFFFC ); + STATIC_CONST( ROW_SIZE = 0xFFFA ); + /** Initialize AttributeHeader at location aHeaderPtr */ static AttributeHeader& init(void* aHeaderPtr, Uint32 anAttributeId, Uint32 aDataSize); diff --git a/ndb/include/ndbapi/NdbDictionary.hpp b/ndb/include/ndbapi/NdbDictionary.hpp index 3efedbfcda7..5f855ac06de 100644 --- a/ndb/include/ndbapi/NdbDictionary.hpp +++ b/ndb/include/ndbapi/NdbDictionary.hpp @@ -388,6 +388,7 @@ public: static const Column * FRAGMENT; static const Column * ROW_COUNT; static const Column * COMMIT_COUNT; + static const Column * ROW_SIZE; #endif private: diff --git a/ndb/include/util/Bitmask.hpp b/ndb/include/util/Bitmask.hpp index 2cd95190ed7..a88c48b4cb3 100644 --- a/ndb/include/util/Bitmask.hpp +++ b/ndb/include/util/Bitmask.hpp @@ -814,7 +814,6 @@ inline void BitmaskImpl::getField(unsigned size, const Uint32 src[], unsigned pos, unsigned len, Uint32 dst[]) { - assert(len > 0); assert(pos + len < (size << 5)); src += (pos >> 5); @@ -834,7 +833,6 @@ inline void BitmaskImpl::setField(unsigned size, Uint32 dst[], unsigned pos, unsigned len, const Uint32 src[]) { - assert(len > 0); assert(pos + len < (size << 5)); dst += (pos >> 5); diff --git a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp index fc27cca9cda..943f1ab02c4 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp @@ -344,6 +344,7 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal) ljam(); Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor); fragOperPtr.p->currNullBit += bitCount; + break; } } default: diff --git a/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp b/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp index 4c872e62cef..fc0982d96b8 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp @@ -1000,11 +1000,14 @@ Dbtup::read_psuedo(Uint32 attrId, Uint32* outBuffer){ case AttributeHeader::FRAGMENT: * outBuffer = operPtr.p->fragId >> 1; // remove "hash" bit return 1; + case AttributeHeader::ROW_SIZE: + * outBuffer = tabptr.p->tupheadsize << 2; + return 1; case AttributeHeader::ROW_COUNT: case AttributeHeader::COMMIT_COUNT: signal->theData[0] = operPtr.p->userpointer; signal->theData[1] = attrId; - + EXECUTE_DIRECT(DBLQH, GSN_READ_PSUEDO_REQ, signal, 2); outBuffer[0] = signal->theData[0]; outBuffer[1] = signal->theData[1]; @@ -1021,14 +1024,8 @@ Dbtup::readBitsNotNULL(Uint32* outBuffer, Uint32 attrDes2) { Tablerec* const regTabPtr = tabptr.p; + Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2); Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor); - Uint32 offsetInTuple = AttributeOffset::getNullFlagOffset(attrDes2); - Uint32 offsetInWord = AttributeOffset::getNullFlagBitOffset(attrDes2); - ndbrequire(offsetInTuple < regTabPtr->tupNullWords); - offsetInTuple += regTabPtr->tupNullIndex; - ndbrequire(offsetInTuple < tCheckOffset); - - Uint32 pos = offsetInTuple << 5 + offsetInWord; Uint32 indexBuf = tOutBufIndex; Uint32 newIndexBuf = indexBuf + ((bitCount + 31) >> 5); Uint32 maxRead = tMaxRead; @@ -1059,17 +1056,12 @@ Dbtup::readBitsNULLable(Uint32* outBuffer, Uint32 attrDes2) { Tablerec* const regTabPtr = tabptr.p; + Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2); Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor); - Uint32 offsetInTuple = AttributeOffset::getNullFlagOffset(attrDes2); - Uint32 offsetInWord = AttributeOffset::getNullFlagBitOffset(attrDes2); - ndbrequire(offsetInTuple < regTabPtr->tupNullWords); - offsetInTuple += regTabPtr->tupNullIndex; - ndbrequire(offsetInTuple < tCheckOffset); - + Uint32 indexBuf = tOutBufIndex; - Uint32 newIndexBuf = indexBuf + (bitCount + 31) >> 5; + Uint32 newIndexBuf = indexBuf + ((bitCount + 31) >> 5); Uint32 maxRead = tMaxRead; - Uint32 pos = offsetInWord << 5 + offsetInTuple; if(BitmaskImpl::get(regTabPtr->tupNullWords, tTupleHeader+regTabPtr->tupNullIndex, @@ -1108,15 +1100,9 @@ Dbtup::updateBitsNotNULL(Uint32* inBuffer, Uint32 inBufLen = tInBufLen; AttributeHeader ahIn(inBuffer[indexBuf]); Uint32 nullIndicator = ahIn.isNULL(); + Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2); Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor); Uint32 newIndex = indexBuf + 1 + ((bitCount + 31) >> 5); - Uint32 nullFlagOffset = AttributeOffset::getNullFlagOffset(attrDes2); - Uint32 nullFlagBitOffset = AttributeOffset::getNullFlagBitOffset(attrDes2); - Uint32 nullWordOffset = nullFlagOffset + regTabPtr->tupNullIndex; - ndbrequire((nullFlagOffset < regTabPtr->tupNullWords) && - (nullWordOffset < tCheckOffset)); - Uint32 nullBits = tTupleHeader[nullWordOffset]; - Uint32 pos = (nullFlagOffset << 5) + nullFlagBitOffset; if (newIndex <= inBufLen) { if (!nullIndicator) { @@ -1149,15 +1135,9 @@ Dbtup::updateBitsNULLable(Uint32* inBuffer, AttributeHeader ahIn(inBuffer[tInBufIndex]); Uint32 indexBuf = tInBufIndex; Uint32 nullIndicator = ahIn.isNULL(); - Uint32 nullFlagOffset = AttributeOffset::getNullFlagOffset(attrDes2); - Uint32 nullFlagBitOffset = AttributeOffset::getNullFlagBitOffset(attrDes2); - Uint32 nullWordOffset = nullFlagOffset + regTabPtr->tupNullIndex; - ndbrequire((nullFlagOffset < regTabPtr->tupNullWords) && - (nullWordOffset < tCheckOffset)); - Uint32 nullBits = tTupleHeader[nullWordOffset]; - Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor); - Uint32 pos = (nullFlagOffset << 5) + nullFlagBitOffset; - + Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2); + Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor); + if (!nullIndicator) { BitmaskImpl::clear(regTabPtr->tupNullWords, tTupleHeader+regTabPtr->tupNullIndex, @@ -1167,7 +1147,7 @@ Dbtup::updateBitsNULLable(Uint32* inBuffer, pos+1, bitCount, inBuffer+indexBuf+1); - + Uint32 newIndex = indexBuf + 1 + ((bitCount + 31) >> 5); tInBufLen = newIndex; return true; diff --git a/ndb/src/ndbapi/NdbDictionary.cpp b/ndb/src/ndbapi/NdbDictionary.cpp index 462f04acb88..6916eaf4ee0 100644 --- a/ndb/src/ndbapi/NdbDictionary.cpp +++ b/ndb/src/ndbapi/NdbDictionary.cpp @@ -922,6 +922,9 @@ operator<<(NdbOut& out, const NdbDictionary::Column& col) case NdbDictionary::Column::Undefined: out << "Undefined"; break; + case NdbDictionary::Column::Bit: + out << "Bit(" << col.getLength() << ")"; + break; default: out << "Type" << (Uint32)col.getType(); break; @@ -942,4 +945,4 @@ operator<<(NdbOut& out, const NdbDictionary::Column& col) const NdbDictionary::Column * NdbDictionary::Column::FRAGMENT = 0; const NdbDictionary::Column * NdbDictionary::Column::ROW_COUNT = 0; const NdbDictionary::Column * NdbDictionary::Column::COMMIT_COUNT = 0; - +const NdbDictionary::Column * NdbDictionary::Column::ROW_SIZE = 0; diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/ndb/src/ndbapi/NdbDictionaryImpl.cpp index 37dea73838f..b066469bb9e 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -218,6 +218,11 @@ NdbColumnImpl::create_psuedo(const char * name){ col->m_impl.m_attrId = AttributeHeader::COMMIT_COUNT; col->m_impl.m_attrSize = 8; col->m_impl.m_arraySize = 1; + } else if(!strcmp(name, "NDB$ROW_SIZE")){ + col->setType(NdbDictionary::Column::Unsigned); + col->m_impl.m_attrId = AttributeHeader::ROW_SIZE; + col->m_impl.m_attrSize = 4; + col->m_impl.m_arraySize = 1; } else { abort(); } @@ -1157,6 +1162,7 @@ columnTypeMapping[] = { { DictTabInfo::ExtTimespec, NdbDictionary::Column::Timespec }, { DictTabInfo::ExtBlob, NdbDictionary::Column::Blob }, { DictTabInfo::ExtText, NdbDictionary::Column::Text }, + { DictTabInfo::ExtBit, NdbDictionary::Column::Bit }, { -1, -1 } }; @@ -1266,6 +1272,11 @@ NdbDictInterface::parseTableInfo(NdbTableImpl ** ret, col->m_attrType =attrDesc.AttributeType; col->m_attrSize = (1 << attrDesc.AttributeSize) / 8; col->m_arraySize = attrDesc.AttributeArraySize; + if(attrDesc.AttributeSize == 0) + { + col->m_attrSize = 4; + col->m_arraySize = (attrDesc.AttributeArraySize + 31) >> 5; + } col->m_pk = attrDesc.AttributeKeyFlag; col->m_distributionKey = attrDesc.AttributeDKey; diff --git a/ndb/src/ndbapi/NdbRecAttr.cpp b/ndb/src/ndbapi/NdbRecAttr.cpp index bcd91292fcd..e6e97ab60b1 100644 --- a/ndb/src/ndbapi/NdbRecAttr.cpp +++ b/ndb/src/ndbapi/NdbRecAttr.cpp @@ -169,6 +169,9 @@ NdbOut& operator<<(NdbOut& out, const NdbRecAttr &r) case NdbDictionary::Column::Bigunsigned: out << r.u_64_value(); break; + case NdbDictionary::Column::Bit: + out << hex << "H'" << r.u_32_value() << dec; + break; case NdbDictionary::Column::Unsigned: out << r.u_32_value(); break; diff --git a/ndb/test/src/HugoCalculator.cpp b/ndb/test/src/HugoCalculator.cpp index 62c35c54a7a..7ae6985a840 100644 --- a/ndb/test/src/HugoCalculator.cpp +++ b/ndb/test/src/HugoCalculator.cpp @@ -137,13 +137,16 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ if (i != m_updatesCol && id != m_idCol) { const NdbDictionary::Column* attr = m_tab.getColumn(i); + Uint32 len = attr->getLength(); switch (attr->getType()){ + case NdbDictionary::Column::Bit: + len = 4 * ((len + 31) >> 5); case NdbDictionary::Column::Char: case NdbDictionary::Column::Varchar: case NdbDictionary::Column::Binary: case NdbDictionary::Column::Varbinary:{ int result = 0; - char* buf = new char[attr->getLength()+1]; + char* buf = new char[len+1]; const char* res = calcValue(id, i, updates, buf); if (res == NULL){ if (!pRow->attributeStore(i)->isNULL()){ @@ -171,17 +174,16 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ g_err << endl; g_err << "|- Invalid data found in attribute " << i << ": \"" << pRow->attributeStore(i)->aRef() - << "\" != \"" << res << "\"" << endl - << "Length of expected=" << (unsigned)strlen(res) << endl - << "Lenght of read=" - << (unsigned)strlen(pRow->attributeStore(i)->aRef()) << endl; + << "\" != \"" << res << "\"" << endl + << "Length of expected=" << (unsigned)strlen(res) << endl + << "Lenght of read=" + << (unsigned)strlen(pRow->attributeStore(i)->aRef()) << endl; g_err << "|- The row: \"" << (* pRow) << "\"" << endl; result = -1; } } delete []buf; - if (result != 0) - return result; + return result; } break; case NdbDictionary::Column::Int: @@ -190,11 +192,11 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ Int32 val = pRow->attributeStore(i)->int32_value(); if (val != cval){ g_err << "|- Invalid data found: \"" << val << "\" != \"" - << cval << "\"" << endl; + << cval << "\"" << endl; g_err << "|- The row: \"" << (* pRow) << "\"" << endl; return -1; } - break; + return 0; } case NdbDictionary::Column::Bigint: case NdbDictionary::Column::Bigunsigned:{ @@ -202,11 +204,12 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ Uint64 val = pRow->attributeStore(i)->u_64_value(); if (val != cval){ g_err << "|- Invalid data found: \"" << val << "\" != \"" - << cval << "\"" - << endl; + << cval << "\"" + << endl; g_err << "|- The row: \"" << (* pRow) << "\"" << endl; return -1; } + return 0; } break; case NdbDictionary::Column::Float:{ @@ -218,17 +221,16 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ g_err << "|- The row: \"" << (* pRow) << "\"" << endl; return -1; } + return 0; } break; case NdbDictionary::Column::Undefined: - default: - assert(false); break; } - } } - return 0; + assert(0); + return -1; } int diff --git a/ndb/test/src/HugoOperations.cpp b/ndb/test/src/HugoOperations.cpp index d3da8ae4ba2..546f0107499 100644 --- a/ndb/test/src/HugoOperations.cpp +++ b/ndb/test/src/HugoOperations.cpp @@ -407,7 +407,7 @@ HugoOperations::~HugoOperations(){ int HugoOperations::equalForAttr(NdbOperation* pOp, int attrId, int rowId){ - int check = 0; + int check = -1; const NdbDictionary::Column* attr = tab.getColumn(attrId); if (attr->getPrimaryKey() == false){ g_info << "Can't call equalForAttr on non PK attribute" << endl; @@ -415,6 +415,7 @@ int HugoOperations::equalForAttr(NdbOperation* pOp, } switch (attr->getType()){ + case NdbDictionary::Column::Bit: case NdbDictionary::Column::Char: case NdbDictionary::Column::Varchar: case NdbDictionary::Column::Binary: @@ -440,11 +441,6 @@ int HugoOperations::equalForAttr(NdbOperation* pOp, g_info << "Float not allowed as PK value" << endl; check = -1; break; - - default: - g_info << "default" << endl; - check = -1; - break; } return check; } @@ -453,10 +449,11 @@ int HugoOperations::setValueForAttr(NdbOperation* pOp, int attrId, int rowId, int updateId){ - int check = 0; + int check = -1; const NdbDictionary::Column* attr = tab.getColumn(attrId); switch (attr->getType()){ + case NdbDictionary::Column::Bit: case NdbDictionary::Column::Char: case NdbDictionary::Column::Varchar: case NdbDictionary::Column::Binary: @@ -492,9 +489,6 @@ int HugoOperations::setValueForAttr(NdbOperation* pOp, check = pOp->setValue( attr->getName(), (float)calc.calcValue(rowId, attrId, updateId)); break; - default: - check = -1; - break; } return check; } diff --git a/ndb/test/src/NDBT_Tables.cpp b/ndb/test/src/NDBT_Tables.cpp index b61d48b216c..5a5fecd85c1 100644 --- a/ndb/test/src/NDBT_Tables.cpp +++ b/ndb/test/src/NDBT_Tables.cpp @@ -48,7 +48,7 @@ const NDBT_Attribute T2Attribs[] = { NDBT_Attribute("KOL1", NdbDictionary::Column::Bigunsigned, 1, true), NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned), - NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned), + NDBT_Attribute("KOL3", NdbDictionary::Column::Bit, 23), NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned, 1, false, true), // Nullable NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned) From d9ab043e07e7b25fcbaaac4c32b5454c621b2c92 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 16:57:20 +0100 Subject: [PATCH 30/52] ndb - Fix hugoCalcValue for bits ndb/test/include/HugoCalculator.hpp: Fix calcValue for bits ndb/test/src/HugoCalculator.cpp: Fix calcValue for bits ndb/test/src/HugoOperations.cpp: Fix calcValue for bits ndb/test/tools/Makefile.am: Fix calcValue for bits --- ndb/test/include/HugoCalculator.hpp | 2 +- ndb/test/src/HugoCalculator.cpp | 34 ++++++++++++++--------------- ndb/test/src/HugoOperations.cpp | 11 +++++++--- ndb/test/tools/Makefile.am | 2 +- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/ndb/test/include/HugoCalculator.hpp b/ndb/test/include/HugoCalculator.hpp index b782eb003a3..108af0d1358 100644 --- a/ndb/test/include/HugoCalculator.hpp +++ b/ndb/test/include/HugoCalculator.hpp @@ -38,7 +38,7 @@ public: float calcValue(int record, int attrib, int updates) const; double calcValue(int record, int attrib, int updates) const; #endif - const char* calcValue(int record, int attrib, int updates, char* buf) const; + const char* calcValue(int record, int attrib, int updates, char* buf, int len) const; int verifyRowValues(NDBT_ResultRow* const pRow) const; int getIdValue(NDBT_ResultRow* const pRow) const; diff --git a/ndb/test/src/HugoCalculator.cpp b/ndb/test/src/HugoCalculator.cpp index 7ae6985a840..b2202303039 100644 --- a/ndb/test/src/HugoCalculator.cpp +++ b/ndb/test/src/HugoCalculator.cpp @@ -85,17 +85,16 @@ const char* HugoCalculator::calcValue(int record, int attrib, int updates, - char* buf) const { + char* buf, + int len) const { const char a[26] = {"UAWBORCTDPEFQGNYHISJMKXLZ"}; const NdbDictionary::Column* attr = m_tab.getColumn(attrib); int val = calcValue(record, attrib, updates); - int len; if (attr->getPrimaryKey()){ // Create a string where val is printed as chars in the beginning // of the string, then fill with other chars // The string length is set to the same size as the attribute - len = attr->getLength(); BaseString::snprintf(buf, len, "%d", val); for(int i=strlen(buf); i < len; i++) buf[i] = a[((val^i)%25)]; @@ -104,13 +103,13 @@ HugoCalculator::calcValue(int record, // Fill buf with some pattern so that we can detect // anomalies in the area that we don't fill with chars int i; - for (i = 0; igetLength(); i++) + for (i = 0; igetLength() + 1); + len = val % (len + 1); // If len == 0 return NULL if this is a nullable attribute if (len == 0){ if(attr->getNullable() == true) @@ -131,7 +130,8 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ id = pRow->attributeStore(m_idCol)->u_32_value(); updates = pRow->attributeStore(m_updatesCol)->u_32_value(); - + int result = 0; + // Check the values of each column for (int i = 0; iattributeStore(i)->isNULL()){ g_err << "|- NULL ERROR: expected a NULL but the column was not null" << endl; @@ -183,7 +182,6 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ } } delete []buf; - return result; } break; case NdbDictionary::Column::Int: @@ -194,9 +192,9 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ g_err << "|- Invalid data found: \"" << val << "\" != \"" << cval << "\"" << endl; g_err << "|- The row: \"" << (* pRow) << "\"" << endl; - return -1; + result = -1; } - return 0; + break; } case NdbDictionary::Column::Bigint: case NdbDictionary::Column::Bigunsigned:{ @@ -207,9 +205,8 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ << cval << "\"" << endl; g_err << "|- The row: \"" << (* pRow) << "\"" << endl; - return -1; + result = -1; } - return 0; } break; case NdbDictionary::Column::Float:{ @@ -217,20 +214,21 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ float val = pRow->attributeStore(i)->float_value(); if (val != cval){ g_err << "|- Invalid data found: \"" << val << "\" != \"" - << cval << "\"" << endl; + << cval << "\"" << endl; g_err << "|- The row: \"" << (* pRow) << "\"" << endl; - return -1; + result = -1; } - return 0; } break; case NdbDictionary::Column::Undefined: + default: + assert(0); + result = -1; break; } } } - assert(0); - return -1; + return result; } int diff --git a/ndb/test/src/HugoOperations.cpp b/ndb/test/src/HugoOperations.cpp index 546f0107499..3cf4d618833 100644 --- a/ndb/test/src/HugoOperations.cpp +++ b/ndb/test/src/HugoOperations.cpp @@ -414,15 +414,18 @@ int HugoOperations::equalForAttr(NdbOperation* pOp, return NDBT_FAILED; } + int len = attr->getLength(); switch (attr->getType()){ case NdbDictionary::Column::Bit: + len = 4 * ((len + 31) >> 5); case NdbDictionary::Column::Char: case NdbDictionary::Column::Varchar: case NdbDictionary::Column::Binary: case NdbDictionary::Column::Varbinary:{ char buf[8000]; memset(buf, 0, sizeof(buf)); - check = pOp->equal( attr->getName(), calc.calcValue(rowId, attrId, 0, buf)); + check = pOp->equal( attr->getName(), + calc.calcValue(rowId, attrId, 0, buf, len)); break; } case NdbDictionary::Column::Int: @@ -451,16 +454,18 @@ int HugoOperations::setValueForAttr(NdbOperation* pOp, int updateId){ int check = -1; const NdbDictionary::Column* attr = tab.getColumn(attrId); - + + int len = attr->getLength(); switch (attr->getType()){ case NdbDictionary::Column::Bit: + len = 4 * ((len + 31) >> 5); case NdbDictionary::Column::Char: case NdbDictionary::Column::Varchar: case NdbDictionary::Column::Binary: case NdbDictionary::Column::Varbinary:{ char buf[8000]; check = pOp->setValue( attr->getName(), - calc.calcValue(rowId, attrId, updateId, buf)); + calc.calcValue(rowId, attrId, updateId, buf, len)); break; } case NdbDictionary::Column::Int:{ diff --git a/ndb/test/tools/Makefile.am b/ndb/test/tools/Makefile.am index 3255267b636..42dc067a473 100644 --- a/ndb/test/tools/Makefile.am +++ b/ndb/test/tools/Makefile.am @@ -1,5 +1,5 @@ -ndbtest_PROGRAMS = hugoCalculator hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index ndb_cpcc +ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index ndb_cpcc # transproxy From febb9bc75e5379362d3e4f60d9100f3dc78ec055 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 17:03:48 +0100 Subject: [PATCH 31/52] ndb - Fix alloc/dealloc of pseudo ROW_SIZE ndb/src/ndbapi/NdbDictionaryImpl.cpp: Fix alloc/dealloc of pseudo ROW_SIZE --- ndb/src/ndbapi/NdbDictionaryImpl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/ndb/src/ndbapi/NdbDictionaryImpl.cpp index b066469bb9e..f56c3ce94c2 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -621,9 +621,11 @@ NdbDictionaryImpl::~NdbDictionaryImpl() delete NdbDictionary::Column::FRAGMENT; delete NdbDictionary::Column::ROW_COUNT; delete NdbDictionary::Column::COMMIT_COUNT; + delete NdbDictionary::Column::ROW_SIZE; NdbDictionary::Column::FRAGMENT= 0; NdbDictionary::Column::ROW_COUNT= 0; NdbDictionary::Column::COMMIT_COUNT= 0; + NdbDictionary::Column::ROW_SIZE= 0; } m_globalHash->unlock(); } else { @@ -690,6 +692,8 @@ NdbDictionaryImpl::setTransporter(class Ndb* ndb, NdbColumnImpl::create_psuedo("NDB$ROW_COUNT"); NdbDictionary::Column::COMMIT_COUNT= NdbColumnImpl::create_psuedo("NDB$COMMIT_COUNT"); + NdbDictionary::Column::ROW_SIZE= + NdbColumnImpl::create_psuedo("NDB$ROW_SIZE"); } m_globalHash->unlock(); return true; From 6f8ca2513e19db9359c46bc8370f3a8b36d3a5d2 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Dec 2004 22:22:34 +0100 Subject: [PATCH 32/52] instance.cc: explicit setting of reconnect (no behaviour change) server-tools/instance-manager/instance.cc: explicit setting of reconnect (no behaviour change) --- server-tools/instance-manager/instance.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 2c041e31119..42909a134ac 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -106,6 +106,7 @@ bool Instance::is_running() NullS, port, socket, 0)) { + mysql.reconnect= 1; is_connected= TRUE; pthread_mutex_unlock(&LOCK_instance); return TRUE; From c7d7c9ca10b1ee19cb017bfe9bb8b183ef9022a1 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Dec 2004 12:07:11 +0300 Subject: [PATCH 33/52] Don't display 'usage' privilege in TABLE_PRIVILEGES if we have columns privileges mysqldump skips information_schema db 'use' now can use information_schema db changed value of column 'Null' to 'NO' if column is not nullable client/mysqldump.c: mysqldump skips information_schema db mysql-test/r/alter_table.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/create.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/ctype_collate.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/ctype_recoding.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/ctype_ujis.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/drop.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/func_sapdb.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/func_time.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/gis.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/information_schema.result: Added couple of tests mysql-test/r/information_schema_inno.result: Removed coulmn 'CONTRAINT_METOD' from TABLE_CONSTRAINTS Added column 'POSITION_IN_UNIQUE_CONSTRAINT' to KEY_COLUMN_USAGE mysql-test/r/innodb.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/ndb_autodiscover.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/ps_1general.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/rpl000009.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/rpl_create_database.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/schema.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/select.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/show_check.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/sp.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/type_enum.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/r/type_ranges.result: changed value of column 'Null' to 'NO' if column is not nullable mysql-test/t/information_schema.test: Added couple of tests sql/sql_acl.cc: Don't display 'usage' privilege in TABLE_PRIVILEGES if we have columns privileges sql/sql_db.cc: 'use' now can use information_schema db sql/sql_show.cc: code cleanup informaton_schema(IS) db now contains data about IS itself sql/sql_yacc.yy: A fix(wrong behavour of 'SHOW COLUMNS, SHOW KEYS' with 'where condition') --- client/mysqldump.c | 4 + mysql-test/r/alter_table.result | 8 +- mysql-test/r/create.result | 20 +- mysql-test/r/ctype_collate.result | 2 +- mysql-test/r/ctype_recoding.result | 6 +- mysql-test/r/ctype_ujis.result | 2 +- mysql-test/r/drop.result | 2 + mysql-test/r/func_sapdb.result | 8 +- mysql-test/r/func_time.result | 2 +- mysql-test/r/gis.result | 18 +- mysql-test/r/information_schema.result | 101 ++++++--- mysql-test/r/information_schema_inno.result | 20 +- mysql-test/r/innodb.result | 2 +- mysql-test/r/ndb_autodiscover.result | 1 + mysql-test/r/ps_1general.result | 5 +- mysql-test/r/rpl000009.result | 3 + mysql-test/r/rpl_create_database.result | 4 + mysql-test/r/schema.result | 1 + mysql-test/r/select.result | 24 +- mysql-test/r/show_check.result | 5 +- mysql-test/r/sp.result | 8 +- mysql-test/r/type_enum.result | 4 +- mysql-test/r/type_ranges.result | 116 +++++----- mysql-test/t/information_schema.test | 28 ++- sql/sql_acl.cc | 4 +- sql/sql_db.cc | 38 +++- sql/sql_show.cc | 231 ++++++++++++++------ sql/sql_yacc.yy | 23 +- 28 files changed, 443 insertions(+), 247 deletions(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index 9d35de2c953..17c881d7f36 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1935,6 +1935,10 @@ static int dump_databases(char **db_names) static int init_dumping(char *database) { + if (mysql_get_server_version(sock) >= 50003 && + !strcmp(database, "information_schema")) + return 1; + if (mysql_select_db(sock, database)) { DBerror(sock, "when selecting the database"); diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index e0d484312e7..37f5d0aa26a 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -52,9 +52,9 @@ KEY NAME (NAME)); ALTER TABLE t1 CHANGE NAME NAME CHAR(80) not null; SHOW FULL COLUMNS FROM t1; Field Type Collation Null Key Default Extra Privileges Comment -GROUP_ID int(10) unsigned NULL PRI 0 select,insert,update,references -LANG_ID smallint(5) unsigned NULL PRI 0 select,insert,update,references -NAME char(80) latin1_swedish_ci MUL select,insert,update,references +GROUP_ID int(10) unsigned NULL NO PRI 0 select,insert,update,references +LANG_ID smallint(5) unsigned NULL NO PRI 0 select,insert,update,references +NAME char(80) latin1_swedish_ci NO MUL select,insert,update,references DROP TABLE t1; create table t1 (n int); insert into t1 values(9),(3),(12),(10); @@ -187,7 +187,7 @@ alter table t1 rename t2; alter table t2 rename t1, add c char(10) comment "no comment"; show columns from t1; Field Type Null Key Default Extra -i int(10) unsigned PRI NULL auto_increment +i int(10) unsigned NO PRI NULL auto_increment c char(10) YES NULL drop table t1; create table t1 (a int, b int); diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index ce4515f900b..f2e91c36f75 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -100,12 +100,12 @@ drop table t2; create table t2 select now() as a , curtime() as b, curdate() as c , 1+1 as d , 1.0 + 1 as e , 33333333333333333 + 3 as f; describe t2; Field Type Null Key Default Extra -a datetime 0000-00-00 00:00:00 -b time 00:00:00 -c date 0000-00-00 -d bigint(17) 0 -e double(18,1) 0.0 -f bigint(17) 0 +a datetime NO 0000-00-00 00:00:00 +b time NO 00:00:00 +c date NO 0000-00-00 +d bigint(17) NO 0 +e double(18,1) NO 0.0 +f bigint(17) NO 0 drop table t2; create table t2 select CAST("2001-12-29" AS DATE) as d, CAST("20:45:11" AS TIME) as t, CAST("2001-12-29 20:45:11" AS DATETIME) as dt; describe t2; @@ -412,13 +412,13 @@ from t1; explain t2; Field Type Null Key Default Extra a int(11) YES NULL -b bigint(11) 0 -c bigint(10) 0 +b bigint(11) NO 0 +c bigint(10) NO 0 d date YES NULL -e varchar(1) +e varchar(1) NO f datetime YES NULL g time YES NULL -h longblob +h longblob NO dd time YES NULL select * from t2; a b c d e f g h dd diff --git a/mysql-test/r/ctype_collate.result b/mysql-test/r/ctype_collate.result index aebf8b4637b..201e1c6de08 100644 --- a/mysql-test/r/ctype_collate.result +++ b/mysql-test/r/ctype_collate.result @@ -488,7 +488,7 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 SHOW FIELDS FROM t1; Field Type Null Key Default Extra -latin1_f char(32) +latin1_f char(32) NO ALTER TABLE t1 CHANGE latin1_f latin1_f CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin; SHOW CREATE TABLE t1; diff --git a/mysql-test/r/ctype_recoding.result b/mysql-test/r/ctype_recoding.result index 7d5f9d5b59a..da0007fdfbe 100644 --- a/mysql-test/r/ctype_recoding.result +++ b/mysql-test/r/ctype_recoding.result @@ -54,7 +54,7 @@ Table Create Table ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='ËÏÍÍÅÎÔÁÒÉÊ ÔÁÂÌÉÃÙ' SHOW FIELDS FROM ÔÁÂÌÉÃÁ; Field Type Null Key Default Extra -ÐÏÌÅ char(32) +ÐÏÌÅ char(32) NO SET CHARACTER SET cp1251; SHOW TABLES; Tables_in_test @@ -66,7 +66,7 @@ Table Create Table ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='êîììåíòàðèé òàáëèöû' SHOW FIELDS FROM òàáëèöà; Field Type Null Key Default Extra -ïîëå char(32) +ïîëå char(32) NO SET CHARACTER SET utf8; SHOW TABLES; Tables_in_test @@ -78,7 +78,7 @@ Table Create Table ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='комментарий таблицы' SHOW FIELDS FROM таблица; Field Type Null Key Default Extra -поле char(32) +поле char(32) NO SET CHARACTER SET koi8r; DROP TABLE ÔÁÂÌÉÃÁ; SET CHARACTER SET default; diff --git a/mysql-test/r/ctype_ujis.result b/mysql-test/r/ctype_ujis.result index d02ac0062f8..c9d37fb845a 100644 --- a/mysql-test/r/ctype_ujis.result +++ b/mysql-test/r/ctype_ujis.result @@ -123,7 +123,7 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=ujis SHOW COLUMNS FROM t1; Field Type Null Key Default Extra -a char(1) +a char(1) NO b enum('¤¢','¤¤') YES NULL DROP TABLE t1; CREATE TABLE t1 diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result index 223ceb003b3..901871f437e 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -32,6 +32,7 @@ unlock tables; create database mysqltest; show databases; Database +information_schema mysql mysqltest test @@ -42,6 +43,7 @@ unlock tables; drop database mysqltest; show databases; Database +information_schema mysql test drop database mysqltest; diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index fb344855e83..f80b0281dd8 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -174,12 +174,12 @@ date("1997-12-31 23:59:59.000001") as f8, time("1997-12-31 23:59:59.000001") as f9; describe t1; Field Type Null Key Default Extra -f1 date 0000-00-00 +f1 date NO 0000-00-00 f2 datetime YES NULL f3 time YES NULL -f4 time 00:00:00 -f5 time 00:00:00 -f6 time 00:00:00 +f4 time NO 00:00:00 +f5 time NO 00:00:00 +f6 time NO 00:00:00 f7 datetime YES NULL f8 date YES NULL f9 time YES NULL diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 131496f3d9f..32c80bb330e 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -611,7 +611,7 @@ create table t1 select last_day('2000-02-05') as a, from_days(to_days("960101")) as b; describe t1; Field Type Null Key Default Extra -a date 0000-00-00 +a date NO 0000-00-00 b date YES NULL select * from t1; a b diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index ee0a30e27d0..3b196a60d68 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -9,35 +9,35 @@ CREATE TABLE gis_geometrycollection (fid INTEGER NOT NULL PRIMARY KEY, g GEOMET CREATE TABLE gis_geometry (fid INTEGER NOT NULL PRIMARY KEY, g GEOMETRY); SHOW FIELDS FROM gis_point; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g point YES NULL SHOW FIELDS FROM gis_line; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g linestring YES NULL SHOW FIELDS FROM gis_polygon; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g polygon YES NULL SHOW FIELDS FROM gis_multi_point; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g multipoint YES NULL SHOW FIELDS FROM gis_multi_line; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g multilinestring YES NULL SHOW FIELDS FROM gis_multi_polygon; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g multipolygon YES NULL SHOW FIELDS FROM gis_geometrycollection; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g geometrycollection YES NULL SHOW FIELDS FROM gis_geometry; Field Type Null Key Default Extra -fid int(11) PRI +fid int(11) NO PRI g geometry YES NULL INSERT INTO gis_point VALUES (101, PointFromText('POINT(10 10)')), @@ -430,7 +430,7 @@ mln multilinestring YES NULL mpg multipolygon YES NULL gc geometrycollection YES NULL gm geometry YES NULL -fid int(11) +fid int(11) NO DROP TABLE t1; SELECT AsText(GeometryFromWKB(AsWKB(GeometryFromText('POINT(1 4)')))); AsText(GeometryFromWKB(AsWKB(GeometryFromText('POINT(1 4)')))) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index f716d90a073..ab6e180e6b7 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -5,10 +5,12 @@ NULL mysql latin1 NULL NULL test latin1 NULL select schema_name from information_schema.schemata; schema_name +information_schema mysql test show databases *; CATALOG_NAME SCHEMA_NAME DEFAULT_CHARACTER_SET_NAME SQL_PATH +NULL information_schema utf8 NULL NULL mysql latin1 NULL NULL test latin1 NULL show databases like 't%'; @@ -16,6 +18,7 @@ Database (t%) test show databases; Database +information_schema mysql test show databases * where schema_name like 't%'; @@ -31,6 +34,22 @@ create table testtets.t4(a int); create view v1 (c) as select table_name from information_schema.TABLES; select * from v1; c +SCHEMATA +TABLES +COLUMNS +CHARACTER_SETS +COLLATIONS +COLLATION_CHARACTER_SET_APPLICABILITY +ROUTINES +STATISTICS +VIEWS +USER_PRIVILEGES +SCHEMA_PRIVILEGES +TABLE_PRIVILEGES +COLUMN_PRIVILEGES +TABLE_CONSTRAINTS +KEY_COLUMN_USAGE +TABLE_NAMES columns_priv db func @@ -56,6 +75,10 @@ select c,table_name from v1 left join information_schema.TABLES v2 on (v1.c=v2.table_name) where v1.c like "t%"; c table_name +TABLES TABLES +TABLE_PRIVILEGES TABLE_PRIVILEGES +TABLE_CONSTRAINTS TABLE_CONSTRAINTS +TABLE_NAMES TABLE_NAMES tables_priv tables_priv time_zone time_zone time_zone_leap_second time_zone_leap_second @@ -70,6 +93,10 @@ select c, v2.table_name from v1 right join information_schema.TABLES v2 on (v1.c=v2.table_name) where v1.c like "t%"; c table_name +TABLES TABLES +TABLE_PRIVILEGES TABLE_PRIVILEGES +TABLE_CONSTRAINTS TABLE_CONSTRAINTS +TABLE_NAMES TABLE_NAMES tables_priv tables_priv time_zone time_zone time_zone_leap_second time_zone_leap_second @@ -88,11 +115,10 @@ t4 select * from information_schema.STATISTICS where TABLE_SCHEMA = "testtets"; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME NON_UNIQUE INDEX_SCHEMA INDEX_NAME SEQ_IN_INDEX COLUMN_NAME COLLATION CARDINALITY SUB_PART PACKED NULLABLE INDEX_TYPE COMMENT NULL testtets t1 1 testtets string_data 1 b A NULL NULL NULL YES BTREE -show keys * where TABLE_SCHEMA Like "test%"; +show keys * from t3 where TABLE_SCHEMA Like "test%"; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME NON_UNIQUE INDEX_SCHEMA INDEX_NAME SEQ_IN_INDEX COLUMN_NAME COLLATION CARDINALITY SUB_PART PACKED NULLABLE INDEX_TYPE COMMENT NULL test t3 1 test a_data 1 a A NULL NULL NULL YES BTREE -NULL testtets t1 1 testtets string_data 1 b A NULL NULL NULL YES BTREE -show keys where INDEX_NAME = "a_data"; +show keys from t3 where INDEX_NAME = "a_data"; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment t3 1 a_data 1 a A NULL NULL NULL YES BTREE show tables like 't%'; @@ -113,15 +139,15 @@ Field Type Collation Null Key Default Extra Privileges Comment a int(11) NULL YES MUL NULL select,insert,update,references show full columns from mysql.db like "Insert%"; Field Type Collation Null Key Default Extra Privileges Comment -Insert_priv enum('N','Y') utf8_bin N select,insert,update,references +Insert_priv enum('N','Y') utf8_bin NO N select,insert,update,references show full columns from v1; Field Type Collation Null Key Default Extra Privileges Comment -c varchar(64) utf8_general_ci select,insert,update,references +c varchar(64) utf8_general_ci NO select,insert,update,references select * from information_schema.COLUMNS where table_name="t1" and column_name= "a"; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT NULL testtets t1 a 1 NULL YES int 11 11 11 0 NULL NULL int(11) select,insert,update,references -show columns * where table_name = "t1"; +show columns * from testtets.t1 where table_name = "t1"; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT NULL testtets t1 a 1 NULL YES int 11 11 11 0 NULL NULL int(11) select,insert,update,references NULL testtets t1 b 2 NULL YES varchar 30 30 NULL NULL latin1 latin1_swedish_ci varchar(30) MUL select,insert,update,references @@ -255,6 +281,7 @@ count(*) create view v0 (c) as select schema_name from information_schema.schemata; select * from v0; c +information_schema mysql test explain select * from v0; @@ -351,18 +378,18 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 select * from information_schema.TABLE_CONSTRAINTS where TABLE_SCHEMA= "test"; -CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE CONSTRAINT_METHOD -NULL test PRIMARY test t1 PRIMARY KEY NULL -NULL test constraint_1 test t1 UNIQUE NULL -NULL test key_1 test t1 UNIQUE NULL -NULL test key_2 test t1 UNIQUE NULL +CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE +NULL test PRIMARY test t1 PRIMARY KEY +NULL test constraint_1 test t1 UNIQUE +NULL test key_1 test t1 UNIQUE +NULL test key_2 test t1 UNIQUE select * from information_schema.KEY_COLUMN_USAGE where TABLE_SCHEMA= "test"; -CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME -NULL test PRIMARY NULL test t1 a 1 NULL NULL NULL -NULL test constraint_1 NULL test t1 a 1 NULL NULL NULL -NULL test key_1 NULL test t1 a 1 NULL NULL NULL -NULL test key_2 NULL test t1 a 1 NULL NULL NULL +CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME +NULL test PRIMARY NULL test t1 a 1 NULL NULL NULL NULL +NULL test constraint_1 NULL test t1 a 1 NULL NULL NULL NULL +NULL test key_1 NULL test t1 a 1 NULL NULL NULL NULL +NULL test key_2 NULL test t1 a 1 NULL NULL NULL NULL select table_name from information_schema.TABLES where table_schema like "test%"; table_name t1 @@ -392,7 +419,6 @@ GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRAN 'joe'@'localhost' NULL test t1 a SELECT YES select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES; GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE -'joe'@'localhost' NULL test t1 USAGE YES drop view v1, v2, v3; drop table t1; delete from mysql.user where user='joe'; @@ -404,7 +430,7 @@ create procedure px5 () begin declare v int; declare c cursor for select version from -information_schema.tables; +information_schema.tables where table_schema <> 'information_schema'; open c; fetch c into v; select v; @@ -432,6 +458,7 @@ select s1 from t1 where s1 in (select version from information_schema.tables) union select version from information_schema.tables; s1 +0 9 10 drop table t1; @@ -508,11 +535,6 @@ proc modified timestamp proc sql_mode set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO') proc comment char(64) drop table t115; -create view vk as select count(*) from information_schema.tables a; -select * from vk; -count(*) -17 -drop view vk; create procedure p108 () begin declare c cursor for select data_type from information_schema.columns; open c; open c; end;// call p108()// @@ -529,8 +551,37 @@ show index from vo; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment select * from information_schema.TABLE_CONSTRAINTS where TABLE_NAME= "vo"; -CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE CONSTRAINT_METHOD +CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE select * from information_schema.KEY_COLUMN_USAGE where TABLE_NAME= "vo"; -CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME +CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME drop view vo; +select TABLE_NAME,TABLE_TYPE,ENGINE +from information_schema.tables +where table_schema='information_schema' limit 2; +TABLE_NAME TABLE_TYPE ENGINE +SCHEMATA TEMPORARY MyISAM +TABLES TEMPORARY MyISAM +show tables from information_schema like "t%"; +Tables_in_information_schema (t%) +create database information_schema; +ERROR HY000: Can't create database 'information_schema'; database exists +use information_schema; +show full tables like "T%"; +Tables_in_information_schema (T%) Table_type +TABLES TEMPORARY +TABLE_PRIVILEGES TEMPORARY +TABLE_CONSTRAINTS TEMPORARY +TABLE_NAMES TEMPORARY +create table t1(a int); +ERROR 42S02: Unknown table 't1' in information_schema +use test; +show tables; +Tables_in_test +use information_schema; +show tables like "T%"; +Tables_in_information_schema (T%) +TABLES +TABLE_PRIVILEGES +TABLE_CONSTRAINTS +TABLE_NAMES diff --git a/mysql-test/r/information_schema_inno.result b/mysql-test/r/information_schema_inno.result index e6dcda2c15d..cdbdda5fd43 100644 --- a/mysql-test/r/information_schema_inno.result +++ b/mysql-test/r/information_schema_inno.result @@ -4,16 +4,16 @@ FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE, FOREIGN KEY (t1_id) REFERENCES t1(id) ON UPDATE CASCADE) ENGINE=INNODB; select * from information_schema.TABLE_CONSTRAINTS where TABLE_SCHEMA= "test"; -CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE CONSTRAINT_METHOD -NULL test PRIMARY test t1 PRIMARY KEY NULL -NULL test PRIMARY test t2 PRIMARY KEY NULL -NULL test t2_ibfk_1 test t2 FOREIGN KEY ON DELETE CASCADE -NULL test t2_ibfk_2 test t2 FOREIGN KEY ON UPDATE CASCADE +CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE +NULL test PRIMARY test t1 PRIMARY KEY +NULL test PRIMARY test t2 PRIMARY KEY +NULL test t2_ibfk_1 test t2 FOREIGN KEY +NULL test t2_ibfk_2 test t2 FOREIGN KEY select * from information_schema.KEY_COLUMN_USAGE where TABLE_SCHEMA= "test"; -CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME -NULL test PRIMARY NULL test t1 id 1 NULL NULL NULL -NULL test PRIMARY NULL test t2 id 1 NULL NULL NULL -NULL test t2_ibfk_1 NULL test t2 t1_id 1 NULL id -NULL test t2_ibfk_2 NULL test t2 t1_id 1 NULL id +CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME +NULL test PRIMARY NULL test t1 id 1 NULL NULL NULL NULL +NULL test PRIMARY NULL test t2 id 1 NULL NULL NULL NULL +NULL test t2_ibfk_1 NULL test t2 t1_id 1 1 test t1 id +NULL test t2_ibfk_2 NULL test t2 t1_id 1 1 test t1 id drop table t2, t1; diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 60028763b17..9a6c69b7bea 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -931,7 +931,7 @@ drop table t1; create table t1 (t int not null default 1, key (t)) engine=innodb; desc t1; Field Type Null Key Default Extra -t int(11) MUL 1 +t int(11) NO MUL 1 drop table t1; CREATE TABLE t1 ( number bigint(20) NOT NULL default '0', diff --git a/mysql-test/r/ndb_autodiscover.result b/mysql-test/r/ndb_autodiscover.result index 82ff4072378..ba7bcd05673 100644 --- a/mysql-test/r/ndb_autodiscover.result +++ b/mysql-test/r/ndb_autodiscover.result @@ -355,6 +355,7 @@ drop table t2; drop database test2; show databases; Database +information_schema mysql test use test; diff --git a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result index 0868ec3a364..ef399b6662d 100644 --- a/mysql-test/r/ps_1general.result +++ b/mysql-test/r/ps_1general.result @@ -254,6 +254,7 @@ a int primary key, b char(10) prepare stmt4 from ' show databases '; execute stmt4; Database +information_schema mysql test prepare stmt4 from ' show tables from test like ''t2%'' '; @@ -263,7 +264,7 @@ t2 prepare stmt4 from ' show columns from t2 from test like ''a%'' '; execute stmt4; Field Type Null Key Default Extra -a int(11) PRI +a int(11) NO PRI create index t2_idx on t2(b); prepare stmt4 from ' show index from t2 from test '; execute stmt4; @@ -410,7 +411,7 @@ drop user drop_user@localhost; prepare stmt3 from ' describe t2 '; execute stmt3; Field Type Null Key Default Extra -a int(11) PRI +a int(11) NO PRI b char(10) YES MUL NULL drop table t2 ; execute stmt3; diff --git a/mysql-test/r/rpl000009.result b/mysql-test/r/rpl000009.result index bb82dcb1e6a..a4dbf54f39b 100644 --- a/mysql-test/r/rpl000009.result +++ b/mysql-test/r/rpl000009.result @@ -32,6 +32,7 @@ create database mysqltest2; create database mysqltest; show databases; Database +information_schema mysql mysqltest mysqltest2 @@ -48,6 +49,7 @@ insert into mysqltest.t2 values (11, 'eleven test'), (12, 'twelve test'), set sql_log_bin = 1; show databases; Database +information_schema mysql test create database mysqltest2; @@ -66,6 +68,7 @@ insert into mysqltest.t3 values (1, 'original bar.t3'); load data from master; show databases; Database +information_schema mysql mysqltest mysqltest2 diff --git a/mysql-test/r/rpl_create_database.result b/mysql-test/r/rpl_create_database.result index 86282ce3cc5..ca4585d0d8d 100644 --- a/mysql-test/r/rpl_create_database.result +++ b/mysql-test/r/rpl_create_database.result @@ -22,6 +22,7 @@ USE mysqltest_sisyfos; ALTER DATABASE mysqltest_bob CHARACTER SET latin1; SHOW DATABASES; Database +information_schema mysql mysqltest_bob mysqltest_prometheus @@ -29,6 +30,7 @@ mysqltest_sisyfos test SHOW DATABASES; Database +information_schema mysql mysqltest_prometheus mysqltest_sisyfos @@ -57,6 +59,7 @@ master-bin.000001 # Query 1 # CREATE DATABASE mysqltest_sisyfos master-bin.000001 # Query 1 # use `mysqltest_sisyfos`; CREATE TABLE t2 (a INT) SHOW DATABASES; Database +information_schema mysql mysqltest_bob mysqltest_prometheus @@ -64,6 +67,7 @@ mysqltest_sisyfos test SHOW DATABASES; Database +information_schema mysql mysqltest_prometheus mysqltest_sisyfos diff --git a/mysql-test/r/schema.result b/mysql-test/r/schema.result index d7bd6fef655..48e6ebcfad2 100644 --- a/mysql-test/r/schema.result +++ b/mysql-test/r/schema.result @@ -4,6 +4,7 @@ Database Create Database foo CREATE DATABASE `foo` /*!40100 DEFAULT CHARACTER SET latin1 */ show schemas; Database +information_schema foo mysql test diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index e4232a759b1..ac0157fcfd1 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2034,20 +2034,20 @@ show tables from test like "t?"; Tables_in_test (t?) show full columns from t2; Field Type Collation Null Key Default Extra Privileges Comment -auto int(11) NULL PRI NULL auto_increment select,insert,update,references -fld1 int(6) unsigned zerofill NULL UNI 000000 select,insert,update,references -companynr tinyint(2) unsigned zerofill NULL 00 select,insert,update,references -fld3 char(30) latin1_swedish_ci MUL select,insert,update,references -fld4 char(35) latin1_swedish_ci select,insert,update,references -fld5 char(35) latin1_swedish_ci select,insert,update,references -fld6 char(4) latin1_swedish_ci select,insert,update,references +auto int(11) NULL NO PRI NULL auto_increment select,insert,update,references +fld1 int(6) unsigned zerofill NULL NO UNI 000000 select,insert,update,references +companynr tinyint(2) unsigned zerofill NULL NO 00 select,insert,update,references +fld3 char(30) latin1_swedish_ci NO MUL select,insert,update,references +fld4 char(35) latin1_swedish_ci NO select,insert,update,references +fld5 char(35) latin1_swedish_ci NO select,insert,update,references +fld6 char(4) latin1_swedish_ci NO select,insert,update,references show full columns from t2 from test like 'f%'; Field Type Collation Null Key Default Extra Privileges Comment -fld1 int(6) unsigned zerofill NULL UNI 000000 select,insert,update,references -fld3 char(30) latin1_swedish_ci MUL select,insert,update,references -fld4 char(35) latin1_swedish_ci select,insert,update,references -fld5 char(35) latin1_swedish_ci select,insert,update,references -fld6 char(4) latin1_swedish_ci select,insert,update,references +fld1 int(6) unsigned zerofill NULL NO UNI 000000 select,insert,update,references +fld3 char(30) latin1_swedish_ci NO MUL select,insert,update,references +fld4 char(35) latin1_swedish_ci NO select,insert,update,references +fld5 char(35) latin1_swedish_ci NO select,insert,update,references +fld6 char(4) latin1_swedish_ci NO select,insert,update,references show full columns from t2 from test like 's%'; Field Type Collation Null Key Default Extra Privileges Comment show keys from t2; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 931fb8b20b9..dab35262e0a 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -50,6 +50,7 @@ show table status from test like "this_doesn't_exists%"; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment show databases; Database +information_schema mysql test show databases like "test%"; @@ -118,9 +119,9 @@ t1 CREATE TABLE t1 ( set sql_quote_show_create=1; show full columns from t1; Field Type Collation Null Key Default Extra Privileges Comment -test_set set('val1','val2','val3') latin1_swedish_ci select,insert,update,references +test_set set('val1','val2','val3') latin1_swedish_ci NO select,insert,update,references name char(20) latin1_swedish_ci YES O'Brien select,insert,update,references O'Brien as default -c int(11) NULL select,insert,update,references int column +c int(11) NULL NO select,insert,update,references int column c-b int(11) NULL YES NULL select,insert,update,references name with a minus space 2 int(11) NULL YES NULL select,insert,update,references name with a space drop table t1; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 8745a274851..0d7f9f7d50c 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1646,8 +1646,8 @@ test CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */ Database (foo) Level Code Message Field Type Null Key Default Extra -id char(16) -data int(11) +id char(16) NO +data int(11) NO Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment @@ -1696,8 +1696,8 @@ test CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */ Database (foo) Level Code Message Field Type Null Key Default Extra -id char(16) -data int(11) +id char(16) NO +data int(11) NO Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment diff --git a/mysql-test/r/type_enum.result b/mysql-test/r/type_enum.result index 2ccf32367fb..6e92cb7b54c 100644 --- a/mysql-test/r/type_enum.result +++ b/mysql-test/r/type_enum.result @@ -1654,7 +1654,7 @@ set names latin1; create table t1 (a enum(0xE4, '1', '2') not null default 0xE4); show columns from t1; Field Type Null Key Default Extra -a enum('ä','1','2') ä +a enum('ä','1','2') NO ä show create table t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -1675,7 +1675,7 @@ t1 CREATE TABLE `t1` ( show columns from t1; Field Type Null Key Default Extra a int(11) YES 1 -b enum('value','öäü_value','ÊÃÕ') value +b enum('value','öäü_value','ÊÃÕ') NO value drop table t1; CREATE TABLE t1 (c enum('a', 'A') BINARY); INSERT INTO t1 VALUES ('a'),('A'); diff --git a/mysql-test/r/type_ranges.result b/mysql-test/r/type_ranges.result index ef4f65eda1f..1a66f0d91d2 100644 --- a/mysql-test/r/type_ranges.result +++ b/mysql-test/r/type_ranges.result @@ -40,30 +40,30 @@ KEY (options,flags) ); show full fields from t1; Field Type Collation Null Key Default Extra Privileges Comment -auto int(5) unsigned NULL PRI NULL auto_increment select,insert,update,references +auto int(5) unsigned NULL NO PRI NULL auto_increment select,insert,update,references string char(10) latin1_swedish_ci YES hello select,insert,update,references -tiny tinyint(4) NULL MUL 0 select,insert,update,references -short smallint(6) NULL MUL 1 select,insert,update,references -medium mediumint(8) NULL MUL 0 select,insert,update,references -long_int int(11) NULL 0 select,insert,update,references -longlong bigint(13) NULL MUL 0 select,insert,update,references -real_float float(13,1) NULL MUL 0.0 select,insert,update,references +tiny tinyint(4) NULL NO MUL 0 select,insert,update,references +short smallint(6) NULL NO MUL 1 select,insert,update,references +medium mediumint(8) NULL NO MUL 0 select,insert,update,references +long_int int(11) NULL NO 0 select,insert,update,references +longlong bigint(13) NULL NO MUL 0 select,insert,update,references +real_float float(13,1) NULL NO MUL 0.0 select,insert,update,references real_double double(16,4) NULL YES NULL select,insert,update,references -utiny tinyint(3) unsigned NULL MUL 0 select,insert,update,references -ushort smallint(5) unsigned zerofill NULL MUL 00000 select,insert,update,references -umedium mediumint(8) unsigned NULL MUL 0 select,insert,update,references -ulong int(11) unsigned NULL MUL 0 select,insert,update,references -ulonglong bigint(13) unsigned NULL MUL 0 select,insert,update,references +utiny tinyint(3) unsigned NULL NO MUL 0 select,insert,update,references +ushort smallint(5) unsigned zerofill NULL NO MUL 00000 select,insert,update,references +umedium mediumint(8) unsigned NULL NO MUL 0 select,insert,update,references +ulong int(11) unsigned NULL NO MUL 0 select,insert,update,references +ulonglong bigint(13) unsigned NULL NO MUL 0 select,insert,update,references time_stamp timestamp NULL YES CURRENT_TIMESTAMP select,insert,update,references date_field date NULL YES NULL select,insert,update,references time_field time NULL YES NULL select,insert,update,references date_time datetime NULL YES NULL select,insert,update,references blob_col blob NULL YES NULL select,insert,update,references tinyblob_col tinyblob NULL YES NULL select,insert,update,references -mediumblob_col mediumblob NULL select,insert,update,references -longblob_col longblob NULL select,insert,update,references -options enum('one','two','tree') latin1_swedish_ci MUL one select,insert,update,references -flags set('one','two','tree') latin1_swedish_ci select,insert,update,references +mediumblob_col mediumblob NULL NO select,insert,update,references +longblob_col longblob NULL NO select,insert,update,references +options enum('one','two','tree') latin1_swedish_ci NO MUL one select,insert,update,references +flags set('one','two','tree') latin1_swedish_ci NO select,insert,update,references show keys from t1; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment t1 0 PRIMARY 1 auto A 0 NULL NULL BTREE @@ -208,56 +208,56 @@ Warning 1265 Data truncated for column 'options' at row 6 update t2 set string="changed" where auto=16; show full columns from t1; Field Type Collation Null Key Default Extra Privileges Comment -auto int(5) unsigned NULL MUL NULL auto_increment select,insert,update,references +auto int(5) unsigned NULL NO MUL NULL auto_increment select,insert,update,references string char(10) latin1_swedish_ci YES new defaul select,insert,update,references -tiny tinyint(4) NULL MUL 0 select,insert,update,references -short smallint(6) NULL MUL 0 select,insert,update,references -medium mediumint(8) NULL MUL 0 select,insert,update,references -long_int int(11) NULL 0 select,insert,update,references -longlong bigint(13) NULL MUL 0 select,insert,update,references -real_float float(13,1) NULL MUL 0.0 select,insert,update,references +tiny tinyint(4) NULL NO MUL 0 select,insert,update,references +short smallint(6) NULL NO MUL 0 select,insert,update,references +medium mediumint(8) NULL NO MUL 0 select,insert,update,references +long_int int(11) NULL NO 0 select,insert,update,references +longlong bigint(13) NULL NO MUL 0 select,insert,update,references +real_float float(13,1) NULL NO MUL 0.0 select,insert,update,references real_double double(16,4) NULL YES NULL select,insert,update,references -utiny tinyint(3) unsigned NULL 0 select,insert,update,references -ushort smallint(5) unsigned zerofill NULL 00000 select,insert,update,references -umedium mediumint(8) unsigned NULL MUL 0 select,insert,update,references -ulong int(11) unsigned NULL MUL 0 select,insert,update,references -ulonglong bigint(13) unsigned NULL MUL 0 select,insert,update,references +utiny tinyint(3) unsigned NULL NO 0 select,insert,update,references +ushort smallint(5) unsigned zerofill NULL NO 00000 select,insert,update,references +umedium mediumint(8) unsigned NULL NO MUL 0 select,insert,update,references +ulong int(11) unsigned NULL NO MUL 0 select,insert,update,references +ulonglong bigint(13) unsigned NULL NO MUL 0 select,insert,update,references time_stamp timestamp NULL YES CURRENT_TIMESTAMP select,insert,update,references date_field char(10) latin1_swedish_ci YES NULL select,insert,update,references time_field time NULL YES NULL select,insert,update,references date_time datetime NULL YES NULL select,insert,update,references new_blob_col varchar(20) latin1_swedish_ci YES NULL select,insert,update,references tinyblob_col tinyblob NULL YES NULL select,insert,update,references -mediumblob_col mediumblob NULL select,insert,update,references -options enum('one','two','tree') latin1_swedish_ci MUL one select,insert,update,references -flags set('one','two','tree') latin1_swedish_ci select,insert,update,references -new_field char(10) latin1_swedish_ci new select,insert,update,references +mediumblob_col mediumblob NULL NO select,insert,update,references +options enum('one','two','tree') latin1_swedish_ci NO MUL one select,insert,update,references +flags set('one','two','tree') latin1_swedish_ci NO select,insert,update,references +new_field char(10) latin1_swedish_ci NO new select,insert,update,references show full columns from t2; Field Type Collation Null Key Default Extra Privileges Comment -auto int(5) unsigned NULL 0 select,insert,update,references +auto int(5) unsigned NULL NO 0 select,insert,update,references string char(10) latin1_swedish_ci YES new defaul select,insert,update,references -tiny tinyint(4) NULL 0 select,insert,update,references -short smallint(6) NULL 0 select,insert,update,references -medium mediumint(8) NULL 0 select,insert,update,references -long_int int(11) NULL 0 select,insert,update,references -longlong bigint(13) NULL 0 select,insert,update,references -real_float float(13,1) NULL 0.0 select,insert,update,references +tiny tinyint(4) NULL NO 0 select,insert,update,references +short smallint(6) NULL NO 0 select,insert,update,references +medium mediumint(8) NULL NO 0 select,insert,update,references +long_int int(11) NULL NO 0 select,insert,update,references +longlong bigint(13) NULL NO 0 select,insert,update,references +real_float float(13,1) NULL NO 0.0 select,insert,update,references real_double double(16,4) NULL YES NULL select,insert,update,references -utiny tinyint(3) unsigned NULL 0 select,insert,update,references -ushort smallint(5) unsigned zerofill NULL 00000 select,insert,update,references -umedium mediumint(8) unsigned NULL 0 select,insert,update,references -ulong int(11) unsigned NULL 0 select,insert,update,references -ulonglong bigint(13) unsigned NULL 0 select,insert,update,references +utiny tinyint(3) unsigned NULL NO 0 select,insert,update,references +ushort smallint(5) unsigned zerofill NULL NO 00000 select,insert,update,references +umedium mediumint(8) unsigned NULL NO 0 select,insert,update,references +ulong int(11) unsigned NULL NO 0 select,insert,update,references +ulonglong bigint(13) unsigned NULL NO 0 select,insert,update,references time_stamp timestamp NULL YES 0000-00-00 00:00:00 select,insert,update,references date_field char(10) latin1_swedish_ci YES NULL select,insert,update,references time_field time NULL YES NULL select,insert,update,references date_time datetime NULL YES NULL select,insert,update,references new_blob_col varchar(20) latin1_swedish_ci YES NULL select,insert,update,references tinyblob_col tinyblob NULL YES NULL select,insert,update,references -mediumblob_col mediumblob NULL select,insert,update,references -options enum('one','two','tree') latin1_swedish_ci one select,insert,update,references -flags set('one','two','tree') latin1_swedish_ci select,insert,update,references -new_field char(10) latin1_swedish_ci new select,insert,update,references +mediumblob_col mediumblob NULL NO select,insert,update,references +options enum('one','two','tree') latin1_swedish_ci NO one select,insert,update,references +flags set('one','two','tree') latin1_swedish_ci NO select,insert,update,references +new_field char(10) latin1_swedish_ci NO new select,insert,update,references select t1.auto,t2.auto from t1,t2 where t1.auto=t2.auto and ((t1.string<>t2.string and (t1.string is not null or t2.string is not null)) or (t1.tiny<>t2.tiny and (t1.tiny is not null or t2.tiny is not null)) or (t1.short<>t2.short and (t1.short is not null or t2.short is not null)) or (t1.medium<>t2.medium and (t1.medium is not null or t2.medium is not null)) or (t1.long_int<>t2.long_int and (t1.long_int is not null or t2.long_int is not null)) or (t1.longlong<>t2.longlong and (t1.longlong is not null or t2.longlong is not null)) or (t1.real_float<>t2.real_float and (t1.real_float is not null or t2.real_float is not null)) or (t1.real_double<>t2.real_double and (t1.real_double is not null or t2.real_double is not null)) or (t1.utiny<>t2.utiny and (t1.utiny is not null or t2.utiny is not null)) or (t1.ushort<>t2.ushort and (t1.ushort is not null or t2.ushort is not null)) or (t1.umedium<>t2.umedium and (t1.umedium is not null or t2.umedium is not null)) or (t1.ulong<>t2.ulong and (t1.ulong is not null or t2.ulong is not null)) or (t1.ulonglong<>t2.ulonglong and (t1.ulonglong is not null or t2.ulonglong is not null)) or (t1.time_stamp<>t2.time_stamp and (t1.time_stamp is not null or t2.time_stamp is not null)) or (t1.date_field<>t2.date_field and (t1.date_field is not null or t2.date_field is not null)) or (t1.time_field<>t2.time_field and (t1.time_field is not null or t2.time_field is not null)) or (t1.date_time<>t2.date_time and (t1.date_time is not null or t2.date_time is not null)) or (t1.new_blob_col<>t2.new_blob_col and (t1.new_blob_col is not null or t2.new_blob_col is not null)) or (t1.tinyblob_col<>t2.tinyblob_col and (t1.tinyblob_col is not null or t2.tinyblob_col is not null)) or (t1.mediumblob_col<>t2.mediumblob_col and (t1.mediumblob_col is not null or t2.mediumblob_col is not null)) or (t1.options<>t2.options and (t1.options is not null or t2.options is not null)) or (t1.flags<>t2.flags and (t1.flags is not null or t2.flags is not null)) or (t1.new_field<>t2.new_field and (t1.new_field is not null or t2.new_field is not null))); auto auto 16 16 @@ -268,15 +268,15 @@ drop table t2; create table t2 (primary key (auto)) select auto+1 as auto,1 as t1, 'a' as t2, repeat('a',256) as t3, binary repeat('b',256) as t4, repeat('a',4096) as t5, binary repeat('b',4096) as t6, '' as t7, binary '' as t8 from t1; show full columns from t2; Field Type Collation Null Key Default Extra Privileges Comment -auto bigint(17) unsigned NULL PRI 0 select,insert,update,references -t1 bigint(1) NULL 0 select,insert,update,references -t2 varchar(1) latin1_swedish_ci select,insert,update,references -t3 varchar(256) latin1_swedish_ci select,insert,update,references -t4 varbinary(256) NULL select,insert,update,references -t5 longtext latin1_swedish_ci select,insert,update,references -t6 longblob NULL select,insert,update,references -t7 char(0) latin1_swedish_ci select,insert,update,references -t8 binary(0) NULL select,insert,update,references +auto bigint(17) unsigned NULL NO PRI 0 select,insert,update,references +t1 bigint(1) NULL NO 0 select,insert,update,references +t2 varchar(1) latin1_swedish_ci NO select,insert,update,references +t3 varchar(256) latin1_swedish_ci NO select,insert,update,references +t4 varbinary(256) NULL NO select,insert,update,references +t5 longtext latin1_swedish_ci NO select,insert,update,references +t6 longblob NULL NO select,insert,update,references +t7 char(0) latin1_swedish_ci NO select,insert,update,references +t8 binary(0) NULL NO select,insert,update,references select t1,t2,length(t3),length(t4),length(t5),length(t6),t7,t8 from t2; t1 t2 length(t3) length(t4) length(t5) length(t6) t7 t8 1 a 256 256 4096 4096 @@ -297,7 +297,7 @@ show full columns from t3; Field Type Collation Null Key Default Extra Privileges Comment c1 int(11) NULL YES NULL select,insert,update,references c2 int(11) NULL YES NULL select,insert,update,references -const bigint(1) NULL 0 select,insert,update,references +const bigint(1) NULL NO 0 select,insert,update,references drop table t1,t2,t3; create table t1 ( myfield INT NOT NULL, UNIQUE INDEX (myfield), unique (myfield), index(myfield)); drop table t1; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 1f44ad49812..123967f1c4a 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -34,8 +34,8 @@ select table_name from information_schema.TABLES where table_schema = "testtets" and table_name like "t%"; select * from information_schema.STATISTICS where TABLE_SCHEMA = "testtets"; -show keys * where TABLE_SCHEMA Like "test%"; -show keys where INDEX_NAME = "a_data"; +show keys * from t3 where TABLE_SCHEMA Like "test%"; +show keys from t3 where INDEX_NAME = "a_data"; show tables like 't%'; --replace_column 15 # 16 # @@ -48,7 +48,7 @@ show full columns from mysql.db like "Insert%"; show full columns from v1; select * from information_schema.COLUMNS where table_name="t1" and column_name= "a"; -show columns * where table_name = "t1"; +show columns * from testtets.t1 where table_name = "t1"; drop view v1; drop tables testtets.t4, testtets.t1, t2, t3; @@ -198,7 +198,7 @@ create procedure px5 () begin declare v int; declare c cursor for select version from -information_schema.tables; +information_schema.tables where table_schema <> 'information_schema'; open c; fetch c into v; select v; @@ -250,10 +250,6 @@ from information_schema.columns where table_name = 'proc'; select * from t115; drop table t115; -create view vk as select count(*) from information_schema.tables a; -select * from vk; -drop view vk; - delimiter //; create procedure p108 () begin declare c cursor for select data_type from information_schema.columns; open c; open c; end;// @@ -274,3 +270,19 @@ TABLE_NAME= "vo"; select * from information_schema.KEY_COLUMN_USAGE where TABLE_NAME= "vo"; drop view vo; + +select TABLE_NAME,TABLE_TYPE,ENGINE +from information_schema.tables +where table_schema='information_schema' limit 2; +show tables from information_schema like "t%"; + +--error 1007 +create database information_schema; +use information_schema; +show full tables like "T%"; +--error 1109 +create table t1(a int); +use test; +show tables; +use information_schema; +show tables like "T%"; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 8be915b89e2..b0e4672c570 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -4670,9 +4670,11 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) if (!(user=grant_table->user)) user= ""; ulong table_access= grant_table->privs; - if (table_access != 0) + if (table_access) { ulong test_access= table_access & ~GRANT_ACL; + if (!test_access && grant_table->cols) + continue; if (!(table_access & GRANT_ACL)) is_grantable= "NO"; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 3e606029bec..ea81013a401 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -389,6 +389,13 @@ bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, uint create_options= create_info ? create_info->options : 0; uint path_len; DBUG_ENTER("mysql_create_db"); + + /* do not create 'information_schema' db */ + if (!my_strcasecmp(system_charset_info, db, information_schema_name.str)) + { + my_error(ER_DB_CREATE_EXISTS, MYF(0), db); + DBUG_RETURN(-1); + } VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); @@ -1015,6 +1022,7 @@ bool mysql_change_db(THD *thd, const char *name) char *dbname=my_strdup((char*) name,MYF(MY_WME)); char path[FN_REFLEN]; HA_CREATE_INFO create; + bool schema_db= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; #endif @@ -1034,6 +1042,15 @@ bool mysql_change_db(THD *thd, const char *name) DBUG_RETURN(1); } DBUG_PRINT("info",("Use database: %s", dbname)); + if (!my_strcasecmp(system_charset_info, dbname, information_schema_name.str)) + { + schema_db= 1; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + db_access= SELECT_ACL; +#endif + goto end; + } + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (test_all_bits(thd->master_access,DB_ACLS)) db_access=DB_ACLS; @@ -1064,6 +1081,7 @@ bool mysql_change_db(THD *thd, const char *name) my_free(dbname,MYF(0)); DBUG_RETURN(1); } +end: send_ok(thd); x_free(thd->db); thd->db=dbname; // THD::~THD will free this @@ -1071,11 +1089,19 @@ bool mysql_change_db(THD *thd, const char *name) #ifndef NO_EMBEDDED_ACCESS_CHECKS thd->db_access=db_access; #endif - strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); - thd->db_charset= create.default_table_charset ? - create.default_table_charset : - thd->variables.collation_server; - thd->variables.collation_database= thd->db_charset; + if (schema_db) + { + thd->db_charset= system_charset_info; + thd->variables.collation_database= system_charset_info; + } + else + { + strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); + load_db_opt(thd, path, &create); + thd->db_charset= create.default_table_charset ? + create.default_table_charset : + thd->variables.collation_server; + thd->variables.collation_database= thd->db_charset; + } DBUG_RETURN(0); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 7acbe564f58..0095d38ad94 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1941,6 +1941,40 @@ static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table) } +int schema_db_add(THD *thd, List *files, const char *wild) +{ + if (wild && wild_compare(information_schema_name.str, wild, 0)) + return 0; + if (files->push_back(thd->strdup(information_schema_name.str))) + return -1; + return 1; +} + + +int schema_tables_add(THD *thd, List *files, const char *wild) +{ + ST_SCHEMA_TABLE *tmp_schema_table= schema_tables; + for ( ; tmp_schema_table->table_name; tmp_schema_table++) + { + if (wild) + { + if (lower_case_table_names) + { + if (wild_case_compare(files_charset_info, + tmp_schema_table->table_name, + wild)) + continue; + } + else if (wild_compare(tmp_schema_table->table_name, wild, 0)) + continue; + } + if (files->push_back(thd->strdup(tmp_schema_table->table_name))) + return 1; + } + return 0; +} + + int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) { LEX *lex= thd->lex; @@ -1970,8 +2004,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) SELECT_LEX sel; INDEX_FIELD_VALUES idx_field_vals; - char path[FN_REFLEN], *end, *base_name, *file_name; - uint len; + char path[FN_REFLEN], *end= 0, *base_name, *file_name; + uint len= 0; + int with_i_schema; List bases; lex->all_selects_list= &sel; enum enum_schema_tables schema_table_idx= @@ -1980,6 +2015,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (schema_table_idx == SCH_TABLES) lock_type= TL_READ; get_index_field_values(lex, &idx_field_vals); + + /* information schema name always is first in list */ + with_i_schema= schema_db_add(thd, &bases, idx_field_vals.db_value); + if (with_i_schema < 0) + return 1; + if (mysql_find_files(thd, &bases, NullS, mysql_data_home, idx_field_vals.db_value, 1)) return 1; @@ -1995,19 +2036,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) (base_name= select_lex->db) && !bases.elements)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!check_access(thd,SELECT_ACL, base_name, &thd->col_access,0,1) || + if (with_i_schema || // don't check the rights if information schema db + !check_access(thd,SELECT_ACL, base_name, &thd->col_access,0,1) || thd->master_access & (DB_ACLS | SHOW_DB_ACL) || acl_get(thd->host, thd->ip, thd->priv_user, base_name,0) || (grant_option && !check_grant_db(thd, base_name))) #endif { List files; - strxmov(path, mysql_data_home, "/", base_name, NullS); - end= path + (len= unpack_dirname(path,path)); - len= FN_LEN - len; - if (mysql_find_files(thd, &files, base_name, - path, idx_field_vals.table_value, 0)) - DBUG_RETURN(1); + if (with_i_schema) // information schema table names + { + if (schema_tables_add(thd, &files, idx_field_vals.table_value)) + DBUG_RETURN(1); + } + else + { + strxmov(path, mysql_data_home, "/", base_name, NullS); + end= path + (len= unpack_dirname(path,path)); + len= FN_LEN - len; + if (mysql_find_files(thd, &files, base_name, + path, idx_field_vals.table_value, 0)) + DBUG_RETURN(1); + } List_iterator_fast it(files); while ((file_name=it++)) @@ -2023,20 +2073,27 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) { if (lex->verbose || lex->orig_sql_command == SQLCOM_END) { - my_snprintf(end, len, "/%s%s", file_name, reg_ext); - switch (mysql_frm_type(path)) + if (with_i_schema) { - case FRMTYPE_ERROR: - table->field[3]->store("ERROR", 5, system_charset_info); - break; - case FRMTYPE_TABLE: - table->field[3]->store("BASE TABLE", 10, system_charset_info); - break; - case FRMTYPE_VIEW: - table->field[3]->store("VIEW", 4, system_charset_info); - break; - default: - DBUG_ASSERT(0); + table->field[3]->store("TEMPORARY", 9, system_charset_info); + } + else + { + my_snprintf(end, len, "/%s%s", file_name, reg_ext); + switch (mysql_frm_type(path)) + { + case FRMTYPE_ERROR: + table->field[3]->store("ERROR", 5, system_charset_info); + break; + case FRMTYPE_TABLE: + table->field[3]->store("BASE TABLE", 10, system_charset_info); + break; + case FRMTYPE_VIEW: + table->field[3]->store("VIEW", 4, system_charset_info); + break; + default: + DBUG_ASSERT(0); + } } } table->file->write_row(table->record[0]); @@ -2059,6 +2116,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) } } } + with_i_schema= 0; } } lex->all_selects_list= select_lex; @@ -2066,6 +2124,16 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) } +void store_schema_shemata(TABLE *table, const char *db_name, + const char* cs_name) +{ + restore_record(table, default_values); + table->field[1]->store(db_name, strlen(db_name), system_charset_info); + table->field[2]->store(cs_name, strlen(cs_name), system_charset_info); + table->file->write_row(table->record[0]); +} + + int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) { char path[FN_REFLEN],*end; @@ -2074,16 +2142,26 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) List files; char *file_name; uint length; + int with_i_schema; HA_CREATE_INFO create; TABLE *table= tables->table; get_index_field_values(thd->lex, &idx_field_vals); + with_i_schema= schema_db_add(thd, &files, idx_field_vals.db_value); + if (with_i_schema < 0) + return 1; if (mysql_find_files(thd, &files, NullS, mysql_data_home, idx_field_vals.db_value, 1)) return 1; List_iterator_fast it(files); while ((file_name=it++)) { + if (with_i_schema) // information schema name is always first in list + { + store_schema_shemata(table, file_name, system_charset_info->csname); + with_i_schema= 0; + continue; + } #ifndef NO_EMBEDDED_ACCESS_CHECKS if (thd->master_access & (DB_ACLS | SHOW_DB_ACL) || acl_get(thd->host, thd->ip, thd->priv_user, file_name,0) || @@ -2103,12 +2181,8 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) path[length-1]= FN_LIBCHAR; strmov(path+length, MY_DB_OPT_FILE); load_db_opt(thd, path, &create); - restore_record(table, default_values); - table->field[1]->store(file_name, strlen(file_name), system_charset_info); - table->field[2]->store(create.default_table_charset->csname, - strlen(create.default_table_charset->csname), - system_charset_info); - table->file->write_row(table->record[0]); + store_schema_shemata(table, file_name, + create.default_table_charset->csname); } } return 0; @@ -2147,7 +2221,11 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, TABLE *show_table= tables->table; handler *file= show_table->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK); - table->field[3]->store("BASE TABLE", 10, cs); + if (table->tmp_table == TMP_TABLE) + table->field[3]->store("TEMPORARY", 9, cs); + else + table->field[3]->store("BASE TABLE", 10, cs); + for (int i= 4; i < 20; i++) { if ((i > 12 && i < 17) || i == 18) @@ -2341,7 +2419,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, } pos=(byte*) ((flags & NOT_NULL_FLAG) && field->type() != FIELD_TYPE_TIMESTAMP ? - "" : "YES"); + "NO" : "YES"); table->field[6]->store((const char*) pos, strlen((const char*) pos), cs); if (field->has_charset()) @@ -2741,12 +2819,26 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, } +void store_constarints(TABLE *table, const char*db, const char *tname, + const char *key_name, uint key_len, + const char *con_type, uint con_len) +{ + CHARSET_INFO *cs= system_charset_info; + restore_record(table, default_values); + table->field[1]->store(db, strlen(db), cs); + table->field[2]->store(key_name, key_len, cs); + table->field[3]->store(db, strlen(db), cs); + table->field[4]->store(tname, strlen(tname), cs); + table->field[5]->store(con_type, con_len, cs); + table->file->write_row(table->record[0]); +} + + static int get_schema_constarints_record(THD *thd, struct st_table_list *tables, TABLE *table, bool res, const char *base_name, const char *file_name) { - CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_schema_constarints_record"); if (!res && !tables->view) { @@ -2760,17 +2852,14 @@ static int get_schema_constarints_record(THD *thd, struct st_table_list *tables, for (uint i=0 ; i < show_table->keys ; i++, key_info++) { if (i != primary_key && !(key_info->flags & HA_NOSAME)) - continue; - restore_record(table, default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[2]->store(key_info->name, strlen(key_info->name), cs); - table->field[3]->store(base_name, strlen(base_name), cs); - table->field[4]->store(file_name, strlen(file_name), cs); + continue; + if (i == primary_key && !strcmp(key_info->name, primary_key_name)) - table->field[5]->store("PRIMARY KEY", 11, cs); + store_constarints(table, base_name, file_name, key_info->name, + strlen(key_info->name), "PRIMARY KEY", 11); else if (key_info->flags & HA_NOSAME) - table->field[5]->store("UNIQUE", 6, cs); - table->file->write_row(table->record[0]); + store_constarints(table, base_name, file_name, key_info->name, + strlen(key_info->name), "UNIQUE", 6); } show_table->file->get_foreign_key_list(thd, &f_key_list); @@ -2778,23 +2867,28 @@ static int get_schema_constarints_record(THD *thd, struct st_table_list *tables, List_iterator_fast it(f_key_list); while ((f_key_info=it++)) { - restore_record(table, default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[2]->store(f_key_info->forein_id->str, - f_key_info->forein_id->length, cs); - table->field[3]->store(base_name, strlen(base_name), cs); - table->field[4]->store(file_name, strlen(file_name), cs); - table->field[5]->store("FOREIGN KEY", 11, system_charset_info); - table->field[6]->store(f_key_info->constraint_method->str, - f_key_info->constraint_method->length, cs); - table->field[6]->set_notnull(); - table->file->write_row(table->record[0]); + store_constarints(table, base_name, file_name, f_key_info->forein_id->str, + strlen(f_key_info->forein_id->str), "FOREIGN KEY", 11); } } DBUG_RETURN(res); } +void store_key_column_usage(TABLE *table, const char*db, const char *tname, + const char *key_name, uint key_len, + const char *con_type, uint con_len, longlong idx) +{ + CHARSET_INFO *cs= system_charset_info; + table->field[1]->store(db, strlen(db), cs); + table->field[2]->store(key_name, key_len, cs); + table->field[4]->store(db, strlen(db), cs); + table->field[5]->store(tname, strlen(tname), cs); + table->field[6]->store(con_type, con_len, cs); + table->field[7]->store((longlong) idx); +} + + static int get_schema_key_column_usage_record(THD *thd, struct st_table_list *tables, TABLE *table, bool res, @@ -2825,13 +2919,12 @@ static int get_schema_key_column_usage_record(THD *thd, { f_idx++; restore_record(table, default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[2]->store(key_info->name, strlen(key_info->name), cs); - table->field[4]->store(base_name, strlen(base_name), cs); - table->field[5]->store(file_name, strlen(file_name), cs); - table->field[6]->store(key_part->field->field_name, - strlen(key_part->field->field_name), cs); - table->field[7]->store((longlong) f_idx); + store_key_column_usage(table, base_name, file_name, + key_info->name, + strlen(key_info->name), + key_part->field->field_name, + strlen(key_part->field->field_name), + (longlong) f_idx); table->file->write_row(table->record[0]); } } @@ -2851,21 +2944,21 @@ static int get_schema_key_column_usage_record(THD *thd, r_info= it1++; f_idx++; restore_record(table, default_values); - table->field[1]->store(base_name, strlen(base_name), cs); - table->field[2]->store(f_key_info->forein_id->str, - f_key_info->forein_id->length, cs); - table->field[4]->store(base_name, strlen(base_name), cs); - table->field[5]->store(file_name, strlen(file_name), cs); - table->field[6]->store(f_info->str, f_info->length, cs); - table->field[7]->store((longlong) f_idx); - table->field[8]->store(f_key_info->referenced_db->str, + store_key_column_usage(table, base_name, file_name, + f_key_info->forein_id->str, + f_key_info->forein_id->length, + f_info->str, f_info->length, + (longlong) f_idx); + table->field[8]->store((longlong) f_idx); + table->field[8]->set_notnull(); + table->field[9]->store(f_key_info->referenced_db->str, f_key_info->referenced_db->length, cs); table->field[9]->set_notnull(); table->field[10]->store(f_key_info->referenced_table->str, f_key_info->referenced_table->length, cs); - table->field[9]->set_notnull(); - table->field[10]->store(r_info->str, r_info->length, cs); table->field[10]->set_notnull(); + table->field[11]->store(r_info->str, r_info->length, cs); + table->field[11]->set_notnull(); table->file->write_row(table->record[0]); } } @@ -3466,7 +3559,6 @@ ST_FIELD_INFO table_constraints_fields_info[]= {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"CONSTRAINT_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CONSTRAINT_METHOD", 20, MYSQL_TYPE_STRING, 0, 1, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -3481,6 +3573,7 @@ ST_FIELD_INFO key_column_usage_fields_info[]= {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONG, 0, 0, 0}, + {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONG, 0, 1, 0}, {"REFERENCED_TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"REFERENCED_TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"REFERENCED_COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 8b3cdda5d9e..3511777dd27 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -670,7 +670,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_table_alias %type - table_ident table_ident_nodb references from_table_ident + table_ident table_ident_nodb references %type remember_name remember_end opt_ident opt_db text_or_password @@ -5902,14 +5902,14 @@ show_param: | ENGINE_SYM storage_engines { Lex->create_info.db_type= $2; } show_engine_param - | opt_full COLUMNS ext_select_item_list from_table_ident opt_db wild_and_where + | opt_full COLUMNS ext_select_item_list from_or_in table_ident opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_FIELDS; - if ($5) - $4->change_db($5); - if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) + if ($6) + $5->change_db($6); + if (prepare_schema_table(YYTHD, lex, $5, SCH_COLUMNS)) YYABORT; } | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ @@ -5935,14 +5935,14 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; } opt_limit_clause_init - | keys_or_index ext_select_item_list from_table_ident opt_db where_clause + | keys_or_index ext_select_item_list from_or_in table_ident opt_db where_clause { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_KEYS; - if ($4) - $3->change_db($4); - if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS)) + if ($5) + $4->change_db($5); + if (prepare_schema_table(YYTHD, lex, $4, SCH_STATISTICS)) YYABORT; } | COLUMN_SYM TYPES_SYM @@ -6159,11 +6159,6 @@ binlog_from: /* empty */ { Lex->mi.pos = 4; /* skip magic number */ } | FROM ulonglong_num { Lex->mi.pos = $2; }; -from_table_ident: - /* empty */ { $$= 0; } - | from_or_in table_ident { $$= $2; } - ; - wild_and_where: /* empty */ | LIKE TEXT_STRING_sys From d4076cec4b8381ad171d7811461e1f22e1c0c7ed Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Dec 2004 10:49:33 +0100 Subject: [PATCH 34/52] ndb - bitfields ndbapi test prg + bug fixes ndb/src/common/util/Bitmask.cpp: Bug fixes + better unit test ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp: fix ndb/test/ndbapi/testBitfield.cpp: impl. create_random_table and transactions ndb/test/src/HugoCalculator.cpp: Only use "var" size when var-size --- ndb/src/common/util/Bitmask.cpp | 48 +++++++++++------ ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp | 2 +- ndb/test/ndbapi/testBitfield.cpp | 51 ++++++++++++++++++- ndb/test/src/HugoCalculator.cpp | 29 +++++++---- 4 files changed, 103 insertions(+), 27 deletions(-) diff --git a/ndb/src/common/util/Bitmask.cpp b/ndb/src/common/util/Bitmask.cpp index dde6111c32b..2499e016a13 100644 --- a/ndb/src/common/util/Bitmask.cpp +++ b/ndb/src/common/util/Bitmask.cpp @@ -17,6 +17,7 @@ void print(const Uint32 src[], Uint32 len, Uint32 pos = 0) } #ifndef __TEST_BITMASK__ + void BitmaskImpl::getFieldImpl(const Uint32 src[], unsigned shiftL, unsigned len, Uint32 dst[]) @@ -32,8 +33,15 @@ BitmaskImpl::getFieldImpl(const Uint32 src[], len -= 32; } - * dst++ |= (* src) << shiftL; - * dst = ((* src) >> shiftR) & ((1 << len) - 1) & undefined; + if(len < shiftR) + { + * dst |= ((* src) & ((1 << len) - 1)) << shiftL; + } + else + { + * dst++ |= ((* src) << shiftL); + * dst = ((* src) >> shiftR) & ((1 << (len - shiftR)) - 1) & undefined; + } } void @@ -57,10 +65,16 @@ BitmaskImpl::setFieldImpl(Uint32 dst[], Uint32 mask = ((1 << len) -1); * dst = (* dst & ~mask); - * dst |= ((* src++) >> shiftL) & mask; - * dst |= ((* src) << shiftR) & mask & undefined; + if(len < shiftR) + { + * dst |= ((* src++) >> shiftL) & mask; + } + else + { + * dst |= ((* src++) >> shiftL); + * dst |= ((* src) & ((1 << (len - shiftR)) - 1)) << shiftR ; + } } - #else #define DEBUG 0 @@ -121,7 +135,7 @@ static void rand(Uint32 dst[], Uint32 len) { for(int i = 0; i> 5, dst, i, (lrand() % 1000) > 500); + BitmaskImpl::set((len + 31) >> 5, dst, i, (lrand() % 1000) > 500); } static @@ -135,9 +149,9 @@ void simple(int pos, int size) const Uint32 sz = 4 * sz32; Uint32 zero = 0; - _mask.fill(sz32, zero); - _src.fill(sz32, zero); - _dst.fill(sz32, zero); + _mask.fill(sz32+1, zero); + _src.fill(sz32+1, zero); + _dst.fill(sz32+1, zero); Uint32 * src = _src.getBase(); Uint32 * dst = _dst.getBase(); @@ -149,18 +163,18 @@ void simple(int pos, int size) rand(src, size); BitmaskImpl::setField(sz32, mask, pos, size, src); BitmaskImpl::getField(sz32, mask, pos, size, dst); - printf("src: "); print(src, size); printf("\n"); - printf("msk: "); print(mask, sz32 << 5); printf("\n"); - printf("dst: "); print(dst, size); printf("\n"); - require(cmp(src, dst, size)); + printf("src: "); print(src, size+31); printf("\n"); + printf("msk: "); print(mask, (sz32 << 5) + 31); printf("\n"); + printf("dst: "); print(dst, size+31); printf("\n"); + require(cmp(src, dst, size+31)); }; static void do_test(int bitmask_size) { -#if 0 +#if 1 simple(rand() % 33, (rand() % 63)+1); -#else +//#else Vector alloc_list; bitmask_size = (bitmask_size + 31) & ~31; Uint32 sz32 = (bitmask_size >> 5); @@ -235,8 +249,10 @@ do_test(int bitmask_size) !BitmaskImpl::get(sz32, alloc_mask.getBase(), pos+free)) free++; - Uint32 sz = (lrand() % free); + Uint32 sz = + (free <= 64 && ((lrand() % 100) > 80)) ? free : (lrand() % free); sz = sz ? sz : 1; + sz = pos + sz == bitmask_size ? sz - 1 : sz; Alloc a; a.pos = pos; a.size = sz; diff --git a/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp b/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp index fc0982d96b8..5bcbb482a8c 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp @@ -1149,7 +1149,7 @@ Dbtup::updateBitsNULLable(Uint32* inBuffer, inBuffer+indexBuf+1); Uint32 newIndex = indexBuf + 1 + ((bitCount + 31) >> 5); - tInBufLen = newIndex; + tInBufIndex = newIndex; return true; } else { Uint32 newIndex = tInBufIndex + 1; diff --git a/ndb/test/ndbapi/testBitfield.cpp b/ndb/test/ndbapi/testBitfield.cpp index d176b952933..1438f65418b 100644 --- a/ndb/test/ndbapi/testBitfield.cpp +++ b/ndb/test/ndbapi/testBitfield.cpp @@ -3,6 +3,7 @@ #include #include #include +#include static const char* opt_connect_str= 0; static const char* _dbname = "TEST_DB"; @@ -129,13 +130,61 @@ static const NdbDictionary::Table* create_random_table(Ndb* pNdb) { + NdbDictionary::Table tab; + Uint32 cols = 1 + (rand() % (NDB_MAX_ATTRIBUTES_IN_TABLE - 1)); + Uint32 keys = NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY; + Uint32 length = 4096; + Uint32 key_size = NDB_MAX_KEYSIZE_IN_WORDS; + + BaseString name; + name.assfmt("TAB_%d", rand() & 65535); + tab.setName(name.c_str()); + for(int i = 0; i 0; i++) + { + NdbDictionary::Column col; + name.assfmt("COL_%d", i); + col.setName(name.c_str()); + if(i == 0 || i == 1) + { + col.setType(NdbDictionary::Column::Unsigned); + col.setLength(1); + col.setNullable(false); + col.setPrimaryKey(i == 0); + tab.addColumn(col); + continue; + } + + col.setType(NdbDictionary::Column::Bit); + + Uint32 len = 1 + (rand() % 128); //(length - 1)); + col.setLength(len); length -= len; + col.setNullable((rand() >> 16) & 1); + col.setPrimaryKey(false); + tab.addColumn(col); + } + + ndbout << (NDBT_Table&)tab << endl; + if(pNdb->getDictionary()->createTable(tab) == 0) + { + return pNdb->getDictionary()->getTable(tab.getName()); + } return 0; } + static int transactions(Ndb* pNdb, const NdbDictionary::Table* tab) { - return 0; + int i = 0; + HugoTransactions trans(* tab); + i |= trans.loadTable(pNdb, 1000); + i |= trans.pkReadRecords(pNdb, 1000, 13); + i |= trans.scanReadRecords(pNdb, 1000, 25); + i |= trans.pkUpdateRecords(pNdb, 1000, 37); + i |= trans.scanUpdateRecords(pNdb, 1000, 25); + i |= trans.pkDelRecords(pNdb, 500, 23); + i |= trans.clearTable(pNdb); + return i; } static diff --git a/ndb/test/src/HugoCalculator.cpp b/ndb/test/src/HugoCalculator.cpp index b2202303039..a885a2371f1 100644 --- a/ndb/test/src/HugoCalculator.cpp +++ b/ndb/test/src/HugoCalculator.cpp @@ -108,8 +108,14 @@ HugoCalculator::calcValue(int record, // Calculate length of the string to create. We want the string // length to be varied between max and min of this attribute. + Uint32 org = len; - len = val % (len + 1); + if(attr->getType() == NdbDictionary::Column::Varchar) + len = val % (len + 1); + else + if((val % (len + 1)) == 0) + len = 0; + // If len == 0 return NULL if this is a nullable attribute if (len == 0){ if(attr->getNullable() == true) @@ -120,6 +126,14 @@ HugoCalculator::calcValue(int record, for(i=0; i < len; i++) buf[i] = a[((val^i)%25)]; buf[len] = 0; + + if(attr->getType() == NdbDictionary::Column::Bit) + { + Uint32 bits= attr->getLength(); + Uint32 pos = bits >> 5; + Uint32 size = bits & 31; + ((Uint32*)buf)[pos] &= ((1 << size) - 1); + } } return buf; }; @@ -154,16 +168,13 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ result = -1; } } else{ - if (memcmp(res, pRow->attributeStore(i)->aRef(), pRow->attributeStore(i)->arraySize()) != 0){ + if (memcmp(res, pRow->attributeStore(i)->aRef(), len) != 0){ // if (memcmp(res, pRow->attributeStore(i)->aRef(), pRow->attributeStore(i)->getLength()) != 0){ - g_err << "arraySize(): " - << pRow->attributeStore(i)->arraySize() - << ", NdbDict::Column::getLength(): " << attr->getLength() - << endl; + g_err << "Column: " << attr->getName() << endl; const char* buf2 = pRow->attributeStore(i)->aRef(); - for (Uint32 j = 0; j < pRow->attributeStore(i)->arraySize(); j++) + for (Uint32 j = 0; j < len; j++) { - g_err << j << ":" << buf[j] << "[" << buf2[j] << "]"; + g_err << j << ":" << hex << (int)buf[j] << "[" << hex << (int)buf2[j] << "]"; if (buf[j] != buf2[j]) { g_err << "==>Match failed!"; @@ -172,7 +183,7 @@ HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{ } g_err << endl; g_err << "|- Invalid data found in attribute " << i << ": \"" - << pRow->attributeStore(i)->aRef() + << pRow->attributeStore(i)->aRef() << "\" != \"" << res << "\"" << endl << "Length of expected=" << (unsigned)strlen(res) << endl << "Lenght of read=" From 0404a607a569bf9797b79dd9fb74832789de9479 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Dec 2004 12:55:56 +0200 Subject: [PATCH 35/52] InnoDB: Reduce the stack space consumption of ON UPDATE CASCADE operations. innobase/include/lock0lock.h: Added lock_clust_rec_read_check_and_lock_alt(), a variant of lock_clust_rec_read_check_and_lock() without the parameter "offsets". innobase/lock/lock0lock.c: Added lock_clust_rec_read_check_and_lock_alt(), a variant of lock_clust_rec_read_check_and_lock() without the parameter "offsets". innobase/row/row0ins.c: row_ins_foreign_check_on_constraint(): Do not allocate offsets from stack. This reduces the stack space consumption of ON UPDATE CASCADE operations by 400 bytes per cascaded UPDATE operation. --- innobase/include/lock0lock.h | 27 +++++++++++++++++++++++ innobase/lock/lock0lock.c | 42 ++++++++++++++++++++++++++++++++++++ innobase/row/row0ins.c | 8 ++----- 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/innobase/include/lock0lock.h b/innobase/include/lock0lock.h index b99359fe998..e533ac08545 100644 --- a/innobase/include/lock0lock.h +++ b/innobase/include/lock0lock.h @@ -345,6 +345,33 @@ lock_clust_rec_read_check_and_lock( LOCK_REC_NOT_GAP */ que_thr_t* thr); /* in: query thread */ /************************************************************************* +Checks if locks of other transactions prevent an immediate read, or passing +over by a read cursor, of a clustered index record. If they do, first tests +if the query thread should anyway be suspended for some reason; if not, then +puts the transaction and the query thread to the lock wait state and inserts a +waiting request for a record lock to the lock queue. Sets the requested mode +lock on the record. This is an alternative version of +lock_clust_rec_read_check_and_lock() that does not require the parameter +"offsets". */ + +ulint +lock_clust_rec_read_check_and_lock_alt( +/*===================================*/ + /* out: DB_SUCCESS, DB_LOCK_WAIT, + DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ + ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set, + does nothing */ + rec_t* rec, /* in: user record or page supremum record + which should be read or passed over by a read + cursor */ + dict_index_t* index, /* in: clustered index */ + ulint mode, /* in: mode of the lock which the read cursor + should set on records: LOCK_S or LOCK_X; the + latter is possible in SELECT FOR UPDATE */ + ulint gap_mode,/* in: LOCK_ORDINARY, LOCK_GAP, or + LOCK_REC_NOT_GAP */ + que_thr_t* thr); /* in: query thread */ +/************************************************************************* Checks that a record is seen in a consistent read. */ ibool diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 961210dbd06..70075019389 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -5105,3 +5105,45 @@ lock_clust_rec_read_check_and_lock( return(err); } +/************************************************************************* +Checks if locks of other transactions prevent an immediate read, or passing +over by a read cursor, of a clustered index record. If they do, first tests +if the query thread should anyway be suspended for some reason; if not, then +puts the transaction and the query thread to the lock wait state and inserts a +waiting request for a record lock to the lock queue. Sets the requested mode +lock on the record. This is an alternative version of +lock_clust_rec_read_check_and_lock() that does not require the parameter +"offsets". */ + +ulint +lock_clust_rec_read_check_and_lock_alt( +/*===================================*/ + /* out: DB_SUCCESS, DB_LOCK_WAIT, + DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ + ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set, + does nothing */ + rec_t* rec, /* in: user record or page supremum record + which should be read or passed over by a read + cursor */ + dict_index_t* index, /* in: clustered index */ + ulint mode, /* in: mode of the lock which the read cursor + should set on records: LOCK_S or LOCK_X; the + latter is possible in SELECT FOR UPDATE */ + ulint gap_mode,/* in: LOCK_ORDINARY, LOCK_GAP, or + LOCK_REC_NOT_GAP */ + que_thr_t* thr) /* in: query thread */ +{ + mem_heap_t* tmp_heap = NULL; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + ulint ret; + + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &tmp_heap); + ret = lock_clust_rec_read_check_and_lock(flags, rec, index, + offsets, mode, gap_mode, thr); + if (tmp_heap) { + mem_heap_free(tmp_heap); + } + return(ret); +} diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 969c3341be3..8b19f9d9a11 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -717,8 +717,6 @@ row_ins_foreign_check_on_constraint( ulint i; trx_t* trx; mem_heap_t* tmp_heap = NULL; - ulint offsets_[100] = { 100, }; - ulint* offsets = offsets_; ut_a(thr && foreign && pcur && mtr); @@ -886,10 +884,8 @@ row_ins_foreign_check_on_constraint( we already have a normal shared lock on the appropriate gap if the search criterion was not unique */ - offsets = rec_get_offsets(clust_rec, clust_index, offsets, - ULINT_UNDEFINED, &tmp_heap); - err = lock_clust_rec_read_check_and_lock(0, clust_rec, - clust_index, offsets, LOCK_X, LOCK_REC_NOT_GAP, thr); + err = lock_clust_rec_read_check_and_lock_alt(0, clust_rec, + clust_index, LOCK_X, LOCK_REC_NOT_GAP, thr); } if (err != DB_SUCCESS) { From 0773520842b249b3cef63223bbb3fc4383b28d6c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Dec 2004 16:15:36 +0100 Subject: [PATCH 36/52] ndb - bitfields - more tests, now passes all api tests!! ndb/include/kernel/ndb_limits.h: #define for max null bits ndb/include/kernel/signaldata/TupFrag.hpp: Error code for too many nullbits ndb/include/util/Bitmask.hpp: Cosmetic fix ndb/src/common/util/Bitmask.cpp: handle (pos % 32) == 0 more unit tests ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp: Check bitmask overflow ndb/src/ndbapi/ndberror.c: New error code ndb/test/ndbapi/testBitfield.cpp: Update test program ndb/test/run-test/daily-basic-tests.txt: Add testBitfield to autotest --- ndb/include/kernel/ndb_limits.h | 1 + ndb/include/kernel/signaldata/TupFrag.hpp | 3 +- ndb/include/util/Bitmask.hpp | 2 +- ndb/src/common/util/Bitmask.cpp | 68 ++++++++++++++++++- ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp | 9 ++- ndb/src/ndbapi/ndberror.c | 3 +- ndb/test/ndbapi/testBitfield.cpp | 80 ++++++++++++----------- ndb/test/run-test/daily-basic-tests.txt | 4 ++ 8 files changed, 127 insertions(+), 43 deletions(-) diff --git a/ndb/include/kernel/ndb_limits.h b/ndb/include/kernel/ndb_limits.h index 48a56c019bb..4ed38a194e4 100644 --- a/ndb/include/kernel/ndb_limits.h +++ b/ndb/include/kernel/ndb_limits.h @@ -61,6 +61,7 @@ #define MAX_FIXED_KEY_LENGTH_IN_WORDS 8 #define MAX_KEY_SIZE_IN_WORDS 1023 #define MAX_FRM_DATA_SIZE 6000 +#define MAX_NULL_BITS 4096 #define MIN_ATTRBUF ((MAX_ATTRIBUTES_IN_TABLE/24) + 1) /* diff --git a/ndb/include/kernel/signaldata/TupFrag.hpp b/ndb/include/kernel/signaldata/TupFrag.hpp index c132b19c50a..fe543cd1004 100644 --- a/ndb/include/kernel/signaldata/TupFrag.hpp +++ b/ndb/include/kernel/signaldata/TupFrag.hpp @@ -145,7 +145,8 @@ public: STATIC_CONST( SignalLength = 2 ); enum ErrorCode { NoError = 0, - InvalidCharset = 743 + InvalidCharset = 743, + TooManyBitsUsed = 831 }; private: Uint32 userPtr; diff --git a/ndb/include/util/Bitmask.hpp b/ndb/include/util/Bitmask.hpp index a88c48b4cb3..ade57a5ee57 100644 --- a/ndb/include/util/Bitmask.hpp +++ b/ndb/include/util/Bitmask.hpp @@ -818,7 +818,7 @@ BitmaskImpl::getField(unsigned size, const Uint32 src[], src += (pos >> 5); Uint32 offset = pos & 31; - dst[0] = (* src >> offset) & (len >= 32 ? ~0 : (1 << len) - 1); + * dst = (* src >> offset) & (len >= 32 ? ~0 : (1 << len) - 1); if(offset + len <= 32) { diff --git a/ndb/src/common/util/Bitmask.cpp b/ndb/src/common/util/Bitmask.cpp index 2499e016a13..4169434483f 100644 --- a/ndb/src/common/util/Bitmask.cpp +++ b/ndb/src/common/util/Bitmask.cpp @@ -23,9 +23,12 @@ BitmaskImpl::getFieldImpl(const Uint32 src[], unsigned shiftL, unsigned len, Uint32 dst[]) { assert(shiftL < 32); - + unsigned shiftR = 32 - shiftL; unsigned undefined = shiftL ? ~0 : 0; + + * dst = shiftL ? * dst : 0; + while(len >= 32) { * dst++ |= (* src) << shiftL; @@ -169,6 +172,69 @@ void simple(int pos, int size) require(cmp(src, dst, size+31)); }; +static +void simple2(int size, int loops) +{ + ndbout_c("simple2 %d - ", size); + Vector _mask; + Vector _src; + Vector _dst; + + Uint32 sz32 = (size + 32) >> 5; + Uint32 sz = sz32 << 2; + + Uint32 zero = 0; + _mask.fill(sz32+1, zero); + _src.fill(sz32+1, zero); + _dst.fill(sz32+1, zero); + + Uint32 * src = _src.getBase(); + Uint32 * dst = _dst.getBase(); + Uint32 * mask = _mask.getBase(); + + Vector save; + for(int i = 0; icurrNullBit; + Uint32 bitCount = 0; if (AttributeDescriptor::getNullable(attrDescriptor)) { if (!AttributeDescriptor::getDynamic(attrDescriptor)) { @@ -342,7 +343,7 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal) else { ljam(); - Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor); + bitCount = AttributeDescriptor::getArraySize(attrDescriptor); fragOperPtr.p->currNullBit += bitCount; break; } @@ -351,6 +352,12 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal) ndbrequire(false); break; }//switch + if(nullBitPos + bitCount + 1 >= MAX_NULL_BITS) + { + terrorCode = TupAddAttrRef::TooManyBitsUsed; + addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId); + return; + } AttributeOffset::setOffset(attrDes2, attributePos); AttributeOffset::setNullFlagPos(attrDes2, nullBitPos); } else { diff --git a/ndb/src/ndbapi/ndberror.c b/ndb/src/ndbapi/ndberror.c index c0a6b6ba122..638996530e2 100644 --- a/ndb/src/ndbapi/ndberror.c +++ b/ndb/src/ndbapi/ndberror.c @@ -259,6 +259,7 @@ ErrorBundle ErrorCodes[] = { * Application error */ { 823, AE, "Too much attrinfo from application in tuple manager" }, + { 831, AE, "Too many nullable/bitfields in table definition" }, { 876, AE, "876" }, { 877, AE, "877" }, { 878, AE, "878" }, @@ -270,7 +271,7 @@ ErrorBundle ErrorCodes[] = { { 897, AE, "Update attempt of primary key via ndbcluster internal api (if this occurs via the MySQL server it is a bug, please report)" }, { 4256, AE, "Must call Ndb::init() before this function" }, { 4257, AE, "Tried to read too much - too many getValue calls" }, - + /** * Scan application errors */ diff --git a/ndb/test/ndbapi/testBitfield.cpp b/ndb/test/ndbapi/testBitfield.cpp index 1438f65418b..42439e20959 100644 --- a/ndb/test/ndbapi/testBitfield.cpp +++ b/ndb/test/ndbapi/testBitfield.cpp @@ -7,7 +7,7 @@ static const char* opt_connect_str= 0; static const char* _dbname = "TEST_DB"; -static int g_loops = 5; +static int g_loops = 7; static struct my_option my_long_options[] = { NDB_STD_OPTS("ndb_desc"), @@ -130,44 +130,48 @@ static const NdbDictionary::Table* create_random_table(Ndb* pNdb) { - NdbDictionary::Table tab; - Uint32 cols = 1 + (rand() % (NDB_MAX_ATTRIBUTES_IN_TABLE - 1)); - Uint32 keys = NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY; - Uint32 length = 4096; - Uint32 key_size = NDB_MAX_KEYSIZE_IN_WORDS; - - BaseString name; - name.assfmt("TAB_%d", rand() & 65535); - tab.setName(name.c_str()); - for(int i = 0; i 0; i++) - { - NdbDictionary::Column col; - name.assfmt("COL_%d", i); - col.setName(name.c_str()); - if(i == 0 || i == 1) - { - col.setType(NdbDictionary::Column::Unsigned); - col.setLength(1); - col.setNullable(false); - col.setPrimaryKey(i == 0); - tab.addColumn(col); - continue; - } - - col.setType(NdbDictionary::Column::Bit); + do { + NdbDictionary::Table tab; + Uint32 cols = 1 + (rand() % (NDB_MAX_ATTRIBUTES_IN_TABLE - 1)); + Uint32 keys = NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY; + Uint32 length = 4090; + Uint32 key_size = NDB_MAX_KEYSIZE_IN_WORDS; - Uint32 len = 1 + (rand() % 128); //(length - 1)); - col.setLength(len); length -= len; - col.setNullable((rand() >> 16) & 1); - col.setPrimaryKey(false); - tab.addColumn(col); - } - - ndbout << (NDBT_Table&)tab << endl; - if(pNdb->getDictionary()->createTable(tab) == 0) - { - return pNdb->getDictionary()->getTable(tab.getName()); - } + BaseString name; + name.assfmt("TAB_%d", rand() & 65535); + tab.setName(name.c_str()); + for(int i = 0; i 2; i++) + { + NdbDictionary::Column col; + name.assfmt("COL_%d", i); + col.setName(name.c_str()); + if(i == 0 || i == 1) + { + col.setType(NdbDictionary::Column::Unsigned); + col.setLength(1); + col.setNullable(false); + col.setPrimaryKey(i == 0); + tab.addColumn(col); + continue; + } + + col.setType(NdbDictionary::Column::Bit); + + Uint32 len = 1 + (rand() % (length - 1)); + col.setLength(len); length -= len; + int nullable = (rand() >> 16) & 1; + col.setNullable(nullable); length -= nullable; + col.setPrimaryKey(false); + tab.addColumn(col); + } + + pNdb->getDictionary()->dropTable(tab.getName()); + if(pNdb->getDictionary()->createTable(tab) == 0) + { + ndbout << (NDBT_Table&)tab << endl; + return pNdb->getDictionary()->getTable(tab.getName()); + } + } while(0); return 0; } diff --git a/ndb/test/run-test/daily-basic-tests.txt b/ndb/test/run-test/daily-basic-tests.txt index 8a927b88194..dce89766319 100644 --- a/ndb/test/run-test/daily-basic-tests.txt +++ b/ndb/test/run-test/daily-basic-tests.txt @@ -506,6 +506,10 @@ max-time: 2500 cmd: testOIBasic args: +max-time: 2500 +cmd: testBitfield +args: + # # # SYSTEM RESTARTS From 6bcee4f149bd8a3dba88d52c2021b8622453b2fe Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Dec 2004 18:25:12 +0300 Subject: [PATCH 37/52] fix according to review --- client/mysqldump.c | 4 ++-- sql/sql_acl.cc | 4 ++++ sql/sql_show.cc | 43 +++++++++++++++++++++++++++++++------------ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index 17c881d7f36..8fb48753a8c 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1936,8 +1936,8 @@ static int dump_databases(char **db_names) static int init_dumping(char *database) { if (mysql_get_server_version(sock) >= 50003 && - !strcmp(database, "information_schema")) - return 1; + !my_strcasecmp(&my_charset_latin1, database, "information_schema")) + return 1; if (mysql_select_db(sock, database)) { diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b0e4672c570..d33faffb2c2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -4673,6 +4673,10 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) if (table_access) { ulong test_access= table_access & ~GRANT_ACL; + /* + We should skip 'usage' privilege on table if + we have any privileges on column(s) of this table + */ if (!test_access && grant_table->cols) continue; if (!(table_access & GRANT_ACL)) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 0095d38ad94..7cc746793dc 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1941,13 +1941,33 @@ static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table) } -int schema_db_add(THD *thd, List *files, const char *wild) +/* + Add 'information_schema' name to db_names list + + SYNOPSIS + schema_db_add() + thd thread handler + files list of db names + wild wild string + with_i_schema returns 1 if we added 'IS' name to list + otherwise returns 0 + + RETURN + 1 error + 0 success +*/ + +int schema_db_add(THD *thd, List *files, + const char *wild, bool *with_i_schema) { - if (wild && wild_compare(information_schema_name.str, wild, 0)) - return 0; - if (files->push_back(thd->strdup(information_schema_name.str))) - return -1; - return 1; + *with_i_schema= 0; + if (!wild || !wild_compare(information_schema_name.str, wild, 0)) + { + *with_i_schema= 1; + if (files->push_back(thd->strdup(information_schema_name.str))) + return 1; + } + return 0; } @@ -2006,7 +2026,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) INDEX_FIELD_VALUES idx_field_vals; char path[FN_REFLEN], *end= 0, *base_name, *file_name; uint len= 0; - int with_i_schema; + bool with_i_schema; List bases; lex->all_selects_list= &sel; enum enum_schema_tables schema_table_idx= @@ -2017,8 +2037,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) get_index_field_values(lex, &idx_field_vals); /* information schema name always is first in list */ - with_i_schema= schema_db_add(thd, &bases, idx_field_vals.db_value); - if (with_i_schema < 0) + if (schema_db_add(thd, &bases, idx_field_vals.db_value, &with_i_schema)) return 1; if (mysql_find_files(thd, &bases, NullS, mysql_data_home, @@ -2142,13 +2161,13 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) List files; char *file_name; uint length; - int with_i_schema; + bool with_i_schema; HA_CREATE_INFO create; TABLE *table= tables->table; get_index_field_values(thd->lex, &idx_field_vals); - with_i_schema= schema_db_add(thd, &files, idx_field_vals.db_value); - if (with_i_schema < 0) + /* information schema name always is first in list */ + if (schema_db_add(thd, &files, idx_field_vals.db_value, &with_i_schema)) return 1; if (mysql_find_files(thd, &files, NullS, mysql_data_home, idx_field_vals.db_value, 1)) From 4d83b7a53a751937c7c88bff7a430f90604225ae Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Dec 2004 16:55:04 +0100 Subject: [PATCH 38/52] fix char(0) to use ndb bit format --- sql/ha_ndbcluster.cc | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index ae3f78fa5b8..06e2218cf7a 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -90,7 +90,7 @@ static int ndb_get_table_statistics(Ndb*, const char *, Dummy buffer to read zero pack_length fields which are mapped to 1 char */ -static byte dummy_buf[1]; +static uint32 dummy_buf; /* Error handling functions @@ -470,11 +470,15 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, if (ndb_supported_type(field->type())) { // ndb currently does not support size 0 - const byte *empty_field= ""; + uint32 empty_field; if (pack_len == 0) { - pack_len= 1; - field_ptr= empty_field; + pack_len= sizeof(empty_field); + field_ptr= (byte *)&empty_field; + if (field->is_null()) + empty_field= 0; + else + empty_field= 1; } if (! (field->flags & BLOB_FLAG)) { @@ -623,7 +627,7 @@ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field, if (field->pack_length() != 0) field_buf= buf + (field->ptr - table->record[0]); else - field_buf= dummy_buf; + field_buf= (byte *)&dummy_buf; m_value[fieldnr].rec= ndb_op->getValue(fieldnr, field_buf); DBUG_RETURN(m_value[fieldnr].rec == NULL); @@ -3329,19 +3333,22 @@ static int create_ndb_column(NDBCOL &col, break; // Char types case MYSQL_TYPE_STRING: - if (field->flags & BINARY_FLAG) - col.setType(NDBCOL::Binary); - else { - col.setType(NDBCOL::Char); - col.setCharset(cs); - } if (field->pack_length() == 0) { col.setType(NDBCOL::Bit); col.setLength(1); } - else + else if (field->flags & BINARY_FLAG) + { + col.setType(NDBCOL::Binary); col.setLength(field->pack_length()); + } + else + { + col.setType(NDBCOL::Char); + col.setCharset(cs); + col.setLength(field->pack_length()); + } break; case MYSQL_TYPE_VAR_STRING: if (field->flags & BINARY_FLAG) From a7fe1c393391f77f90a84ed2240542af3ca33ad4 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 Dec 2004 18:37:36 +0100 Subject: [PATCH 39/52] ndb: wl-1732 support all charsets ndb/include/kernel/AttributeDescriptor.hpp: wl-1732 support all charsets ndb/include/kernel/ndb_limits.h: wl-1732 support all charsets ndb/include/kernel/signaldata/TuxBound.hpp: wl-1732 support all charsets ndb/include/util/NdbSqlUtil.hpp: wl-1732 support all charsets ndb/src/common/util/NdbSqlUtil.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbacc/Dbacc.hpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbacc/DbaccInit.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbacc/DbaccMain.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbdict/Dbdict.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dblqh/DblqhMain.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtc/Dbtc.hpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtc/DbtcMain.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtup/Dbtup.hpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp: wl-1732 support all charsets ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp: wl-1732 support all charsets ndb/src/ndbapi/NdbDictionaryImpl.cpp: wl-1732 support all charsets ndb/src/ndbapi/NdbOperationDefine.cpp: wl-1732 support all charsets ndb/src/ndbapi/NdbOperationSearch.cpp: wl-1732 support all charsets ndb/src/ndbapi/NdbScanOperation.cpp: wl-1732 support all charsets ndb/src/ndbapi/ndberror.c: wl-1732 support all charsets ndb/test/ndbapi/Makefile.am: wl-1732 support all charsets ndb/test/ndbapi/testOIBasic.cpp: wl-1732 support all charsets ndb/tools/desc.cpp: wl-1732 support all charsets --- ndb/include/kernel/AttributeDescriptor.hpp | 12 + ndb/include/kernel/ndb_limits.h | 5 + ndb/include/kernel/signaldata/TuxBound.hpp | 4 +- ndb/include/util/NdbSqlUtil.hpp | 24 +- ndb/src/common/util/NdbSqlUtil.cpp | 647 ++++----- ndb/src/kernel/blocks/dbacc/Dbacc.hpp | 21 +- ndb/src/kernel/blocks/dbacc/DbaccInit.cpp | 1 + ndb/src/kernel/blocks/dbacc/DbaccMain.cpp | 143 +- ndb/src/kernel/blocks/dbdict/Dbdict.cpp | 29 +- ndb/src/kernel/blocks/dblqh/DblqhMain.cpp | 2 +- ndb/src/kernel/blocks/dbtc/Dbtc.hpp | 16 +- ndb/src/kernel/blocks/dbtc/DbtcMain.cpp | 101 +- ndb/src/kernel/blocks/dbtup/Dbtup.hpp | 20 +- ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp | 8 +- ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp | 9 +- ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp | 162 ++- ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp | 6 +- ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp | 36 +- ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp | 2 +- ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp | 23 +- ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp | 2 - ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp | 7 +- ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp | 110 +- ndb/src/ndbapi/NdbDictionaryImpl.cpp | 5 + ndb/src/ndbapi/NdbOperationDefine.cpp | 10 - ndb/src/ndbapi/NdbOperationSearch.cpp | 29 +- ndb/src/ndbapi/NdbScanOperation.cpp | 29 +- ndb/src/ndbapi/ndberror.c | 2 + ndb/test/ndbapi/Makefile.am | 4 +- ndb/test/ndbapi/testOIBasic.cpp | 1205 ++++++++++++----- ndb/tools/desc.cpp | 2 +- 31 files changed, 1642 insertions(+), 1034 deletions(-) diff --git a/ndb/include/kernel/AttributeDescriptor.hpp b/ndb/include/kernel/AttributeDescriptor.hpp index 071d45e2607..789325c7939 100644 --- a/ndb/include/kernel/AttributeDescriptor.hpp +++ b/ndb/include/kernel/AttributeDescriptor.hpp @@ -19,6 +19,8 @@ class AttributeDescriptor { friend class Dbdict; + friend class Dbtc; + friend class Dbacc; friend class Dbtup; friend class Dbtux; @@ -36,6 +38,7 @@ private: static Uint32 getType(const Uint32 &); static Uint32 getSize(const Uint32 &); + static Uint32 getSizeInBytes(const Uint32 &); static Uint32 getSizeInWords(const Uint32 &); static Uint32 getArrayType(const Uint32 &); static Uint32 getArraySize(const Uint32 &); @@ -79,6 +82,8 @@ private: #define AD_SIZE_SHIFT (4) #define AD_SIZE_MASK (7) +#define AD_SIZE_IN_BYTES_SHIFT (3) + #define AD_SIZE_IN_WORDS_OFFSET (31) #define AD_SIZE_IN_WORDS_SHIFT (5) @@ -185,6 +190,13 @@ AttributeDescriptor::getSize(const Uint32 & desc){ return (desc >> AD_SIZE_SHIFT) & AD_SIZE_MASK; } +inline +Uint32 +AttributeDescriptor::getSizeInBytes(const Uint32 & desc){ + return (getArraySize(desc) << getSize(desc)) + >> AD_SIZE_IN_BYTES_SHIFT; +} + inline Uint32 AttributeDescriptor::getSizeInWords(const Uint32 & desc){ diff --git a/ndb/include/kernel/ndb_limits.h b/ndb/include/kernel/ndb_limits.h index 4ed38a194e4..384882d6e5f 100644 --- a/ndb/include/kernel/ndb_limits.h +++ b/ndb/include/kernel/ndb_limits.h @@ -118,6 +118,11 @@ */ #define NDB_BLOB_HEAD_SIZE 2 /* sizeof(NdbBlob::Head) >> 2 */ +/* + * Character sets. + */ +#define MAX_XFRM_MULTIPLY 8 /* max expansion when normalizing */ + /* * Long signals */ diff --git a/ndb/include/kernel/signaldata/TuxBound.hpp b/ndb/include/kernel/signaldata/TuxBound.hpp index d829d6e82dd..7e12897407b 100644 --- a/ndb/include/kernel/signaldata/TuxBound.hpp +++ b/ndb/include/kernel/signaldata/TuxBound.hpp @@ -34,7 +34,9 @@ public: enum ErrorCode { InvalidAttrInfo = 4110, InvalidBounds = 4259, - OutOfBuffers = 873 + OutOfBuffers = 873, + InvalidCharFormat = 744, + TooMuchAttrInfo = 823 }; STATIC_CONST( SignalLength = 3 ); private: diff --git a/ndb/include/util/NdbSqlUtil.hpp b/ndb/include/util/NdbSqlUtil.hpp index 55fb76df354..3deaf81cfc7 100644 --- a/ndb/include/util/NdbSqlUtil.hpp +++ b/ndb/include/util/NdbSqlUtil.hpp @@ -37,17 +37,23 @@ public: const char* s2, unsigned n2, bool padded); /** - * Compare kernel attribute values. Returns -1, 0, +1 for less, - * equal, greater, respectively. Parameters are pointers to values, - * full attribute size in words, and size of available data in words. - * There is also pointer to type specific extra info. Char types - * receive CHARSET_INFO in it. + * Compare attribute values. Returns -1, 0, +1 for less, equal, + * greater, respectively. Parameters are pointers to values and their + * lengths in bytes. The lengths can differ. * - * If available size is less than full size, CmpUnknown may be - * returned. If a value cannot be parsed, it compares like NULL i.e. - * less than any valid value. + * First value is a full value but second value can be partial. If + * the partial value is not enough to determine the result, CmpUnknown + * will be returned. A shorter second value is not necessarily + * partial. Partial values are allowed only for types where prefix + * comparison is possible (basically, binary types). + * + * First parameter is a pointer to type specific extra info. Char + * types receive CHARSET_INFO in it. + * + * If a value cannot be parsed, it compares like NULL i.e. less than + * any valid value. */ - typedef int Cmp(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size); + typedef int Cmp(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full); enum CmpResult { CmpLess = -1, diff --git a/ndb/src/common/util/NdbSqlUtil.cpp b/ndb/src/common/util/NdbSqlUtil.cpp index 6798655a71a..36b5075ef1b 100644 --- a/ndb/src/common/util/NdbSqlUtil.cpp +++ b/ndb/src/common/util/NdbSqlUtil.cpp @@ -70,7 +70,7 @@ NdbSqlUtil::char_like(const char* s1, unsigned n1, return i1 == n2 && i2 == n2; } -/** +/* * Data types. */ @@ -138,7 +138,7 @@ NdbSqlUtil::m_typeList[] = { }, { Type::Varchar, - cmpVarchar + NULL // cmpVarchar }, { Type::Binary, @@ -146,23 +146,23 @@ NdbSqlUtil::m_typeList[] = { }, { Type::Varbinary, - cmpVarbinary + NULL // cmpVarbinary }, { Type::Datetime, - cmpDatetime + NULL // cmpDatetime }, { Type::Timespec, - cmpTimespec + NULL // cmpTimespec }, { Type::Blob, - cmpBlob + NULL // cmpBlob }, { Type::Text, - cmpText + NULL // cmpText } }; @@ -195,374 +195,299 @@ NdbSqlUtil::getTypeBinary(Uint32 typeId) return getType(typeId); } -// compare +/* + * Comparison functions. + */ int -NdbSqlUtil::cmpTinyint(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpTinyint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - union { Uint32 p[1]; Int8 v; } u1, u2; - u1.p[0] = p1[0]; - u2.p[0] = p2[0]; - if (u1.v < u2.v) - return -1; - if (u1.v > u2.v) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpTinyunsigned(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - union { Uint32 p[1]; Uint8 v; } u1, u2; - u1.p[0] = p1[0]; - u2.p[0] = p2[0]; - if (u1.v < u2.v) - return -1; - if (u1.v > u2.v) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpSmallint(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - union { Uint32 p[1]; Int16 v; } u1, u2; - u1.p[0] = p1[0]; - u2.p[0] = p2[0]; - if (u1.v < u2.v) - return -1; - if (u1.v > u2.v) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpSmallunsigned(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - union { Uint32 p[1]; Uint16 v; } u1, u2; - u1.p[0] = p1[0]; - u2.p[0] = p2[0]; - if (u1.v < u2.v) - return -1; - if (u1.v > u2.v) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpMediumint(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - union { const Uint32* p; const unsigned char* v; } u1, u2; - u1.p = p1; - u2.p = p2; - Int32 v1 = sint3korr(u1.v); - Int32 v2 = sint3korr(u2.v); - if (v1 < v2) - return -1; - if (v1 > v2) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpMediumunsigned(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - union { const Uint32* p; const unsigned char* v; } u1, u2; - u1.p = p1; - u2.p = p2; - Uint32 v1 = uint3korr(u1.v); - Uint32 v2 = uint3korr(u2.v); - if (v1 < v2) - return -1; - if (v1 > v2) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpInt(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - union { Uint32 p[1]; Int32 v; } u1, u2; - u1.p[0] = p1[0]; - u2.p[0] = p2[0]; - if (u1.v < u2.v) - return -1; - if (u1.v > u2.v) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpUnsigned(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - union { Uint32 p[1]; Uint32 v; } u1, u2; - u1.v = p1[0]; - u2.v = p2[0]; - if (u1.v < u2.v) - return -1; - if (u1.v > u2.v) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpBigint(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - if (size >= 2) { - union { Uint32 p[2]; Int64 v; } u1, u2; - u1.p[0] = p1[0]; - u1.p[1] = p1[1]; - u2.p[0] = p2[0]; - u2.p[1] = p2[1]; - if (u1.v < u2.v) + if (n2 >= sizeof(Int8)) { + Int8 v1, v2; + memcpy(&v1, p1, sizeof(Int8)); + memcpy(&v2, p2, sizeof(Int8)); + if (v1 < v2) return -1; - if (u1.v > u2.v) + if (v1 > v2) return +1; return 0; } + assert(! full); return CmpUnknown; } int -NdbSqlUtil::cmpBigunsigned(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpTinyunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - if (size >= 2) { - union { Uint32 p[2]; Uint64 v; } u1, u2; - u1.p[0] = p1[0]; - u1.p[1] = p1[1]; - u2.p[0] = p2[0]; - u2.p[1] = p2[1]; - if (u1.v < u2.v) + if (n2 >= sizeof(Uint8)) { + Uint8 v1, v2; + memcpy(&v1, p1, sizeof(Uint8)); + memcpy(&v2, p2, sizeof(Uint8)); + if (v1 < v2) return -1; - if (u1.v > u2.v) + if (v1 > v2) return +1; return 0; } + assert(! full); return CmpUnknown; } int -NdbSqlUtil::cmpFloat(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpSmallint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - union { Uint32 p[1]; float v; } u1, u2; - u1.p[0] = p1[0]; - u2.p[0] = p2[0]; - // no format check - if (u1.v < u2.v) - return -1; - if (u1.v > u2.v) - return +1; - return 0; -} - -int -NdbSqlUtil::cmpDouble(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - if (size >= 2) { - union { Uint32 p[2]; double v; } u1, u2; - u1.p[0] = p1[0]; - u1.p[1] = p1[1]; - u2.p[0] = p2[0]; - u2.p[1] = p2[1]; - // no format check - if (u1.v < u2.v) + if (n2 >= sizeof(Int16)) { + Int16 v1, v2; + memcpy(&v1, p1, sizeof(Int16)); + memcpy(&v2, p2, sizeof(Int16)); + if (v1 < v2) return -1; - if (u1.v > u2.v) + if (v1 > v2) return +1; return 0; } + assert(! full); return CmpUnknown; } int -NdbSqlUtil::cmpDecimal(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpSmallunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= sizeof(Uint16)) { + Uint16 v1, v2; + memcpy(&v1, p1, sizeof(Uint16)); + memcpy(&v2, p2, sizeof(Uint16)); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpMediumint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= 3) { + Int32 v1, v2; + v1 = sint3korr((const uchar*)p1); + v2 = sint3korr((const uchar*)p2); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpMediumunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= 3) { + Uint32 v1, v2; + v1 = uint3korr((const uchar*)p1); + v2 = uint3korr((const uchar*)p2); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpInt(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= sizeof(Int32)) { + Int32 v1, v2; + memcpy(&v1, p1, sizeof(Int32)); + memcpy(&v2, p2, sizeof(Int32)); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpUnsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= sizeof(Uint32)) { + Uint32 v1, v2; + memcpy(&v1, p1, sizeof(Uint32)); + memcpy(&v2, p2, sizeof(Uint32)); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpBigint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= sizeof(Int64)) { + Int64 v1, v2; + memcpy(&v1, p1, sizeof(Int64)); + memcpy(&v2, p2, sizeof(Int64)); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpBigunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= sizeof(Uint64)) { + Uint64 v1, v2; + memcpy(&v1, p1, sizeof(Uint64)); + memcpy(&v2, p2, sizeof(Uint64)); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpFloat(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= sizeof(float)) { + float v1, v2; + memcpy(&v1, p1, sizeof(float)); + memcpy(&v2, p2, sizeof(float)); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +int +NdbSqlUtil::cmpDouble(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + if (n2 >= sizeof(double)) { + double v1, v2; + memcpy(&v1, p1, sizeof(double)); + memcpy(&v2, p2, sizeof(double)); + if (v1 < v2) + return -1; + if (v1 > v2) + return +1; + return 0; + } + assert(! full); + return CmpUnknown; +} + +// not used by MySQL or NDB +int +NdbSqlUtil::cmpDecimal(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - // not used by MySQL or NDB assert(false); return 0; } int -NdbSqlUtil::cmpChar(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpChar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { // collation does not work on prefix for some charsets - assert(full == size && size > 0); - /* - * Char is blank-padded to length and null-padded to word size. - */ - union { const Uint32* p; const uchar* v; } u1, u2; - u1.p = p1; - u2.p = p2; + assert(full); + const uchar* v1 = (const uchar*)p1; + const uchar* v2 = (const uchar*)p2; // not const in MySQL CHARSET_INFO* cs = (CHARSET_INFO*)(info); - // length in bytes including null padding to Uint32 - uint l1 = (full << 2); - int k = (*cs->coll->strnncollsp)(cs, u1.v, l1, u2.v, l1, 0); + // compare with space padding + int k = (*cs->coll->strnncollsp)(cs, v1, n1, v2, n2, false); return k < 0 ? -1 : k > 0 ? +1 : 0; } +// waiting for MySQL and new NDB implementation int -NdbSqlUtil::cmpVarchar(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpVarchar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - /* - * Varchar is not allowed to contain a null byte and the value is - * null-padded. Therefore comparison does not need to use the length. - * - * Not used before MySQL 5.0. Format is likely to change. Handle - * only binary collation for now. - */ - union { const Uint32* p; const char* v; } u1, u2; - u1.p = p1; - u2.p = p2; - // skip length in first 2 bytes - int k = strncmp(u1.v + 2, u2.v + 2, (size << 2) - 2); - return k < 0 ? -1 : k > 0 ? +1 : full == size ? 0 : CmpUnknown; + assert(false); + return 0; } int -NdbSqlUtil::cmpBinary(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpBinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - /* - * Binary data of full length. Compare bytewise. - */ - union { const Uint32* p; const unsigned char* v; } u1, u2; - u1.p = p1; - u2.p = p2; - int k = memcmp(u1.v, u2.v, size << 2); - return k < 0 ? -1 : k > 0 ? +1 : full == size ? 0 : CmpUnknown; -} - -int -NdbSqlUtil::cmpVarbinary(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - /* - * Binary data of variable length padded with nulls. The comparison - * does not need to use the length. - * - * Not used before MySQL 5.0. Format is likely to change. - */ - union { const Uint32* p; const unsigned char* v; } u1, u2; - u1.p = p1; - u2.p = p2; - // skip length in first 2 bytes - int k = memcmp(u1.v + 2, u2.v + 2, (size << 2) - 2); - return k < 0 ? -1 : k > 0 ? +1 : full == size ? 0 : CmpUnknown; -} - -int -NdbSqlUtil::cmpDatetime(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) -{ - assert(full >= size && size > 0); - /* - * Datetime is CC YY MM DD hh mm ss \0 - * - * Not used via MySQL. - */ - union { const Uint32* p; const unsigned char* v; } u1, u2; - u1.p = p1; - u2.p = p2; - // no format check - int k = memcmp(u1.v, u2.v, 4); - if (k != 0) - return k < 0 ? -1 : +1; - if (size >= 2) { - k = memcmp(u1.v + 4, u2.v + 4, 4); - return k < 0 ? -1 : k > 0 ? +1 : 0; + const uchar* v1 = (const uchar*)p1; + const uchar* v2 = (const uchar*)p2; + // compare as binary strings + unsigned n = (n1 <= n2 ? n1 : n2); + int k = memcmp(v1, v2, n); + if (k == 0) { + if (full) + k = (int)n1 - (int)n2; + else + k = (int)n - (int)n2; } - return CmpUnknown; + return k < 0 ? -1 : k > 0 ? +1 : full ? 0 : CmpUnknown; } +// waiting for MySQL and new NDB implementation int -NdbSqlUtil::cmpTimespec(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpVarbinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - /* - * Timespec is CC YY MM DD hh mm ss \0 NN NN NN NN - * - * Not used via MySQL. - */ - union { const Uint32* p; const unsigned char* v; } u1, u2; - u1.p = p1; - u2.p = p2; - // no format check - int k = memcmp(u1.v, u2.v, 4); - if (k != 0) - return k < 0 ? -1 : +1; - if (size >= 2) { - k = memcmp(u1.v + 4, u2.v + 4, 4); - if (k != 0) - return k < 0 ? -1 : +1; - if (size >= 3) { - Uint32 n1 = *(const Uint32*)(u1.v + 8); - Uint32 n2 = *(const Uint32*)(u2.v + 8); - if (n1 < n2) - return -1; - if (n2 > n1) - return +1; - return 0; - } - } - return CmpUnknown; + assert(false); + return 0; } +// not used by MySQL or NDB int -NdbSqlUtil::cmpBlob(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpDatetime(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - assert(full >= size && size > 0); - /* - * Blob comparison is on the inline bytes (null padded). - */ - const unsigned head = NDB_BLOB_HEAD_SIZE; - // skip blob head - if (size >= head + 1) { - union { const Uint32* p; const unsigned char* v; } u1, u2; - u1.p = p1 + head; - u2.p = p2 + head; - int k = memcmp(u1.v, u2.v, (size - head) << 2); - return k < 0 ? -1 : k > 0 ? +1 : full == size ? 0 : CmpUnknown; - } - return CmpUnknown; + assert(false); + return 0; } +// not used by MySQL or NDB int -NdbSqlUtil::cmpText(const void* info, const Uint32* p1, const Uint32* p2, Uint32 full, Uint32 size) +NdbSqlUtil::cmpTimespec(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) { - // collation does not work on prefix for some charsets - assert(full == size && size > 0); - /* - * Text comparison is on the inline bytes (blank padded). Currently - * not supported for multi-byte charsets. - */ - const unsigned head = NDB_BLOB_HEAD_SIZE; - // skip blob head - if (size >= head + 1) { - union { const Uint32* p; const uchar* v; } u1, u2; - u1.p = p1 + head; - u2.p = p2 + head; - // not const in MySQL - CHARSET_INFO* cs = (CHARSET_INFO*)(info); - // length in bytes including null padding to Uint32 - uint l1 = (full << 2); - int k = (*cs->coll->strnncollsp)(cs, u1.v, l1, u2.v, l1,0); - return k < 0 ? -1 : k > 0 ? +1 : 0; - } - return CmpUnknown; + assert(false); + return 0; +} + +// not supported +int +NdbSqlUtil::cmpBlob(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + assert(false); + return 0; +} + +// not supported +int +NdbSqlUtil::cmpText(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full) +{ + assert(false); + return 0; } // check charset @@ -572,8 +497,6 @@ NdbSqlUtil::usable_in_pk(Uint32 typeId, const void* info) { const Type& type = getType(typeId); switch (type.m_typeId) { - case Type::Undefined: - break; case Type::Char: { const CHARSET_INFO *cs = (const CHARSET_INFO*)info; @@ -582,11 +505,12 @@ NdbSqlUtil::usable_in_pk(Uint32 typeId, const void* info) cs->cset != 0 && cs->coll != 0 && cs->coll->strnxfrm != 0 && - cs->strxfrm_multiply <= 1; // current limitation + cs->strxfrm_multiply <= MAX_XFRM_MULTIPLY; } break; + case Type::Undefined: case Type::Varchar: - return true; // Varchar not used via MySQL + case Type::Varbinary: case Type::Blob: case Type::Text: break; @@ -606,9 +530,9 @@ bool NdbSqlUtil::usable_in_ordered_index(Uint32 typeId, const void* info) { const Type& type = getType(typeId); + if (type.m_cmp == NULL) + return false; switch (type.m_typeId) { - case Type::Undefined: - break; case Type::Char: { const CHARSET_INFO *cs = (const CHARSET_INFO*)info; @@ -618,92 +542,17 @@ NdbSqlUtil::usable_in_ordered_index(Uint32 typeId, const void* info) cs->coll != 0 && cs->coll->strnxfrm != 0 && cs->coll->strnncollsp != 0 && - cs->strxfrm_multiply <= 1; // current limitation + cs->strxfrm_multiply <= MAX_XFRM_MULTIPLY; } break; + case Type::Undefined: case Type::Varchar: - return true; // Varchar not used via MySQL + case Type::Varbinary: + case Type::Blob: case Type::Text: - { - const CHARSET_INFO *cs = (const CHARSET_INFO*)info; - return - cs != 0 && - cs->mbmaxlen == 1 && // extra limitation - cs->cset != 0 && - cs->coll != 0 && - cs->coll->strnxfrm != 0 && - cs->coll->strnncollsp != 0 && - cs->strxfrm_multiply <= 1; // current limitation - } break; default: return true; } return false; } - -#ifdef NDB_SQL_UTIL_TEST - -#include -#include - -struct Testcase { - int op; // 1=compare 2=like - int res; - const char* s1; - const char* s2; - int pad; -}; -const Testcase testcase[] = { - { 2, 1, "abc", "abc", 0 }, - { 2, 1, "abc", "abc%", 0 }, - { 2, 1, "abcdef", "abc%", 0 }, - { 2, 1, "abcdefabcdefabcdef", "abc%", 0 }, - { 2, 1, "abcdefabcdefabcdef", "abc%f", 0 }, - { 2, 0, "abcdefabcdefabcdef", "abc%z", 0 }, - { 2, 1, "abcdefabcdefabcdef", "%f", 0 }, - { 2, 1, "abcdef", "a%b%c%d%e%f", 0 }, - { 0, 0, 0, 0 } -}; - -int -main(int argc, char** argv) -{ - ndb_init(); // for charsets - unsigned count = argc > 1 ? atoi(argv[1]) : 1000000; - ndbout_c("count = %u", count); - assert(count != 0); - for (const Testcase* t = testcase; t->s1 != 0; t++) { - ndbout_c("%d = '%s' %s '%s' pad=%d", - t->res, t->s1, t->op == 1 ? "comp" : "like", t->s2); - NDB_TICKS x1 = NdbTick_CurrentMillisecond(); - unsigned n1 = strlen(t->s1); - unsigned n2 = strlen(t->s2); - for (unsigned i = 0; i < count; i++) { - if (t->op == 1) { - int res = NdbSqlUtil::char_compare(t->s1, n1, t->s2, n2, t->pad); - assert(res == t->res); - continue; - } - if (t->op == 2) { - int res = NdbSqlUtil::char_like(t->s1, n1, t->s2, n2, t->pad); - assert(res == t->res); - continue; - } - assert(false); - } - NDB_TICKS x2 = NdbTick_CurrentMillisecond(); - if (x2 < x1) - x2 = x1; - double usec = 1000000.0 * double(x2 - x1) / double(count); - ndbout_c("time %.0f usec per call", usec); - } - // quick check - for (unsigned i = 0; i < sizeof(m_typeList) / sizeof(m_typeList[0]); i++) { - const NdbSqlUtil::Type& t = m_typeList[i]; - assert(t.m_typeId == i); - } - return 0; -} - -#endif diff --git a/ndb/src/kernel/blocks/dbacc/Dbacc.hpp b/ndb/src/kernel/blocks/dbacc/Dbacc.hpp index af8ee136934..6a65da5bb6a 100644 --- a/ndb/src/kernel/blocks/dbacc/Dbacc.hpp +++ b/ndb/src/kernel/blocks/dbacc/Dbacc.hpp @@ -607,8 +607,7 @@ struct Fragmentrec { //----------------------------------------------------------------------------- // elementLength: Length of element in bucket and overflow pages -// keyLength: Length of key (== 0 if long key or variable key length) -// wl-2066 always Length of key +// keyLength: Length of key //----------------------------------------------------------------------------- Uint8 elementLength; Uint16 keyLength; @@ -637,6 +636,11 @@ struct Fragmentrec { //----------------------------------------------------------------------------- Uint8 nodetype; Uint8 stopQueOp; + +//----------------------------------------------------------------------------- +// flag to avoid accessing table record if no char attributes +//----------------------------------------------------------------------------- + Uint8 hasCharAttr; }; typedef Ptr FragmentrecPtr; @@ -719,6 +723,7 @@ struct Operationrec { State transactionstate; Uint16 elementContainer; Uint16 tupkeylen; + Uint32 xfrmtupkeylen; Uint32 userblockref; Uint32 scanBits; Uint8 elementIsDisappeared; @@ -846,6 +851,13 @@ struct Tabrec { Uint32 fragptrholder[MAX_FRAG_PER_NODE]; Uint32 tabUserPtr; BlockReference tabUserRef; + + Uint8 noOfKeyAttr; + Uint8 hasCharAttr; + struct KeyAttr { + Uint32 attributeDescriptor; + CHARSET_INFO* charsetInfo; + } keyAttr[MAX_ATTRIBUTES_IN_INDEX]; }; typedef Ptr TabrecPtr; @@ -891,6 +903,7 @@ private: void execACCKEYREQ(Signal* signal); void execACCSEIZEREQ(Signal* signal); void execACCFRAGREQ(Signal* signal); + void execTC_SCHVERREQ(Signal* signal); void execACC_SRREQ(Signal* signal); void execNEXT_SCANREQ(Signal* signal); void execACC_ABORTREQ(Signal* signal); @@ -1016,7 +1029,7 @@ private: void increaselistcont(Signal* signal); void seizeLeftlist(Signal* signal); void seizeRightlist(Signal* signal); - void readTablePk(Uint32 localkey1); + Uint32 readTablePk(Uint32 localkey1); void getElement(Signal* signal); void getdirindex(Signal* signal); void commitdelete(Signal* signal, bool systemRestart); @@ -1123,6 +1136,8 @@ private: void lcp_write_op_to_undolog(Signal* signal); void reenable_expand_after_redo_log_exection_complete(Signal*); + // charsets + void xfrmKeyData(Signal* signal); // Initialisation void initData(); diff --git a/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp b/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp index c9b6c0ea17d..0b2e8aeeb9a 100644 --- a/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp +++ b/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp @@ -179,6 +179,7 @@ Dbacc::Dbacc(const class Configuration & conf): addRecSignal(GSN_ACCKEYREQ, &Dbacc::execACCKEYREQ); addRecSignal(GSN_ACCSEIZEREQ, &Dbacc::execACCSEIZEREQ); addRecSignal(GSN_ACCFRAGREQ, &Dbacc::execACCFRAGREQ); + addRecSignal(GSN_TC_SCHVERREQ, &Dbacc::execTC_SCHVERREQ); addRecSignal(GSN_ACC_SRREQ, &Dbacc::execACC_SRREQ); addRecSignal(GSN_NEXT_SCANREQ, &Dbacc::execNEXT_SCANREQ); addRecSignal(GSN_ACC_ABORTREQ, &Dbacc::execACC_ABORTREQ); diff --git a/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp b/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp index 1a5e22ac70a..eaf2176b390 100644 --- a/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp +++ b/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp @@ -16,6 +16,7 @@ #define DBACC_C #include "Dbacc.hpp" +#include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include // TO_DO_RONM is a label for comments on what needs to be improved in future versions // when more time is given. @@ -1033,6 +1035,12 @@ void Dbacc::initialiseTableRec(Signal* signal) tabptr.p->fragholder[i] = RNIL; tabptr.p->fragptrholder[i] = RNIL; }//for + tabptr.p->noOfKeyAttr = 0; + tabptr.p->hasCharAttr = 0; + for (Uint32 k = 0; k < MAX_ATTRIBUTES_IN_INDEX; k++) { + tabptr.p->keyAttr[k].attributeDescriptor = 0; + tabptr.p->keyAttr[k].charsetInfo = 0; + } }//for }//Dbacc::initialiseTableRec() @@ -1187,6 +1195,66 @@ void Dbacc::addFragRefuse(Signal* signal, Uint32 errorCode) return; }//Dbacc::addFragRefuseEarly() +void +Dbacc::execTC_SCHVERREQ(Signal* signal) +{ + jamEntry(); + if (! assembleFragments(signal)) { + jam(); + return; + } + tabptr.i = signal->theData[0]; + ptrCheckGuard(tabptr, ctablesize, tabrec); + Uint32 noOfKeyAttr = signal->theData[6]; + ndbrequire(noOfKeyAttr <= MAX_ATTRIBUTES_IN_INDEX); + Uint32 hasCharAttr = 0; + + SegmentedSectionPtr s0Ptr; + signal->getSection(s0Ptr, 0); + SectionReader r0(s0Ptr, getSectionSegmentPool()); + Uint32 i = 0; + while (i < noOfKeyAttr) { + jam(); + Uint32 attributeDescriptor = ~0; + Uint32 csNumber = ~0; + if (! r0.getWord(&attributeDescriptor) || + ! r0.getWord(&csNumber)) { + jam(); + break; + } + CHARSET_INFO* cs = 0; + if (csNumber != 0) { + cs = all_charsets[csNumber]; + ndbrequire(cs != 0); + hasCharAttr = 1; + } + tabptr.p->keyAttr[i].attributeDescriptor = attributeDescriptor; + tabptr.p->keyAttr[i].charsetInfo = cs; + i++; + } + ndbrequire(i == noOfKeyAttr); + releaseSections(signal); + + tabptr.p->noOfKeyAttr = noOfKeyAttr; + tabptr.p->hasCharAttr = hasCharAttr; + + // copy char attr flag to each fragment + for (Uint32 i1 = 0; i1 < MAX_FRAG_PER_NODE; i1++) { + jam(); + if (tabptr.p->fragptrholder[i1] != RNIL) { + rootfragrecptr.i = tabptr.p->fragptrholder[i1]; + ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec); + for (Uint32 i2 = 0; i2 < 2; i2++) { + fragrecptr.i = rootfragrecptr.p->fragmentptr[i2]; + ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); + fragrecptr.p->hasCharAttr = hasCharAttr; + } + } + } + + // no reply to DICT +} + void Dbacc::execDROP_TAB_REQ(Signal* signal){ jamEntry(); @@ -1550,6 +1618,7 @@ void Dbacc::initOpRec(Signal* signal) operationRecPtr.p->hashValue = signal->theData[3]; operationRecPtr.p->tupkeylen = signal->theData[4]; + operationRecPtr.p->xfrmtupkeylen = signal->theData[4]; operationRecPtr.p->transId1 = signal->theData[5]; operationRecPtr.p->transId2 = signal->theData[6]; operationRecPtr.p->transactionstate = ACTIVE; @@ -1664,6 +1733,10 @@ void Dbacc::execACCKEYREQ(Signal* signal) ndbrequire(operationRecPtr.p->transactionstate == IDLE); initOpRec(signal); + // normalize key if any char attr + if (! operationRecPtr.p->isAccLockReq && fragrecptr.p->hasCharAttr) + xfrmKeyData(signal); + /*---------------------------------------------------------------*/ /* */ /* WE WILL USE THE HASH VALUE TO LOOK UP THE PROPER MEMORY */ @@ -1758,6 +1831,54 @@ void Dbacc::execACCKEYREQ(Signal* signal) return; }//Dbacc::execACCKEYREQ() +void +Dbacc::xfrmKeyData(Signal* signal) +{ + tabptr.i = fragrecptr.p->myTableId; + ptrCheckGuard(tabptr, ctablesize, tabrec); + + Uint32 dst[1024]; + Uint32 dstSize = (sizeof(dst) >> 2); + Uint32* src = &signal->theData[7]; + const Uint32 noOfKeyAttr = tabptr.p->noOfKeyAttr; + Uint32 dstPos = 0; + Uint32 srcPos = 0; + Uint32 i = 0; + + while (i < noOfKeyAttr) { + const Tabrec::KeyAttr& keyAttr = tabptr.p->keyAttr[i]; + + Uint32 srcBytes = AttributeDescriptor::getSizeInBytes(keyAttr.attributeDescriptor); + Uint32 srcWords = (srcBytes + 3) / 4; + Uint32 dstWords = ~0; + uchar* dstPtr = (uchar*)&dst[dstPos]; + const uchar* srcPtr = (const uchar*)&src[srcPos]; + CHARSET_INFO* cs = keyAttr.charsetInfo; + + if (cs == 0) { + jam(); + memcpy(dstPtr, srcPtr, srcWords << 2); + dstWords = srcWords; + } else { + jam(); + Uint32 xmul = cs->strxfrm_multiply; + if (xmul == 0) + xmul = 1; + Uint32 dstLen = xmul * srcBytes; + ndbrequire(dstLen <= ((dstSize - dstPos) << 2)); + uint n = (*cs->coll->strnxfrm)(cs, dstPtr, dstLen, srcPtr, srcBytes); + while ((n & 3) != 0) + dstPtr[n++] = 0; + dstWords = (n >> 2); + } + dstPos += dstWords; + srcPos += srcWords; + i++; + } + memcpy(src, dst, dstPos << 2); + operationRecPtr.p->xfrmtupkeylen = dstPos; +} + void Dbacc::accIsLockedLab(Signal* signal) { ndbrequire(csystemRestart == ZFALSE); @@ -1848,6 +1969,7 @@ void Dbacc::insertelementLab(Signal* signal) }//if }//if if (fragrecptr.p->keyLength != operationRecPtr.p->tupkeylen) { + // historical ndbrequire(fragrecptr.p->keyLength == 0); }//if @@ -3251,7 +3373,7 @@ void Dbacc::getdirindex(Signal* signal) ptrCheckGuard(gdiPageptr, cpagesize, page8); }//Dbacc::getdirindex() -void +Uint32 Dbacc::readTablePk(Uint32 localkey1) { Uint32 tableId = fragrecptr.p->myTableId; @@ -3259,10 +3381,11 @@ Dbacc::readTablePk(Uint32 localkey1) Uint32 fragPageId = localkey1 >> MAX_TUPLES_BITS; Uint32 pageIndex = localkey1 & ((1 << MAX_TUPLES_BITS ) - 1); #ifdef VM_TRACE - memset(ckeys, 0x1f, fragrecptr.p->keyLength << 2); + memset(ckeys, 0x1f, (fragrecptr.p->keyLength * MAX_XFRM_MULTIPLY) << 2); #endif - int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, ckeys); - ndbrequire(ret == fragrecptr.p->keyLength); + int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, ckeys, true); + ndbrequire(ret > 0); + return ret; } /* --------------------------------------------------------------------------------- */ @@ -3306,7 +3429,6 @@ void Dbacc::getElement(Signal* signal) Uint32 tgeNextptrtype; register Uint32 tgeKeyptr; register Uint32 tgeRemLen; - register Uint32 tgeCompareLen; register Uint32 TelemLen = fragrecptr.p->elementLength; register Uint32* Tkeydata = (Uint32*)&signal->theData[7]; @@ -3314,7 +3436,6 @@ void Dbacc::getElement(Signal* signal) tgePageindex = tgdiPageindex; gePageptr = gdiPageptr; tgeResult = ZFALSE; - tgeCompareLen = fragrecptr.p->keyLength; /* * The value seached is * - table key for ACCKEYREQ, stored in TUP @@ -3381,12 +3502,15 @@ void Dbacc::getElement(Signal* signal) Uint32 localkey2 = 0; bool found; if (! searchLocalKey) { - readTablePk(localkey1); - found = (memcmp(Tkeydata, ckeys, fragrecptr.p->keyLength << 2) == 0); + Uint32 len = readTablePk(localkey1); + found = (len == operationRecPtr.p->xfrmtupkeylen) && + (memcmp(Tkeydata, ckeys, len << 2) == 0); } else { + jam(); found = (localkey1 == Tkeydata[0]); } if (found) { + jam(); tgeLocked = ElementHeader::getLocked(tgeElementHeader); tgeResult = ZTRUE; operationRecPtr.p->localdata[0] = localkey1; @@ -7731,6 +7855,7 @@ void Dbacc::initFragGeneral(FragmentrecPtr regFragPtr) regFragPtr.p->activeDataPage = 0; regFragPtr.p->createLcp = ZFALSE; regFragPtr.p->stopQueOp = ZFALSE; + regFragPtr.p->hasCharAttr = ZFALSE; regFragPtr.p->nextAllocPage = 0; regFragPtr.p->nrWaitWriteUndoExit = 0; regFragPtr.p->lastUndoIsStored = ZFALSE; @@ -8680,6 +8805,7 @@ void Dbacc::srDoUndoLab(Signal* signal) const Uint32 tkeylen = undopageptr.p->undoword[tmpindex]; tmpindex++; operationRecPtr.p->tupkeylen = tkeylen; + operationRecPtr.p->xfrmtupkeylen = 0; // not used operationRecPtr.p->fragptr = fragrecptr.i; ndbrequire(fragrecptr.p->keyLength != 0 && @@ -9750,6 +9876,7 @@ void Dbacc::initScanOpRec(Signal* signal) arrGuard(tisoLocalPtr, 2048); operationRecPtr.p->keydata[0] = isoPageptr.p->word32[tisoLocalPtr]; operationRecPtr.p->tupkeylen = fragrecptr.p->keyLength; + operationRecPtr.p->xfrmtupkeylen = 0; // not used }//Dbacc::initScanOpRec() /* --------------------------------------------------------------------------------- */ diff --git a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp index cedd2bed415..17972abc2dd 100644 --- a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp +++ b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp @@ -4318,7 +4318,28 @@ Dbdict::execTAB_COMMITCONF(Signal* signal){ signal->theData[3] = reference(); signal->theData[4] = (Uint32)tabPtr.p->tableType; signal->theData[5] = createTabPtr.p->key; - sendSignal(DBTC_REF, GSN_TC_SCHVERREQ, signal, 6, JBB); + signal->theData[6] = (Uint32)tabPtr.p->noOfPrimkey; + + Uint32 buf[2 * MAX_ATTRIBUTES_IN_INDEX]; + Uint32 sz = 0; + Uint32 tAttr = tabPtr.p->firstAttribute; + while (tAttr != RNIL) { + jam(); + AttributeRecord* aRec = c_attributeRecordPool.getPtr(tAttr); + if (aRec->tupleKey) { + buf[sz++] = aRec->attributeDescriptor; + buf[sz++] = (aRec->extPrecision >> 16); // charset number + } + tAttr = aRec->nextAttrInTable; + } + ndbrequire(sz == 2 * tabPtr.p->noOfPrimkey); + + LinearSectionPtr lsPtr[3]; + lsPtr[0].p = buf; + lsPtr[0].sz = sz; + // note: ACC does not reply + sendSignal(DBACC_REF, GSN_TC_SCHVERREQ, signal, 7, JBB, lsPtr, 1); + sendSignal(DBTC_REF, GSN_TC_SCHVERREQ, signal, 7, JBB, lsPtr, 1); return; } @@ -4785,12 +4806,18 @@ void Dbdict::handleTabInfo(SimpleProperties::Reader & it, // charset in upper half of precision unsigned csNumber = (attrPtr.p->extPrecision >> 16); if (csNumber != 0) { + /* + * A new charset is first accessed here on this node. + * TODO use separate thread (e.g. via NDBFS) if need to load from file + */ CHARSET_INFO* cs = get_charset(csNumber, MYF(0)); if (cs == NULL) { parseP->errorCode = CreateTableRef::InvalidCharset; parseP->errorLine = __LINE__; return; } + // XXX should be done somewhere in mysql + all_charsets[cs->number] = cs; unsigned i = 0; while (i < noOfCharsets) { if (charsets[i] == csNumber) diff --git a/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp b/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp index 2b531ede3d1..e141ab09617 100644 --- a/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp +++ b/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp @@ -8280,7 +8280,7 @@ Dblqh::readPrimaryKeys(ScanRecord *scanP, TcConnectionrec *tcConP, Uint32 *dst) tableId = tFragPtr.p->tabRef; } - int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, dst); + int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, dst, false); if(0) ndbout_c("readPrimaryKeys(table: %d fragment: %d [ %d %d ] -> %d", tableId, fragId, fragPageId, pageIndex, ret); diff --git a/ndb/src/kernel/blocks/dbtc/Dbtc.hpp b/ndb/src/kernel/blocks/dbtc/Dbtc.hpp index ccb7d55396d..9afe4a5da0e 100644 --- a/ndb/src/kernel/blocks/dbtc/Dbtc.hpp +++ b/ndb/src/kernel/blocks/dbtc/Dbtc.hpp @@ -972,6 +972,7 @@ public: typedef Ptr HostRecordPtr; /* *********** TABLE RECORD ********************************************* */ + /********************************************************/ /* THIS RECORD CONTAINS THE CURRENT SCHEMA VERSION OF */ /* ALL TABLES IN THE SYSTEM. */ @@ -982,13 +983,21 @@ public: Uint8 dropping; Uint8 tableType; Uint8 storedTable; - + + Uint8 noOfKeyAttr; + Uint8 hasCharAttr; + + struct KeyAttr { + Uint32 attributeDescriptor; + CHARSET_INFO* charsetInfo; + } keyAttr[MAX_ATTRIBUTES_IN_INDEX]; + bool checkTable(Uint32 schemaVersion) const { return enabled && !dropping && (schemaVersion == currentSchemaVersion); } - + Uint32 getErrorCode(Uint32 schemaVersion) const; - + struct DropTable { Uint32 senderRef; Uint32 senderData; @@ -1436,6 +1445,7 @@ private: void gcpTcfinished(Signal* signal); void handleGcp(Signal* signal); void hash(Signal* signal); + Uint32 xfrmKeyData(Signal* signal, Uint32* dst, Uint32 dstSize, const Uint32* src); void initApiConnect(Signal* signal); void initApiConnectRec(Signal* signal, ApiConnectRecord * const regApiPtr, diff --git a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp index 42821e4fe7f..1ce6ffa4817 100644 --- a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp +++ b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp @@ -20,6 +20,7 @@ #include "md5_hash.hpp" #include #include +#include #include #include @@ -63,6 +64,8 @@ #include #include #include +#include +#include #include #include @@ -313,6 +316,10 @@ void Dbtc::execREAD_NODESREF(Signal* signal) void Dbtc::execTC_SCHVERREQ(Signal* signal) { jamEntry(); + if (! assembleFragments(signal)) { + jam(); + return; + } tabptr.i = signal->theData[0]; ptrCheckGuard(tabptr, ctabrecFilesize, tableRecord); tabptr.p->currentSchemaVersion = signal->theData[1]; @@ -320,10 +327,41 @@ void Dbtc::execTC_SCHVERREQ(Signal* signal) BlockReference retRef = signal->theData[3]; tabptr.p->tableType = (Uint8)signal->theData[4]; BlockReference retPtr = signal->theData[5]; + Uint32 noOfKeyAttr = signal->theData[6]; + ndbrequire(noOfKeyAttr <= MAX_ATTRIBUTES_IN_INDEX); + Uint32 hasCharAttr = 0; + + SegmentedSectionPtr s0Ptr; + signal->getSection(s0Ptr, 0); + SectionReader r0(s0Ptr, getSectionSegmentPool()); + Uint32 i = 0; + while (i < noOfKeyAttr) { + jam(); + Uint32 attributeDescriptor = ~0; + Uint32 csNumber = ~0; + if (! r0.getWord(&attributeDescriptor) || + ! r0.getWord(&csNumber)) { + jam(); + break; + } + CHARSET_INFO* cs = 0; + if (csNumber != 0) { + cs = all_charsets[csNumber]; + ndbrequire(cs != 0); + hasCharAttr = 1; + } + tabptr.p->keyAttr[i].attributeDescriptor = attributeDescriptor; + tabptr.p->keyAttr[i].charsetInfo = cs; + i++; + } + ndbrequire(i == noOfKeyAttr); + releaseSections(signal); ndbrequire(tabptr.p->enabled == false); tabptr.p->enabled = true; tabptr.p->dropping = false; + tabptr.p->noOfKeyAttr = noOfKeyAttr; + tabptr.p->hasCharAttr = hasCharAttr; signal->theData[0] = tabptr.i; signal->theData[1] = retPtr; @@ -2221,6 +2259,7 @@ void Dbtc::hash(Signal* signal) UintR Tdata3; UintR* Tdata32; Uint64 Tdata[512]; + Uint64 Txfrmdata[512 * MAX_XFRM_MULTIPLY]; CacheRecord * const regCachePtr = cachePtr.p; Tdata32 = (UintR*)&Tdata[0]; @@ -2250,8 +2289,21 @@ void Dbtc::hash(Signal* signal) ti += 4; }//while }//if + + UintR keylen = (UintR)regCachePtr->keylen; + + TableRecordPtr tabptrSave = tabptr; + tabptr.i = regCachePtr->tableref; // table or hash index id + ptrCheckGuard(tabptr, ctabrecFilesize, tableRecord); + if (tabptr.p->hasCharAttr) { + jam(); + keylen = xfrmKeyData(signal, (Uint32*)Txfrmdata, sizeof(Txfrmdata) >> 2, Tdata32); + Tdata32 = (UintR*)&Txfrmdata[0]; + } + tabptr = tabptrSave; + Uint32 tmp[4]; - md5_hash(tmp, (Uint64*)&Tdata32[0], (UintR)regCachePtr->keylen); + md5_hash(tmp, (Uint64*)&Tdata32[0], keylen); thashValue = tmp[0]; if (regCachePtr->distributionKeyIndicator == 1) { @@ -2263,6 +2315,47 @@ void Dbtc::hash(Signal* signal) }//if }//Dbtc::hash() +Uint32 +Dbtc::xfrmKeyData(Signal* signal, Uint32* dst, Uint32 dstSize, const Uint32* src) +{ + const Uint32 noOfKeyAttr = tabptr.p->noOfKeyAttr; + Uint32 dstPos = 0; + Uint32 srcPos = 0; + Uint32 i = 0; + while (i < noOfKeyAttr) { + const TableRecord::KeyAttr& keyAttr = tabptr.p->keyAttr[i]; + + Uint32 srcBytes = AttributeDescriptor::getSizeInBytes(keyAttr.attributeDescriptor); + Uint32 srcWords = (srcBytes + 3) / 4; + Uint32 dstWords = ~0; + uchar* dstPtr = (uchar*)&dst[dstPos]; + const uchar* srcPtr = (const uchar*)&src[srcPos]; + CHARSET_INFO* cs = keyAttr.charsetInfo; + + if (cs == NULL) { + jam(); + memcpy(dstPtr, srcPtr, srcWords << 2); + dstWords = srcWords; + } else { + jam(); + Uint32 xmul = cs->strxfrm_multiply; + if (xmul == 0) + xmul = 1; + Uint32 dstLen = xmul * srcBytes; + ndbrequire(dstLen <= ((dstSize - dstPos) << 2)); + uint n = (*cs->coll->strnxfrm)(cs, dstPtr, dstLen, srcPtr, srcBytes); + while ((n & 3) != 0) { + dstPtr[n++] = 0; + } + dstWords = (n >> 2); + } + dstPos += dstWords; + srcPos += srcWords; + i++; + } + return dstPos; +} + /* INIT_API_CONNECT_REC --------------------------- @@ -9973,6 +10066,12 @@ void Dbtc::initTable(Signal* signal) tabptr.p->tableType = 0; tabptr.p->enabled = false; tabptr.p->dropping = false; + tabptr.p->noOfKeyAttr = 0; + tabptr.p->hasCharAttr = 0; + for (unsigned k = 0; k < MAX_ATTRIBUTES_IN_INDEX; k++) { + tabptr.p->keyAttr[k].attributeDescriptor = 0; + tabptr.p->keyAttr[k].charsetInfo = 0; + } }//for }//Dbtc::initTable() diff --git a/ndb/src/kernel/blocks/dbtup/Dbtup.hpp b/ndb/src/kernel/blocks/dbtup/Dbtup.hpp index 1c7662c5d55..6d169d20d16 100644 --- a/ndb/src/kernel/blocks/dbtup/Dbtup.hpp +++ b/ndb/src/kernel/blocks/dbtup/Dbtup.hpp @@ -207,6 +207,8 @@ #define ZTUPLE_DELETED_ERROR 626 #define ZINSERT_ERROR 630 +#define ZINVALID_CHAR_FORMAT 744 + /* SOME WORD POSITIONS OF FIELDS IN SOME HEADERS */ #define ZPAGE_STATE_POS 0 /* POSITION OF PAGE STATE */ @@ -1020,14 +1022,14 @@ public: * for md5 summing and when returning keyinfo. Returns number of * words or negative (-terrorCode) on error. */ - int tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* dataOut); + int tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* dataOut, bool xfrmFlag); /* * ACC reads primary key without headers into an array of words. At * this point in ACC deconstruction, ACC still uses logical references * to fragment and tuple. */ - int accReadPk(Uint32 tableId, Uint32 fragId, Uint32 fragPageId, Uint32 pageIndex, Uint32* dataOut); + int accReadPk(Uint32 tableId, Uint32 fragId, Uint32 fragPageId, Uint32 pageIndex, Uint32* dataOut, bool xfrmFlag); /* * TUX checks if tuple is visible to scan. @@ -1637,20 +1639,6 @@ private: bool readBitsNotNULL(Uint32* outBuffer, AttributeHeader*, Uint32, Uint32); bool updateBitsNotNULL(Uint32* inBuffer, Uint32, Uint32); -// ***************************************************************** -// Read char routines optionally (tXfrmFlag) apply strxfrm -// ***************************************************************** - - bool readCharNotNULL(Uint32* outBuffer, - AttributeHeader* ahOut, - Uint32 attrDescriptor, - Uint32 attrDes2); - - bool readCharNULLable(Uint32* outBuffer, - AttributeHeader* ahOut, - Uint32 attrDescriptor, - Uint32 attrDes2); - //------------------------------------------------------------------ //------------------------------------------------------------------ bool nullFlagCheck(Uint32 attrDes2); diff --git a/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp b/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp index 3b0ba1c196f..ab6e0642e11 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp @@ -173,7 +173,7 @@ Dbtup::tuxReadAttrs(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32 tu } int -Dbtup::tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* dataOut) +Dbtup::tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* dataOut, bool xfrmFlag) { ljamEntry(); // use own variables instead of globals @@ -200,7 +200,7 @@ Dbtup::tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* data operPtr.i = RNIL; operPtr.p = NULL; // do it - int ret = readAttributes(pagePtr.p, pageOffset, attrIds, numAttrs, dataOut, ZNIL, true); + int ret = readAttributes(pagePtr.p, pageOffset, attrIds, numAttrs, dataOut, ZNIL, xfrmFlag); // restore globals tabptr = tabptr_old; fragptr = fragptr_old; @@ -229,7 +229,7 @@ Dbtup::tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* data } int -Dbtup::accReadPk(Uint32 tableId, Uint32 fragId, Uint32 fragPageId, Uint32 pageIndex, Uint32* dataOut) +Dbtup::accReadPk(Uint32 tableId, Uint32 fragId, Uint32 fragPageId, Uint32 pageIndex, Uint32* dataOut, bool xfrmFlag) { ljamEntry(); // get table @@ -245,7 +245,7 @@ Dbtup::accReadPk(Uint32 tableId, Uint32 fragId, Uint32 fragPageId, Uint32 pageIn ndbrequire((pageIndex & 0x1) == 0); Uint32 pageOffset = ZPAGE_HEADER_SIZE + (pageIndex >> 1) * tablePtr.p->tupheadsize; // use TUX routine - optimize later - int ret = tuxReadPk(fragPtr.i, pageId, pageOffset, dataOut); + int ret = tuxReadPk(fragPtr.i, pageId, pageOffset, dataOut, xfrmFlag); return ret; } diff --git a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp index 56ae861270c..13593602abc 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp @@ -364,13 +364,8 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal) ndbrequire(false); }//if if (csNumber != 0) { - CHARSET_INFO* cs = get_charset(csNumber, MYF(0)); - if (cs == NULL) { - ljam(); - terrorCode = TupAddAttrRef::InvalidCharset; - addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId); - return; - } + CHARSET_INFO* cs = all_charsets[csNumber]; + ndbrequire(cs != NULL); Uint32 i = 0; while (i < fragOperPtr.p->charsetIndex) { ljam(); diff --git a/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp b/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp index 5bcbb482a8c..73cddfd0382 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp @@ -59,10 +59,11 @@ Dbtup::setUpQueryRoutines(Tablerec* const regTabPtr) } else { ndbrequire(false); }//if - // replace read function of char attribute + // replace functions for char attribute if (AttributeOffset::getCharsetFlag(attrOffset)) { ljam(); - regTabPtr->readFunctionArray[i] = &Dbtup::readCharNotNULL; + regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHManyWordNotNULL; + regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNotNULL; } } else { if (AttributeDescriptor::getSize(attrDescriptor) == 0){ @@ -86,10 +87,11 @@ Dbtup::setUpQueryRoutines(Tablerec* const regTabPtr) regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHZeroWordNULLable; regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNULLable; }//if - // replace read function of char attribute + // replace functions for char attribute if (AttributeOffset::getCharsetFlag(attrOffset)) { ljam(); - regTabPtr->readFunctionArray[i] = &Dbtup::readCharNULLable; + regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHManyWordNULLable; + regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNULLable; } }//if } else if (AttributeDescriptor::getArrayType(attrDescriptor) == ZVAR_ARRAY) { @@ -337,25 +339,68 @@ Dbtup::readFixedSizeTHManyWordNotNULL(Uint32* outBuffer, Uint32 attrDes2) { Uint32 indexBuf = tOutBufIndex; + Uint32 charsetFlag = AttributeOffset::getCharsetFlag(attrDes2); Uint32 readOffset = AttributeOffset::getOffset(attrDes2); Uint32 attrNoOfWords = AttributeDescriptor::getSizeInWords(attrDescriptor); - Uint32 newIndexBuf = indexBuf + attrNoOfWords; Uint32 maxRead = tMaxRead; ndbrequire((readOffset + attrNoOfWords - 1) < tCheckOffset); - if (newIndexBuf <= maxRead) { - ljam(); - ahOut->setDataSize(attrNoOfWords); - MEMCOPY_NO_WORDS(&outBuffer[indexBuf], - &tTupleHeader[readOffset], - attrNoOfWords); - tOutBufIndex = newIndexBuf; - return true; + if (! charsetFlag || ! tXfrmFlag) { + Uint32 newIndexBuf = indexBuf + attrNoOfWords; + if (newIndexBuf <= maxRead) { + ljam(); + ahOut->setDataSize(attrNoOfWords); + MEMCOPY_NO_WORDS(&outBuffer[indexBuf], + &tTupleHeader[readOffset], + attrNoOfWords); + tOutBufIndex = newIndexBuf; + return true; + } else { + ljam(); + terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR; + }//if } else { ljam(); - terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR; - return false; - }//if + Tablerec* regTabPtr = tabptr.p; + Uint32 srcBytes = AttributeDescriptor::getSizeInBytes(attrDescriptor); + Uint32 i = AttributeOffset::getCharsetPos(attrDes2); + ndbrequire(i < regTabPtr->noOfCharsets); + CHARSET_INFO* cs = regTabPtr->charsetArray[i]; + Uint32 xmul = cs->strxfrm_multiply; + if (xmul == 0) + xmul = 1; + Uint32 dstLen = xmul * srcBytes; + Uint32 maxIndexBuf = indexBuf + (dstLen >> 2); + if (maxIndexBuf <= maxRead) { + ljam(); + uchar* dstPtr = (uchar*)&outBuffer[indexBuf]; + const uchar* srcPtr = (uchar*)&tTupleHeader[readOffset]; + const char* ssrcPtr = (const char*)srcPtr; + // could verify data format optionally + if (true || + (*cs->cset->well_formed_len)(cs, ssrcPtr, ssrcPtr + srcBytes, ZNIL) == srcBytes) { + ljam(); + // normalize + Uint32 n = (*cs->coll->strnxfrm)(cs, dstPtr, dstLen, srcPtr, srcBytes); + while ((n & 3) != 0) { + dstPtr[n++] = 0; + } + Uint32 dstWords = (n >> 2); + ahOut->setDataSize(dstWords); + Uint32 newIndexBuf = indexBuf + dstWords; + ndbrequire(newIndexBuf <= maxRead); + tOutBufIndex = newIndexBuf; + return true; + } else { + ljam(); + terrorCode = ZTUPLE_CORRUPTED_ERROR; + } + } else { + ljam(); + terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR; + } + } + return false; }//Dbtup::readFixedSizeTHManyWordNotNULL() bool @@ -402,7 +447,6 @@ Dbtup::readFixedSizeTHManyWordNULLable(Uint32* outBuffer, Uint32 attrDescriptor, Uint32 attrDes2) { -ljam(); if (!nullFlagCheck(attrDes2)) { ljam(); return readFixedSizeTHManyWordNotNULL(outBuffer, @@ -563,74 +607,6 @@ Dbtup::readDynSmallVarSize(Uint32* outBuffer, return false; }//Dbtup::readDynSmallVarSize() - -bool -Dbtup::readCharNotNULL(Uint32* outBuffer, - AttributeHeader* ahOut, - Uint32 attrDescriptor, - Uint32 attrDes2) -{ - Uint32 indexBuf = tOutBufIndex; - Uint32 readOffset = AttributeOffset::getOffset(attrDes2); - Uint32 attrNoOfWords = AttributeDescriptor::getSizeInWords(attrDescriptor); - Uint32 newIndexBuf = indexBuf + attrNoOfWords; - Uint32 maxRead = tMaxRead; - - ndbrequire((readOffset + attrNoOfWords - 1) < tCheckOffset); - if (newIndexBuf <= maxRead) { - ljam(); - ahOut->setDataSize(attrNoOfWords); - if (! tXfrmFlag) { - MEMCOPY_NO_WORDS(&outBuffer[indexBuf], - &tTupleHeader[readOffset], - attrNoOfWords); - } else { - ljam(); - Tablerec* regTabPtr = tabptr.p; - Uint32 i = AttributeOffset::getCharsetPos(attrDes2); - ndbrequire(i < tabptr.p->noOfCharsets); - // not const in MySQL - CHARSET_INFO* cs = tabptr.p->charsetArray[i]; - // XXX should strip Uint32 null padding - const unsigned nBytes = attrNoOfWords << 2; - unsigned n = - (*cs->coll->strnxfrm)(cs, - (uchar*)&outBuffer[indexBuf], - nBytes, - (const uchar*)&tTupleHeader[readOffset], - nBytes); - // pad with ascii spaces - while (n < nBytes) - ((uchar*)&outBuffer[indexBuf])[n++] = 0x20; - } - tOutBufIndex = newIndexBuf; - return true; - } else { - ljam(); - terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR; - return false; - } -} - -bool -Dbtup::readCharNULLable(Uint32* outBuffer, - AttributeHeader* ahOut, - Uint32 attrDescriptor, - Uint32 attrDes2) -{ - if (!nullFlagCheck(attrDes2)) { - ljam(); - return readCharNotNULL(outBuffer, - ahOut, - attrDescriptor, - attrDes2); - } else { - ljam(); - ahOut->setNULL(); - return true; - } -} - /* ---------------------------------------------------------------------- */ /* THIS ROUTINE IS USED TO UPDATE A NUMBER OF ATTRIBUTES. IT IS */ /* USED BY THE INSERT ROUTINE, THE UPDATE ROUTINE AND IT CAN BE */ @@ -818,6 +794,7 @@ Dbtup::updateFixedSizeTHManyWordNotNULL(Uint32* inBuffer, Uint32 indexBuf = tInBufIndex; Uint32 inBufLen = tInBufLen; Uint32 updateOffset = AttributeOffset::getOffset(attrDes2); + Uint32 charsetFlag = AttributeOffset::getCharsetFlag(attrDes2); AttributeHeader ahIn(inBuffer[indexBuf]); Uint32 nullIndicator = ahIn.isNULL(); Uint32 noOfWords = AttributeDescriptor::getSizeInWords(attrDescriptor); @@ -827,6 +804,21 @@ Dbtup::updateFixedSizeTHManyWordNotNULL(Uint32* inBuffer, if (newIndex <= inBufLen) { if (!nullIndicator) { ljam(); + if (charsetFlag) { + ljam(); + Tablerec* regTabPtr = tabptr.p; + Uint32 bytes = AttributeDescriptor::getSizeInBytes(attrDescriptor); + Uint32 i = AttributeOffset::getCharsetPos(attrDes2); + ndbrequire(i < regTabPtr->noOfCharsets); + // not const in MySQL + CHARSET_INFO* cs = regTabPtr->charsetArray[i]; + const char* ssrc = (const char*)&inBuffer[tInBufIndex + 1]; + if ((*cs->cset->well_formed_len)(cs, ssrc, ssrc + bytes, ZNIL) != bytes) { + ljam(); + terrorCode = ZINVALID_CHAR_FORMAT; + return false; + } + } tInBufIndex = newIndex; MEMCOPY_NO_WORDS(&tTupleHeader[updateOffset], &inBuffer[indexBuf + 1], diff --git a/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp b/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp index aac5c326cad..476a4b5724b 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp @@ -753,7 +753,7 @@ bool Dbtup::readTriggerInfo(TupTriggerData* const trigPtr, regTabPtr->noOfKeyAttr, keyBuffer, ZATTR_BUFFER_SIZE, - true); + false); ndbrequire(ret != -1); noPrimKey= ret; @@ -796,7 +796,7 @@ bool Dbtup::readTriggerInfo(TupTriggerData* const trigPtr, numAttrsToRead, mainBuffer, ZATTR_BUFFER_SIZE, - true); + false); ndbrequire(ret != -1); noMainWords= ret; } else { @@ -822,7 +822,7 @@ bool Dbtup::readTriggerInfo(TupTriggerData* const trigPtr, numAttrsToRead, copyBuffer, ZATTR_BUFFER_SIZE, - true); + false); ndbrequire(ret != -1); noCopyWords = ret; diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp index ddab77b97b5..ffce9969074 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp @@ -18,24 +18,26 @@ #include "Dbtux.hpp" /* - * Search key vs node prefix or entry + * Search key vs node prefix or entry. * * The comparison starts at given attribute position. The position is * updated by number of equal initial attributes found. The entry data * may be partial in which case CmpUnknown may be returned. + * + * The attributes are normalized and have variable size given in words. */ int Dbtux::cmpSearchKey(const Frag& frag, unsigned& start, ConstData searchKey, ConstData entryData, unsigned maxlen) { const unsigned numAttrs = frag.m_numAttrs; const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff); - // number of words of attribute data left - unsigned len2 = maxlen; // skip to right position in search key only for (unsigned i = 0; i < start; i++) { jam(); searchKey += AttributeHeaderSize + searchKey.ah().getDataSize(); } + // number of words of entry data left + unsigned len2 = maxlen; int ret = 0; while (start < numAttrs) { if (len2 <= AttributeHeaderSize) { @@ -47,18 +49,20 @@ Dbtux::cmpSearchKey(const Frag& frag, unsigned& start, ConstData searchKey, Cons if (! searchKey.ah().isNULL()) { if (! entryData.ah().isNULL()) { jam(); - // current attribute + // verify attribute id const DescAttr& descAttr = descEnt.m_descAttr[start]; - // full data size - const unsigned size1 = AttributeDescriptor::getSizeInWords(descAttr.m_attrDesc); - ndbrequire(size1 != 0 && size1 == entryData.ah().getDataSize()); - const unsigned size2 = min(size1, len2); + ndbrequire(searchKey.ah().getAttributeId() == descAttr.m_primaryAttrId); + ndbrequire(entryData.ah().getAttributeId() == descAttr.m_primaryAttrId); + // sizes + const unsigned size1 = searchKey.ah().getDataSize(); + const unsigned size2 = min(entryData.ah().getDataSize(), len2); len2 -= size2; // compare NdbSqlUtil::Cmp* const cmp = c_sqlCmp[start]; const Uint32* const p1 = &searchKey[AttributeHeaderSize]; const Uint32* const p2 = &entryData[AttributeHeaderSize]; - ret = (*cmp)(0, p1, p2, size1, size2); + const bool full = (maxlen == MaxAttrDataSize); + ret = (*cmp)(0, p1, size1 << 2, p2, size2 << 2, full); if (ret != 0) { jam(); break; @@ -104,6 +108,8 @@ Dbtux::cmpSearchKey(const Frag& frag, unsigned& start, ConstData searchKey, Cons * 0 a >= 2 and b > 3 yes +1 * 1 a <= 2 and b <= 3 no +1 * 1 a <= 2 and b < 3 yes -1 + * + * The attributes are normalized and have variable size given in words. */ int Dbtux::cmpScanBound(const Frag& frag, unsigned dir, ConstData boundInfo, unsigned boundCount, ConstData entryData, unsigned maxlen) @@ -127,21 +133,21 @@ Dbtux::cmpScanBound(const Frag& frag, unsigned dir, ConstData boundInfo, unsigne if (! boundInfo.ah().isNULL()) { if (! entryData.ah().isNULL()) { jam(); - // current attribute - const unsigned index = boundInfo.ah().getAttributeId(); + // verify attribute id + const Uint32 index = boundInfo.ah().getAttributeId(); ndbrequire(index < frag.m_numAttrs); const DescAttr& descAttr = descEnt.m_descAttr[index]; ndbrequire(entryData.ah().getAttributeId() == descAttr.m_primaryAttrId); - // full data size + // sizes const unsigned size1 = boundInfo.ah().getDataSize(); - ndbrequire(size1 != 0 && size1 == entryData.ah().getDataSize()); - const unsigned size2 = min(size1, len2); + const unsigned size2 = min(entryData.ah().getDataSize(), len2); len2 -= size2; // compare NdbSqlUtil::Cmp* const cmp = c_sqlCmp[index]; const Uint32* const p1 = &boundInfo[AttributeHeaderSize]; const Uint32* const p2 = &entryData[AttributeHeaderSize]; - int ret = (*cmp)(0, p1, p2, size1, size2); + const bool full = (maxlen == MaxAttrDataSize); + int ret = (*cmp)(0, p1, size1 << 2, p2, size2 << 2, full); if (ret != 0) { jam(); return ret; diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp index 3c2613c6cd1..0efcc950e02 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp @@ -340,7 +340,7 @@ operator<<(NdbOut& out, const Dbtux::ScanOp& scan) out << " [savePointId " << dec << scan.m_savePointId << "]"; out << " [accLockOp " << hex << scan.m_accLockOp << "]"; out << " [accLockOps"; - for (unsigned i = 0; i < Dbtux::MaxAccLockOps; i++) { + for (unsigned i = 0; i < scan.m_maxAccLockOps; i++) { if (scan.m_accLockOps[i] != RNIL) out << " " << hex << scan.m_accLockOps[i]; } diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp index 18aa914de05..b6d48b44aef 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp @@ -217,6 +217,7 @@ Dbtux::setKeyAttrs(const Frag& frag) const unsigned numAttrs = frag.m_numAttrs; const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff); for (unsigned i = 0; i < numAttrs; i++) { + jam(); const DescAttr& descAttr = descEnt.m_descAttr[i]; Uint32 size = AttributeDescriptor::getSizeInWords(descAttr.m_attrDesc); // set attr id and fixed size @@ -244,6 +245,26 @@ Dbtux::readKeyAttrs(const Frag& frag, TreeEnt ent, unsigned start, Data keyData) jamEntry(); // TODO handle error ndbrequire(ret > 0); +#ifdef VM_TRACE + if (debugFlags & (DebugMaint | DebugScan)) { + debugOut << "readKeyAttrs:" << endl; + ConstData data = keyData; + Uint32 totalSize = 0; + for (Uint32 i = start; i < numAttrs; i++) { + Uint32 attrId = data.ah().getAttributeId(); + Uint32 dataSize = data.ah().getDataSize(); + debugOut << i << " attrId=" << attrId << " size=" << dataSize; + data += 1; + for (Uint32 j = 0; j < dataSize; j++) { + debugOut << " " << hex << data[0]; + data += 1; + } + debugOut << endl; + totalSize += 1 + dataSize; + } + ndbassert(totalSize == ret); + } +#endif } void @@ -251,7 +272,7 @@ Dbtux::readTablePk(const Frag& frag, TreeEnt ent, Data pkData, unsigned& pkSize) { const Uint32 tableFragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit]; const TupLoc tupLoc = ent.m_tupLoc; - int ret = c_tup->tuxReadPk(tableFragPtrI, tupLoc.getPageId(), tupLoc.getPageOffset(), pkData); + int ret = c_tup->tuxReadPk(tableFragPtrI, tupLoc.getPageId(), tupLoc.getPageOffset(), pkData, true); jamEntry(); // TODO handle error ndbrequire(ret > 0); diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp index 2d256487dcc..4b568badc67 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp @@ -59,7 +59,6 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) // get base fragment id and extra bits const Uint32 fragId = req->fragId & ~1; const Uint32 fragBit = req->fragId & 1; - // get the fragment FragPtr fragPtr; fragPtr.i = RNIL; @@ -71,7 +70,6 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) break; } } - ndbrequire(fragPtr.i != RNIL); Frag& frag = *fragPtr.p; // set up index keys for this operation diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp index cee4aaa8625..021d3d94d8e 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp @@ -16,6 +16,7 @@ #define DBTUX_META_CPP #include "Dbtux.hpp" +#include /* * Create index. @@ -215,17 +216,15 @@ Dbtux::execTUX_ADD_ATTRREQ(Signal* signal) errorCode = TuxAddAttrRef::InvalidAttributeType; break; } -#ifdef dbtux_uses_charset if (descAttr.m_charset != 0) { - CHARSET_INFO *cs = get_charset(descAttr.m_charset, MYF(0)); - // here use the non-binary type + CHARSET_INFO *cs = all_charsets[descAttr.m_charset]; + ndbrequire(cs != 0); if (! NdbSqlUtil::usable_in_ordered_index(descAttr.m_typeId, cs)) { jam(); errorCode = TuxAddAttrRef::InvalidCharset; break; } } -#endif const bool lastAttr = (indexPtr.p->m_numAttrs == fragOpPtr.p->m_numAttrsRecvd); if (ERROR_INSERTED(12003) && fragOpPtr.p->m_fragNo == 0 && attrId == 0 || ERROR_INSERTED(12004) && fragOpPtr.p->m_fragNo == 0 && lastAttr || diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp index 31a11a5c16b..34f2ab3c525 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp @@ -16,6 +16,7 @@ #define DBTUX_SCAN_CPP #include "Dbtux.hpp" +#include void Dbtux::execACC_SCANREQ(Signal* signal) @@ -112,50 +113,89 @@ Dbtux::execACC_SCANREQ(Signal* signal) * keys and that all but possibly last bound is non-strict. * * Finally save the sets of lower and upper bounds (i.e. start key and - * end key). Full bound type (< 4) is included but only the strict bit - * is used since lower and upper have now been separated. + * end key). Full bound type is included but only the strict bit is + * used since lower and upper have now been separated. */ void Dbtux::execTUX_BOUND_INFO(Signal* signal) { jamEntry(); - struct BoundInfo { - int type; - unsigned offset; - unsigned size; - }; - TuxBoundInfo* const sig = (TuxBoundInfo*)signal->getDataPtrSend(); - const TuxBoundInfo reqCopy = *(const TuxBoundInfo*)sig; - const TuxBoundInfo* const req = &reqCopy; // get records + TuxBoundInfo* const sig = (TuxBoundInfo*)signal->getDataPtrSend(); + const TuxBoundInfo* const req = (const TuxBoundInfo*)sig; ScanOp& scan = *c_scanOpPool.getPtr(req->tuxScanPtrI); - Index& index = *c_indexPool.getPtr(scan.m_indexId); - // collect lower and upper bounds + const Index& index = *c_indexPool.getPtr(scan.m_indexId); + const DescEnt& descEnt = getDescEnt(index.m_descPage, index.m_descOff); + // collect normalized lower and upper bounds + struct BoundInfo { + int type2; // with EQ -> LE/GE + Uint32 offset; // offset in xfrmData + Uint32 size; + }; BoundInfo boundInfo[2][MaxIndexAttributes]; + const unsigned dstSize = 1024 * MAX_XFRM_MULTIPLY; + Uint32 xfrmData[dstSize]; + Uint32 dstPos = 0; // largest attrId seen plus one Uint32 maxAttrId[2] = { 0, 0 }; - unsigned offset = 0; - const Uint32* const data = (Uint32*)sig + TuxBoundInfo::SignalLength; // walk through entries + const Uint32* const data = (Uint32*)sig + TuxBoundInfo::SignalLength; + Uint32 offset = 0; while (offset + 2 <= req->boundAiLength) { jam(); const unsigned type = data[offset]; - if (type > 4) { - jam(); - scan.m_state = ScanOp::Invalid; - sig->errorCode = TuxBoundInfo::InvalidAttrInfo; - return; - } const AttributeHeader* ah = (const AttributeHeader*)&data[offset + 1]; const Uint32 attrId = ah->getAttributeId(); const Uint32 dataSize = ah->getDataSize(); - if (attrId >= index.m_numAttrs) { + if (type > 4 || attrId >= index.m_numAttrs || dstPos + 2 + dataSize > dstSize) { jam(); scan.m_state = ScanOp::Invalid; sig->errorCode = TuxBoundInfo::InvalidAttrInfo; return; } + // copy header + xfrmData[dstPos + 0] = data[offset + 0]; + xfrmData[dstPos + 1] = data[offset + 1]; + // copy bound value + Uint32 dstWords = 0; + if (! ah->isNULL()) { + jam(); + const DescAttr& descAttr = descEnt.m_descAttr[attrId]; + Uint32 srcBytes = AttributeDescriptor::getSizeInBytes(descAttr.m_attrDesc); + Uint32 srcWords = (srcBytes + 3) / 4; + if (srcWords != dataSize) { + jam(); + scan.m_state = ScanOp::Invalid; + sig->errorCode = TuxBoundInfo::InvalidAttrInfo; + return; + } + uchar* dstPtr = (uchar*)&xfrmData[dstPos + 2]; + const uchar* srcPtr = (const uchar*)&data[offset + 2]; + if (descAttr.m_charset == 0) { + memcpy(dstPtr, srcPtr, srcWords << 2); + dstWords = srcWords; + } else { + jam(); + CHARSET_INFO* cs = all_charsets[descAttr.m_charset]; + Uint32 xmul = cs->strxfrm_multiply; + if (xmul == 0) + xmul = 1; + Uint32 dstLen = xmul * srcBytes; + if (dstLen > ((dstSize - dstPos) << 2)) { + jam(); + scan.m_state = ScanOp::Invalid; + sig->errorCode = TuxBoundInfo::TooMuchAttrInfo; + return; + } + Uint32 n = (*cs->coll->strnxfrm)(cs, dstPtr, dstLen, srcPtr, srcBytes); + while ((n & 3) != 0) { + dstPtr[n++] = 0; + } + dstWords = n / 4; + } + } for (unsigned j = 0; j <= 1; j++) { + jam(); // check if lower/upper bit matches const unsigned luBit = (j << 1); if ((type & 0x2) != luBit && type != 4) @@ -164,29 +204,35 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal) const unsigned type2 = (type & 0x1) | luBit; // fill in any gap while (maxAttrId[j] <= attrId) { + jam(); BoundInfo& b = boundInfo[j][maxAttrId[j]++]; - b.type = -1; + b.type2 = -1; } BoundInfo& b = boundInfo[j][attrId]; - if (b.type != -1) { - // compare with previous bound - if (b.type != (int)type2 || - b.size != 2 + dataSize || - memcmp(&data[b.offset + 2], &data[offset + 2], dataSize << 2) != 0) { + if (b.type2 != -1) { + // compare with previously defined bound + if (b.type2 != (int)type2 || + b.size != 2 + dstWords || + memcmp(&xfrmData[b.offset + 2], &xfrmData[dstPos + 2], dstWords << 2) != 0) { jam(); scan.m_state = ScanOp::Invalid; sig->errorCode = TuxBoundInfo::InvalidBounds; return; } } else { + // fix length + AttributeHeader* ah = (AttributeHeader*)&xfrmData[dstPos + 1]; + ah->setDataSize(dstWords); // enter new bound - b.type = type2; - b.offset = offset; - b.size = 2 + dataSize; + jam(); + b.type2 = type2; + b.offset = dstPos; + b.size = 2 + dstWords; } } // jump to next offset += 2 + dataSize; + dstPos += 2 + dstWords; } if (offset != req->boundAiLength) { jam(); @@ -200,13 +246,13 @@ Dbtux::execTUX_BOUND_INFO(Signal* signal) jam(); const BoundInfo& b = boundInfo[j][i]; // check for gap or strict bound before last - if (b.type == -1 || (i + 1 < maxAttrId[j] && (b.type & 0x1))) { + if (b.type2 == -1 || (i + 1 < maxAttrId[j] && (b.type2 & 0x1))) { jam(); scan.m_state = ScanOp::Invalid; sig->errorCode = TuxBoundInfo::InvalidBounds; return; } - bool ok = scan.m_bound[j]->append(&data[b.offset], b.size); + bool ok = scan.m_bound[j]->append(&xfrmData[b.offset], b.size); if (! ok) { jam(); scan.m_state = ScanOp::Invalid; diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/ndb/src/ndbapi/NdbDictionaryImpl.cpp index f56c3ce94c2..3f6ef69ca96 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -1546,6 +1546,11 @@ NdbDictInterface::createOrAlterTable(Ndb & ndb, m_error.code = 743; return -1; } + // distribution key not supported for Char attribute + if (col->m_distributionKey && col->m_cs != NULL) { + m_error.code = 745; + return -1; + } // charset in upper half of precision if (col->getCharType()) { tmpAttr.AttributeExtPrecision |= (col->m_cs->number << 16); diff --git a/ndb/src/ndbapi/NdbOperationDefine.cpp b/ndb/src/ndbapi/NdbOperationDefine.cpp index d9aa860f71f..aabb58fc974 100644 --- a/ndb/src/ndbapi/NdbOperationDefine.cpp +++ b/ndb/src/ndbapi/NdbOperationDefine.cpp @@ -520,16 +520,6 @@ NdbOperation::setValue( const NdbColumnImpl* tAttrInfo, // Insert Attribute Id into ATTRINFO part. const Uint32 sizeInBytes = tAttrInfo->m_attrSize * tAttrInfo->m_arraySize; - CHARSET_INFO* cs = tAttrInfo->m_cs; - // invalid data can crash kernel - if (cs != NULL && - (*cs->cset->well_formed_len)(cs, - aValue, - aValue + sizeInBytes, - sizeInBytes) != sizeInBytes) { - setErrorCodeAbort(744); - return -1; - } #if 0 tAttrSize = tAttrInfo->theAttrSize; tArraySize = tAttrInfo->theArraySize; diff --git a/ndb/src/ndbapi/NdbOperationSearch.cpp b/ndb/src/ndbapi/NdbOperationSearch.cpp index 7ae5bd70a11..972dc3d94eb 100644 --- a/ndb/src/ndbapi/NdbOperationSearch.cpp +++ b/ndb/src/ndbapi/NdbOperationSearch.cpp @@ -62,7 +62,6 @@ NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo, Uint32 tData; Uint32 tKeyInfoPosition; const char* aValue = aValuePassed; - Uint64 xfrmData[512]; Uint64 tempData[512]; if ((theStatus == OperationDefined) && @@ -140,21 +139,6 @@ NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo, aValue = (char*)&tempData[0]; }//if } - const char* aValueToWrite = aValue; - - CHARSET_INFO* cs = tAttrInfo->m_cs; - if (cs != 0) { - // current limitation: strxfrm does not increase length - assert(cs->strxfrm_multiply <= 1); - ((Uint32*)xfrmData)[sizeInBytes >> 2] = 0; - unsigned n = - (*cs->coll->strnxfrm)(cs, - (uchar*)xfrmData, sizeof(xfrmData), - (const uchar*)aValue, sizeInBytes); - while (n < sizeInBytes) - ((uchar*)xfrmData)[n++] = 0x20; - aValue = (char*)xfrmData; - } Uint32 totalSizeInWords = (sizeInBytes + 3)/4; // Inc. bits in last word @@ -200,13 +184,6 @@ NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo, *************************************************************************/ if ((tOpType == InsertRequest) || (tOpType == WriteRequest)) { - // invalid data can crash kernel - if (cs != NULL && - (*cs->cset->well_formed_len)(cs, - aValueToWrite, - aValueToWrite + sizeInBytes, - sizeInBytes) != sizeInBytes) - goto equal_error4; Uint32 ahValue; const Uint32 sz = totalSizeInWords; @@ -224,7 +201,7 @@ NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo, } insertATTRINFO( ahValue ); - insertATTRINFOloop((Uint32*)aValueToWrite, sz); + insertATTRINFOloop((Uint32*)aValue, sz); }//if /************************************************************************** @@ -321,10 +298,6 @@ NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo, equal_error3: setErrorCodeAbort(4209); return -1; - - equal_error4: - setErrorCodeAbort(744); - return -1; } /****************************************************************************** diff --git a/ndb/src/ndbapi/NdbScanOperation.cpp b/ndb/src/ndbapi/NdbScanOperation.cpp index f4973b2e66f..671996bc534 100644 --- a/ndb/src/ndbapi/NdbScanOperation.cpp +++ b/ndb/src/ndbapi/NdbScanOperation.cpp @@ -1072,29 +1072,6 @@ NdbIndexScanOperation::setBound(const NdbColumnImpl* tAttrInfo, setErrorCodeAbort(4209); return -1; } - - // normalize char bound - CHARSET_INFO* cs = tAttrInfo->m_cs; - Uint64 xfrmData[1001]; - if (cs != NULL && aValue != NULL) { - // current limitation: strxfrm does not increase length - assert(cs->strxfrm_multiply <= 1); - ((Uint32*)xfrmData)[len >> 2] = 0; - unsigned n = - (*cs->coll->strnxfrm)(cs, - (uchar*)xfrmData, sizeof(xfrmData), - (const uchar*)aValue, len); - - while (n < len) - ((uchar*)xfrmData)[n++] = 0x20; - - if(len & 3) - { - len += (4 - (len & 3)); - } - - aValue = (char*)xfrmData; - } // insert attribute header Uint32 tIndexAttrId = tAttrInfo->m_attrId; @@ -1117,7 +1094,7 @@ NdbIndexScanOperation::setBound(const NdbColumnImpl* tAttrInfo, theTotalNrOfKeyWordInSignal = currLen + totalLen; } else { if(!aligned || !nobytes){ - Uint32 *tempData = (Uint32*)xfrmData; + Uint32 tempData[2000]; tempData[0] = type; tempData[1] = ahValue; tempData[2 + (len >> 2)] = 0; @@ -1273,10 +1250,10 @@ NdbIndexScanOperation::compare(Uint32 skip, Uint32 cols, return (r1_null ? -1 : 1); } const NdbColumnImpl & col = NdbColumnImpl::getImpl(* r1->m_column); - Uint32 size = (r1->theAttrSize * r1->theArraySize + 3) / 4; + Uint32 len = r1->theAttrSize * r1->theArraySize; if(!r1_null){ const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getType(col.m_extType); - int r = (*sqlType.m_cmp)(col.m_cs, d1, d2, size, size); + int r = (*sqlType.m_cmp)(col.m_cs, d1, len, d2, len, true); if(r){ assert(r != NdbSqlUtil::CmpUnknown); return r; diff --git a/ndb/src/ndbapi/ndberror.c b/ndb/src/ndbapi/ndberror.c index 638996530e2..9762eb85de1 100644 --- a/ndb/src/ndbapi/ndberror.c +++ b/ndb/src/ndbapi/ndberror.c @@ -205,6 +205,7 @@ ErrorBundle ErrorCodes[] = { */ { 892, IE, "Inconsistent hash index. The index needs to be dropped and recreated" }, { 895, IE, "Inconsistent ordered index. The index needs to be dropped and recreated" }, + { 896, IE, "Tuple corrupted - wrong checksum or column data in invalid format" }, { 202, IE, "202" }, { 203, IE, "203" }, { 207, IE, "207" }, @@ -311,6 +312,7 @@ ErrorBundle ErrorCodes[] = { { 742, SE, "Unsupported attribute type in index" }, { 743, SE, "Unsupported character set in table or index" }, { 744, SE, "Character string is invalid for given character set" }, + { 745, SE, "Distribution key not supported for char attribute (use binary attribute)" }, { 241, SE, "Invalid schema object version" }, { 283, SE, "Table is being dropped" }, { 284, SE, "Table not defined in transaction coordinator" }, diff --git a/ndb/test/ndbapi/Makefile.am b/ndb/test/ndbapi/Makefile.am index 90f0a08d633..c050c42a701 100644 --- a/ndb/test/ndbapi/Makefile.am +++ b/ndb/test/ndbapi/Makefile.am @@ -32,7 +32,7 @@ testTransactions \ testDeadlock \ test_event ndbapi_slow_select testReadPerf testLcp \ testPartitioning \ -testBitfield +testBitfield foo #flexTimedAsynch #testBlobs @@ -73,6 +73,7 @@ testReadPerf_SOURCES = testReadPerf.cpp testLcp_SOURCES = testLcp.cpp testPartitioning_SOURCES = testPartitioning.cpp testBitfield_SOURCES = testBitfield.cpp +foo_SOURCES = foo.cpp INCLUDES_LOC = -I$(top_srcdir)/ndb/include/kernel @@ -89,3 +90,4 @@ testBackup_LDADD = $(LDADD) bank/libbank.a %::SCCS/s.% + diff --git a/ndb/test/ndbapi/testOIBasic.cpp b/ndb/test/ndbapi/testOIBasic.cpp index 41f0686e63b..2dd63178d01 100644 --- a/ndb/test/ndbapi/testOIBasic.cpp +++ b/ndb/test/ndbapi/testOIBasic.cpp @@ -37,6 +37,7 @@ struct Opt { unsigned m_batch; const char* m_bound; const char* m_case; + bool m_collsp; bool m_core; const char* m_csname; CHARSET_INFO* m_cs; @@ -55,17 +56,18 @@ struct Opt { unsigned m_scanbat; unsigned m_scanpar; unsigned m_scanstop; - unsigned m_seed; + int m_seed; unsigned m_subloop; const char* m_table; unsigned m_threads; - unsigned m_v; + int m_v; // int for lint Opt() : m_batch(32), m_bound("01234"), m_case(0), + m_collsp(false), m_core(false), - m_csname("latin1_bin"), + m_csname("random"), m_cs(0), m_die(0), m_dups(false), @@ -82,7 +84,7 @@ struct Opt { m_scanbat(0), m_scanpar(0), m_scanstop(0), - m_seed(0), + m_seed(-1), m_subloop(4), m_table(0), m_threads(10), @@ -104,12 +106,13 @@ printhelp() << " -batch N pk operations in batch [" << d.m_batch << "]" << endl << " -bound xyz use only these bound types 0-4 [" << d.m_bound << "]" << endl << " -case abc only given test cases (letters a-z)" << endl + << " -collsp use strnncollsp instead of strnxfrm" << endl << " -core core dump on error [" << d.m_core << "]" << endl - << " -csname S charset (collation) of non-pk char column [" << d.m_csname << "]" << endl + << " -csname S charset or collation [" << d.m_csname << "]" << endl << " -die nnn exit immediately on NDB error code nnn" << endl << " -dups allow duplicate tuples from index scan [" << d.m_dups << "]" << endl << " -fragtype T fragment type single/small/medium/large" << endl - << " -index xyz only given index numbers (digits 1-9)" << endl + << " -index xyz only given index numbers (digits 0-9)" << endl << " -loop N loop count full suite 0=forever [" << d.m_loop << "]" << endl << " -nologging create tables in no-logging mode" << endl << " -noverify skip index verifications" << endl @@ -118,9 +121,9 @@ printhelp() << " -samples N samples for some timings (0=all) [" << d.m_samples << "]" << endl << " -scanbat N scan batch per fragment (ignored by ndb api) [" << d.m_scanbat << "]" << endl << " -scanpar N scan parallelism [" << d.m_scanpar << "]" << endl - << " -seed N srandom seed 0=loop number[" << d.m_seed << "]" << endl + << " -seed N srandom seed 0=loop number -1=random [" << d.m_seed << "]" << endl << " -subloop N subtest loop count [" << d.m_subloop << "]" << endl - << " -table xyz only given table numbers (digits 1-9)" << endl + << " -table xyz only given table numbers (digits 0-9)" << endl << " -threads N number of threads [" << d.m_threads << "]" << endl << " -vN verbosity [" << d.m_v << "]" << endl << " -h or -help print this help text" << endl @@ -135,9 +138,33 @@ static const bool g_store_null_key = true; // compare NULL like normal value (NULL < not NULL, NULL == NULL) static const bool g_compare_null = true; +static const char* hexstr = "0123456789abcdef"; + +// random ints + +static unsigned +urandom(unsigned n) +{ + if (n == 0) + return 0; + unsigned i = random() % n; + return i; +} + +static int +irandom(unsigned n) +{ + if (n == 0) + return 0; + int i = random() % n; + if (random() & 0x1) + i = -i; + return i; +} + // log and error macros -static NdbMutex *ndbout_mutex= NULL; +static NdbMutex *ndbout_mutex = NULL; static unsigned getthrno(); @@ -198,7 +225,7 @@ getthrstr() return -1; \ } while (0) -// method parameters base class +// method parameters class Thr; class Con; @@ -222,6 +249,8 @@ struct Par : public Opt { // value calculation unsigned m_range; unsigned m_pctrange; + unsigned m_pctbrange; + int m_bdir; // choice of key bool m_randomkey; // do verify after read @@ -240,7 +269,9 @@ struct Par : public Opt { m_slno(0), m_totrows(m_threads * m_rows), m_range(m_rows), - m_pctrange(0), + m_pctrange(40), + m_pctbrange(80), + m_bdir(0), m_randomkey(false), m_verify(false), m_deadlock(false) { @@ -248,15 +279,15 @@ struct Par : public Opt { }; static bool -usetable(unsigned i) +usetable(Par par, unsigned i) { - return g_opt.m_table == 0 || strchr(g_opt.m_table, '1' + i) != 0; + return par.m_table == 0 || strchr(par.m_table, '0' + i) != 0; } static bool -useindex(unsigned i) +useindex(Par par, unsigned i) { - return g_opt.m_index == 0 || strchr(g_opt.m_index, '1' + i) != 0; + return par.m_index == 0 || strchr(par.m_index, '0' + i) != 0; } static unsigned @@ -382,38 +413,196 @@ Lst::reset() m_cnt = 0; } +// character sets + +static const unsigned maxcsnumber = 512; +static const unsigned maxcharcount = 32; +static const unsigned maxcharsize = 4; +static const unsigned maxxmulsize = 8; + +// single mb char +struct Chr { + unsigned char m_bytes[maxcharsize]; + unsigned char m_xbytes[maxxmulsize * maxcharsize]; + unsigned m_size; + Chr(); +}; + +Chr::Chr() +{ + memset(m_bytes, 0, sizeof(m_bytes)); + memset(m_xbytes, 0, sizeof(m_xbytes)); + m_size = 0; +} + +// charset and random valid chars to use +struct Chs { + CHARSET_INFO* m_cs; + unsigned m_xmul; + Chr* m_chr; + Chs(CHARSET_INFO* cs); + ~Chs(); +}; + +Chs::Chs(CHARSET_INFO* cs) : + m_cs(cs) +{ + m_xmul = m_cs->strxfrm_multiply; + if (m_xmul == 0) + m_xmul = 1; + assert(m_xmul <= maxxmulsize); + m_chr = new Chr [maxcharcount]; + unsigned i = 0; + unsigned miss1 = 0; + unsigned miss2 = 0; + while (i < maxcharcount) { + unsigned char* bytes = m_chr[i].m_bytes; + unsigned char* xbytes = m_chr[i].m_xbytes; + unsigned size = m_cs->mbminlen + urandom(m_cs->mbmaxlen - m_cs->mbminlen + 1); + assert(m_cs->mbminlen <= size && size <= m_cs->mbmaxlen); + // prefer longer chars + if (size == m_cs->mbminlen && m_cs->mbminlen < m_cs->mbmaxlen && urandom(5) != 0) + continue; + for (unsigned j = 0; j < size; j++) { + bytes[j] = urandom(256); + } + const char* sbytes = (const char*)bytes; + if ((*cs->cset->well_formed_len)(cs, sbytes, sbytes + size, 1) != size) { + miss1++; + continue; + } + // do not trust well_formed_len currently + memset(xbytes, 0, sizeof(xbytes)); + // currently returns buffer size always + int xlen = (*cs->coll->strnxfrm)(cs, xbytes, m_xmul * size, bytes, size); + // check we got something + bool xok = false; + for (unsigned j = 0; j < xlen; j++) { + if (xbytes[j] != 0) { + xok = true; + break; + } + } + if (! xok) { + miss2++; + continue; + } + // occasional duplicate char is ok + m_chr[i].m_size = size; + i++; + } + bool disorder = true; + unsigned bubbels = 0; + while (disorder) { + disorder = false; + for (unsigned i = 1; i < maxcharcount; i++) { + unsigned len = sizeof(m_chr[i].m_xbytes); + if (memcmp(m_chr[i-1].m_xbytes, m_chr[i].m_xbytes, len) > 0) { + Chr chr = m_chr[i]; + m_chr[i] = m_chr[i-1]; + m_chr[i-1] = chr; + disorder = true; + bubbels++; + } + } + } + LL3("inited charset " << cs->name << " miss1=" << miss1 << " miss2=" << miss2 << " bubbels=" << bubbels); +} + +Chs::~Chs() +{ + delete [] m_chr; +} + +static Chs* cslist[maxcsnumber]; + +static void +resetcslist() +{ + for (unsigned i = 0; i < maxcsnumber; i++) { + delete cslist[i]; + cslist[i] = 0; + } +} + +static Chs* +getcs(Par par) +{ + CHARSET_INFO* cs; + if (par.m_cs != 0) { + cs = par.m_cs; + } else { + while (1) { + unsigned n = urandom(maxcsnumber); + cs = get_charset(n, MYF(0)); + if (cs != 0) { + // prefer complex charsets + if (cs->mbmaxlen != 1 || urandom(5) == 0) + break; + } + } + } + if (cslist[cs->number] == 0) + cslist[cs->number] = new Chs(cs); + return cslist[cs->number]; +} + // tables and indexes // Col - table column struct Col { + const class Tab& m_tab; unsigned m_num; const char* m_name; bool m_pk; NdbDictionary::Column::Type m_type; unsigned m_length; + unsigned m_bytelength; bool m_nullable; + const Chs* m_chs; + Col(const class Tab& tab, unsigned num, const char* name, bool pk, NdbDictionary::Column::Type type, unsigned length, bool nullable, const Chs* chs); + ~Col(); + bool equal(const Col& col2) const; void verify(const void* addr) const; }; +Col::Col(const class Tab& tab, unsigned num, const char* name, bool pk, NdbDictionary::Column::Type type, unsigned length, bool nullable, const Chs* chs) : + m_tab(tab), + m_num(num), + m_name(strcpy(new char [strlen(name) + 1], name)), + m_pk(pk), + m_type(type), + m_length(length), + m_bytelength(length * (chs == 0 ? 1 : chs->m_cs->mbmaxlen)), + m_nullable(nullable), + m_chs(chs) +{ +} + +Col::~Col() +{ + delete [] m_name; +} + +bool +Col::equal(const Col& col2) const +{ + return m_type == col2.m_type && m_length == col2.m_length && m_chs == col2.m_chs; +} + void Col::verify(const void* addr) const { switch (m_type) { case NdbDictionary::Column::Unsigned: break; - case NdbDictionary::Column::Varchar: + case NdbDictionary::Column::Char: { - const unsigned char* p = (const unsigned char*)addr; - unsigned n = (p[0] << 8) | p[1]; - assert(n <= m_length); - unsigned i; - for (i = 0; i < n; i++) { - assert(p[2 + i] != 0); - } - for (i = n; i < m_length; i++) { - assert(p[2 + i] == 0); - } + CHARSET_INFO* cs = m_chs->m_cs; + const char* src = (const char*)addr; + unsigned len = m_bytelength; + assert((*cs->cset->well_formed_len)(cs, src, src + len, 0xffff) == len); } break; default: @@ -425,14 +614,16 @@ Col::verify(const void* addr) const static NdbOut& operator<<(NdbOut& out, const Col& col) { - out << "col " << col.m_num; - out << " " << col.m_name; + out << "col[" << col.m_num << "] " << col.m_name; switch (col.m_type) { case NdbDictionary::Column::Unsigned: out << " unsigned"; break; - case NdbDictionary::Column::Varchar: - out << " varchar(" << col.m_length << ")"; + case NdbDictionary::Column::Char: + { + CHARSET_INFO* cs = col.m_chs->m_cs; + out << " char(" << col.m_length << "*" << cs->mbmaxlen << ";" << cs->name << ")"; + } break; default: out << "type" << (int)col.m_type; @@ -447,25 +638,60 @@ operator<<(NdbOut& out, const Col& col) // ICol - index column struct ICol { + const class ITab& m_itab; unsigned m_num; - struct Col m_col; + const Col& m_col; + ICol(const class ITab& itab, unsigned num, const Col& col); + ~ICol(); }; +ICol::ICol(const class ITab& itab, unsigned num, const Col& col) : + m_itab(itab), + m_num(num), + m_col(col) +{ +} + +ICol::~ICol() +{ +} + // ITab - index struct ITab { + const class Tab& m_tab; const char* m_name; unsigned m_icols; - const ICol* m_icol; + const ICol** m_icol; + ITab(const class Tab& tab, const char* name, unsigned icols); + ~ITab(); }; +ITab::ITab(const class Tab& tab, const char* name, unsigned icols) : + m_tab(tab), + m_name(strcpy(new char [strlen(name) + 1], name)), + m_icols(icols), + m_icol(new const ICol* [icols + 1]) +{ + for (unsigned i = 0; i <= m_icols; i++) + m_icol[0] = 0; +} + +ITab::~ITab() +{ + delete [] m_name; + for (unsigned i = 0; i < m_icols; i++) + delete m_icol[i]; + delete [] m_icol; +} + static NdbOut& operator<<(NdbOut& out, const ITab& itab) { - out << "itab " << itab.m_name << " " << itab.m_icols; + out << "itab " << itab.m_name << " icols=" << itab.m_icols; for (unsigned k = 0; k < itab.m_icols; k++) { out << endl; - out << "icol " << k << " " << itab.m_icol[k].m_col; + out << "icol[" << k << "] " << itab.m_icol[k]->m_col; } return out; } @@ -475,200 +701,241 @@ operator<<(NdbOut& out, const ITab& itab) struct Tab { const char* m_name; unsigned m_cols; - const Col* m_col; + const Col** m_col; unsigned m_itabs; - const ITab* m_itab; + const ITab** m_itab; + // pk must contain an Unsigned column + unsigned m_keycol; + Tab(const char* name, unsigned cols, unsigned itabs, unsigned keycol); + ~Tab(); }; +Tab::Tab(const char* name, unsigned cols, unsigned itabs, unsigned keycol) : + m_name(strcpy(new char [strlen(name) + 1], name)), + m_cols(cols), + m_col(new const Col* [cols + 1]), + m_itabs(itabs), + m_itab(new const ITab* [itabs + 1]), + m_keycol(keycol) +{ + for (unsigned i = 0; i <= cols; i++) + m_col[i] = 0; + for (unsigned i = 0; i <= itabs; i++) + m_itab[i] = 0; +} + +Tab::~Tab() +{ + delete [] m_name; + for (unsigned i = 0; i < m_cols; i++) + delete m_col[i]; + delete [] m_col; + for (unsigned i = 0; i < m_itabs; i++) + delete m_itab[i]; + delete [] m_itab; +} + static NdbOut& operator<<(NdbOut& out, const Tab& tab) { - out << "tab " << tab.m_name << " " << tab.m_cols; + out << "tab " << tab.m_name << " cols=" << tab.m_cols; for (unsigned k = 0; k < tab.m_cols; k++) { out << endl; - out << tab.m_col[k]; + out << *tab.m_col[k]; } for (unsigned i = 0; i < tab.m_itabs; i++) { - if (! useindex(i)) + if (tab.m_itab[i] == 0) continue; out << endl; - out << tab.m_itab[i]; + out << *tab.m_itab[i]; } return out; } -// tt1 + tt1x1 tt1x2 tt1x3 tt1x4 tt1x5 +// make table structs -static const Col -tt1col[] = { - { 0, "A", 1, NdbDictionary::Column::Unsigned, 1, 0 }, - { 1, "B", 0, NdbDictionary::Column::Unsigned, 1, 1 }, - { 2, "C", 0, NdbDictionary::Column::Unsigned, 1, 1 }, - { 3, "D", 0, NdbDictionary::Column::Unsigned, 1, 1 }, - { 4, "E", 0, NdbDictionary::Column::Unsigned, 1, 1 } -}; +static const Tab** tablist = 0; +static unsigned tabcount = 0; -static const ICol -tt1x1col[] = { - { 0, tt1col[0] } -}; +static void +verifytables() +{ + for (unsigned j = 0; j < tabcount; j++) { + const Tab* t = tablist[j]; + if (t == 0) + continue; + assert(t->m_cols != 0 && t->m_col != 0); + for (unsigned k = 0; k < t->m_cols; k++) { + const Col* c = t->m_col[k]; + assert(c != 0 && c->m_num == k); + assert(! (c->m_pk && c->m_nullable)); + } + assert(t->m_col[t->m_cols] == 0); + { + assert(t->m_keycol < t->m_cols); + const Col* c = t->m_col[t->m_keycol]; + assert(c->m_pk && c->m_type == NdbDictionary::Column::Unsigned); + } + assert(t->m_itabs != 0 && t->m_itab != 0); + for (unsigned i = 0; i < t->m_itabs; i++) { + const ITab* x = t->m_itab[i]; + if (x == 0) + continue; + assert(x != 0 && x->m_icols != 0 && x->m_icol != 0); + for (unsigned k = 0; k < x->m_icols; k++) { + const ICol* c = x->m_icol[k]; + assert(c != 0 && c->m_num == k && c->m_col.m_num < t->m_cols); + } + } + assert(t->m_itab[t->m_itabs] == 0); + } +} -static const ICol -tt1x2col[] = { - { 0, tt1col[1] } -}; - -static const ICol -tt1x3col[] = { - { 0, tt1col[1] }, - { 1, tt1col[2] } -}; - -static const ICol -tt1x4col[] = { - { 0, tt1col[3] }, - { 1, tt1col[2] }, - { 2, tt1col[1] } -}; - -static const ICol -tt1x5col[] = { - { 0, tt1col[1] }, - { 1, tt1col[4] }, - { 2, tt1col[2] }, - { 3, tt1col[3] } -}; - -static const ITab -tt1x1 = { - "TT1X1", 1, tt1x1col -}; - -static const ITab -tt1x2 = { - "TT1X2", 1, tt1x2col -}; - -static const ITab -tt1x3 = { - "TT1X3", 2, tt1x3col -}; - -static const ITab -tt1x4 = { - "TT1X4", 3, tt1x4col -}; - -static const ITab -tt1x5 = { - "TT1X5", 4, tt1x5col -}; - -static const ITab -tt1itab[] = { - tt1x1, - tt1x2, - tt1x3, - tt1x4, - tt1x5 -}; - -static const Tab -tt1 = { - "TT1", 5, tt1col, 5, tt1itab -}; - -// tt2 + tt2x1 tt2x2 tt2x3 tt2x4 tt2x5 - -static const Col -tt2col[] = { - { 0, "A", 1, NdbDictionary::Column::Unsigned, 1, 0 }, - { 1, "B", 0, NdbDictionary::Column::Unsigned, 1, 1 }, - { 2, "C", 0, NdbDictionary::Column::Varchar, 20, 1 }, - { 3, "D", 0, NdbDictionary::Column::Varchar, 5, 1 }, - { 4, "E", 0, NdbDictionary::Column::Varchar, 5, 1 } -}; - -static const ICol -tt2x1col[] = { - { 0, tt2col[0] } -}; - -static const ICol -tt2x2col[] = { - { 0, tt2col[1] }, - { 1, tt2col[2] } -}; - -static const ICol -tt2x3col[] = { - { 0, tt2col[2] }, - { 1, tt2col[1] } -}; - -static const ICol -tt2x4col[] = { - { 0, tt2col[3] }, - { 1, tt2col[4] } -}; - -static const ICol -tt2x5col[] = { - { 0, tt2col[4] }, - { 1, tt2col[3] }, - { 2, tt2col[2] }, - { 3, tt2col[1] } -}; - -static const ITab -tt2x1 = { - "TT2X1", 1, tt2x1col -}; - -static const ITab -tt2x2 = { - "TT2X2", 2, tt2x2col -}; - -static const ITab -tt2x3 = { - "TT2X3", 2, tt2x3col -}; - -static const ITab -tt2x4 = { - "TT2X4", 2, tt2x4col -}; - -static const ITab -tt2x5 = { - "TT2X5", 4, tt2x5col -}; - -static const ITab -tt2itab[] = { - tt2x1, - tt2x2, - tt2x3, - tt2x4, - tt2x5 -}; - -static const Tab -tt2 = { - "TT2", 5, tt2col, 5, tt2itab -}; - -// all tables - -static const Tab -tablist[] = { - tt1, - tt2 -}; - -static const unsigned -tabcount = sizeof(tablist) / sizeof(tablist[0]); +static void +makebuiltintables(Par par) +{ + LL2("makebuiltintables"); + resetcslist(); + tabcount = 3; + if (tablist == 0) { + tablist = new const Tab* [tabcount]; + for (unsigned j = 0; j < tabcount; j++) { + tablist[j] = 0; + } + } else { + for (unsigned j = 0; j < tabcount; j++) { + delete tablist[j]; + tablist[j] = 0; + } + } + // ti0 - basic + if (usetable(par, 0)) { + const Tab* t = new Tab("ti0", 5, 5, 0); + // name - pk - type - length - nullable - cs + t->m_col[0] = new Col(*t, 0, "a", 1, NdbDictionary::Column::Unsigned, 1, 0, 0); + t->m_col[1] = new Col(*t, 1, "b", 0, NdbDictionary::Column::Unsigned, 1, 1, 0); + t->m_col[2] = new Col(*t, 2, "c", 0, NdbDictionary::Column::Unsigned, 1, 1, 0); + t->m_col[3] = new Col(*t, 3, "d", 0, NdbDictionary::Column::Unsigned, 1, 1, 0); + t->m_col[4] = new Col(*t, 4, "e", 0, NdbDictionary::Column::Unsigned, 1, 1, 0); + if (useindex(par, 0)) { + // a + const ITab* x = t->m_itab[0] = new ITab(*t, "ti0x0", 1); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[0]); + } + if (useindex(par, 1)) { + // b + const ITab* x = t->m_itab[1] = new ITab(*t, "ti0x1", 1); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[1]); + } + if (useindex(par, 2)) { + // b, c + const ITab* x = t->m_itab[2] = new ITab(*t, "ti0x2", 2); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[1]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[2]); + } + if (useindex(par, 3)) { + // d, c, b + const ITab* x = t->m_itab[3] = new ITab(*t, "ti0x3", 3); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[3]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[2]); + x->m_icol[2] = new ICol(*x, 2, *t->m_col[1]); + } + if (useindex(par, 4)) { + // b, e, c, d + const ITab* x = t->m_itab[4] = new ITab(*t, "ti0x4", 4); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[1]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[4]); + x->m_icol[2] = new ICol(*x, 2, *t->m_col[2]); + x->m_icol[3] = new ICol(*x, 3, *t->m_col[3]); + } + tablist[0] = t; + } + // ti1 - simple char fields + if (usetable(par, 1)) { + const Tab* t = new Tab("ti1", 5, 5, 1); + // name - pk - type - length - nullable - cs + t->m_col[0] = new Col(*t, 0, "a", 0, NdbDictionary::Column::Unsigned, 1, 1, 0); + t->m_col[1] = new Col(*t, 1, "b", 1, NdbDictionary::Column::Unsigned, 1, 0, 0); + t->m_col[2] = new Col(*t, 2, "c", 0, NdbDictionary::Column::Char, 20, 1, getcs(par)); + t->m_col[3] = new Col(*t, 3, "d", 0, NdbDictionary::Column::Char, 5, 1, getcs(par)); + t->m_col[4] = new Col(*t, 4, "e", 0, NdbDictionary::Column::Char, 5, 1, getcs(par)); + if (useindex(par, 0)) { + // b + const ITab* x = t->m_itab[0] = new ITab(*t, "ti1x0", 1); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[1]); + } + if (useindex(par, 1)) { + // a, c + const ITab* x = t->m_itab[1] = new ITab(*t, "ti1x1", 2); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[0]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[2]); + } + if (useindex(par, 2)) { + // c, a + const ITab* x = t->m_itab[2] = new ITab(*t, "ti1x2", 2); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[2]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[0]); + } + if (useindex(par, 3)) { + // e + const ITab* x = t->m_itab[3] = new ITab(*t, "ti1x3", 1); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[4]); + } + if (useindex(par, 4)) { + // e, d, c, b + const ITab* x = t->m_itab[4] = new ITab(*t, "ti1x4", 4); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[4]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[3]); + x->m_icol[2] = new ICol(*x, 2, *t->m_col[2]); + x->m_icol[3] = new ICol(*x, 3, *t->m_col[1]); + } + tablist[1] = t; + } + // ti2 - complex char fields + if (usetable(par, 2)) { + const Tab* t = new Tab("ti2", 5, 5, 2); + // name - pk - type - length - nullable - cs + t->m_col[0] = new Col(*t, 0, "a", 1, NdbDictionary::Column::Char, 101, 0, getcs(par)); + t->m_col[1] = new Col(*t, 1, "b", 0, NdbDictionary::Column::Char, 4, 1, getcs(par)); + t->m_col[2] = new Col(*t, 2, "c", 1, NdbDictionary::Column::Unsigned, 1, 0, 0); + t->m_col[3] = new Col(*t, 3, "d", 1, NdbDictionary::Column::Char, 3, 0, getcs(par)); + t->m_col[4] = new Col(*t, 4, "e", 0, NdbDictionary::Column::Char, 101, 0, getcs(par)); + if (useindex(par, 0)) { + // a, c, d + const ITab* x = t->m_itab[0] = new ITab(*t, "ti2x0", 3); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[0]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[2]); + x->m_icol[2] = new ICol(*x, 2, *t->m_col[3]); + } + if (useindex(par, 1)) { + // e, d, c, b, a + const ITab* x = t->m_itab[1] = new ITab(*t, "ti2x1", 5); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[4]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[3]); + x->m_icol[2] = new ICol(*x, 2, *t->m_col[2]); + x->m_icol[3] = new ICol(*x, 3, *t->m_col[1]); + x->m_icol[4] = new ICol(*x, 4, *t->m_col[0]); + } + if (useindex(par, 2)) { + // d + const ITab* x = t->m_itab[2] = new ITab(*t, "ti2x2", 1); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[3]); + } + if (useindex(par, 3)) { + // b + const ITab* x = t->m_itab[3] = new ITab(*t, "ti2x3", 1); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[1]); + } + if (useindex(par, 4)) { + // a, e + const ITab* x = t->m_itab[4] = new ITab(*t, "ti2x4", 2); + x->m_icol[0] = new ICol(*x, 0, *t->m_col[0]); + x->m_icol[1] = new ICol(*x, 1, *t->m_col[4]); + } + tablist[2] = t; + } + verifytables(); +} // connections @@ -834,7 +1101,7 @@ Con::execute(ExecType t, bool& deadlock) int Con::openScanRead(unsigned scanbat, unsigned scanpar) { - assert(m_tx != 0 && m_op != 0); + assert(m_tx != 0 && m_scanop != 0); NdbOperation::LockMode lm = NdbOperation::LM_Read; CHKCON((m_resultset = m_scanop->readTuples(lm, scanbat, scanpar)) != 0, *this); return 0; @@ -843,7 +1110,7 @@ Con::openScanRead(unsigned scanbat, unsigned scanpar) int Con::openScanExclusive(unsigned scanbat, unsigned scanpar) { - assert(m_tx != 0 && m_op != 0); + assert(m_tx != 0 && m_scanop != 0); NdbOperation::LockMode lm = NdbOperation::LM_Exclusive; CHKCON((m_resultset = m_scanop->readTuples(lm, scanbat, scanpar)) != 0, *this); return 0; @@ -936,7 +1203,7 @@ Con::printerror(NdbOut& out) if ((code = m_tx->getNdbError().code) != 0) { LL0(++any << " con: error " << m_tx->getNdbError()); die += (code == g_opt.m_die); - if (code == 266 || code == 274 || code == 296 || code == 297 || code == 499) + if (code == 266 || code == 274 || code == 296 || code == 297 || code == 499 || code == 631) m_errtype = ErrDeadlock; } if (m_op && m_op->getNdbError().code != 0) { @@ -972,9 +1239,9 @@ invalidateindex(Par par) Con& con = par.con(); const Tab& tab = par.tab(); for (unsigned i = 0; i < tab.m_itabs; i++) { - if (! useindex(i)) + if (tab.m_itab[i] == 0) continue; - const ITab& itab = tab.m_itab[i]; + const ITab& itab = *tab.m_itab[i]; invalidateindex(par, itab); } return 0; @@ -1022,16 +1289,14 @@ createtable(Par par) t.setLogging(false); } for (unsigned k = 0; k < tab.m_cols; k++) { - const Col& col = tab.m_col[k]; + const Col& col = *tab.m_col[k]; NdbDictionary::Column c(col.m_name); c.setType(col.m_type); - c.setLength(col.m_length); + c.setLength(col.m_bytelength); // NDB API uses length in bytes c.setPrimaryKey(col.m_pk); c.setNullable(col.m_nullable); - if (c.getCharset()) { // test if char type - if (! col.m_pk) - c.setCharset(par.m_cs); - } + if (col.m_chs != 0) + c.setCharset(col.m_chs->m_cs); t.addColumn(c); } con.m_dic = con.m_ndb->getDictionary(); @@ -1062,9 +1327,9 @@ dropindex(Par par) { const Tab& tab = par.tab(); for (unsigned i = 0; i < tab.m_itabs; i++) { - if (! useindex(i)) + if (tab.m_itab[i] == 0) continue; - const ITab& itab = tab.m_itab[i]; + const ITab& itab = *tab.m_itab[i]; CHK(dropindex(par, itab) == 0); } return 0; @@ -1082,7 +1347,8 @@ createindex(Par par, const ITab& itab) x.setType(NdbDictionary::Index::OrderedIndex); x.setLogging(false); for (unsigned k = 0; k < itab.m_icols; k++) { - const Col& col = itab.m_icol[k].m_col; + const ICol& icol = *itab.m_icol[k]; + const Col& col = icol.m_col; x.addColumnName(col.m_name); } con.m_dic = con.m_ndb->getDictionary(); @@ -1096,9 +1362,9 @@ createindex(Par par) { const Tab& tab = par.tab(); for (unsigned i = 0; i < tab.m_itabs; i++) { - if (! useindex(i)) + if (tab.m_itab[i] == 0) continue; - const ITab& itab = tab.m_itab[i]; + const ITab& itab = *tab.m_itab[i]; CHK(createindex(par, itab) == 0); } return 0; @@ -1106,33 +1372,13 @@ createindex(Par par) // data sets -static unsigned -urandom(unsigned n) -{ - if (n == 0) - return 0; - unsigned i = random() % n; - return i; -} - -static int -irandom(unsigned n) -{ - if (n == 0) - return 0; - int i = random() % n; - if (random() & 0x1) - i = -i; - return i; -} - // Val - typed column value struct Val { const Col& m_col; union { Uint32 m_uint32; - char* m_varchar; + unsigned char* m_char; }; Val(const Col& col); ~Val(); @@ -1142,6 +1388,8 @@ struct Val { bool m_null; int setval(Par par) const; void calc(Par par, unsigned i); + void calckey(Par par, unsigned i); + void calcnokey(Par par); int verify(const Val& val2) const; int cmp(const Val& val2) const; private: @@ -1157,8 +1405,8 @@ Val::Val(const Col& col) : switch (col.m_type) { case NdbDictionary::Column::Unsigned: break; - case NdbDictionary::Column::Varchar: - m_varchar = new char [2 + col.m_length]; + case NdbDictionary::Column::Char: + m_char = new unsigned char [col.m_bytelength]; break; default: assert(false); @@ -1172,8 +1420,8 @@ Val::~Val() switch (col.m_type) { case NdbDictionary::Column::Unsigned: break; - case NdbDictionary::Column::Varchar: - delete [] m_varchar; + case NdbDictionary::Column::Char: + delete [] m_char; break; default: assert(false); @@ -1202,8 +1450,8 @@ Val::copy(const void* addr) case NdbDictionary::Column::Unsigned: m_uint32 = *(const Uint32*)addr; break; - case NdbDictionary::Column::Varchar: - memcpy(m_varchar, addr, 2 + col.m_length); + case NdbDictionary::Column::Char: + memcpy(m_char, addr, col.m_bytelength); break; default: assert(false); @@ -1219,8 +1467,8 @@ Val::dataaddr() const switch (col.m_type) { case NdbDictionary::Column::Unsigned: return &m_uint32; - case NdbDictionary::Column::Varchar: - return m_varchar; + case NdbDictionary::Column::Char: + return m_char; default: break; } @@ -1236,55 +1484,105 @@ Val::setval(Par par) const const char* addr = (const char*)dataaddr(); if (m_null) addr = 0; + LL5("setval [" << m_col << "] " << *this); if (col.m_pk) CHK(con.equal(col.m_num, addr) == 0); else CHK(con.setValue(col.m_num, addr) == 0); - LL5("setval [" << m_col << "] " << *this); return 0; } void Val::calc(Par par, unsigned i) +{ + const Col& col = m_col; + col.m_pk ? calckey(par, i) : calcnokey(par); + if (! m_null) + col.verify(dataaddr()); +} + +void +Val::calckey(Par par, unsigned i) { const Col& col = m_col; m_null = false; - if (col.m_pk) { - m_uint32 = i; - return; - } - if (col.m_nullable && urandom(100) < par.m_pctnull) { - m_null = true; - return; - } - unsigned v = par.m_range + irandom((par.m_pctrange * par.m_range) / 100); switch (col.m_type) { case NdbDictionary::Column::Unsigned: - m_uint32 = v; + m_uint32 = i; break; - case NdbDictionary::Column::Varchar: + case NdbDictionary::Column::Char: { + const Chs* chs = col.m_chs; + CHARSET_INFO* cs = chs->m_cs; unsigned n = 0; - while (n < col.m_length) { - if (urandom(1 + col.m_length) == 0) { - // nice distribution on lengths + // our random chars may not fill value exactly + while (n + cs->mbmaxlen <= col.m_bytelength) { + if (i % (1 + n) == 0) { break; } - m_varchar[2 + n++] = 'a' + urandom((par.m_pctrange * 10) / 100); - } - m_varchar[0] = (n >> 8); - m_varchar[1] = (n & 0xff); - while (n < col.m_length) { - m_varchar[2 + n++] = 0; + const Chr& chr = chs->m_chr[i % maxcharcount]; + memcpy(&m_char[n], chr.m_bytes, chr.m_size); + n += chr.m_size; } + // this will extend by appropriate space + (*cs->cset->fill)(cs, (char*)&m_char[n], col.m_bytelength - n, 0x20); + } + break; + default: + assert(false); + break; + } +} + +void +Val::calcnokey(Par par) +{ + const Col& col = m_col; + m_null = false; + if (col.m_nullable && urandom(100) < par.m_pctnull) { + m_null = true; + return; + } + int r = irandom((par.m_pctrange * par.m_range) / 100); + if (par.m_bdir != 0 && urandom(10) != 0) { + if (r < 0 && par.m_bdir > 0 || r > 0 && par.m_bdir < 0) + r = -r; + } + unsigned v = par.m_range + r; + switch (col.m_type) { + case NdbDictionary::Column::Unsigned: + m_uint32 = v; + break; + case NdbDictionary::Column::Char: + { + const Chs* chs = col.m_chs; + CHARSET_INFO* cs = chs->m_cs; + unsigned n = 0; + // our random chars may not fill value exactly + while (n + cs->mbmaxlen <= col.m_bytelength) { + if (urandom(1 + col.m_bytelength) == 0) { + break; + } + unsigned half = maxcharcount / 2; + int r = irandom((par.m_pctrange * half) / 100); + if (par.m_bdir != 0 && urandom(10) != 0) { + if (r < 0 && par.m_bdir > 0 || r > 0 && par.m_bdir < 0) + r = -r; + } + unsigned i = half + r; + assert(i < maxcharcount); + const Chr& chr = chs->m_chr[i]; + memcpy(&m_char[n], chr.m_bytes, chr.m_size); + n += chr.m_size; + } + // this will extend by appropriate space + (*cs->cset->fill)(cs, (char*)&m_char[n], col.m_bytelength - n, 0x20); } break; default: assert(false); break; } - // verify format - col.verify(dataaddr()); } int @@ -1299,7 +1597,7 @@ Val::cmp(const Val& val2) const { const Col& col = m_col; const Col& col2 = val2.m_col; - assert(col.m_type == col2.m_type && col.m_length == col2.m_length); + assert(col.equal(col2)); if (m_null || val2.m_null) { if (! m_null) return +1; @@ -1313,13 +1611,37 @@ Val::cmp(const Val& val2) const // compare switch (col.m_type) { case NdbDictionary::Column::Unsigned: - if (m_uint32 < val2.m_uint32) - return -1; - if (m_uint32 > val2.m_uint32) - return +1; - return 0; - case NdbDictionary::Column::Varchar: - return memcmp(&m_varchar[2], &val2.m_varchar[2], col.m_length); + { + if (m_uint32 < val2.m_uint32) + return -1; + if (m_uint32 > val2.m_uint32) + return +1; + return 0; + } + break; + case NdbDictionary::Column::Char: + { + const Chs* chs = col.m_chs; + CHARSET_INFO* cs = chs->m_cs; + unsigned len = col.m_bytelength; + int k; + if (! g_opt.m_collsp) { + unsigned char x1[maxxmulsize * 8000]; + unsigned char x2[maxxmulsize * 8000]; + int n1 = (*cs->coll->strnxfrm)(cs, x1, chs->m_xmul * len, m_char, len); + int n2 = (*cs->coll->strnxfrm)(cs, x2, chs->m_xmul * len, val2.m_char, len); + // currently same but do not assume it + unsigned n = (n1 > n2 ? n1 : n2); + // assume null padding + memset(x1 + n1, 0x0, n - n1); + memset(x2 + n2, 0x0, n - n2); + k = memcmp(x1, x2, n); + } else { + k = (*cs->coll->strnncollsp)(cs, m_char, len, val2.m_char, len, false); + } + return k < 0 ? -1 : k > 0 ? +1 : 0; + } + break; default: break; } @@ -1339,12 +1661,26 @@ operator<<(NdbOut& out, const Val& val) case NdbDictionary::Column::Unsigned: out << val.m_uint32; break; - case NdbDictionary::Column::Varchar: + case NdbDictionary::Column::Char: { - char buf[8000]; - unsigned n = (val.m_varchar[0] << 8) | val.m_varchar[1]; - assert(n <= col.m_length); - sprintf(buf, "'%.*s'[%d]", n, &val.m_varchar[2], n); + char buf[4 * 8000]; + char *p = buf; + *p++ = '['; + for (unsigned i = 0; i < col.m_bytelength; i++) { + unsigned char c = val.m_char[i]; + if (c == '\\') { + *p++ = '\\'; + *p++ = '\\'; + } else if (0x20 <= c && c < 0x7e) { + *p++ = c; + } else { + *p++ = '\\'; + *p++ = hexstr[c >> 4]; + *p++ = hexstr[c & 15]; + } + } + *p++ = ']'; + *p = 0; out << buf; } break; @@ -1384,7 +1720,7 @@ Row::Row(const Tab& tab) : { m_val = new Val* [tab.m_cols]; for (unsigned k = 0; k < tab.m_cols; k++) { - const Col& col = tab.m_col[k]; + const Col& col = *tab.m_col[k]; m_val[k] = new Val(col); } m_exist = false; @@ -1461,6 +1797,16 @@ Row::updrow(Par par) CHKCON(con.m_op->updateTuple() == 0, con); for (unsigned k = 0; k < tab.m_cols; k++) { const Val& val = *m_val[k]; + const Col& col = val.m_col; + if (! col.m_pk) + continue; + CHK(val.setval(par) == 0); + } + for (unsigned k = 0; k < tab.m_cols; k++) { + const Val& val = *m_val[k]; + const Col& col = val.m_col; + if (col.m_pk) + continue; CHK(val.setval(par) == 0); } m_pending = UpdOp; @@ -1667,6 +2013,27 @@ Set::pending(unsigned i) const return m_row[i]->m_pending; } +void +Set::notpending(unsigned i) +{ + assert(m_row[i] != 0); + Row& row = *m_row[i]; + if (row.m_pending == Row::InsOp) + row.m_exist = true; + if (row.m_pending == Row::DelOp) + row.m_exist = false; + row.m_pending = Row::NoOp; +} + +void +Set::notpending(const Lst& lst) +{ + for (unsigned j = 0; j < lst.m_cnt; j++) { + unsigned i = lst.m_arr[j]; + notpending(i); + } +} + void Set::calc(Par par, unsigned i) { @@ -1674,8 +2041,6 @@ Set::calc(Par par, unsigned i) if (m_row[i] == 0) m_row[i] = new Row(tab); Row& row = *m_row[i]; - // value generation parameters - par.m_pctrange = 40; row.calc(par, i); } @@ -1739,9 +2104,11 @@ Set::getval(Par par) int Set::getkey(Par par, unsigned* i) { - assert(m_rec[0] != 0); - const char* aRef0 = m_rec[0]->aRef(); - Uint32 key = *(const Uint32*)aRef0; + const Tab& tab = m_tab; + unsigned k = tab.m_keycol; + assert(m_rec[k] != 0); + const char* aRef = m_rec[k]->aRef(); + Uint32 key = *(const Uint32*)aRef; CHK(key < m_rows); *i = key; return 0; @@ -1772,32 +2139,12 @@ Set::putval(unsigned i, bool force) return 0; } -void -Set::notpending(unsigned i) -{ - assert(m_row[i] != 0); - Row& row = *m_row[i]; - if (row.m_pending == Row::InsOp) - row.m_exist = true; - if (row.m_pending == Row::DelOp) - row.m_exist = false; - row.m_pending = Row::NoOp; -} - -void -Set::notpending(const Lst& lst) -{ - for (unsigned j = 0; j < lst.m_cnt; j++) { - unsigned i = lst.m_arr[j]; - notpending(i); - } -} - int Set::verify(const Set& set2) const { const Tab& tab = m_tab; assert(&tab == &set2.m_tab && m_rows == set2.m_rows); + LL3("verify set1 count=" << count() << " vs set2 count=" << set2.count()); for (unsigned i = 0; i < m_rows; i++) { CHK(exist(i) == set2.exist(i)); if (! exist(i)) @@ -1884,10 +2231,10 @@ operator<<(NdbOut& out, const BVal& bval) const ICol& icol = bval.m_icol; const Col& col = icol.m_col; const Val& val = bval; - out << "type " << bval.m_type; - out << " icol " << icol.m_num; - out << " col " << col.m_name << "(" << col.m_num << ")"; - out << " value " << val; + out << "type=" << bval.m_type; + out << " icol=" << icol.m_num; + out << " col=" << col.m_num << "," << col.m_name; + out << " value=" << val; return out; } @@ -1939,12 +2286,15 @@ void BSet::calc(Par par) { const ITab& itab = m_itab; + par.m_pctrange = par.m_pctbrange; reset(); for (unsigned k = 0; k < itab.m_icols; k++) { - const ICol& icol = itab.m_icol[k]; + const ICol& icol = *itab.m_icol[k]; const Col& col = icol.m_col; for (unsigned i = 0; i <= 1; i++) { - if (urandom(10) == 0) + if (m_bvals == 0 && urandom(100) == 0) + return; + if (m_bvals != 0 && urandom(3) == 0) return; assert(m_bvals < m_alloc); BVal& bval = *new BVal(icol); @@ -1963,12 +2313,14 @@ BSet::calc(Par par) bval.m_type = 4; if (k + 1 < itab.m_icols) bval.m_type = 4; - // value generation parammeters if (! g_compare_null) par.m_pctnull = 0; - par.m_pctrange = 50; // bit higher + if (bval.m_type == 0 || bval.m_type == 1) + par.m_bdir = -1; + if (bval.m_type == 2 || bval.m_type == 3) + par.m_bdir = +1; do { - bval.calc(par, 0); + bval.calcnokey(par); if (i == 1) { assert(m_bvals >= 2); const BVal& bv1 = *m_bval[m_bvals - 2]; @@ -1990,7 +2342,7 @@ BSet::calcpk(Par par, unsigned i) const ITab& itab = m_itab; reset(); for (unsigned k = 0; k < itab.m_icols; k++) { - const ICol& icol = itab.m_icol[k]; + const ICol& icol = *itab.m_icol[k]; const Col& col = icol.m_col; assert(col.m_pk); assert(m_bvals < m_alloc); @@ -2037,7 +2389,7 @@ BSet::filter(const Set& set, Set& set2) const if (! g_store_null_key) { bool ok1 = false; for (unsigned k = 0; k < itab.m_icols; k++) { - const ICol& icol = itab.m_icol[k]; + const ICol& icol = *itab.m_icol[k]; const Col& col = icol.m_col; const Val& val = *row.m_val[col.m_num]; if (! val.m_null) { @@ -2055,6 +2407,7 @@ BSet::filter(const Set& set, Set& set2) const const Col& col = icol.m_col; const Val& val = *row.m_val[col.m_num]; int ret = bval.cmp(val); + LL5("cmp: ret=" << ret << " " << bval << " vs " << val); if (bval.m_type == 0) ok2 = (ret <= 0); else if (bval.m_type == 1) @@ -2087,9 +2440,8 @@ operator<<(NdbOut& out, const BSet& bset) { out << "bounds=" << bset.m_bvals; for (unsigned j = 0; j < bset.m_bvals; j++) { - out << endl; const BVal& bval = *bset.m_bval[j]; - out << "bound " << j << ": " << bval; + out << " [bound " << j << ": " << bval << "]"; } return out; } @@ -2318,26 +2670,36 @@ scanreadtable(Par par) const Set& set = par.set(); // expected const Set& set1 = set; - LL3((par.m_verify ? "scanverify " : "scanread ") << tab.m_name); + LL3("scanread " << tab.m_name << " verify=" << par.m_verify); + LL4("expect " << set.count() << " rows"); Set set2(tab, set.m_rows); CHK(con.startTransaction() == 0); CHK(con.getNdbScanOperation(tab) == 0); CHK(con.openScanRead(par.m_scanbat, par.m_scanpar) == 0); set2.getval(par); CHK(con.executeScan() == 0); + unsigned n = 0; + bool deadlock = false; while (1) { int ret; - CHK((ret = con.nextScanResult(true)) == 0 || ret == 1); + deadlock = par.m_deadlock; + CHK((ret = con.nextScanResult(true, deadlock)) == 0 || ret == 1); if (ret == 1) break; + if (deadlock) { + LL1("scanreadtable: stop on deadlock"); + break; + } unsigned i = (unsigned)-1; CHK(set2.getkey(par, &i) == 0); CHK(set2.putval(i, false) == 0); - LL4("row " << set2.count() << ": " << *set2.m_row[i]); + LL4("row " << n << ": " << *set2.m_row[i]); + n++; } con.closeTransaction(); if (par.m_verify) CHK(set1.verify(set2) == 0); + LL3("scanread " << tab.m_name << " rows=" << n); return 0; } @@ -2369,16 +2731,30 @@ scanreadtablefast(Par par, unsigned countcheck) } static int -scanreadindex(Par par, const ITab& itab, const BSet& bset) +scanreadindex(Par par, const ITab& itab, BSet& bset, bool calc) { Con& con = par.con(); const Tab& tab = par.tab(); const Set& set = par.set(); - // expected - Set set1(tab, set.m_rows); - bset.filter(set, set1); - LL3((par.m_verify ? "scanverify " : "scanread ") << itab.m_name << " bounds=" << bset.m_bvals); LL4(bset); + Set set1(tab, set.m_rows); + if (calc) { + while (true) { + bset.calc(par); + bset.filter(set, set1); + unsigned n = set1.count(); + // prefer proper subset + if (0 < n && n < set.m_rows) + break; + if (urandom(5) == 0) + break; + set1.reset(); + } + } else { + bset.filter(set, set1); + } + LL3("scanread " << itab.m_name << " bounds=" << bset.m_bvals << " verify=" << par.m_verify); + LL4("expect " << set1.count() << " rows"); Set set2(tab, set.m_rows); CHK(con.startTransaction() == 0); CHK(con.getNdbScanOperation(itab, tab) == 0); @@ -2386,20 +2762,29 @@ scanreadindex(Par par, const ITab& itab, const BSet& bset) CHK(bset.setbnd(par) == 0); set2.getval(par); CHK(con.executeScan() == 0); + unsigned n = 0; + bool deadlock = false; while (1) { int ret; - CHK((ret = con.nextScanResult(true)) == 0 || ret == 1); + deadlock = par.m_deadlock; + CHK((ret = con.nextScanResult(true, deadlock)) == 0 || ret == 1); if (ret == 1) break; + if (deadlock) { + LL1("scanreadindex: stop on deadlock"); + break; + } unsigned i = (unsigned)-1; CHK(set2.getkey(par, &i) == 0); LL4("key " << i); CHK(set2.putval(i, par.m_dups) == 0); - LL4("row " << set2.count() << ": " << *set2.m_row[i]); + LL4("row " << n << ": " << *set2.m_row[i]); + n++; } con.closeTransaction(); if (par.m_verify) CHK(set1.verify(set2) == 0); + LL3("scanread " << itab.m_name << " rows=" << n); return 0; } @@ -2438,8 +2823,7 @@ scanreadindex(Par par, const ITab& itab) const Tab& tab = par.tab(); for (unsigned i = 0; i < par.m_subsubloop; i++) { BSet bset(tab, itab, par.m_rows); - bset.calc(par); - CHK(scanreadindex(par, itab, bset) == 0); + CHK(scanreadindex(par, itab, bset, true) == 0); } return 0; } @@ -2449,9 +2833,9 @@ scanreadindex(Par par) { const Tab& tab = par.tab(); for (unsigned i = 0; i < tab.m_itabs; i++) { - if (! useindex(i)) + if (tab.m_itab[i] == 0) continue; - const ITab& itab = tab.m_itab[i]; + const ITab& itab = *tab.m_itab[i]; CHK(scanreadindex(par, itab) == 0); } return 0; @@ -2481,7 +2865,7 @@ static int timescanpkindex(Par par) { const Tab& tab = par.tab(); - const ITab& itab = tab.m_itab[0]; // 1st index is on PK + const ITab& itab = *tab.m_itab[0]; // 1st index is on PK BSet bset(tab, itab, par.m_rows); par.tmr().on(); CHK(scanreadindexfast(par, itab, bset, par.m_totrows) == 0); @@ -2505,7 +2889,7 @@ static int timepkreadindex(Par par) { const Tab& tab = par.tab(); - const ITab& itab = tab.m_itab[0]; // 1st index is on PK + const ITab& itab = *tab.m_itab[0]; // 1st index is on PK BSet bset(tab, itab, par.m_rows); unsigned count = par.m_samples; if (count == 0) @@ -2575,7 +2959,12 @@ scanupdatetable(Par par) } set.unlock(); if (lst.cnt() == par.m_batch) { - CHK(con2.execute(Commit) == 0); + deadlock = par.m_deadlock; + CHK(con2.execute(Commit, deadlock) == 0); + if (deadlock) { + LL1("scanupdateindex: stop on deadlock"); + goto out; + } con2.closeTransaction(); set.lock(); set.notpending(lst); @@ -2586,7 +2975,12 @@ scanupdatetable(Par par) } CHK((ret = con.nextScanResult(false)) == 0 || ret == 1 || ret == 2); if (ret == 2 && lst.cnt() != 0) { - CHK(con2.execute(Commit) == 0); + deadlock = par.m_deadlock; + CHK(con2.execute(Commit, deadlock) == 0); + if (deadlock) { + LL1("scanupdateindex: stop on deadlock"); + goto out; + } con2.closeTransaction(); set.lock(); set.notpending(lst); @@ -2599,6 +2993,7 @@ scanupdatetable(Par par) if (ret == 1) break; } +out: con2.closeTransaction(); LL3("scan update " << tab.m_name << " rows updated=" << count); con.closeTransaction(); @@ -2659,7 +3054,12 @@ scanupdateindex(Par par, const ITab& itab, const BSet& bset) } set.unlock(); if (lst.cnt() == par.m_batch) { - CHK(con2.execute(Commit) == 0); + deadlock = par.m_deadlock; + CHK(con2.execute(Commit, deadlock) == 0); + if (deadlock) { + LL1("scanupdateindex: stop on deadlock"); + goto out; + } con2.closeTransaction(); set.lock(); set.notpending(lst); @@ -2670,7 +3070,12 @@ scanupdateindex(Par par, const ITab& itab, const BSet& bset) } CHK((ret = con.nextScanResult(false)) == 0 || ret == 1 || ret == 2); if (ret == 2 && lst.cnt() != 0) { - CHK(con2.execute(Commit) == 0); + deadlock = par.m_deadlock; + CHK(con2.execute(Commit, deadlock) == 0); + if (deadlock) { + LL1("scanupdateindex: stop on deadlock"); + goto out; + } con2.closeTransaction(); set.lock(); set.notpending(lst); @@ -2681,6 +3086,7 @@ scanupdateindex(Par par, const ITab& itab, const BSet& bset) } } while (ret == 0); } +out: con2.closeTransaction(); LL3("scan update " << itab.m_name << " rows updated=" << count); con.closeTransaction(); @@ -2704,9 +3110,9 @@ scanupdateindex(Par par) { const Tab& tab = par.tab(); for (unsigned i = 0; i < tab.m_itabs; i++) { - if (! useindex(i)) + if (tab.m_itab[i] == 0) continue; - const ITab& itab = tab.m_itab[i]; + const ITab& itab = *tab.m_itab[i]; CHK(scanupdateindex(par, itab) == 0); } return 0; @@ -2743,16 +3149,24 @@ readverifyfull(Par par) CHK(scanreadtable(par) == 0); else { const Tab& tab = par.tab(); - unsigned i = par.m_no; - if (i <= tab.m_itabs && useindex(i)) { - const ITab& itab = tab.m_itab[i - 1]; + unsigned i = par.m_no - 1; + if (i < tab.m_itabs && tab.m_itab[i] != 0) { + const ITab& itab = *tab.m_itab[i]; BSet bset(tab, itab, par.m_rows); - CHK(scanreadindex(par, itab, bset) == 0); + CHK(scanreadindex(par, itab, bset, false) == 0); } } return 0; } +static int +readverifyindex(Par par) +{ + par.m_verify = true; + CHK(scanreadindex(par) == 0); + return 0; +} + static int pkops(Par par) { @@ -2786,6 +3200,7 @@ static int pkupdatescanread(Par par) { par.m_dups = true; + par.m_deadlock = true; unsigned sel = urandom(10); if (sel < 5) { CHK(pkupdate(par) == 0); @@ -3073,6 +3488,24 @@ tbuild(Par par) return 0; } +static int +tindexscan(Par par) +{ + RUNSTEP(par, droptable, ST); + RUNSTEP(par, createtable, ST); + RUNSTEP(par, invalidatetable, MT); + RUNSTEP(par, createindex, ST); + RUNSTEP(par, invalidateindex, MT); + RUNSTEP(par, pkinsert, MT); + RUNSTEP(par, readverifyfull, MT); + for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) { + LL4("subloop " << par.m_slno); + RUNSTEP(par, readverifyindex, MT); + } + return 0; +} + + static int tpkops(Par par) { @@ -3188,6 +3621,10 @@ ttimemaint(Par par) static int ttimescan(Par par) { + if (par.tab().m_itab[0] == 0) { + LL1("ttimescan - no index 0, skipped"); + return 0; + } Tmr t1, t2; RUNSTEP(par, droptable, ST); RUNSTEP(par, createtable, ST); @@ -3210,6 +3647,10 @@ ttimescan(Par par) static int ttimepkread(Par par) { + if (par.tab().m_itab[0] == 0) { + LL1("ttimescan - no index 0, skipped"); + return 0; + } Tmr t1, t2; RUNSTEP(par, droptable, ST); RUNSTEP(par, createtable, ST); @@ -3250,10 +3691,11 @@ struct TCase { static const TCase tcaselist[] = { TCase("a", tbuild, "index build"), - TCase("b", tpkops, "pk operations"), - TCase("c", tpkopsread, "pk operations and scan reads"), - TCase("d", tmixedops, "pk operations and scan operations"), - TCase("e", tbusybuild, "pk operations and index build"), + TCase("b", tindexscan, "index scans"), + TCase("c", tpkops, "pk operations"), + TCase("d", tpkopsread, "pk operations and scan reads"), + TCase("e", tmixedops, "pk operations and scan operations"), + TCase("f", tbusybuild, "pk operations and index build"), TCase("t", ttimebuild, "time index build"), TCase("u", ttimemaint, "time index maintenance"), TCase("v", ttimescan, "time full scan table vs index on pk"), @@ -3277,12 +3719,16 @@ printcases() static void printtables() { - ndbout << "tables and indexes (X1 is on table PK):" << endl; + Par par(g_opt); + makebuiltintables(par); + ndbout << "builtin tables (index x0 is on table pk):" << endl; for (unsigned j = 0; j < tabcount; j++) { - const Tab& tab = tablist[j]; + if (tablist[j] == 0) + continue; + const Tab& tab = *tablist[j]; ndbout << " " << tab.m_name; for (unsigned i = 0; i < tab.m_itabs; i++) { - const ITab& itab = tab.m_itab[i]; + const ITab& itab = *tab.m_itab[i]; ndbout << " " << itab.m_name; } ndbout << endl; @@ -3293,15 +3739,25 @@ static int runtest(Par par) { LL1("start"); - if (par.m_seed != 0) + if (par.m_seed == -1) { + // good enough for daily run + unsigned short seed = (getpid() ^ time(0)); + LL1("random seed: " << seed); + srandom((unsigned)seed); + } else if (par.m_seed != 0) srandom(par.m_seed); + // cs assert(par.m_csname != 0); - CHARSET_INFO* cs; - CHK((cs = get_charset_by_name(par.m_csname, MYF(0))) != 0 || (cs = get_charset_by_csname(par.m_csname, MY_CS_PRIMARY, MYF(0))) != 0); - par.m_cs = cs; + if (strcmp(par.m_csname, "random") != 0) { + CHARSET_INFO* cs; + CHK((cs = get_charset_by_name(par.m_csname, MYF(0))) != 0 || (cs = get_charset_by_csname(par.m_csname, MY_CS_PRIMARY, MYF(0))) != 0); + par.m_cs = cs; + } + // con Con con; CHK(con.connect() == 0); par.m_con = &con; + // threads g_thrlist = new Thr* [par.m_threads]; unsigned n; for (n = 0; n < par.m_threads; n++) { @@ -3320,16 +3776,18 @@ runtest(Par par) const TCase& tcase = tcaselist[i]; if (par.m_case != 0 && strchr(par.m_case, tcase.m_name[0]) == 0) continue; + makebuiltintables(par); LL1("case " << tcase.m_name << " - " << tcase.m_desc); for (unsigned j = 0; j < tabcount; j++) { - if (! usetable(j)) + if (tablist[j] == 0) continue; - const Tab& tab = tablist[j]; + const Tab& tab = *tablist[j]; par.m_tab = &tab; - delete par.m_set; par.m_set = new Set(tab, par.m_totrows); LL1("table " << tab.m_name); CHK(tcase.m_func(par) == 0); + delete par.m_set; + par.m_set = 0; } } } @@ -3353,7 +3811,7 @@ NDB_COMMAND(testOIBasic, "testOIBasic", "testOIBasic", "testOIBasic", 65535) { ndb_init(); if (ndbout_mutex == NULL) - ndbout_mutex= NdbMutex_Create(); + ndbout_mutex = NdbMutex_Create(); while (++argv, --argc > 0) { const char* arg = argv[0]; if (*arg != '-') { @@ -3381,6 +3839,10 @@ NDB_COMMAND(testOIBasic, "testOIBasic", "testOIBasic", "testOIBasic", 65535) continue; } } + if (strcmp(arg, "-collsp") == 0) { + g_opt.m_collsp = true; + continue; + } if (strcmp(arg, "-core") == 0) { g_opt.m_core = true; continue; @@ -3517,7 +3979,6 @@ NDB_COMMAND(testOIBasic, "testOIBasic", "testOIBasic", "testOIBasic", 65535) if (runtest(par) < 0) goto failed; } - // always exit with NDBT code ok: return NDBT_ProgramExit(NDBT_OK); failed: diff --git a/ndb/tools/desc.cpp b/ndb/tools/desc.cpp index c5e9efdfa8a..2f59d5fcd0b 100644 --- a/ndb/tools/desc.cpp +++ b/ndb/tools/desc.cpp @@ -102,7 +102,7 @@ int main(int argc, char** argv){ unsigned j; for (j= 0; (int)j < pTab->getNoOfPrimaryKeys(); j++) { - const NdbDictionary::Column * col = pTab->getColumn(j); + const NdbDictionary::Column * col = pTab->getColumn(pTab->getPrimaryKey(j)); ndbout << col->getName(); if ((int)j < pTab->getNoOfPrimaryKeys()-1) ndbout << ", "; From 5ae35e327a39ec036a9d938f8555278bf96f2a27 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 Dec 2004 19:54:26 +0200 Subject: [PATCH 40/52] Fix test results that may change from run to run Added comments (from code review on pull) mysql-test/r/heap.result: Fix results that may change from run to run mysql-test/r/ps_5merge.result: Fix wrong result mysql-test/t/heap.test: Make results predictable sql/sql_show.cc: Add comments Fixed typo --- mysql-test/r/heap.result | 2 +- mysql-test/r/ps_5merge.result | 4 ++-- mysql-test/t/heap.test | 2 ++ sql/sql_show.cc | 12 ++++++++---- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/heap.result b/mysql-test/r/heap.result index 7c4ce234d58..bcdb24668bb 100644 --- a/mysql-test/r/heap.result +++ b/mysql-test/r/heap.result @@ -622,7 +622,7 @@ qq *a *a*a * explain select * from t1 where v='a'; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref v v 13 const 7 Using where +1 SIMPLE t1 ref v v 13 const # Using where drop table t1; create table t1 (a char(10), unique using btree (a)) engine=heap; insert into t1 values ('a'); diff --git a/mysql-test/r/ps_5merge.result b/mysql-test/r/ps_5merge.result index 9a0b0cb8b75..1eaf747e8c6 100644 --- a/mysql-test/r/ps_5merge.result +++ b/mysql-test/r/ps_5merge.result @@ -1698,7 +1698,7 @@ t5 CREATE TABLE `t5` ( `param03` double default NULL, `const04` varchar(3) NOT NULL default '', `param04` longtext, - `const05` binary(3) NOT NULL default '', + `const05` varbinary(3) NOT NULL default '', `param05` longblob, `const06` varchar(10) NOT NULL default '', `param06` longtext, @@ -4707,7 +4707,7 @@ t5 CREATE TABLE `t5` ( `param03` double default NULL, `const04` varchar(3) NOT NULL default '', `param04` longtext, - `const05` varchar(3) NOT NULL default '', + `const05` varbinary(3) NOT NULL default '', `param05` longblob, `const06` varchar(10) NOT NULL default '', `param06` longtext, diff --git a/mysql-test/t/heap.test b/mysql-test/t/heap.test index 31b7f997519..6353cd78d6a 100644 --- a/mysql-test/t/heap.test +++ b/mysql-test/t/heap.test @@ -360,6 +360,8 @@ explain select count(*) from t1 where v between 'a' and 'a ' and v between 'a ' --error 1062 alter table t1 add unique(v); select concat('*',v,'*',c,'*',t,'*') as qq from t1 where v='a' order by length(concat('*',v,'*',c,'*',t,'*')); +# Number of rows is not constant for b-trees keys +--replace_column 9 # explain select * from t1 where v='a'; drop table t1; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 7cc746793dc..71467664085 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2135,6 +2135,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) } } } + /* + If we have information schema its always the first table and only + the first table. Reset for other tables. + */ with_i_schema= 0; } } @@ -2838,7 +2842,7 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, } -void store_constarints(TABLE *table, const char*db, const char *tname, +void store_constraints(TABLE *table, const char*db, const char *tname, const char *key_name, uint key_len, const char *con_type, uint con_len) { @@ -2874,10 +2878,10 @@ static int get_schema_constarints_record(THD *thd, struct st_table_list *tables, continue; if (i == primary_key && !strcmp(key_info->name, primary_key_name)) - store_constarints(table, base_name, file_name, key_info->name, + store_constraints(table, base_name, file_name, key_info->name, strlen(key_info->name), "PRIMARY KEY", 11); else if (key_info->flags & HA_NOSAME) - store_constarints(table, base_name, file_name, key_info->name, + store_constraints(table, base_name, file_name, key_info->name, strlen(key_info->name), "UNIQUE", 6); } @@ -2886,7 +2890,7 @@ static int get_schema_constarints_record(THD *thd, struct st_table_list *tables, List_iterator_fast it(f_key_list); while ((f_key_info=it++)) { - store_constarints(table, base_name, file_name, f_key_info->forein_id->str, + store_constraints(table, base_name, file_name, f_key_info->forein_id->str, strlen(f_key_info->forein_id->str), "FOREIGN KEY", 11); } } From 596876f920c3d8dddbd3616ea45da915bc2bd0d2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 Dec 2004 20:59:15 +0300 Subject: [PATCH 41/52] fix autobuild failures server-tools/instance-manager/buffer.cc: fix typo server-tools/instance-manager/commands.cc: get rid of compiler warnings server-tools/instance-manager/guardian.cc: fix compiler warning server-tools/instance-manager/instance.cc: fix warnings server-tools/instance-manager/instance_map.cc: fix return value server-tools/instance-manager/instance_map.h: fix problem, caused autobuild failure. Looks like a gcc problem - "friend class " doesn't work if specified before enclosed class (this is not present in newer gcc versions) --- server-tools/instance-manager/buffer.cc | 2 +- server-tools/instance-manager/commands.cc | 14 +++++++------- server-tools/instance-manager/guardian.cc | 2 +- server-tools/instance-manager/instance.cc | 4 ++-- server-tools/instance-manager/instance_map.cc | 9 ++++++--- server-tools/instance-manager/instance_map.h | 2 +- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 7e785f0450e..ca84adbfd10 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -85,7 +85,7 @@ int Buffer::reserve(uint position, uint len_arg) min(MAX_BUFFER_SIZE, max((uint) (buffer_size*1.5), position + len_arg))); - if (buffer= NULL) + if (buffer == NULL) goto err; buffer_size= (uint) (buffer_size*1.5); } diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 2ac97382aa8..207ec3183e6 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -63,7 +63,7 @@ int Show_instances::do_command(struct st_net *net) Instance_map::Iterator iterator(instance_map); instance_map->lock(); - while (instance= iterator.next()) + while ((instance= iterator.next())) { position= 0; store_to_string(&send_buff, instance->options.instance_name, &position); @@ -117,7 +117,7 @@ Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= instance_map->find(name, len)) + if ((instance= instance_map->find(name, len))) { instance_name= instance->options.instance_name; } @@ -222,7 +222,7 @@ Show_instance_options::Show_instance_options(Instance_map *instance_map_arg, Instance *instance; /* we make a search here, since we don't want t store the name */ - if (instance= instance_map->find(name, len)) + if ((instance= instance_map->find(name, len))) { instance_name= instance->options.instance_name; } @@ -306,7 +306,7 @@ int Show_instance_options::do_command(struct st_net *net, } /* loop through the options stored in DYNAMIC_ARRAY */ - for (int i= 0; i < instance->options.options_array.elements; i++) + for (uint i= 0; i < instance->options.options_array.elements; i++) { char *tmp_option, *option_value; get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); @@ -355,7 +355,7 @@ Start_instance::Start_instance(Instance_map *instance_map_arg, :Command(instance_map_arg) { /* we make a search here, since we don't want t store the name */ - if (instance= instance_map->find(name, len)) + if ((instance= instance_map->find(name, len))) instance_name= instance->options.instance_name; } @@ -388,7 +388,7 @@ Stop_instance::Stop_instance(Instance_map *instance_map_arg, :Command(instance_map_arg) { /* we make a search here, since we don't want t store the name */ - if (instance= instance_map->find(name, len)) + if ((instance= instance_map->find(name, len))) instance_name= instance->options.instance_name; } @@ -406,7 +406,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) if (instance->options.is_guarded != NULL) instance_map->guardian-> stop_guard(instance); - if (err_code= instance->stop()) + if ((err_code= instance->stop())) return err_code; printf("instance was stopped\n"); net_send_ok(net, connection_id); diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index ac3e6298ea1..7375453673b 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -110,7 +110,7 @@ int Guardian_thread::start() Instance_map::Iterator iterator(instance_map); instance_map->lock(); - while (instance= iterator.next()) + while ((instance= iterator.next())) { if ((instance->options.is_guarded != NULL) && (instance->is_running())) if (guard(instance)) diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 42909a134ac..58bc5b85dd8 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -88,8 +88,8 @@ Instance::~Instance() bool Instance::is_running() { - uint port; - const char *socket; + uint port= 0; + const char *socket= NULL; if (options.mysqld_port) port= atoi(strchr(options.mysqld_port, '=') + 1); diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 355b51269d5..9399d6e2563 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -203,9 +203,12 @@ int Instance_map::cleanup() while (i < hash.records) { instance= (Instance *) hash_element(&hash, i); - instance->cleanup(); + if (instance->cleanup()) + return 1; i++; } + + return 0; } @@ -250,7 +253,7 @@ Instance *Instance_map::Iterator::next() { if (current_instance < instance_map->hash.records) return (Instance *) hash_element(&instance_map->hash, current_instance++); - else - return NULL; + + return NULL; } diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index e1bfe6ab391..522785ce9b9 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -38,7 +38,6 @@ extern void free_groups(char **groups); class Instance_map { - friend class Iterator; public: /* Instance_map iterator */ class Iterator @@ -54,6 +53,7 @@ public: void go_to_first(); Instance *next(); }; + friend class Iterator; public: /* returns a pointer to the instance or NULL, if there is no such instance */ Instance *find(const char *name, uint name_len); From a59a6361e8521458f2a9b5867681ce1a86af468b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Dec 2004 08:56:53 +0100 Subject: [PATCH 42/52] ndb - Fix memory leak in HugoLoad ndb/test/src/HugoTransactions.cpp: Fix memory leak in HugoLoad --- ndb/test/src/HugoTransactions.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ndb/test/src/HugoTransactions.cpp b/ndb/test/src/HugoTransactions.cpp index 096f5406bbf..ef5db6d9065 100644 --- a/ndb/test/src/HugoTransactions.cpp +++ b/ndb/test/src/HugoTransactions.cpp @@ -865,7 +865,7 @@ HugoTransactions::loadTable(Ndb* pNdb, g_info << "|- Inserting records..." << endl; for (int c=0 ; c= retryMax){ g_info << "Record " << c << " could not be inserted, has retried " << retryAttempt << " times " << endl; @@ -983,6 +983,9 @@ HugoTransactions::loadTable(Ndb* pNdb, c = c+batch; retryAttempt = 0; } + + if(pTrans) + pNdb->closeTransaction(pTrans); return NDBT_OK; } From a2649d0ddfda2ba0cf8caea9f26a3e4e61f10120 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Dec 2004 12:36:32 +0100 Subject: [PATCH 43/52] ndb: nothing ndb/test/ndbapi/Makefile.am: foo --- ndb/test/ndbapi/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndb/test/ndbapi/Makefile.am b/ndb/test/ndbapi/Makefile.am index c050c42a701..c50cd9648f0 100644 --- a/ndb/test/ndbapi/Makefile.am +++ b/ndb/test/ndbapi/Makefile.am @@ -32,7 +32,7 @@ testTransactions \ testDeadlock \ test_event ndbapi_slow_select testReadPerf testLcp \ testPartitioning \ -testBitfield foo +testBitfield #flexTimedAsynch #testBlobs @@ -73,7 +73,6 @@ testReadPerf_SOURCES = testReadPerf.cpp testLcp_SOURCES = testLcp.cpp testPartitioning_SOURCES = testPartitioning.cpp testBitfield_SOURCES = testBitfield.cpp -foo_SOURCES = foo.cpp INCLUDES_LOC = -I$(top_srcdir)/ndb/include/kernel @@ -91,3 +90,4 @@ testBackup_LDADD = $(LDADD) bank/libbank.a + From f507b37767334fb52ce3f7d32c710754ef15d022 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Dec 2004 12:50:33 +0100 Subject: [PATCH 44/52] ndb - Handle shm-transporter wo/ busy-wait + also handled mixed tcp/shm transporters + bug#7124 ndb/src/common/transporter/SHM_Transporter.cpp: Add remote/own pid for signaling availability of data on shm-segment ndb/src/common/transporter/SHM_Transporter.hpp: Add remote/own pid for signaling availability of data on shm-segment ndb/src/common/transporter/TransporterRegistry.cpp: Add remote/own pid for signaling availability of data on shm-segment ndb/src/kernel/main.cpp: Set pid to use for shm-signaling ndb/src/mgmsrv/ConfigInfo.cpp: bug#7124 ndb/src/ndbapi/TransporterFacade.cpp: Set pid to use for shm-signaling --- .../common/transporter/SHM_Transporter.cpp | 83 ++-- .../common/transporter/SHM_Transporter.hpp | 33 +- .../transporter/TransporterRegistry.cpp | 398 ++++++++++-------- ndb/src/kernel/main.cpp | 3 + ndb/src/mgmsrv/ConfigInfo.cpp | 63 ++- ndb/src/ndbapi/TransporterFacade.cpp | 4 + 6 files changed, 336 insertions(+), 248 deletions(-) diff --git a/ndb/src/common/transporter/SHM_Transporter.cpp b/ndb/src/common/transporter/SHM_Transporter.cpp index ab161d8c18c..7c2c2aafb85 100644 --- a/ndb/src/common/transporter/SHM_Transporter.cpp +++ b/ndb/src/common/transporter/SHM_Transporter.cpp @@ -26,6 +26,8 @@ #include #include +extern int g_shm_pid; + SHM_Transporter::SHM_Transporter(TransporterRegistry &t_reg, const char *lHostName, const char *rHostName, @@ -52,6 +54,7 @@ SHM_Transporter::SHM_Transporter(TransporterRegistry &t_reg, #ifdef DEBUG_TRANSPORTER printf("shm key (%d - %d) = %d\n", lNodeId, rNodeId, shmKey); #endif + m_signal_threshold = 4096; } SHM_Transporter::~SHM_Transporter(){ @@ -182,42 +185,6 @@ SHM_Transporter::setupBuffers(){ #endif } -#if 0 -SendStatus -SHM_Transporter::prepareSend(const SignalHeader * const signalHeader, - Uint8 prio, - const Uint32 * const signalData, - const LinearSegmentPtr ptr[3], - bool force){ - - if(isConnected()){ - - const Uint32 lenBytes = m_packer.getMessageLength(signalHeader, ptr); - - Uint32 * insertPtr = (Uint32 *)writer->getWritePtr(lenBytes); - - if(insertPtr != 0){ - - m_packer.pack(insertPtr, prio, signalHeader, signalData, ptr); - - /** - * Do funky membar stuff - */ - - writer->updateWritePtr(lenBytes); - return SEND_OK; - - } else { - // NdbSleep_MilliSleep(3); - //goto tryagain; - return SEND_BUFFER_FULL; - } - } - return SEND_DISCONNECTED; -} -#endif - - bool SHM_Transporter::connect_server_impl(NDB_SOCKET_TYPE sockfd) { @@ -247,10 +214,17 @@ SHM_Transporter::connect_server_impl(NDB_SOCKET_TYPE sockfd) } // Send ok to client - s_output.println("shm server 1 ok"); - + s_output.println("shm server 1 ok: %d", g_shm_pid); + // Wait for ok from client - if (s_input.gets(buf, 256) == 0) { + if (s_input.gets(buf, 256) == 0) + { + NDB_CLOSE_SOCKET(sockfd); + DBUG_RETURN(false); + } + + if(sscanf(buf, "shm client 1 ok: %d", &m_remote_pid) != 1) + { NDB_CLOSE_SOCKET(sockfd); DBUG_RETURN(false); } @@ -289,6 +263,12 @@ SHM_Transporter::connect_client_impl(NDB_SOCKET_TYPE sockfd) DBUG_RETURN(false); } + if(sscanf(buf, "shm server 1 ok: %d", &m_remote_pid) != 1) + { + NDB_CLOSE_SOCKET(sockfd); + DBUG_RETURN(false); + } + // Create if(!_shmSegCreated){ if (!ndb_shm_get()) { @@ -313,10 +293,10 @@ SHM_Transporter::connect_client_impl(NDB_SOCKET_TYPE sockfd) } // Send ok to server - s_output.println("shm client 1 ok"); - + s_output.println("shm client 1 ok: %d", g_shm_pid); + int r= connect_common(sockfd); - + if (r) { // Wait for ok from server if (s_input.gets(buf, 256) == 0) { @@ -344,18 +324,33 @@ SHM_Transporter::connect_common(NDB_SOCKET_TYPE sockfd) return false; } - if(!setupBuffersDone) { + if(!setupBuffersDone) + { setupBuffers(); setupBuffersDone=true; } - if(setupBuffersDone) { + if(setupBuffersDone) + { NdbSleep_MilliSleep(m_timeOutMillis); if(*serverStatusFlag == 1 && *clientStatusFlag == 1) + { + m_last_signal = 0; return true; + } } DBUG_PRINT("error", ("Failed to set up buffers to node %d", remoteNodeId)); return false; } + +void +SHM_Transporter::doSend() +{ + if(m_last_signal) + { + m_last_signal = 0; + kill(m_remote_pid, SIGUSR1); + } +} diff --git a/ndb/src/common/transporter/SHM_Transporter.hpp b/ndb/src/common/transporter/SHM_Transporter.hpp index 27692209ffe..b501f652168 100644 --- a/ndb/src/common/transporter/SHM_Transporter.hpp +++ b/ndb/src/common/transporter/SHM_Transporter.hpp @@ -47,18 +47,25 @@ public: * SHM destructor */ virtual ~SHM_Transporter(); - + /** * Do initialization */ bool initTransporter(); - - Uint32 * getWritePtr(Uint32 lenBytes, Uint32 prio){ + + Uint32 * getWritePtr(Uint32 lenBytes, Uint32 prio) + { return (Uint32 *)writer->getWritePtr(lenBytes); } - void updateWritePtr(Uint32 lenBytes, Uint32 prio){ + void updateWritePtr(Uint32 lenBytes, Uint32 prio) + { writer->updateWritePtr(lenBytes); + m_last_signal += lenBytes; + if(m_last_signal >= m_signal_threshold) + { + doSend(); + } } void getReceivePtr(Uint32 ** ptr, Uint32 ** eod){ @@ -123,28 +130,36 @@ protected: */ void setupBuffers(); + /** + * doSend (i.e signal receiver) + */ + void doSend(); + int m_remote_pid; + Uint32 m_last_signal; + Uint32 m_signal_threshold; + private: bool _shmSegCreated; bool _attached; bool m_connected; - + key_t shmKey; volatile Uint32 * serverStatusFlag; volatile Uint32 * clientStatusFlag; bool setupBuffersDone; - + #ifdef NDB_WIN32 HANDLE hFileMapping; #else int shmId; #endif - + int shmSize; char * shmBuf; - + SHM_Reader * reader; SHM_Writer * writer; - + /** * @return - True if the reader has data to read on its segment. */ diff --git a/ndb/src/common/transporter/TransporterRegistry.cpp b/ndb/src/common/transporter/TransporterRegistry.cpp index a2fab8f9806..61924fe55b2 100644 --- a/ndb/src/common/transporter/TransporterRegistry.cpp +++ b/ndb/src/common/transporter/TransporterRegistry.cpp @@ -47,6 +47,8 @@ #include #include +int g_shm_pid = 0; + SocketServer::Session * TransporterService::newSession(NDB_SOCKET_TYPE sockfd) { DBUG_ENTER("SocketServer::Session * TransporterService::newSession"); @@ -622,11 +624,28 @@ TransporterRegistry::pollReceive(Uint32 timeOutMillis){ return retVal; #endif - if((nSHMTransporters+nSCITransporters) > 0) + if((nSCITransporters) > 0) + { timeOutMillis=0; + } + +#ifdef NDB_SHM_TRANSPORTER + if(nSHMTransporters > 0) + { + Uint32 res = poll_SHM(0); + if(res) + { + retVal |= res; + timeOutMillis = 0; + } + } +#endif + #ifdef NDB_TCP_TRANSPORTER - if(nTCPTransporters > 0) + if(nTCPTransporters > 0 || retVal == 0) + { retVal |= poll_TCP(timeOutMillis); + } else tcpReadSelectReply = 0; #endif @@ -635,8 +654,11 @@ TransporterRegistry::pollReceive(Uint32 timeOutMillis){ retVal |= poll_SCI(timeOutMillis); #endif #ifdef NDB_SHM_TRANSPORTER - if(nSHMTransporters > 0) - retVal |= poll_SHM(timeOutMillis); + if(nSHMTransporters > 0 && retVal == 0) + { + int res = poll_SHM(0); + retVal |= res; + } #endif return retVal; } @@ -644,8 +666,8 @@ TransporterRegistry::pollReceive(Uint32 timeOutMillis){ #ifdef NDB_SCI_TRANSPORTER Uint32 -TransporterRegistry::poll_SCI(Uint32 timeOutMillis){ - +TransporterRegistry::poll_SCI(Uint32 timeOutMillis) +{ for (int i=0; iisConnected()) { @@ -659,73 +681,29 @@ TransporterRegistry::poll_SCI(Uint32 timeOutMillis){ #ifdef NDB_SHM_TRANSPORTER +static int g_shm_counter = 0; Uint32 TransporterRegistry::poll_SHM(Uint32 timeOutMillis) { - for(int j=0; j < 20; j++) - for (int i=0; iisConnected()) { - if(t->hasDataToRead()) { - return 1; - } - } - } - /** - * @note: granularity of linux/i386 timer is not good enough. - * Can't sleep if using SHM as it is now. - */ - /* - if(timeOutMillis > 0) - NdbSleep_MilliSleep(timeOutMillis); - else - NdbSleep_MilliSleep(1); - */ - return 0; -#if 0 - NDB_TICKS startTime = NdbTick_CurrentMillisecond(); - for(int i=0; i<100; i++) { + for(int j=0; j < 100; j++) + { for (int i=0; iisConnected()) { - if(t->hasDataToRead()){ + if(t->hasDataToRead()) { return 1; } - else - continue; - } - else - continue; - } - - if(NdbTick_CurrentMillisecond() > (startTime +timeOutMillis)) - return 0; - } - NdbSleep_MilliSleep(5); - return 0; - -#endif -#if 0 - - for(int j=0; j < 100; j++) { - for (int i=0; iisConnected()) { - if(t->hasDataToRead()) - return 1; } } } return 0; -#endif } - - #endif #ifdef NDB_OSE_TRANSPORTER Uint32 -TransporterRegistry::poll_OSE(Uint32 timeOutMillis){ +TransporterRegistry::poll_OSE(Uint32 timeOutMillis) +{ if(theOSEReceiver != NULL){ return theOSEReceiver->doReceive(timeOutMillis); } @@ -736,18 +714,18 @@ TransporterRegistry::poll_OSE(Uint32 timeOutMillis){ #ifdef NDB_TCP_TRANSPORTER Uint32 -TransporterRegistry::poll_TCP(Uint32 timeOutMillis){ - - if (nTCPTransporters == 0){ +TransporterRegistry::poll_TCP(Uint32 timeOutMillis) +{ + if (false && nTCPTransporters == 0) + { tcpReadSelectReply = 0; return 0; } struct timeval timeout; #ifdef NDB_OSE - // Return directly if there are no TCP transporters configured - + if(timeOutMillis <= 1){ timeout.tv_sec = 0; timeout.tv_usec = 1025; @@ -760,7 +738,7 @@ TransporterRegistry::poll_TCP(Uint32 timeOutMillis){ timeout.tv_usec = (timeOutMillis % 1000) * 1000; #endif - NDB_SOCKET_TYPE maxSocketValue = 0; + NDB_SOCKET_TYPE maxSocketValue = -1; // Needed for TCP/IP connections // The read- and writeset are used by select @@ -788,23 +766,29 @@ TransporterRegistry::poll_TCP(Uint32 timeOutMillis){ maxSocketValue++; tcpReadSelectReply = select(maxSocketValue, &tcpReadset, 0, 0, &timeout); + if(false && tcpReadSelectReply == -1 && errno == EINTR) + ndbout_c("woke-up by signal"); + #ifdef NDB_WIN32 if(tcpReadSelectReply == SOCKET_ERROR) { NdbSleep_MilliSleep(timeOutMillis); } #endif - + return tcpReadSelectReply; } #endif void -TransporterRegistry::performReceive(){ +TransporterRegistry::performReceive() +{ #ifdef NDB_OSE_TRANSPORTER - if(theOSEReceiver != 0){ - while(theOSEReceiver->hasData()){ + if(theOSEReceiver != 0) + { + while(theOSEReceiver->hasData()) + { NodeId remoteNodeId; Uint32 * readPtr; Uint32 sz = theOSEReceiver->getReceiveData(&remoteNodeId, &readPtr); @@ -827,16 +811,20 @@ TransporterRegistry::performReceive(){ #endif #ifdef NDB_TCP_TRANSPORTER - if(tcpReadSelectReply > 0){ - for (int i=0; i 0) + { + for (int i=0; igetRemoteNodeId(); const NDB_SOCKET_TYPE socket = t->getSocket(); if(is_connected(nodeId)){ - if(t->isConnected() && FD_ISSET(socket, &tcpReadset)) { + if(t->isConnected() && FD_ISSET(socket, &tcpReadset)) + { const int receiveSize = t->doReceive(); - if(receiveSize > 0){ + if(receiveSize > 0) + { Uint32 * ptr; Uint32 sz = t->getReceiveData(&ptr); Uint32 szUsed = unpack(ptr, sz, nodeId, ioStates[nodeId]); @@ -847,142 +835,165 @@ TransporterRegistry::performReceive(){ } } #endif - - + #ifdef NDB_SCI_TRANSPORTER //performReceive //do prepareReceive on the SCI transporters (prepareReceive(t,,,,)) - for (int i=0; igetRemoteNodeId(); - if(is_connected(nodeId)){ - if(t->isConnected() && t->checkConnected()){ - Uint32 * readPtr, * eodPtr; - t->getReceivePtr(&readPtr, &eodPtr); - Uint32 *newPtr = unpack(readPtr, eodPtr, nodeId, ioStates[nodeId]); - t->updateReceivePtr(newPtr); - } - } - } + for (int i=0; igetRemoteNodeId(); + if(is_connected(nodeId)) + { + if(t->isConnected() && t->checkConnected()) + { + Uint32 * readPtr, * eodPtr; + t->getReceivePtr(&readPtr, &eodPtr); + Uint32 *newPtr = unpack(readPtr, eodPtr, nodeId, ioStates[nodeId]); + t->updateReceivePtr(newPtr); + } + } + } #endif #ifdef NDB_SHM_TRANSPORTER - for (int i=0; igetRemoteNodeId(); - if(is_connected(nodeId)){ - if(t->isConnected() && t->checkConnected()){ - Uint32 * readPtr, * eodPtr; - t->getReceivePtr(&readPtr, &eodPtr); - Uint32 *newPtr = unpack(readPtr, eodPtr, nodeId, ioStates[nodeId]); - t->updateReceivePtr(newPtr); - } - } - } + for (int i=0; igetRemoteNodeId(); + if(is_connected(nodeId)){ + if(t->isConnected() && t->checkConnected()) + { + Uint32 * readPtr, * eodPtr; + t->getReceivePtr(&readPtr, &eodPtr); + Uint32 *newPtr = unpack(readPtr, eodPtr, nodeId, ioStates[nodeId]); + t->updateReceivePtr(newPtr); + } + } + } #endif } static int x = 0; void -TransporterRegistry::performSend(){ - int i; - sendCounter = 1; - +TransporterRegistry::performSend() +{ + int i; + sendCounter = 1; + #ifdef NDB_OSE_TRANSPORTER - for (int i = 0; i < nOSETransporters; i++){ - OSE_Transporter *t = theOSETransporters[i]; - if((is_connected(t->getRemoteNodeId()) && - (t->isConnected())) { - t->doSend(); - }//if - }//for + for (int i = 0; i < nOSETransporters; i++) + { + OSE_Transporter *t = theOSETransporters[i]; + if(is_connected(t->getRemoteNodeId()) &&& (t->isConnected())) + { + t->doSend(); + }//if + }//for #endif - + #ifdef NDB_TCP_TRANSPORTER #ifdef NDB_OSE + { + int maxSocketValue = 0; + + // Needed for TCP/IP connections + // The writeset are used by select + fd_set writeset; + FD_ZERO(&writeset); + + // Prepare for sending and receiving + for (i = 0; i < nTCPTransporters; i++) { + TCP_Transporter * t = theTCPTransporters[i]; + + // If the transporter is connected + if ((t->hasDataToSend()) && (t->isConnected())) { + const int socket = t->getSocket(); + // Find the highest socket value. It will be used by select + if (socket > maxSocketValue) { + maxSocketValue = socket; + }//if + FD_SET(socket, &writeset); + }//if + }//for + + // The highest socket value plus one + if(maxSocketValue == 0) + return; + + maxSocketValue++; + struct timeval timeout = { 0, 1025 }; + Uint32 tmp = select(maxSocketValue, 0, &writeset, 0, &timeout); + + if (tmp == 0) { - int maxSocketValue = 0; - - // Needed for TCP/IP connections - // The writeset are used by select - fd_set writeset; - FD_ZERO(&writeset); - - // Prepare for sending and receiving - for (i = 0; i < nTCPTransporters; i++) { - TCP_Transporter * t = theTCPTransporters[i]; - - // If the transporter is connected - if ((t->hasDataToSend()) && (t->isConnected())) { - const int socket = t->getSocket(); - // Find the highest socket value. It will be used by select - if (socket > maxSocketValue) { - maxSocketValue = socket; - }//if - FD_SET(socket, &writeset); - }//if - }//for - - // The highest socket value plus one - if(maxSocketValue == 0) - return; - - maxSocketValue++; - struct timeval timeout = { 0, 1025 }; - Uint32 tmp = select(maxSocketValue, 0, &writeset, 0, &timeout); - - if (tmp == 0) { - return; - }//if - for (i = 0; i < nTCPTransporters; i++) { - TCP_Transporter *t = theTCPTransporters[i]; - const NodeId nodeId = t->getRemoteNodeId(); - const int socket = t->getSocket(); - if(is_connected(nodeId)){ - if(t->isConnected() && FD_ISSET(socket, &writeset)) { - t->doSend(); - }//if - }//if - }//for + return; + }//if + for (i = 0; i < nTCPTransporters; i++) { + TCP_Transporter *t = theTCPTransporters[i]; + const NodeId nodeId = t->getRemoteNodeId(); + const int socket = t->getSocket(); + if(is_connected(nodeId)){ + if(t->isConnected() && FD_ISSET(socket, &writeset)) { + t->doSend(); + }//if + }//if + }//for } #endif #ifdef NDB_TCP_TRANSPORTER - for (i = x; i < nTCPTransporters; i++) { - TCP_Transporter *t = theTCPTransporters[i]; - if (t && - (t->hasDataToSend()) && - (t->isConnected()) && - (is_connected(t->getRemoteNodeId()))) { - t->doSend(); - }//if - }//for - for (i = 0; i < x && i < nTCPTransporters; i++) { - TCP_Transporter *t = theTCPTransporters[i]; - if (t && - (t->hasDataToSend()) && - (t->isConnected()) && - (is_connected(t->getRemoteNodeId()))) { - t->doSend(); - }//if - }//for - x++; - if (x == nTCPTransporters) x = 0; + for (i = x; i < nTCPTransporters; i++) + { + TCP_Transporter *t = theTCPTransporters[i]; + if (t && t->hasDataToSend() && t->isConnected() && + is_connected(t->getRemoteNodeId())) + { + t->doSend(); + } + } + for (i = 0; i < x && i < nTCPTransporters; i++) + { + TCP_Transporter *t = theTCPTransporters[i]; + if (t && t->hasDataToSend() && t->isConnected() && + is_connected(t->getRemoteNodeId())) + { + t->doSend(); + } + } + x++; + if (x == nTCPTransporters) x = 0; #endif #endif #ifdef NDB_SCI_TRANSPORTER - //scroll through the SCI transporters, - // get each transporter, check if connected, send data - for (i=0; igetRemoteNodeId(); - - if(is_connected(nodeId)){ - if(t->isConnected() && t->hasDataToSend()) { - t->doSend(); - } //if + //scroll through the SCI transporters, + // get each transporter, check if connected, send data + for (i=0; igetRemoteNodeId(); + + if(is_connected(nodeId)) + { + if(t->isConnected() && t->hasDataToSend()) { + t->doSend(); } //if - } //if + } //if + } +#endif + +#ifdef NDB_SHM_TRANSPORTER + for (i=0; igetRemoteNodeId(); + if(is_connected(nodeId)) + { + if(t->isConnected()) + { + t->doSend(); + } + } + } #endif } @@ -1169,7 +1180,8 @@ TransporterRegistry::stop_clients() } void -TransporterRegistry::add_transporter_interface(const char *interface, unsigned short port) +TransporterRegistry::add_transporter_interface(const char *interface, + unsigned short port) { DBUG_ENTER("TransporterRegistry::add_transporter_interface"); DBUG_PRINT("enter",("interface=%s, port= %d", interface, port)); @@ -1232,6 +1244,15 @@ TransporterRegistry::start_service(SocketServer& socket_server) return true; } +#ifdef NDB_SHM_TRANSPORTER +static +RETSIGTYPE +shm_sig_handler(int signo) +{ + g_shm_counter++; +} +#endif + void TransporterRegistry::startReceiving() { @@ -1250,6 +1271,13 @@ TransporterRegistry::startReceiving() for(int i = 0; itheReceiverPid = theReceiverPid; #endif + +#ifdef NDB_SHM_TRANSPORTER + if(nSHMTransporters) + { + signal(SIGUSR1, shm_sig_handler); + } +#endif } void diff --git a/ndb/src/kernel/main.cpp b/ndb/src/kernel/main.cpp index 926647838c9..7d6b597e6dd 100644 --- a/ndb/src/kernel/main.cpp +++ b/ndb/src/kernel/main.cpp @@ -49,6 +49,8 @@ void catchsigs(bool ignore); // for process signal handling extern "C" void handler_shutdown(int signum); // for process signal handling extern "C" void handler_error(int signum); // for process signal handling +extern int g_shm_pid; + // Shows system information void systemInfo(const Configuration & conf, const LogLevel & ll); @@ -145,6 +147,7 @@ int main(int argc, char** argv) } g_eventLogger.info("Angel pid: %d ndb pid: %d", getppid(), getpid()); + g_shm_pid = getpid(); theConfig->setupConfiguration(); systemInfo(* theConfig, * theConfig->m_logLevel); diff --git a/ndb/src/mgmsrv/ConfigInfo.cpp b/ndb/src/mgmsrv/ConfigInfo.cpp index 4af1556a0c0..35229d1f666 100644 --- a/ndb/src/mgmsrv/ConfigInfo.cpp +++ b/ndb/src/mgmsrv/ConfigInfo.cpp @@ -82,7 +82,7 @@ static bool transformConnection(InitConfigFileParser::Context & ctx, const char static bool applyDefaultValues(InitConfigFileParser::Context & ctx, const char *); static bool checkMandatory(InitConfigFileParser::Context & ctx, const char *); static bool fixPortNumber(InitConfigFileParser::Context & ctx, const char *); -static bool fixShmkey(InitConfigFileParser::Context & ctx, const char *); +static bool fixShmKey(InitConfigFileParser::Context & ctx, const char *); static bool checkDbConstraints(InitConfigFileParser::Context & ctx, const char *); static bool checkConnectionConstraints(InitConfigFileParser::Context &, const char *); static bool checkTCPConstraints(InitConfigFileParser::Context &, const char *); @@ -131,13 +131,15 @@ ConfigInfo::m_SectionRules[] = { { "TCP", fixHostname, "HostName2" }, { "SCI", fixHostname, "HostName1" }, { "SCI", fixHostname, "HostName2" }, + { "SHM", fixHostname, "HostName1" }, + { "SHM", fixHostname, "HostName2" }, { "OSE", fixHostname, "HostName1" }, { "OSE", fixHostname, "HostName2" }, { "TCP", fixPortNumber, 0 }, // has to come after fixHostName { "SHM", fixPortNumber, 0 }, // has to come after fixHostName { "SCI", fixPortNumber, 0 }, // has to come after fixHostName - //{ "SHM", fixShmKey, 0 }, + { "SHM", fixShmKey, 0 }, /** * fixExtConnection must be after fixNodeId @@ -168,6 +170,8 @@ ConfigInfo::m_SectionRules[] = { { "TCP", checkTCPConstraints, "HostName2" }, { "SCI", checkTCPConstraints, "HostName1" }, { "SCI", checkTCPConstraints, "HostName2" }, + { "SHM", checkTCPConstraints, "HostName1" }, + { "SHM", checkTCPConstraints, "HostName2" }, { "*", checkMandatory, 0 }, @@ -1687,16 +1691,27 @@ const ConfigInfo::ParamInfo ConfigInfo::m_ParamInfo[] = { 0, 0 }, { - CFG_CONNECTION_NODE_1, - "NodeId1", + CFG_CONNECTION_HOSTNAME_1, + "HostName1", "SHM", - "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", - ConfigInfo::USED, + "Name/IP of computer on one side of the connection", + ConfigInfo::INTERNAL, false, ConfigInfo::STRING, - MANDATORY, + UNDEFINED, 0, 0 }, - + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "SHM", + "Name/IP of computer on one side of the connection", + ConfigInfo::INTERNAL, + false, + ConfigInfo::STRING, + UNDEFINED, + 0, 0 }, + { CFG_CONNECTION_SERVER_PORT, "PortNumber", @@ -1709,6 +1724,17 @@ const ConfigInfo::ParamInfo ConfigInfo::m_ParamInfo[] = { "0", STR_VALUE(MAX_INT_RNIL) }, + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "SHM", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + ConfigInfo::USED, + false, + ConfigInfo::STRING, + MANDATORY, + 0, 0 }, + { CFG_CONNECTION_NODE_2, "NodeId2", @@ -3025,15 +3051,32 @@ fixPortNumber(InitConfigFileParser::Context & ctx, const char * data){ << "per connection, please remove from config. " << "Will be changed to " << port << endl; ctx.m_currentSection->put("PortNumber", port, true); - } else + } + else + { ctx.m_currentSection->put("PortNumber", port); - + } DBUG_PRINT("info", ("connection %d-%d port %d host %s", id1, id2, port, hostname.c_str())); DBUG_RETURN(true); } +static +bool +fixShmKey(InitConfigFileParser::Context & ctx, const char *) +{ + Uint32 id1= 0, id2= 0, key= 0; + require(ctx.m_currentSection->get("NodeId1", &id1)); + require(ctx.m_currentSection->get("NodeId2", &id2)); + if(ctx.m_currentSection->get("ShmKey", &key)) + return true; + + key= (id1 > id2 ? id1 << 16 | id2 : id2 << 16 | id1); + ctx.m_currentSection->put("ShmKey", key); + return true; +} + /** * DB Node rule: Check various constraints */ diff --git a/ndb/src/ndbapi/TransporterFacade.cpp b/ndb/src/ndbapi/TransporterFacade.cpp index dfb090c8416..f6a7496decc 100644 --- a/ndb/src/ndbapi/TransporterFacade.cpp +++ b/ndb/src/ndbapi/TransporterFacade.cpp @@ -450,8 +450,12 @@ runReceiveResponse_C(void * me) return me; } +extern int g_shm_pid; + void TransporterFacade::threadMainReceive(void) { + g_shm_pid = getpid(); + theTransporterRegistry->startReceiving(); NdbMutex_Lock(theMutexPtr); theTransporterRegistry->update_connections(); From 7e7a2b32e8a9c3e784e6e3f3a6927635cd4ec9a8 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Dec 2004 08:10:22 +0100 Subject: [PATCH 45/52] ndb - shm transporter benchmark result ndb/docs/wl2077.txt: Add result from micro benchmark with shm-transporter ndb/include/transporter/TransporterRegistry.hpp: Move pid into transporter registry ndb/src/common/transporter/SHM_Transporter.cpp: Move pid into transporter registry ndb/src/common/transporter/TransporterRegistry.cpp: Move pid into transporter registry ndb/src/kernel/main.cpp: Move pid into transporter registry ndb/src/ndbapi/TransporterFacade.cpp: Move pid into transporter registry --- ndb/docs/wl2077.txt | 33 +++++++++++++------ .../transporter/TransporterRegistry.hpp | 2 ++ .../common/transporter/SHM_Transporter.cpp | 6 ++-- .../transporter/TransporterRegistry.cpp | 6 ++-- ndb/src/kernel/main.cpp | 3 -- ndb/src/ndbapi/TransporterFacade.cpp | 4 --- 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/ndb/docs/wl2077.txt b/ndb/docs/wl2077.txt index 5a77c18aa2a..f5b10bb702e 100644 --- a/ndb/docs/wl2077.txt +++ b/ndb/docs/wl2077.txt @@ -3,18 +3,24 @@ 1 host, 1 ndbd, api co-hosted results in 1000 rows / sec - wo/reset bounds w/ rb -4.1-read committed a) 4.9 b) 7.4 -4.1-read hold lock c) 4.7 d) 6.7 + wo/reset bounds w/ rb +4.1-read committed a) 4.9 b) 7.4 +4.1-read hold lock c) 4.7 d) 6.7 -wl2077-read committed 6.4 (+30%) 10.8 (+45%) -wl2077-read hold lock 4.6 (-1%) 6.7 (+ 0%) +wl2077-read committed 6.4 (+30%) 10.8 (+45%) +wl2077-read hold lock 4.6 (-1%) 6.7 (+ 0%) --- Comparision e) -serial pk: 10.9' -batched (1000): 59' -serial uniq index: 8.4' -batched (1000): 33' +5.0-ndb batch read committed f) 50' (+680%) g) 50' (+360%) +5.0-ndb batch read hold lock h) 12' (+160%) i) 13' (+79%) + +shm-mem read committed (cmp. wl2077) a) 9.5' (+48%) b) 14' (+30%) + read hold lock c) 6.7' (+45%) d) 9.8' (+46%) + +-- Comparision e) shm +serial pk: 10.9' 20' (+83%) +batched (1000): 59' 62' (+5%) +serial uniq index: 8.4' 14' (+66%) +batched (1000): 33' 36' (+9%) index range (1000): 186' ---- @@ -25,6 +31,8 @@ b) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 0 -r 2 -q 1 T1 c) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 1 -r 2 -q 0 T1 d) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 1 -r 2 -q 1 T1 e) testReadPerf -i 25 -c 0 -d 0 T1 +f) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 0 -r 3 -q 0 -m 1000 -i 10 T1 +g) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 0 -r 3 -q 1 -m 1000 -i 10 T1 --- music join 1db-co 2db-co @@ -33,3 +41,8 @@ e) testReadPerf -i 25 -c 0 -d 0 T1 wl2077 12s 14s wl2077 wo/ blobs 1.2s (-30%) 2.5s (-22%) + +pekka-blob-fix 1.3s + +shm 1.2s 2.0s +shm wo/ blobs 1.1s 2.0s diff --git a/ndb/include/transporter/TransporterRegistry.hpp b/ndb/include/transporter/TransporterRegistry.hpp index ac6291f9e57..4ca288d0a0a 100644 --- a/ndb/include/transporter/TransporterRegistry.hpp +++ b/ndb/include/transporter/TransporterRegistry.hpp @@ -312,6 +312,8 @@ private: Uint32 poll_TCP(Uint32 timeOutMillis); Uint32 poll_SCI(Uint32 timeOutMillis); Uint32 poll_SHM(Uint32 timeOutMillis); + + int m_shm_own_pid; }; #endif // Define of TransporterRegistry_H diff --git a/ndb/src/common/transporter/SHM_Transporter.cpp b/ndb/src/common/transporter/SHM_Transporter.cpp index 7c2c2aafb85..e4051519b86 100644 --- a/ndb/src/common/transporter/SHM_Transporter.cpp +++ b/ndb/src/common/transporter/SHM_Transporter.cpp @@ -214,7 +214,8 @@ SHM_Transporter::connect_server_impl(NDB_SOCKET_TYPE sockfd) } // Send ok to client - s_output.println("shm server 1 ok: %d", g_shm_pid); + s_output.println("shm server 1 ok: %d", + m_transporter_registry.m_shm_own_pid); // Wait for ok from client if (s_input.gets(buf, 256) == 0) @@ -293,7 +294,8 @@ SHM_Transporter::connect_client_impl(NDB_SOCKET_TYPE sockfd) } // Send ok to server - s_output.println("shm client 1 ok: %d", g_shm_pid); + s_output.println("shm client 1 ok: %d", + m_transporter_registry.m_shm_own_pid); int r= connect_common(sockfd); diff --git a/ndb/src/common/transporter/TransporterRegistry.cpp b/ndb/src/common/transporter/TransporterRegistry.cpp index 61924fe55b2..77e72a4fa2c 100644 --- a/ndb/src/common/transporter/TransporterRegistry.cpp +++ b/ndb/src/common/transporter/TransporterRegistry.cpp @@ -1273,10 +1273,8 @@ TransporterRegistry::startReceiving() #endif #ifdef NDB_SHM_TRANSPORTER - if(nSHMTransporters) - { - signal(SIGUSR1, shm_sig_handler); - } + m_shm_own_pid = getpid(); + signal(SIGUSR1, shm_sig_handler); #endif } diff --git a/ndb/src/kernel/main.cpp b/ndb/src/kernel/main.cpp index 7d6b597e6dd..926647838c9 100644 --- a/ndb/src/kernel/main.cpp +++ b/ndb/src/kernel/main.cpp @@ -49,8 +49,6 @@ void catchsigs(bool ignore); // for process signal handling extern "C" void handler_shutdown(int signum); // for process signal handling extern "C" void handler_error(int signum); // for process signal handling -extern int g_shm_pid; - // Shows system information void systemInfo(const Configuration & conf, const LogLevel & ll); @@ -147,7 +145,6 @@ int main(int argc, char** argv) } g_eventLogger.info("Angel pid: %d ndb pid: %d", getppid(), getpid()); - g_shm_pid = getpid(); theConfig->setupConfiguration(); systemInfo(* theConfig, * theConfig->m_logLevel); diff --git a/ndb/src/ndbapi/TransporterFacade.cpp b/ndb/src/ndbapi/TransporterFacade.cpp index f6a7496decc..dfb090c8416 100644 --- a/ndb/src/ndbapi/TransporterFacade.cpp +++ b/ndb/src/ndbapi/TransporterFacade.cpp @@ -450,12 +450,8 @@ runReceiveResponse_C(void * me) return me; } -extern int g_shm_pid; - void TransporterFacade::threadMainReceive(void) { - g_shm_pid = getpid(); - theTransporterRegistry->startReceiving(); NdbMutex_Lock(theMutexPtr); theTransporterRegistry->update_connections(); From ebee4cfad31de754185483811219135b5ef7c68a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Dec 2004 08:26:53 +0100 Subject: [PATCH 46/52] ndb - fix protection wrt shm ndb/include/transporter/TransporterRegistry.hpp: fix protection --- ndb/include/transporter/TransporterRegistry.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ndb/include/transporter/TransporterRegistry.hpp b/ndb/include/transporter/TransporterRegistry.hpp index 4ca288d0a0a..4946222d23e 100644 --- a/ndb/include/transporter/TransporterRegistry.hpp +++ b/ndb/include/transporter/TransporterRegistry.hpp @@ -87,6 +87,7 @@ public: */ class TransporterRegistry { friend class OSE_Receiver; + friend class SHM_Transporter; friend class Transporter; friend class TransporterService; public: @@ -312,7 +313,7 @@ private: Uint32 poll_TCP(Uint32 timeOutMillis); Uint32 poll_SCI(Uint32 timeOutMillis); Uint32 poll_SHM(Uint32 timeOutMillis); - + int m_shm_own_pid; }; From 63b36352d8af260af499596f1188f1f56c62b73f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Dec 2004 08:57:50 +0100 Subject: [PATCH 47/52] ndb - Handle connections without corresponding node ndb/src/mgmsrv/ConfigInfo.cpp: Handle connections without corresponding node --- ndb/src/mgmsrv/ConfigInfo.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ndb/src/mgmsrv/ConfigInfo.cpp b/ndb/src/mgmsrv/ConfigInfo.cpp index 35229d1f666..7417bef5619 100644 --- a/ndb/src/mgmsrv/ConfigInfo.cpp +++ b/ndb/src/mgmsrv/ConfigInfo.cpp @@ -2973,7 +2973,13 @@ fixHostname(InitConfigFileParser::Context & ctx, const char * data){ require(ctx.m_currentSection->get(buf, &id)); const Properties * node; - require(ctx.m_config->get("Node", id, &node)); + if(!ctx.m_config->get("Node", id, &node)) + { + ctx.reportError("Unknown node: \"%d\" specified in connection " + "[%s] starting at line: %d", + id, ctx.fname, ctx.m_sectionLineno); + return false; + } const char * hostname; require(node->get("HostName", &hostname)); From 253dab847d3c75f2904d00331ce7e1a199827e90 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Dec 2004 10:58:29 +0100 Subject: [PATCH 48/52] wl2240 - ndb partitioning, fix scan take over ndb/include/kernel/AttributeDescriptor.hpp: remove deprecated bits ndb/include/kernel/signaldata/DictTabInfo.hpp: removed deprecated parameters moved never implemented to Unimplemented ndb/src/common/debugger/signaldata/DictTabInfo.cpp: removed deprecated parameters moved never implemented to Unimplemented ndb/src/kernel/blocks/dbdict/Dbdict.cpp: removed deprecated parameters moved never implemented to Unimplemented ndb/src/kernel/vm/MetaData.hpp: removed deprecated parameters moved never implemented to Unimplemented ndb/src/ndbapi/NdbDictionaryImpl.cpp: removed deprecated parameters moved never implemented to Unimplemented ndb/src/ndbapi/NdbScanOperation.cpp: Do scan take over via distribution key code --- ndb/include/kernel/AttributeDescriptor.hpp | 37 +---------- ndb/include/kernel/signaldata/DictTabInfo.hpp | 63 ++++++------------- .../debugger/signaldata/DictTabInfo.cpp | 14 ----- ndb/src/kernel/blocks/dbdict/Dbdict.cpp | 14 ----- ndb/src/kernel/vm/MetaData.hpp | 6 -- ndb/src/ndbapi/NdbDictionaryImpl.cpp | 2 - ndb/src/ndbapi/NdbScanOperation.cpp | 2 + 7 files changed, 21 insertions(+), 117 deletions(-) diff --git a/ndb/include/kernel/AttributeDescriptor.hpp b/ndb/include/kernel/AttributeDescriptor.hpp index 789325c7939..834070651a3 100644 --- a/ndb/include/kernel/AttributeDescriptor.hpp +++ b/ndb/include/kernel/AttributeDescriptor.hpp @@ -33,7 +33,6 @@ private: static void setDGroup(Uint32 &, Uint32 dgroup); static void setDKey(Uint32 &, Uint32 dkey); static void setPrimaryKey(Uint32 &, Uint32 dkey); - static void setStoredInTup(Uint32 &, Uint32 storedInTup); static void setDynamic(Uint32 &, Uint32 dynamicInd); static Uint32 getType(const Uint32 &); @@ -44,10 +43,8 @@ private: static Uint32 getArraySize(const Uint32 &); static Uint32 getOriginal(const Uint32 &); static Uint32 getNullable(const Uint32 &); - static Uint32 getDGroup(const Uint32 &); static Uint32 getDKey(const Uint32 &); static Uint32 getPrimaryKey(const Uint32 &); - static Uint32 getStoredInTup(const Uint32 &); static Uint32 getDynamic(const Uint32 &); }; @@ -58,18 +55,15 @@ private: * s = Attribute size - 3 Bits -> Max 7 (Bit 4-6) * o = Original attribute - 1 Bit 7 * n = Nullable - 1 Bit 8 - * ? = Stored in tup - 1 Bit 9 * d = Disk based - 1 Bit 10 - * g = Distribution Group Ind- 1 Bit 11 * k = Distribution Key Ind - 1 Bit 12 - * r = Distribution group sz - 1 Bit 13 * p = Primary key attribute - 1 Bit 14 * y = Dynamic attribute - 1 Bit 15 * z = Array size - 16 Bits -> Max 65535 (Bit 16-31) * * 1111111111222222222233 * 01234567890123456789012345678901 - * aattsss n dgkrpyzzzzzzzzzzzzzzzz + * aattsss n d k pyzzzzzzzzzzzzzzzz * */ @@ -89,11 +83,8 @@ private: #define AD_ORIGINAL_SHIFT (8) #define AD_NULLABLE_SHIFT (8) -#define AD_TUP_STORED_SHIFT (9) -#define AD_DISTR_GROUP_SHIFT (11) #define AD_DISTR_KEY_SHIFT (12) -#define AD_DISTR_GROUP_SZ (13) #define AD_PRIMARY_KEY (14) #define AD_DYNAMIC (15) @@ -140,13 +131,6 @@ AttributeDescriptor::setOriginal(Uint32 & desc, Uint32 original){ desc |= (original << AD_ORIGINAL_SHIFT); } -inline -void -AttributeDescriptor::setDGroup(Uint32 & desc, Uint32 dgroup){ - ASSERT_BOOL(dgroup, "AttributeDescriptor::setDGroup"); - desc |= (dgroup << AD_DISTR_GROUP_SHIFT); -} - inline void AttributeDescriptor::setDKey(Uint32 & desc, Uint32 dkey){ @@ -161,13 +145,6 @@ AttributeDescriptor::setPrimaryKey(Uint32 & desc, Uint32 dkey){ desc |= (dkey << AD_PRIMARY_KEY); } -inline -void -AttributeDescriptor::setStoredInTup(Uint32 & desc, Uint32 storedInTup){ - ASSERT_BOOL(storedInTup, "AttributeDescriptor::setStoredInTup"); - desc |= (storedInTup << AD_TUP_STORED_SHIFT); -} - inline void AttributeDescriptor::setDynamic(Uint32 & desc, Uint32 dynamic){ @@ -229,12 +206,6 @@ AttributeDescriptor::getOriginal(const Uint32 & desc){ return (desc >> AD_ORIGINAL_SHIFT) & 1; } -inline -Uint32 -AttributeDescriptor::getDGroup(const Uint32 & desc){ - return (desc >> AD_DISTR_GROUP_SHIFT) & 1; -} - inline Uint32 AttributeDescriptor::getDKey(const Uint32 & desc){ @@ -253,10 +224,4 @@ AttributeDescriptor::getDynamic(const Uint32 & desc){ return (desc >> AD_DYNAMIC) & 1; } -inline -Uint32 -AttributeDescriptor::getStoredInTup(const Uint32 & desc){ - return (desc >> AD_TUP_STORED_SHIFT) & 1; -} - #endif diff --git a/ndb/include/kernel/signaldata/DictTabInfo.hpp b/ndb/include/kernel/signaldata/DictTabInfo.hpp index 62830b1dbc1..4d459ddf813 100644 --- a/ndb/include/kernel/signaldata/DictTabInfo.hpp +++ b/ndb/include/kernel/signaldata/DictTabInfo.hpp @@ -85,10 +85,6 @@ public: MaxLoadFactor = 11, //Default 80 KeyLength = 12, //Default 1 (No of words in primary key) FragmentTypeVal = 13, //Default AllNodesSmallTable - TableStorageVal = 14, //Default StorageType::MainMemory - ScanOptimised = 15, //Default updateOptimised - FragmentKeyTypeVal = 16, //Default PrimaryKey - SecondTableId = 17, //Mandatory between DICT's otherwise not allowed TableTypeVal = 18, //Default TableType::UserTable PrimaryTable = 19, //Mandatory for index otherwise RNIL PrimaryTableId = 20, //ditto @@ -110,10 +106,7 @@ public: AttributeKeyFlag = 1006, //Default noKey AttributeStorage = 1007, //Default MainMemory AttributeNullableFlag = 1008, //Default NotNullable - AttributeDGroup = 1009, //Default NotDGroup AttributeDKey = 1010, //Default NotDKey - AttributeStoredInd = 1011, //Default NotStored - AttributeGroup = 1012, //Default 0 AttributeExtType = 1013, //Default 0 (undefined) AttributeExtPrecision = 1014, //Default 0 AttributeExtScale = 1015, //Default 0 @@ -127,12 +120,7 @@ public: // have a default value. Thus the default values are part of the protocol. // ---------------------------------------------------------------------- - // FragmentKeyType constants - enum FragmentKeyType { - PrimaryKey = 0, - DistributionKey = 1, - DistributionGroup = 2 - }; + // FragmentType constants enum FragmentType { @@ -142,12 +130,6 @@ public: SingleFragment = 3 }; - // TableStorage AND AttributeStorage constants - enum StorageType { - MainMemory = 0, - DiskMemory = 1 - }; - // TableType constants + objects enum TableType { UndefTableType = 0, @@ -219,10 +201,6 @@ public: StorePermanent = 2 }; - // ScanOptimised constants - STATIC_CONST( updateOptimised = 0 ); - STATIC_CONST( scanOptimised = 1 ); - // AttributeType constants STATIC_CONST( SignedType = 0 ); STATIC_CONST( UnSignedType = 1 ); @@ -236,24 +214,11 @@ public: STATIC_CONST( a32Bit = 5 ); STATIC_CONST( a64Bit = 6 ); STATIC_CONST( a128Bit = 7 ); - - // AttributeDGroup constants - STATIC_CONST( NotDGroup = 0 ); - STATIC_CONST( DGroup = 1 ); - - // AttributeDKey constants - STATIC_CONST( NotDKey = 0 ); - STATIC_CONST( DKey = 1 ); - - // AttributeStoredInd constants - STATIC_CONST( NotStored = 0 ); - STATIC_CONST( Stored = 1 ); - + // Table data interpretation struct Table { char TableName[MAX_TAB_NAME_SIZE]; Uint32 TableId; - Uint32 SecondTableId; char PrimaryTable[MAX_TAB_NAME_SIZE]; // Only used when "index" Uint32 PrimaryTableId; Uint32 TableLoggedFlag; @@ -267,8 +232,6 @@ public: Uint32 KeyLength; Uint32 FragmentType; Uint32 TableStorage; - Uint32 ScanOptimised; - Uint32 FragmentKeyType; Uint32 TableType; Uint32 TableVersion; Uint32 IndexState; @@ -323,12 +286,8 @@ public: Uint32 AttributeSize; Uint32 AttributeArraySize; Uint32 AttributeKeyFlag; - Uint32 AttributeStorage; Uint32 AttributeNullableFlag; - Uint32 AttributeDGroup; Uint32 AttributeDKey; - Uint32 AttributeStoredInd; - Uint32 AttributeGroup; Uint32 AttributeExtType; Uint32 AttributeExtPrecision; Uint32 AttributeExtScale; @@ -460,9 +419,7 @@ public: fprintf(out, "AttributeKeyFlag = %d\n", AttributeKeyFlag); fprintf(out, "AttributeStorage = %d\n", AttributeStorage); fprintf(out, "AttributeNullableFlag = %d\n", AttributeNullableFlag); - fprintf(out, "AttributeDGroup = %d\n", AttributeDGroup); fprintf(out, "AttributeDKey = %d\n", AttributeDKey); - fprintf(out, "AttributeStoredInd = %d\n", AttributeStoredInd); fprintf(out, "AttributeGroup = %d\n", AttributeGroup); fprintf(out, "AttributeAutoIncrement = %d\n", AttributeAutoIncrement); fprintf(out, "AttributeExtType = %d\n", AttributeExtType); @@ -496,6 +453,22 @@ private: */ Uint32 tabInfoData[DataLength]; + +public: + enum Depricated + { + AttributeDGroup = 1009, //Default NotDGroup + AttributeStoredInd = 1011, //Default NotStored + SecondTableId = 17, //Mandatory between DICT's otherwise not allowed + FragmentKeyTypeVal = 16 //Default PrimaryKey + }; + + enum Unimplemented + { + TableStorageVal = 14, //Default StorageType::MainMemory + ScanOptimised = 15, //Default updateOptimised + AttributeGroup = 1012 //Default 0 + }; }; #endif diff --git a/ndb/src/common/debugger/signaldata/DictTabInfo.cpp b/ndb/src/common/debugger/signaldata/DictTabInfo.cpp index 7e7bf87e2db..a0a1c4b169d 100644 --- a/ndb/src/common/debugger/signaldata/DictTabInfo.cpp +++ b/ndb/src/common/debugger/signaldata/DictTabInfo.cpp @@ -23,7 +23,6 @@ SimpleProperties::SP2StructMapping DictTabInfo::TableMapping[] = { DTIMAPS(Table, TableName, TableName, 0, MAX_TAB_NAME_SIZE), DTIMAP(Table, TableId, TableId), - DTIMAP(Table, SecondTableId, SecondTableId), DTIMAPS(Table, PrimaryTable, PrimaryTable, 0, MAX_TAB_NAME_SIZE), DTIMAP(Table, PrimaryTableId, PrimaryTableId), DTIMAP2(Table, TableLoggedFlag, TableLoggedFlag, 0, 1), @@ -32,8 +31,6 @@ DictTabInfo::TableMapping[] = { DTIMAP2(Table, MaxLoadFactor, MaxLoadFactor, 25, 110), DTIMAP2(Table, FragmentTypeVal, FragmentType, 0, 3), DTIMAP2(Table, TableStorageVal, TableStorage, 0, 0), - DTIMAP2(Table, ScanOptimised, ScanOptimised, 0, 0), - DTIMAP2(Table, FragmentKeyTypeVal, FragmentKeyType, 0, 2), DTIMAP2(Table, TableTypeVal, TableType, 1, 3), DTIMAP(Table, NoOfKeyAttr, NoOfKeyAttr), DTIMAP2(Table, NoOfAttributes, NoOfAttributes, 1, MAX_ATTRIBUTES_IN_TABLE), @@ -66,12 +63,8 @@ DictTabInfo::AttributeMapping[] = { DTIMAP2(Attribute, AttributeSize, AttributeSize, 3, 7), DTIMAP2(Attribute, AttributeArraySize, AttributeArraySize, 0, 65535), DTIMAP2(Attribute, AttributeKeyFlag, AttributeKeyFlag, 0, 1), - DTIMAP2(Attribute, AttributeStorage, AttributeStorage, 0, 0), DTIMAP2(Attribute, AttributeNullableFlag, AttributeNullableFlag, 0, 1), - DTIMAP2(Attribute, AttributeDGroup, AttributeDGroup, 0, 1), DTIMAP2(Attribute, AttributeDKey, AttributeDKey, 0, 1), - DTIMAP2(Attribute, AttributeStoredInd, AttributeStoredInd, 0, 1), - DTIMAP2(Attribute, AttributeGroup, AttributeGroup, 0, 0), DTIMAP(Attribute, AttributeExtType, AttributeExtType), DTIMAP(Attribute, AttributeExtPrecision, AttributeExtPrecision), DTIMAP(Attribute, AttributeExtScale, AttributeExtScale), @@ -104,7 +97,6 @@ void DictTabInfo::Table::init(){ memset(TableName, 0, sizeof(TableName));//TableName[0] = 0; TableId = ~0; - SecondTableId = ~0; memset(PrimaryTable, 0, sizeof(PrimaryTable));//PrimaryTable[0] = 0; // Only used when "index" PrimaryTableId = RNIL; TableLoggedFlag = 1; @@ -118,8 +110,6 @@ DictTabInfo::Table::init(){ KeyLength = 0; FragmentType = DictTabInfo::AllNodesSmallTable; TableStorage = 0; - ScanOptimised = 0; - FragmentKeyType = DictTabInfo::PrimaryKey; TableType = DictTabInfo::UndefTableType; TableVersion = 0; IndexState = ~0; @@ -140,12 +130,8 @@ DictTabInfo::Attribute::init(){ AttributeSize = DictTabInfo::a32Bit; AttributeArraySize = 1; AttributeKeyFlag = 0; - AttributeStorage = 1; AttributeNullableFlag = 0; - AttributeDGroup = 0; AttributeDKey = 0; - AttributeStoredInd = 1; - AttributeGroup = 0; AttributeExtType = 0, AttributeExtPrecision = 0, AttributeExtScale = 0, diff --git a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp index 17972abc2dd..88920d54351 100644 --- a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp +++ b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp @@ -256,7 +256,6 @@ Dbdict::packTableIntoPagesImpl(SimpleProperties::Writer & w, w.add(DictTabInfo::MaxLoadFactor, tablePtr.p->maxLoadFactor); w.add(DictTabInfo::TableKValue, tablePtr.p->kValue); w.add(DictTabInfo::FragmentTypeVal, tablePtr.p->fragmentType); - w.add(DictTabInfo::FragmentKeyTypeVal, tablePtr.p->fragmentKeyType); w.add(DictTabInfo::TableTypeVal, tablePtr.p->tableType); w.add(DictTabInfo::FragmentCount, tablePtr.p->fragmentCount); @@ -289,17 +288,13 @@ Dbdict::packTableIntoPagesImpl(SimpleProperties::Writer & w, const Uint32 attrSize = AttributeDescriptor::getSize(desc); const Uint32 arraySize = AttributeDescriptor::getArraySize(desc); const Uint32 nullable = AttributeDescriptor::getNullable(desc); - const Uint32 DGroup = AttributeDescriptor::getDGroup(desc); const Uint32 DKey = AttributeDescriptor::getDKey(desc); - const Uint32 attrStoredInd = AttributeDescriptor::getStoredInTup(desc); w.add(DictTabInfo::AttributeType, attrType); w.add(DictTabInfo::AttributeSize, attrSize); w.add(DictTabInfo::AttributeArraySize, arraySize); w.add(DictTabInfo::AttributeNullableFlag, nullable); - w.add(DictTabInfo::AttributeDGroup, DGroup); w.add(DictTabInfo::AttributeDKey, DKey); - w.add(DictTabInfo::AttributeStoredInd, attrStoredInd); w.add(DictTabInfo::AttributeExtType, attrPtr.p->extType); w.add(DictTabInfo::AttributeExtPrecision, attrPtr.p->extPrecision); w.add(DictTabInfo::AttributeExtScale, attrPtr.p->extScale); @@ -1340,10 +1335,8 @@ void Dbdict::initialiseTableRecord(TableRecordPtr tablePtr) tablePtr.p->tableVersion = (Uint32)-1; tablePtr.p->tabState = TableRecord::NOT_DEFINED; tablePtr.p->tabReturnState = TableRecord::TRS_IDLE; - tablePtr.p->storageType = DictTabInfo::MainMemory; tablePtr.p->myConnect = RNIL; tablePtr.p->fragmentType = DictTabInfo::AllNodesSmallTable; - tablePtr.p->fragmentKeyType = DictTabInfo::PrimaryKey; memset(tablePtr.p->tableName, 0, sizeof(tablePtr.p->tableName)); tablePtr.p->gciTableCreated = 0; tablePtr.p->noOfAttributes = ZNIL; @@ -4698,7 +4691,6 @@ void Dbdict::handleTabInfoInit(SimpleProperties::Reader & it, tablePtr.p->minLoadFactor = tableDesc.MinLoadFactor; tablePtr.p->maxLoadFactor = tableDesc.MaxLoadFactor; tablePtr.p->fragmentType = (DictTabInfo::FragmentType)tableDesc.FragmentType; - tablePtr.p->fragmentKeyType = (DictTabInfo::FragmentKeyType)tableDesc.FragmentKeyType; tablePtr.p->tableType = (DictTabInfo::TableType)tableDesc.TableType; tablePtr.p->kValue = tableDesc.TableKValue; tablePtr.p->fragmentCount = tableDesc.FragmentCount; @@ -4854,11 +4846,8 @@ void Dbdict::handleTabInfo(SimpleProperties::Reader & it, AttributeDescriptor::setSize(desc, attrDesc.AttributeSize); AttributeDescriptor::setArray(desc, attrDesc.AttributeArraySize); AttributeDescriptor::setNullable(desc, attrDesc.AttributeNullableFlag); - AttributeDescriptor::setDGroup(desc, attrDesc.AttributeDGroup); AttributeDescriptor::setDKey(desc, attrDesc.AttributeDKey); AttributeDescriptor::setPrimaryKey(desc, attrDesc.AttributeKeyFlag); - - AttributeDescriptor::setStoredInTup(desc, attrDesc.AttributeStoredInd); attrPtr.p->attributeDescriptor = desc; attrPtr.p->autoIncrement = attrDesc.AttributeAutoIncrement; strcpy(attrPtr.p->defaultValue, attrDesc.AttributeDefaultValue); @@ -6416,7 +6405,6 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) w.add(DictTabInfo::AttributeKeyFlag, (Uint32)false); w.add(DictTabInfo::AttributeNullableFlag, (Uint32)isNullable); } - w.add(DictTabInfo::AttributeStoredInd, (Uint32)DictTabInfo::Stored); // ext type overrides w.add(DictTabInfo::AttributeExtType, aRec->extType); w.add(DictTabInfo::AttributeExtPrecision, aRec->extPrecision); @@ -6431,7 +6419,6 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) w.add(DictTabInfo::AttributeName, "NDB$PK"); w.add(DictTabInfo::AttributeId, opPtr.p->m_attrList.sz); w.add(DictTabInfo::AttributeKeyFlag, (Uint32)false); - w.add(DictTabInfo::AttributeStoredInd, (Uint32)DictTabInfo::Stored); w.add(DictTabInfo::AttributeNullableFlag, (Uint32)false); // ext type overrides w.add(DictTabInfo::AttributeExtType, (Uint32)DictTabInfo::ExtUnsigned); @@ -6444,7 +6431,6 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) w.add(DictTabInfo::AttributeName, "NDB$TNODE"); w.add(DictTabInfo::AttributeId, opPtr.p->m_attrList.sz); w.add(DictTabInfo::AttributeKeyFlag, (Uint32)true); - w.add(DictTabInfo::AttributeStoredInd, (Uint32)DictTabInfo::Stored); w.add(DictTabInfo::AttributeNullableFlag, (Uint32)false); // ext type overrides w.add(DictTabInfo::AttributeExtType, (Uint32)DictTabInfo::ExtUnsigned); diff --git a/ndb/src/kernel/vm/MetaData.hpp b/ndb/src/kernel/vm/MetaData.hpp index 11e262664c1..aded6c96573 100644 --- a/ndb/src/kernel/vm/MetaData.hpp +++ b/ndb/src/kernel/vm/MetaData.hpp @@ -86,15 +86,9 @@ public: /* Primary table of index otherwise RNIL */ Uint32 primaryTableId; - /* Type of storage (memory/disk, not used) */ - DictTabInfo::StorageType storageType; - /* Type of fragmentation (small/medium/large) */ DictTabInfo::FragmentType fragmentType; - /* Key type of fragmentation (pk/dist key/dist group) */ - DictTabInfo::FragmentKeyType fragmentKeyType; - /* Global checkpoint identity when table created */ Uint32 gciTableCreated; diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/ndb/src/ndbapi/NdbDictionaryImpl.cpp index 3f6ef69ca96..6c052dac9f0 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -1525,9 +1525,7 @@ NdbDictInterface::createOrAlterTable(Ndb & ndb, tmpAttr.AttributeId = i; tmpAttr.AttributeKeyFlag = col->m_pk; tmpAttr.AttributeNullableFlag = col->m_nullable; - tmpAttr.AttributeStoredInd = 1; tmpAttr.AttributeDKey = col->m_distributionKey; - tmpAttr.AttributeDGroup = 0; tmpAttr.AttributeExtType = getKernelConstant(col->m_type, diff --git a/ndb/src/ndbapi/NdbScanOperation.cpp b/ndb/src/ndbapi/NdbScanOperation.cpp index 671996bc534..7766068fc3e 100644 --- a/ndb/src/ndbapi/NdbScanOperation.cpp +++ b/ndb/src/ndbapi/NdbScanOperation.cpp @@ -919,6 +919,8 @@ NdbScanOperation::takeOverScanOp(OperationType opType, NdbConnection* pTrans){ TcKeyReq::setTakeOverScanFragment(scanInfo, tTakeOverFragment); TcKeyReq::setTakeOverScanInfo(scanInfo, tScanInfo); newOp->theScanInfo = scanInfo; + newOp->theDistrKeyIndicator_ = 1; + newOp->theDistributionKey = tTakeOverFragment; } // Copy the first 8 words of key info from KEYINF20 into TCKEYREQ From 3f89a4f362b2592b496b38fab4607d5f3dc0baa7 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Dec 2004 22:30:33 +0100 Subject: [PATCH 49/52] ndb - major update HugoOperations/Transactions +more code reuse +easier testing of indexes ndb/include/ndbapi/NdbDictionary.hpp: Remove unimplemented enums ndb/test/include/HugoOperations.hpp: Pass index on constructor ndb/test/include/HugoTransactions.hpp: Pass index on constructor ndb/test/include/UtilTransactions.hpp: Pass index on constructor ndb/test/src/HugoAsynchTransactions.cpp: Pass index on constructor ndb/test/src/HugoOperations.cpp: Use getOperation to select operation type ndb/test/src/HugoTransactions.cpp: Use HugoOpertaions to run transactions ndb/test/src/UtilTransactions.cpp: Use HugoOpertaions to run transactions --- ndb/include/ndbapi/NdbDictionary.hpp | 2 - ndb/test/include/HugoOperations.hpp | 6 +- ndb/test/include/HugoTransactions.hpp | 3 +- ndb/test/include/UtilTransactions.hpp | 20 +- ndb/test/src/HugoAsynchTransactions.cpp | 11 +- ndb/test/src/HugoOperations.cpp | 83 +-- ndb/test/src/HugoTransactions.cpp | 655 +++--------------------- ndb/test/src/UtilTransactions.cpp | 383 +++----------- 8 files changed, 197 insertions(+), 966 deletions(-) diff --git a/ndb/include/ndbapi/NdbDictionary.hpp b/ndb/include/ndbapi/NdbDictionary.hpp index 5f855ac06de..3df322d5a96 100644 --- a/ndb/include/ndbapi/NdbDictionary.hpp +++ b/ndb/include/ndbapi/NdbDictionary.hpp @@ -754,8 +754,6 @@ public: Undefined = 0, ///< Undefined object type (initial value) UniqueHashIndex = 3, ///< Unique un-ordered hash index ///< (only one currently supported) - HashIndex = 4, ///< Non-unique un-ordered hash index - UniqueOrderedIndex = 5, ///< Unique ordered index OrderedIndex = 6 ///< Non-unique ordered index }; diff --git a/ndb/test/include/HugoOperations.hpp b/ndb/test/include/HugoOperations.hpp index fe22e4b5649..1cb97237602 100644 --- a/ndb/test/include/HugoOperations.hpp +++ b/ndb/test/include/HugoOperations.hpp @@ -24,7 +24,9 @@ class HugoOperations : public UtilTransactions { public: - HugoOperations(const NdbDictionary::Table&); + HugoOperations(const NdbDictionary::Table&, + const NdbDictionary::Index* idx = 0); + ~HugoOperations(); int startTransaction(Ndb*); int closeTransaction(Ndb*); @@ -98,8 +100,6 @@ protected: struct RsPair { NdbResultSet* m_result_set; int records; }; Vector m_result_sets; Vector m_executed_result_sets; -private: - NdbConnection* pTrans; }; #endif diff --git a/ndb/test/include/HugoTransactions.hpp b/ndb/test/include/HugoTransactions.hpp index b833f2ac629..83493bcc2a4 100644 --- a/ndb/test/include/HugoTransactions.hpp +++ b/ndb/test/include/HugoTransactions.hpp @@ -25,7 +25,8 @@ class HugoTransactions : public HugoOperations { public: - HugoTransactions(const NdbDictionary::Table&); + HugoTransactions(const NdbDictionary::Table&, + const NdbDictionary::Index* idx = 0); ~HugoTransactions(); int createEvent(Ndb*); int eventOperation(Ndb*, void* stats, diff --git a/ndb/test/include/UtilTransactions.hpp b/ndb/test/include/UtilTransactions.hpp index 23902f3b317..16732008ad1 100644 --- a/ndb/test/include/UtilTransactions.hpp +++ b/ndb/test/include/UtilTransactions.hpp @@ -23,15 +23,11 @@ typedef int (ReadCallBackFn)(NDBT_ResultRow*); class UtilTransactions { public: - enum ScanLock { - SL_Read = 0, - SL_ReadHold = 1, - SL_Exclusive = 2 - }; - - UtilTransactions(const NdbDictionary::Table&); - UtilTransactions(Ndb* ndb, const char * tableName); - + UtilTransactions(const NdbDictionary::Table&, + const NdbDictionary::Index* idx = 0); + UtilTransactions(Ndb* ndb, + const char * tableName, const char * indexName = 0); + int clearTable(Ndb*, int records = 0, int parallelism = 0); @@ -114,6 +110,12 @@ private: protected: int m_defaultClearMethod; const NdbDictionary::Table& tab; + const NdbDictionary::Index* idx; + NdbConnection* pTrans; + + NdbOperation* getOperation(NdbConnection*, + NdbOperation::OperationType); + NdbScanOperation* getScanOperation(NdbConnection*); }; #endif diff --git a/ndb/test/src/HugoAsynchTransactions.cpp b/ndb/test/src/HugoAsynchTransactions.cpp index f75293f5a14..5d2eb451c0b 100644 --- a/ndb/test/src/HugoAsynchTransactions.cpp +++ b/ndb/test/src/HugoAsynchTransactions.cpp @@ -17,11 +17,12 @@ #include #include -HugoAsynchTransactions::HugoAsynchTransactions(const NdbDictionary::Table& _tab): - HugoTransactions(_tab), - transactionsCompleted(0), - numTransactions(0), - transactions(NULL){ +HugoAsynchTransactions::HugoAsynchTransactions(const NdbDictionary::Table& _t) + : HugoTransactions(_t), + transactionsCompleted(0), + numTransactions(0), + transactions(NULL) +{ } HugoAsynchTransactions::~HugoAsynchTransactions(){ diff --git a/ndb/test/src/HugoOperations.cpp b/ndb/test/src/HugoOperations.cpp index 3cf4d618833..0a14e67259c 100644 --- a/ndb/test/src/HugoOperations.cpp +++ b/ndb/test/src/HugoOperations.cpp @@ -57,8 +57,10 @@ int HugoOperations::pkReadRecord(Ndb* pNdb, int a; allocRows(numRecords); int check; + for(int r=0; r < numRecords; r++){ - NdbOperation* pOp = pTrans->getNdbOperation(tab.getName()); + + NdbOperation* pOp = getOperation(pTrans, NdbOperation::ReadRequest); if (pOp == NULL) { ERR(pTrans->getNdbError()); return NDBT_FAILED; @@ -115,7 +117,7 @@ int HugoOperations::pkUpdateRecord(Ndb* pNdb, allocRows(numRecords); int check; for(int r=0; r < numRecords; r++){ - NdbOperation* pOp = pTrans->getNdbOperation(tab.getName()); + NdbOperation* pOp = getOperation(pTrans, NdbOperation::UpdateRequest); if (pOp == NULL) { ERR(pTrans->getNdbError()); return NDBT_FAILED; @@ -157,7 +159,7 @@ int HugoOperations::pkInsertRecord(Ndb* pNdb, int a, check; for(int r=0; r < numRecords; r++){ - NdbOperation* pOp = pTrans->getNdbOperation(tab.getName()); + NdbOperation* pOp = getOperation(pTrans, NdbOperation::InsertRequest); if (pOp == NULL) { ERR(pTrans->getNdbError()); return NDBT_FAILED; @@ -198,7 +200,7 @@ int HugoOperations::pkDeleteRecord(Ndb* pNdb, int a, check; for(int r=0; r < numRecords; r++){ - NdbOperation* pOp = pTrans->getNdbOperation(tab.getName()); + NdbOperation* pOp = getOperation(pTrans, NdbOperation::DeleteRequest); if (pOp == NULL) { ERR(pTrans->getNdbError()); return NDBT_FAILED; @@ -222,65 +224,6 @@ int HugoOperations::pkDeleteRecord(Ndb* pNdb, } return NDBT_OK; } -#if 0 -NdbResultSet* -HugoOperations::scanReadRecords(Ndb* pNdb, ScanLock lock){ - - NDBT_ResultRow * m_tmpRow = new NDBT_ResultRow(tab); - - NdbScanOperation* pOp = pTrans->getNdbScanOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - return 0; - } - - - int check = 0; - NdbResultSet * rs = 0; - switch(lock){ - case SL_ReadHold: - rs = pOp->readTuples(NdbScanOperation::LM_Read, 1, 1); - break; - case SL_Exclusive: - rs = pOp->readTuples(NdbScanOperation::LM_Exclusive, 1, 1); - break; - case SL_Read: - default: - rs = pOp->readTuples(NdbScanOperation::LM_Dirty, 1, 1); - } - - if( rs == 0) { - ERR(pTrans->getNdbError()); - return 0; - } - - check = pOp->interpret_exit_ok(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - return 0; - } - - // Define attributes to read - for(int a = 0; aattributeStore(a) = - pOp->getValue(tab.getColumn(a)->getName())) == 0) { - ERR(pTrans->getNdbError()); - return 0; - } - } - return rs; -} - -int -HugoOperations::readTuples(NdbResultSet* rs){ - int res = 0; - while((res = rs->nextResult()) == 0){ - } - if(res != 1) - return NDBT_FAILED; - return NDBT_OK; -} -#endif int HugoOperations::execute_Commit(Ndb* pNdb, AbortOption eao){ @@ -392,11 +335,11 @@ int HugoOperations::execute_Rollback(Ndb* pNdb){ return NDBT_OK; } -HugoOperations::HugoOperations(const NdbDictionary::Table& _tab): - UtilTransactions(_tab), - calc(_tab), - pTrans(NULL){ - +HugoOperations::HugoOperations(const NdbDictionary::Table& _tab, + const NdbDictionary::Index* idx): + UtilTransactions(_tab, idx), + calc(_tab) +{ } HugoOperations::~HugoOperations(){ @@ -531,14 +474,12 @@ HugoOperations::verifyUpdatesValue(int updatesValue, int _numRows){ } void HugoOperations::allocRows(int _numRows){ - deallocRows(); - if(_numRows <= 0){ g_info << "Illegal value for num rows : " << _numRows << endl; abort(); } - for(int b=0; b<_numRows; b++){ + for(int b=rows.size(); b<_numRows; b++){ rows.push_back(new NDBT_ResultRow(tab)); } } diff --git a/ndb/test/src/HugoTransactions.cpp b/ndb/test/src/HugoTransactions.cpp index ef5db6d9065..307de34ac04 100644 --- a/ndb/test/src/HugoTransactions.cpp +++ b/ndb/test/src/HugoTransactions.cpp @@ -18,8 +18,9 @@ #include -HugoTransactions::HugoTransactions(const NdbDictionary::Table& _tab): - HugoOperations(_tab), +HugoTransactions::HugoTransactions(const NdbDictionary::Table& _tab, + const NdbDictionary::Index* idx): + HugoOperations(_tab, idx), row(_tab){ m_defaultScanUpdateMethod = 3; @@ -40,7 +41,6 @@ HugoTransactions::scanReadRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a; - NdbConnection *pTrans; NdbScanOperation *pOp; while (true){ @@ -65,7 +65,7 @@ HugoTransactions::scanReadRecords(Ndb* pNdb, return NDBT_FAILED; } - pOp = pTrans->getNdbScanOperation(tab.getName()); + pOp = getScanOperation(pTrans); if (pOp == NULL) { ERR(pTrans->getNdbError()); pNdb->closeTransaction(pTrans); @@ -196,7 +196,6 @@ HugoTransactions::scanReadRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a; - NdbConnection *pTrans; NdbIndexScanOperation *pOp; while (true){ @@ -363,162 +362,7 @@ HugoTransactions::scanUpdateRecords1(Ndb* pNdb, int records, int abortPercent, int parallelism){ -#if 1 return scanUpdateRecords3(pNdb, records, abortPercent, 1); -#else - int retryAttempt = 0; - const int retryMax = 100; - int check, a; - NdbConnection *pTrans; - NdbOperation *pOp; - - - while (true){ - - if (retryAttempt >= retryMax){ - g_info << "ERROR: has retried this operation " << retryAttempt - << " times, failing!" << endl; - return NDBT_FAILED; - } - - pTrans = pNdb->startTransaction(); - if (pTrans == NULL) { - const NdbError err = pNdb->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - return NDBT_FAILED; - } - - pOp = pTrans->getNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->openScanExclusive(parallelism); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->interpret_exit_ok(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Read all attributes from this table - for(a=0; agetValue(tab.getColumn(a)->getName())) == NULL){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - - check = pTrans->executeScan(); - if( check == -1 ) { - const NdbError err = pTrans->getNdbError(); - if (err.status == NdbError::TemporaryError){ - ERR(err); - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Abort after 1-100 or 1-records rows - int ranVal = rand(); - int abortCount = ranVal % (records == 0 ? 100 : records); - bool abortTrans = false; - if (abort > 0){ - // Abort if abortCount is less then abortPercent - if (abortCount < abortPercent) - abortTrans = true; - } - - - int eof; - int rows = 0; - - eof = pTrans->nextScanResult(); - while(eof == 0){ - rows++; - - if (abortCount == rows && abortTrans == true){ - g_info << "Scan is aborted" << endl; - // This scan should be aborted - check = pTrans->stopScan(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - pNdb->closeTransaction(pTrans); - return NDBT_OK; - } - int res = takeOverAndUpdateRecord(pNdb, pOp); - if(res == RESTART_SCAN){ - eof = -2; - continue; - } - if (res != 0){ - pNdb->closeTransaction(pTrans); - return res; - } - - eof = pTrans->nextScanResult(); - } - if (eof == -1) { - const NdbError err = pTrans->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - NdbSleep_MilliSleep(50); - switch (err.code){ - case 488: - case 245: - case 490: - // Too many active scans, no limit on number of retry attempts - break; - default: - retryAttempt++; - } - continue; - } - ERR(err); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - if(eof == -2){ - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - - pNdb->closeTransaction(pTrans); - - g_info << rows << " rows have been updated" << endl; - return NDBT_OK; - } - return NDBT_FAILED; -#endif } // Scan all records exclusive and update @@ -530,168 +374,7 @@ HugoTransactions::scanUpdateRecords2(Ndb* pNdb, int records, int abortPercent, int parallelism){ -#if 1 return scanUpdateRecords3(pNdb, records, abortPercent, parallelism); -#else - int retryAttempt = 0; - const int retryMax = 100; - int check, a; - NdbConnection *pTrans; - NdbOperation *pOp; - - - while (true){ - - if (retryAttempt >= retryMax){ - g_info << "ERROR: has retried this operation " << retryAttempt - << " times, failing!" << endl; - return NDBT_FAILED; - } - - pTrans = pNdb->startTransaction(); - if (pTrans == NULL) { - const NdbError err = pNdb->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - return NDBT_FAILED; - } - - pOp = pTrans->getNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->openScanExclusive(parallelism); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->interpret_exit_ok(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Read all attributes from this table - for(a=0; agetValue(tab.getColumn(a)->getName())) == NULL){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - - check = pTrans->executeScan(); - if( check == -1 ) { - const NdbError err = pTrans->getNdbError(); - if (err.status == NdbError::TemporaryError){ - ERR(err); - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Abort after 1-100 or 1-records rows - int ranVal = rand(); - int abortCount = ranVal % (records == 0 ? 100 : records); - bool abortTrans = false; - if (abort > 0){ - // Abort if abortCount is less then abortPercent - if (abortCount < abortPercent) - abortTrans = true; - } - - int eof; - int rows = 0; - NdbConnection* pUpTrans; - - while((eof = pTrans->nextScanResult(true)) == 0){ - pUpTrans = pNdb->startTransaction(); - if (pUpTrans == NULL) { - const NdbError err = pNdb->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - return NDBT_FAILED; - } - do { - rows++; - if (addRowToUpdate(pNdb, pUpTrans, pOp) != 0){ - pNdb->closeTransaction(pUpTrans); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } while((eof = pTrans->nextScanResult(false)) == 0); - - if (abortCount == rows && abortTrans == true){ - g_info << "Scan is aborted" << endl; - // This scan should be aborted - check = pTrans->stopScan(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - pNdb->closeTransaction(pUpTrans); - return NDBT_FAILED; - } - - pNdb->closeTransaction(pTrans); - pNdb->closeTransaction(pUpTrans); - return NDBT_OK; - } - - check = pUpTrans->execute(Commit); - if( check == -1 ) { - const NdbError err = pUpTrans->getNdbError(); - ERR(err); - pNdb->closeTransaction(pUpTrans); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - pNdb->closeTransaction(pUpTrans); - } - if (eof == -1) { - const NdbError err = pTrans->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - pNdb->closeTransaction(pTrans); - - g_info << rows << " rows have been updated" << endl; - return NDBT_OK; - } - return NDBT_FAILED; -#endif } int @@ -702,7 +385,6 @@ HugoTransactions::scanUpdateRecords3(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a; - NdbConnection *pTrans; NdbScanOperation *pOp; @@ -725,7 +407,7 @@ HugoTransactions::scanUpdateRecords3(Ndb* pNdb, return NDBT_FAILED; } - pOp = pTrans->getNdbScanOperation(tab.getName()); + pOp = getScanOperation(pTrans); if (pOp == NULL) { ERR(pTrans->getNdbError()); pNdb->closeTransaction(pTrans); @@ -846,7 +528,6 @@ HugoTransactions::loadTable(Ndb* pNdb, int check, a; int retryAttempt = 0; int retryMax = 5; - NdbConnection *pTrans; NdbOperation *pOp; bool first_batch = true; @@ -866,6 +547,10 @@ HugoTransactions::loadTable(Ndb* pNdb, g_info << "|- Inserting records..." << endl; for (int c=0 ; c records) + batch = records - c; + if (retryAttempt >= retryMax){ g_info << "Record " << c << " could not be inserted, has retried " << retryAttempt << " times " << endl; @@ -894,30 +579,11 @@ HugoTransactions::loadTable(Ndb* pNdb, } } - for(int b = 0; b < batch && c+bgetNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->insertTuple(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Set a calculated value for each attribute in this table - for (a = 0; agetNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } + if(pkInsertRecord(pNdb, c, batch) != NDBT_OK) + { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; } // Execute the transaction and insert the record @@ -995,10 +661,21 @@ HugoTransactions::fillTable(Ndb* pNdb, int check, a, b; int retryAttempt = 0; int retryMax = 5; - NdbConnection *pTrans; NdbOperation *pOp; + + const int org = batch; + const int cols = tab.getNoOfColumns(); + const int brow = tab.getRowSizeInBytes(); + const int bytes = 12 + brow + 4 * cols; + batch = (batch * 256); // -> 512 -> 65536k per commit + batch = batch/bytes; // + batch = batch == 0 ? 1 : batch; + + if(batch != org){ + g_info << "batch = " << org << " rowsize = " << bytes + << " -> rows/commit = " << batch << endl; + } - g_info << "|- Inserting records..." << endl; for (int c=0 ; ; ){ if (retryAttempt >= retryMax){ @@ -1023,30 +700,11 @@ HugoTransactions::fillTable(Ndb* pNdb, return NDBT_FAILED; } - for(b = 0; b < batch; b++){ - - pOp = pTrans->getNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->insertTuple(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Set a calculated value for each attribute in this table - for (a = 0; agetNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } + if(pkInsertRecord(pNdb, c, batch) != NDBT_OK) + { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; } // Execute the transaction and insert the record @@ -1373,7 +1031,7 @@ HugoTransactions::eventOperation(Ndb* pNdb, void* pstats, stats.n_duplicates += recDeleteEvent[i].count-1; } } - + return NDBT_OK; } @@ -1387,7 +1045,6 @@ HugoTransactions::pkReadRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a; - NdbConnection *pTrans; NdbOperation *pOp; if (batchsize == 0) { @@ -1395,15 +1052,16 @@ HugoTransactions::pkReadRecords(Ndb* pNdb, return NDBT_FAILED; } - allocRows(batchsize); - while (r < records){ + if(r + batchsize > records) + batchsize = records - r; + if (retryAttempt >= retryMax){ g_info << "ERROR: has retried this operation " << retryAttempt << " times, failing!" << endl; return NDBT_FAILED; } - + pTrans = pNdb->startTransaction(); if (pTrans == NULL) { const NdbError err = pNdb->getNdbError(); @@ -1418,63 +1076,17 @@ HugoTransactions::pkReadRecords(Ndb* pNdb, return NDBT_FAILED; } - for(int b=0; (bgetNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - rand_lock_mode: - switch(lm){ - case NdbOperation::LM_Read: - check = pOp->readTuple(); - break; - case NdbOperation::LM_Exclusive: - check = pOp->readTupleExclusive(); - break; - case NdbOperation::LM_CommittedRead: - check = pOp->dirtyRead(); - break; - default: - lm = (NdbOperation::LockMode)((rand() >> 16) & 3); - goto rand_lock_mode; - } - - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Define primary keys - for(a = 0; agetPrimaryKey() == true){ - if(equalForAttr(pOp, a, r+b) != 0){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - } - - // Define attributes to read - for(a = 0; aattributeStore(a) = - pOp->getValue(tab.getColumn(a)->getName())) == 0) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - + if(pkReadRecord(pNdb, r, batchsize, lm) != NDBT_OK) + { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; } - + check = pTrans->execute(Commit); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); - + if (err.status == NdbError::TemporaryError){ ERR(err); pNdb->closeTransaction(pTrans); @@ -1503,9 +1115,9 @@ HugoTransactions::pkReadRecords(Ndb* pNdb, r++; } } - + pNdb->closeTransaction(pTrans); - + } deallocRows(); g_info << reads << " records read" << endl; @@ -1524,13 +1136,14 @@ HugoTransactions::pkUpdateRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a, b; - NdbConnection *pTrans; NdbOperation *pOp; allocRows(batch); g_info << "|- Updating records (batch=" << batch << ")..." << endl; while (r < records){ + if(r + batch > records) + batch = records - r; if (retryAttempt >= retryMax){ g_info << "ERROR: has retried this operation " << retryAttempt @@ -1555,43 +1168,13 @@ HugoTransactions::pkUpdateRecords(Ndb* pNdb, return NDBT_FAILED; } - for(b = 0; bgetNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->readTupleExclusive(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Define primary keys - for(a = 0; agetPrimaryKey() == true){ - if(equalForAttr(pOp, a, r+b) != 0){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - } - - // Define attributes to read - for(a = 0; aattributeStore(a) = - pOp->getValue(tab.getColumn(a)->getName())) == 0) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } + if(pkReadRecord(pNdb, r, batch, NdbOperation::LM_Exclusive) != NDBT_OK) + { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; } - + check = pTrans->execute(NoCommit); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); @@ -1608,50 +1191,24 @@ HugoTransactions::pkUpdateRecords(Ndb* pNdb, return NDBT_FAILED; } - for(b = 0; bcloseTransaction(pTrans); return NDBT_FAILED; } int updates = calc.getUpdatesValue(rows[b]) + 1; - NdbOperation* pUpdOp; - pUpdOp = pTrans->getNdbOperation(tab.getName()); - if (pUpdOp == NULL) { + if(pkUpdateRecord(pNdb, r+b, 1, updates) != NDBT_OK) + { ERR(pTrans->getNdbError()); pNdb->closeTransaction(pTrans); return NDBT_FAILED; } - - check = pUpdOp->updateTuple(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - for(a = 0; agetPrimaryKey() == true){ - if(equalForAttr(pUpdOp, a, r+b) != 0){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - } - - for(a = 0; agetPrimaryKey() == false){ - if(setValueForAttr(pUpdOp, a, r+b, updates ) != 0){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - } } - + check = pTrans->execute(Commit); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); @@ -1671,13 +1228,13 @@ HugoTransactions::pkUpdateRecords(Ndb* pNdb, else{ updated += batch; } - - + + pNdb->closeTransaction(pTrans); - + r += batch; // Read next record } - + deallocRows(); g_info << "|- " << updated << " records updated" << endl; return NDBT_OK; @@ -1692,7 +1249,6 @@ HugoTransactions::pkInterpretedUpdateRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a; - NdbConnection *pTrans; while (r < records){ @@ -1871,11 +1427,12 @@ HugoTransactions::pkDelRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a; - NdbConnection *pTrans; NdbOperation *pOp; g_info << "|- Deleting records..." << endl; while (r < records){ + if(r + batch > records) + batch = records - r; if (retryAttempt >= retryMax){ g_info << "ERROR: has retried this operation " << retryAttempt @@ -1900,30 +1457,13 @@ HugoTransactions::pkDelRecords(Ndb* pNdb, return NDBT_FAILED; } - pOp = pTrans->getNdbOperation(tab.getName()); - if (pOp == NULL) { + if(pkDeleteRecord(pNdb, r, batch) != NDBT_OK) + { ERR(pTrans->getNdbError()); pNdb->closeTransaction(pTrans); return NDBT_FAILED; } - check = pOp->deleteTuple(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Define primary keys - for(a = 0; agetPrimaryKey() == true){ - if(equalForAttr(pOp, a, r) != 0){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - } check = pTrans->execute(Commit); if( check == -1) { const NdbError err = pTrans->getNdbError(); @@ -1961,11 +1501,11 @@ HugoTransactions::pkDelRecords(Ndb* pNdb, } } else { - deleted++; + deleted += batch; } pNdb->closeTransaction(pTrans); - - r++; // Read next record + + r += batch; // Read next record } @@ -1986,8 +1526,8 @@ HugoTransactions::lockRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a, b; - NdbConnection *pTrans; NdbOperation *pOp; + NdbOperation::LockMode lm = NdbOperation::LM_Exclusive; // Calculate how many records to lock in each batch if (percentToLock <= 0) @@ -2000,6 +1540,9 @@ HugoTransactions::lockRecords(Ndb* pNdb, allocRows(lockBatch); while (r < records){ + if(r + lockBatch > records) + lockBatch = records - r; + g_info << "|- Locking " << lockBatch << " records..." << endl; if (retryAttempt >= retryMax){ @@ -2022,42 +1565,13 @@ HugoTransactions::lockRecords(Ndb* pNdb, return NDBT_FAILED; } - for(b = 0; (bgetNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->readTupleExclusive(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - // Define primary keys - for(a = 0; agetPrimaryKey() == true){ - if(equalForAttr(pOp, a, r+b) != 0){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } - } - - // Define attributes to read - for(a = 0; aattributeStore(a) = - pOp->getValue(tab.getColumn(a)->getName())) == 0) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } + if(pkReadRecord(pNdb, r, lockBatch, lm) != NDBT_OK) + { + ERR(pTrans->getNdbError()); + pNdb->closeTransaction(pTrans); + return NDBT_FAILED; } + // NoCommit lockTime times with 100 millis interval int sleepInterval = 50; int lockCount = lockTime / sleepInterval; @@ -2115,8 +1629,7 @@ HugoTransactions::lockRecords(Ndb* pNdb, } pNdb->closeTransaction(pTrans); - - + } deallocRows(); g_info << "|- Record locking completed" << endl; @@ -2133,7 +1646,6 @@ HugoTransactions::indexReadRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a; - NdbConnection *pTrans; NdbOperation *pOp; NdbIndexScanOperation *sOp; NdbResultSet * rs; @@ -2284,7 +1796,6 @@ HugoTransactions::indexUpdateRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check, a, b; - NdbConnection *pTrans; NdbOperation *pOp; NdbScanOperation * sOp; NdbResultSet * rs; diff --git a/ndb/test/src/UtilTransactions.cpp b/ndb/test/src/UtilTransactions.cpp index 869f7fc76cb..f93c56f8fc0 100644 --- a/ndb/test/src/UtilTransactions.cpp +++ b/ndb/test/src/UtilTransactions.cpp @@ -20,13 +20,20 @@ #define VERBOSE 0 -UtilTransactions::UtilTransactions(const NdbDictionary::Table& _tab): - tab(_tab){ +UtilTransactions::UtilTransactions(const NdbDictionary::Table& _tab, + const NdbDictionary::Index* _idx): + tab(_tab), idx(_idx), pTrans(0) +{ m_defaultClearMethod = 3; } -UtilTransactions::UtilTransactions(Ndb* ndb, const char * name) : - tab(* ndb->getDictionary()->getTable(name)){ +UtilTransactions::UtilTransactions(Ndb* ndb, + const char * name, + const char * index) : + tab(* ndb->getDictionary()->getTable(name)), + idx(index ? ndb->getDictionary()->getIndex(index, name) : 0), + pTrans(0) +{ m_defaultClearMethod = 3; } @@ -51,313 +58,29 @@ UtilTransactions::clearTable(Ndb* pNdb, int UtilTransactions::clearTable1(Ndb* pNdb, int records, - int parallelism){ -#if 1 + int parallelism) +{ return clearTable3(pNdb, records, 1); -#else - // Scan all records exclusive and delete - // them one by one - int retryAttempt = 0; - const int retryMax = 100; - int check; - NdbConnection *pTrans; - NdbOperation *pOp; - - while (true){ - - if (retryAttempt >= retryMax){ - g_info << "ERROR: Has retried this operation " << retryAttempt - << " times, failing!" << endl; - return NDBT_FAILED; - } - - - pTrans = pNdb->startTransaction(); - if (pTrans == NULL) { - NdbError err = pNdb->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - RETURN_FAIL(err); - } - - pOp = pTrans->getNdbOperation(tab.getName()); - if (pOp == NULL) { - NdbError err = pNdb->getNdbError(); - ERR(err); - pNdb->closeTransaction(pTrans); - RETURN_FAIL(err); - } - - check = pOp->openScanExclusive(parallelism); - if( check == -1 ) { - NdbError err = pNdb->getNdbError(); - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - RETURN_FAIL(err); - } - - check = pOp->interpret_exit_ok(); - if( check == -1 ) { - NdbError err = pNdb->getNdbError(); - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - RETURN_FAIL(err); - } -#if 0 - // It's not necessary to read and PK's - // Information about the PK's are sent in - // KEYINFO20 signals anyway and used by takeOverScan - - // Read the primary keys from this table - for(int a=0; agetPrimaryKey()){ - if(pOp->getValue(tab.getColumn(a)->getName()) == NULL){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - RETURN_FAIL(err); - } - } - } -#endif - - check = pTrans->executeScan(); - if( check == -1 ) { - NdbError err = pTrans->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - pNdb->closeTransaction(pTrans); - RETURN_FAIL(err); - } - - int eof; - int rows = 0; - - eof = pTrans->nextScanResult(); - while(eof == 0){ - rows++; - - int res = takeOverAndDeleteRecord(pNdb, pOp); - if(res == RESTART_SCAN){ - eof = -2; - continue; - } - - if (res != 0){ - NdbError err = pNdb->getNdbError(res); - pNdb->closeTransaction(pTrans); - RETURN_FAIL(err); - } - - eof = pTrans->nextScanResult(); - } - - if (eof == -1) { - const NdbError err = pTrans->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - // If error = 488 there should be no limit on number of retry attempts - if (err.code != 488) - retryAttempt++; - continue; - } - ERR(err); - pNdb->closeTransaction(pTrans); - RETURN_FAIL(err); - } - - if(eof == -2){ - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - - pNdb->closeTransaction(pTrans); - - g_info << rows << " deleted" << endl; - - return NDBT_OK; - } - return NDBT_FAILED; -#endif } int UtilTransactions::clearTable2(Ndb* pNdb, - int records, - int parallelism){ -#if 1 + int records, + int parallelism) +{ return clearTable3(pNdb, records, parallelism); -#else - // Scan all records exclusive and delete - // them one by one - int retryAttempt = 0; - const int retryMax = 10; - int deletedRows = 0; - int check; - NdbConnection *pTrans; - NdbOperation *pOp; - - while (true){ - - if (retryAttempt >= retryMax){ - g_info << "ERROR: has retried this operation " << retryAttempt - << " times, failing!" << endl; - return NDBT_FAILED; - } - - - pTrans = pNdb->startTransaction(); - if (pTrans == NULL) { - const NdbError err = pNdb->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } - ERR(err); - return NDBT_FAILED; - } - - pOp = pTrans->getNdbOperation(tab.getName()); - if (pOp == NULL) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->openScanExclusive(parallelism); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - check = pOp->interpret_exit_ok(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } -#if 0 - // It's not necessary to read any PK's - // Information about the PK's are sent in - // KEYINFO20 signals anyway and used by takeOverScan - - // Read the primary keys from this table - for(int a=0; agetPrimaryKey()){ - if(pOp->getValue(tab.getColumn(a)->getName()) == NULL){ - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return -1; - } - } - } -#endif - - check = pTrans->executeScan(); - if( check == -1 ) { - ERR(pTrans->getNdbError()); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - int eof; - NdbConnection* pDelTrans; - - while((eof = pTrans->nextScanResult(true)) == 0){ - pDelTrans = pNdb->startTransaction(); - if (pDelTrans == NULL) { - const NdbError err = pNdb->getNdbError(); -#if 0 - if (err.status == NdbError::TemporaryError){ - ERR(err); - NdbSleep_MilliSleep(50); - retryAttempt++; - continue; - } -#endif - ERR(err); - pNdb->closeTransaction(pDelTrans); - return NDBT_FAILED; - } - do { - deletedRows++; - if (addRowToDelete(pNdb, pDelTrans, pOp) != 0){ - pNdb->closeTransaction(pDelTrans); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - } while((eof = pTrans->nextScanResult(false)) == 0); - - check = pDelTrans->execute(Commit); - if( check == -1 ) { - const NdbError err = pDelTrans->getNdbError(); - ERR(err); - pNdb->closeTransaction(pDelTrans); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - pNdb->closeTransaction(pDelTrans); - - } - if (eof == -1) { - const NdbError err = pTrans->getNdbError(); - - if (err.status == NdbError::TemporaryError){ - ERR(err); - pNdb->closeTransaction(pTrans); - NdbSleep_MilliSleep(50); - // If error = 488 there should be no limit on number of retry attempts - if (err.code != 488) - retryAttempt++; - continue; - } - ERR(err); - pNdb->closeTransaction(pTrans); - return NDBT_FAILED; - } - - pNdb->closeTransaction(pTrans); - - g_info << deletedRows << " rows deleted" << endl; - - return NDBT_OK; - } - return NDBT_FAILED; -#endif } int UtilTransactions::clearTable3(Ndb* pNdb, - int records, - int parallelism){ + int records, + int parallelism){ // Scan all records exclusive and delete // them one by one int retryAttempt = 0; const int retryMax = 10; int deletedRows = 0; int check; - NdbConnection *pTrans; NdbScanOperation *pOp; NdbError err; @@ -380,8 +103,8 @@ UtilTransactions::clearTable3(Ndb* pNdb, } goto failed; } - - pOp = pTrans->getNdbScanOperation(tab.getName()); + + pOp = getScanOperation(pTrans); if (pOp == NULL) { err = pTrans->getNdbError(); if(err.status == NdbError::TemporaryError){ @@ -468,7 +191,6 @@ UtilTransactions::copyTableData(Ndb* pNdb, int insertedRows = 0; int parallelism = 240; int check; - NdbConnection *pTrans; NdbScanOperation *pOp; NDBT_ResultRow row(tab); @@ -628,7 +350,6 @@ UtilTransactions::scanReadRecords(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check; - NdbConnection *pTrans; NdbScanOperation *pOp; NDBT_ResultRow row(tab); @@ -654,7 +375,7 @@ UtilTransactions::scanReadRecords(Ndb* pNdb, return NDBT_FAILED; } - pOp = pTrans->getNdbScanOperation(tab.getName()); + pOp = getScanOperation(pTrans); if (pOp == NULL) { const NdbError err = pNdb->getNdbError(); pNdb->closeTransaction(pTrans); @@ -768,6 +489,7 @@ UtilTransactions::selectCount(Ndb* pNdb, if(!pTrans) pTrans = pNdb->startTransaction(); + while (true){ if (retryAttempt >= retryMax){ @@ -775,7 +497,7 @@ UtilTransactions::selectCount(Ndb* pNdb, << " times, failing!" << endl; return NDBT_FAILED; } - pOp = pTrans->getNdbScanOperation(tab.getName()); + pOp = getScanOperation(pTrans); if (pOp == NULL) { ERR(pTrans->getNdbError()); pNdb->closeTransaction(pTrans); @@ -906,7 +628,6 @@ UtilTransactions::scanAndCompareUniqueIndex(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check; - NdbConnection *pTrans; NdbScanOperation *pOp; NDBT_ResultRow row(tab); @@ -1295,7 +1016,6 @@ UtilTransactions::verifyOrderedIndex(Ndb* pNdb, int retryAttempt = 0; const int retryMax = 100; int check; - NdbConnection *pTrans; NdbScanOperation *pOp; NdbIndexScanOperation * iop = 0; NdbResultSet* cursor= 0; @@ -1521,3 +1241,60 @@ UtilTransactions::equal(const NdbDictionary::Table* pTable, } return 0; } + +NdbScanOperation* +UtilTransactions::getScanOperation(NdbConnection* pTrans) +{ + return (NdbScanOperation*) + getOperation(pTrans, NdbOperation::OpenScanRequest); +} + +NdbOperation* +UtilTransactions::getOperation(NdbConnection* pTrans, + NdbOperation::OperationType type) +{ + switch(type){ + case NdbOperation::ReadRequest: + case NdbOperation::ReadExclusive: + if(idx) + { + switch(idx->getType()){ + case NdbDictionary::Index::UniqueHashIndex: + return pTrans->getNdbIndexOperation(idx->getName(), tab.getName()); + case NdbDictionary::Index::OrderedIndex: + return pTrans->getNdbIndexScanOperation(idx->getName(), tab.getName()); + } + } + case NdbOperation::InsertRequest: + case NdbOperation::WriteRequest: + return pTrans->getNdbOperation(tab.getName()); + case NdbOperation::UpdateRequest: + case NdbOperation::DeleteRequest: + if(idx) + { + switch(idx->getType()){ + case NdbDictionary::Index::UniqueHashIndex: + return pTrans->getNdbIndexOperation(idx->getName(), tab.getName()); + } + } + return pTrans->getNdbOperation(tab.getName()); + case NdbOperation::OpenScanRequest: + if(idx) + { + switch(idx->getType()){ + case NdbDictionary::Index::OrderedIndex: + return pTrans->getNdbIndexScanOperation(idx->getName(), tab.getName()); + } + } + return pTrans->getNdbScanOperation(tab.getName()); + case NdbOperation::OpenRangeScanRequest: + if(idx) + { + switch(idx->getType()){ + case NdbDictionary::Index::OrderedIndex: + return pTrans->getNdbIndexScanOperation(idx->getName(), tab.getName()); + } + } + return 0; + } +} From 9c81f43b411fde28e6e577889f9710d291115848 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Dec 2004 09:11:33 +0100 Subject: [PATCH 50/52] wl2240 - ndb partitioning, bug fixes ndb/src/common/debugger/signaldata/ScanTab.cpp: Fix printout of SCAN_TABREQ ndb/src/kernel/blocks/dbtc/DbtcMain.cpp: Set correct status on scanfrag record ndb/src/ndbapi/NdbScanOperation.cpp: Fix setting of distribution key wrt scan ndb/tools/select_count.cpp: Fix select_count --- ndb/src/common/debugger/signaldata/ScanTab.cpp | 10 +++++++--- ndb/src/kernel/blocks/dbtc/DbtcMain.cpp | 2 ++ ndb/src/ndbapi/NdbScanOperation.cpp | 3 ++- ndb/tools/select_count.cpp | 9 +++++---- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/ndb/src/common/debugger/signaldata/ScanTab.cpp b/ndb/src/common/debugger/signaldata/ScanTab.cpp index 0755ee0a856..941d6a1514e 100644 --- a/ndb/src/common/debugger/signaldata/ScanTab.cpp +++ b/ndb/src/common/debugger/signaldata/ScanTab.cpp @@ -30,20 +30,24 @@ printSCANTABREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiv fprintf(output, " apiConnectPtr: H\'%.8x", sig->apiConnectPtr); fprintf(output, " requestInfo: H\'%.8x:\n", requestInfo); - fprintf(output, " Parallellism: %u, Batch: %u LockMode: %u, Keyinfo: %u Holdlock: %u, RangeScan: %u ReadCommitted: %u\n", + fprintf(output, " Parallellism: %u, Batch: %u LockMode: %u, Keyinfo: %u Holdlock: %u, RangeScan: %u ReadCommitted: %u\n DistributionKeyFlag: %u", sig->getParallelism(requestInfo), sig->getScanBatch(requestInfo), sig->getLockMode(requestInfo), sig->getKeyinfoFlag(requestInfo), sig->getHoldLockFlag(requestInfo), sig->getRangeScanFlag(requestInfo), - sig->getReadCommittedFlag(requestInfo)); + sig->getReadCommittedFlag(requestInfo), + sig->getDistributionKeyFlag(requestInfo)); + + if(sig->getDistributionKeyFlag(requestInfo)) + fprintf(output, " DKey: %u", sig->distributionKey); Uint32 keyLen = (sig->attrLenKeyLen >> 16); Uint32 attrLen = (sig->attrLenKeyLen & 0xFFFF); fprintf(output, " attrLen: %d, keyLen: %d tableId: %d, tableSchemaVer: %d\n", attrLen, keyLen, sig->tableId, sig->tableSchemaVersion); - + fprintf(output, " transId(1, 2): (H\'%.8x, H\'%.8x) storedProcId: H\'%.8x\n", sig->transId1, sig->transId2, sig->storedProcId); fprintf(output, " batch_byte_size: %d, first_batch_size: %d\n", diff --git a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp index 1ce6ffa4817..58233529495 100644 --- a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp +++ b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp @@ -8952,6 +8952,7 @@ void Dbtc::execDI_FCOUNTCONF(Signal* signal) { ptr.p->m_ops = 0; ptr.p->m_totalLen = 0; + ptr.p->m_scan_frag_conf_status = 1; ptr.p->scanFragState = ScanFragRec::QUEUED_FOR_DELIVERY; ptr.p->stopFragTimer(); @@ -8959,6 +8960,7 @@ void Dbtc::execDI_FCOUNTCONF(Signal* signal) list.next(ptr); list.remove(tmp); queued.add(tmp); + scanptr.p->m_queued_count++; } }//Dbtc::execDI_FCOUNTCONF() diff --git a/ndb/src/ndbapi/NdbScanOperation.cpp b/ndb/src/ndbapi/NdbScanOperation.cpp index 7766068fc3e..d254c696dd0 100644 --- a/ndb/src/ndbapi/NdbScanOperation.cpp +++ b/ndb/src/ndbapi/NdbScanOperation.cpp @@ -777,8 +777,9 @@ NdbScanOperation::doSendScan(int aProcessorId) Uint32 tmp = req->requestInfo; ScanTabReq::setDistributionKeyFlag(tmp, theDistrKeyIndicator_); req->distributionKey = theDistributionKey; + req->requestInfo = tmp; tSignal->setLength(ScanTabReq::StaticLength + theDistrKeyIndicator_); - + TransporterFacade *tp = TransporterFacade::instance(); LinearSectionPtr ptr[3]; ptr[0].p = m_prepared_receivers; diff --git a/ndb/tools/select_count.cpp b/ndb/tools/select_count.cpp index c3491f842d8..9fd3dd3f3b9 100644 --- a/ndb/tools/select_count.cpp +++ b/ndb/tools/select_count.cpp @@ -30,7 +30,7 @@ static int select_count(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism, int* count_rows, - UtilTransactions::ScanLock lock); + NdbOperation::LockMode lock); static const char* opt_connect_str= 0; static const char* _dbname = "TEST_DB"; @@ -116,7 +116,7 @@ int main(int argc, char** argv){ int rows = 0; if (select_count(&MyNdb, pTab, _parallelism, &rows, - (UtilTransactions::ScanLock)_lock) != 0){ + (NdbOperation::LockMode)_lock) != 0){ return NDBT_ProgramExit(NDBT_FAILED); } @@ -129,7 +129,7 @@ int select_count(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism, int* count_rows, - UtilTransactions::ScanLock lock){ + NdbOperation::LockMode lock){ int retryAttempt = 0; const int retryMax = 100; @@ -180,8 +180,9 @@ select_count(Ndb* pNdb, const NdbDictionary::Table* pTab, } Uint64 tmp; + Uint32 row_size; pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&tmp); - + pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&row_size); check = pTrans->execute(NoCommit); if( check == -1 ) { ERR(pTrans->getNdbError()); From 14987c6b61a175f004f57f82a0a857e5795c0105 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Dec 2004 09:14:19 +0100 Subject: [PATCH 51/52] don't compile instance-manager by default, as it never compiles BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted --- BitKeeper/etc/logging_ok | 1 + server-tools/instance-manager/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 5140e43ee41..4e9712f5ac6 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -155,6 +155,7 @@ mysqldev@mysql.com mysqldev@o2k.irixworld.net ndbdev@eel.hemma.oreland.se ndbdev@ndbmaster.mysql.com +ndbdev@shark. nick@mysql.com nick@nick.leippe.com papa@gbichot.local diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 050f9b9bfd2..d1ecdbbd509 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -59,7 +59,7 @@ client_settings.h: Makefile rm -f $(srcdir)/client_settings.h @LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h -bin_PROGRAMS= mysqlmanager +EXTRA_PROGRAMS= mysqlmanager mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ manager.h manager.cc log.h log.cc \ From 7a7ac8f63ad6e1ba7f74ac2c85bcb4b8df3b16ee Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Dec 2004 12:17:29 +0100 Subject: [PATCH 52/52] wl2240 - ndb - scan only 1 fragment if possible ndb/src/common/debugger/signaldata/ScanTab.cpp: Fix printout ndb/src/ndbapi/NdbDictionaryImpl.cpp: Set distribution key for ordered indexes ndb/src/ndbapi/NdbDictionaryImpl.hpp: Allow create_index_object to modify index_table ndb/tools/restore/consumer_restore.cpp: Allow create_index_object to modify index_table --- .../common/debugger/signaldata/ScanTab.cpp | 2 +- ndb/src/ndbapi/NdbDictionaryImpl.cpp | 28 ++++++++++++++++--- ndb/src/ndbapi/NdbDictionaryImpl.hpp | 4 +-- ndb/tools/restore/consumer_restore.cpp | 2 +- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/ndb/src/common/debugger/signaldata/ScanTab.cpp b/ndb/src/common/debugger/signaldata/ScanTab.cpp index 941d6a1514e..247ddbfe8e2 100644 --- a/ndb/src/common/debugger/signaldata/ScanTab.cpp +++ b/ndb/src/common/debugger/signaldata/ScanTab.cpp @@ -41,7 +41,7 @@ printSCANTABREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiv sig->getDistributionKeyFlag(requestInfo)); if(sig->getDistributionKeyFlag(requestInfo)) - fprintf(output, " DKey: %u", sig->distributionKey); + fprintf(output, " DKey: %x", sig->distributionKey); Uint32 keyLen = (sig->attrLenKeyLen >> 16); Uint32 attrLen = (sig->attrLenKeyLen & 0xFFFF); diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/ndb/src/ndbapi/NdbDictionaryImpl.cpp index 6c052dac9f0..a283589055e 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -1930,7 +1930,7 @@ NdbDictionaryImpl::getIndexImpl(const char * externalName, int NdbDictInterface::create_index_obj_from_table(NdbIndexImpl** dst, - const NdbTableImpl* tab, + NdbTableImpl* tab, const NdbTableImpl* prim){ NdbIndexImpl *idx = new NdbIndexImpl(); idx->m_version = tab->m_version; @@ -1938,23 +1938,43 @@ NdbDictInterface::create_index_obj_from_table(NdbIndexImpl** dst, idx->m_indexId = tab->m_tableId; idx->m_externalName.assign(tab->getName()); idx->m_tableName.assign(prim->m_externalName); - idx->m_type = tab->m_indexType; + NdbDictionary::Index::Type type = idx->m_type = tab->m_indexType; idx->m_logging = tab->m_logging; // skip last attribute (NDB$PK or NDB$TNODE) + + Uint32 distKeys = 0; for(unsigned i = 0; i+1m_columns.size(); i++){ + NdbColumnImpl* org = tab->m_columns[i]; + NdbColumnImpl* col = new NdbColumnImpl; // Copy column definition - *col = *tab->m_columns[i]; + *col = * org; idx->m_columns.push_back(col); + /** * reverse map */ - int key_id = prim->getColumn(col->getName())->getColumnNo(); + const NdbColumnImpl* primCol = prim->getColumn(col->getName()); + int key_id = primCol->getColumnNo(); int fill = -1; idx->m_key_ids.fill(key_id, fill); idx->m_key_ids[key_id] = i; col->m_keyInfoPos = key_id; + + /** + * Fix distribution key stuff for ordered indexes + */ + if(type == NdbDictionary::Index::OrderedIndex) + { + if(primCol->m_distributionKey || + (prim->m_noOfDistributionKeys == 0 && primCol->getPrimaryKey())) + { + distKeys++; + org->m_distributionKey = 1; + } + } } + tab->m_noOfDistributionKeys = distKeys; * dst = idx; return 0; diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/ndb/src/ndbapi/NdbDictionaryImpl.hpp index 38477e95d99..3ea20e2af16 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.hpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.hpp @@ -303,8 +303,8 @@ public: bool fullyQualifiedNames); static int create_index_obj_from_table(NdbIndexImpl ** dst, - const NdbTableImpl*, - const NdbTableImpl*); + NdbTableImpl* index_table, + const NdbTableImpl* primary_table); NdbError & m_error; private: diff --git a/ndb/tools/restore/consumer_restore.cpp b/ndb/tools/restore/consumer_restore.cpp index e2c55e5a0b1..ce1beb4aaa7 100644 --- a/ndb/tools/restore/consumer_restore.cpp +++ b/ndb/tools/restore/consumer_restore.cpp @@ -209,7 +209,7 @@ BackupRestore::endOfTables(){ NdbDictionary::Dictionary* dict = m_ndb->getDictionary(); for(size_t i = 0; i split;